1use crate::{cbor, cbor::ToCbor, pallas};
6use anyhow::anyhow;
7use num::ToPrimitive;
8use num_bigint::BigInt;
9use std::{borrow::Cow, fmt};
10
11#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
24#[repr(transparent)]
25pub struct PlutusData<'a>(Cow<'a, pallas::PlutusData>);
26
27impl<'a> fmt::Display for PlutusData<'a> {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 write!(f, "CBOR({})", hex::encode(self.to_cbor()))
30 }
31}
32
33impl<'a> PlutusData<'a> {
36 pub fn integer(i: impl Into<BigInt>) -> Self {
59 let i: BigInt = i.into();
60
61 Self(Cow::Owned(match i.to_i128().map(pallas::Int::try_from) {
62 Some(Ok(i)) => pallas::PlutusData::BigInt(pallas::BigInt::Int(i)),
63 _ => {
64 let (sign, bytes) = i.to_bytes_be();
65 match sign {
66 num_bigint::Sign::Minus => {
67 pallas::PlutusData::BigInt(pallas::BigInt::BigNInt(bytes.into()))
68 }
69 _ => pallas::PlutusData::BigInt(pallas::BigInt::BigUInt(bytes.into())),
70 }
71 }
72 }))
73 }
74
75 pub fn bytes(bytes: impl AsRef<[u8]>) -> Self {
97 Self(Cow::Owned(pallas::PlutusData::BoundedBytes(
98 pallas::BoundedBytes::from(bytes.as_ref().to_vec()),
99 )))
100 }
101
102 pub fn list<T: Into<Self>>(elems: impl IntoIterator<Item = T>) -> Self {
128 let elems = elems
129 .into_iter()
130 .map(Into::<Self>::into)
131 .map(pallas::PlutusData::from)
132 .collect::<Vec<_>>();
133
134 Self(Cow::Owned(pallas::PlutusData::Array(if elems.is_empty() {
135 pallas::MaybeIndefArray::Def(elems)
136 } else {
137 pallas::MaybeIndefArray::Indef(elems)
138 })))
139 }
140
141 pub fn map<K: Into<Self>, V: Into<Self>>(kvs: impl IntoIterator<Item = (K, V)>) -> Self {
164 let kvs = kvs
165 .into_iter()
166 .map(|(k, v)| {
167 (
168 pallas::PlutusData::from(Into::<Self>::into(k)),
169 pallas::PlutusData::from(Into::<Self>::into(v)),
170 )
171 })
172 .collect::<Vec<_>>();
173
174 Self(Cow::Owned(pallas::PlutusData::Map(
175 pallas::KeyValuePairs::from(kvs),
176 )))
177 }
178
179 pub fn constr<T: Into<Self>>(ix: u64, fields: impl IntoIterator<Item = T>) -> Self {
207 let fields = fields
208 .into_iter()
209 .map(Into::<Self>::into)
210 .map(pallas::PlutusData::from)
211 .collect::<Vec<_>>();
212
213 let fields = if fields.is_empty() {
214 pallas::MaybeIndefArray::Def(fields)
215 } else {
216 pallas::MaybeIndefArray::Indef(fields)
217 };
218
219 Self(Cow::Owned(if ix < 7 {
221 pallas::PlutusData::Constr(pallas::Constr {
222 tag: 121 + ix,
223 any_constructor: None,
224 fields,
225 })
226 } else if ix < 128 {
227 pallas::PlutusData::Constr(pallas::Constr {
228 tag: 1280 + ix - 7,
229 any_constructor: None,
230 fields,
231 })
232 } else {
233 pallas::PlutusData::Constr(pallas::Constr {
234 tag: 102,
235 any_constructor: Some(ix),
236 fields,
237 })
238 }))
239 }
240}
241
242#[macro_export]
245macro_rules! constr {
274 ($ix:expr $(,)?) => {
275 $crate::PlutusData::constr::<$crate::PlutusData>($ix, [])
276 };
277
278 ($ix:expr $(, $field:expr)+ $(,)?) => {
280 $crate::PlutusData::constr($ix, [ $( ::core::convert::Into::<$crate::PlutusData>::into($field) ),* ])
281 };
282}
283
284impl<'a> PlutusData<'a> {
287 pub fn as_integer<T>(&'a self) -> Option<T>
288 where
289 T: TryFrom<BigInt>,
290 {
291 match self.0.as_ref() {
292 pallas::PlutusData::BigInt(big_int) => from_pallas_bigint(big_int).try_into().ok(),
293 _ => None,
294 }
295 }
296
297 pub fn as_bytes(&'a self) -> Option<&'a [u8]> {
298 match self.0.as_ref() {
299 pallas::PlutusData::BoundedBytes(bounded_bytes) => Some(bounded_bytes.as_slice()),
300 _ => None,
301 }
302 }
303
304 pub fn as_list(&'a self) -> Option<impl Iterator<Item = Self>> {
305 match self.0.as_ref() {
306 pallas::PlutusData::Array(array) => Some(from_pallas_array(array)),
307 _ => None,
308 }
309 }
310
311 pub fn as_map(&'a self) -> Option<impl Iterator<Item = (Self, Self)>> {
312 match self.0.as_ref() {
313 pallas::PlutusData::Map(map) => Some(from_pallas_map(map)),
314 _ => None,
315 }
316 }
317
318 pub fn as_constr(&'a self) -> Option<(u64, impl Iterator<Item = Self>)> {
319 match self.0.as_ref() {
320 pallas::PlutusData::Constr(constr) => Some(from_pallas_constr(constr)),
321 _ => None,
322 }
323 }
324}
325
326fn from_pallas_bigint(big_int: &pallas::BigInt) -> BigInt {
329 match big_int {
330 pallas::BigInt::Int(int) => BigInt::from(i128::from(*int)),
331 pallas::BigInt::BigUInt(bounded_bytes) => {
332 BigInt::from_bytes_be(num_bigint::Sign::Plus, bounded_bytes)
333 }
334 pallas::BigInt::BigNInt(bounded_bytes) => {
335 BigInt::from_bytes_be(num_bigint::Sign::Minus, bounded_bytes)
336 }
337 }
338}
339
340fn from_pallas_array<'a>(
341 array: &'a pallas::MaybeIndefArray<pallas::PlutusData>,
342) -> impl Iterator<Item = PlutusData<'a>> {
343 match array {
344 pallas::MaybeIndefArray::Def(elems) => elems,
345 pallas::MaybeIndefArray::Indef(elems) => elems,
346 }
347 .iter()
348 .map(|x| PlutusData(Cow::Borrowed(x)))
349}
350
351fn from_pallas_map<'a>(
352 map: &'a pallas::KeyValuePairs<pallas::PlutusData, pallas::PlutusData>,
353) -> impl Iterator<Item = (PlutusData<'a>, PlutusData<'a>)> {
354 match map {
355 pallas::KeyValuePairs::Def(items) => items,
356 pallas::KeyValuePairs::Indef(items) => items,
357 }
358 .iter()
359 .map(|(k, v)| (PlutusData(Cow::Borrowed(k)), PlutusData(Cow::Borrowed(v))))
360}
361
362fn from_pallas_constr<'a>(
363 constr: &'a pallas::Constr<pallas::PlutusData>,
364) -> (u64, impl Iterator<Item = PlutusData<'a>>) {
365 let fields = match &constr.fields {
366 pallas::MaybeIndefArray::Def(fields) => fields,
367 pallas::MaybeIndefArray::Indef(fields) => fields,
368 }
369 .iter()
370 .map(|x| PlutusData(Cow::Borrowed(x)));
371
372 let ix = if constr.tag == 102 {
373 constr
374 .any_constructor
375 .expect("'any_constructor' was 'None' but 'tag' was set to 102? This is absurd.")
376 } else if constr.tag >= 1280 {
377 constr.tag - 1280 + 7
378 } else {
379 constr.tag - 121
380 };
381
382 (ix, fields)
383}
384
385impl From<pallas::PlutusData> for PlutusData<'static> {
388 fn from(data: pallas::PlutusData) -> Self {
389 Self(Cow::Owned(data))
390 }
391}
392
393#[macro_export]
394macro_rules! impl_from_int {
395 ($($t:ty),+ $(,)?) => {
396 $(
397 impl From<$t> for PlutusData<'static> {
398 #[inline]
399 fn from(int: $t) -> Self {
400 Self::integer(int)
401 }
402 }
403 )+
404 };
405}
406
407impl_from_int!(
408 u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, BigInt
409);
410
411impl From<&str> for PlutusData<'static> {
412 fn from(bytes: &str) -> Self {
413 Self::bytes(bytes.as_bytes())
414 }
415}
416
417impl From<String> for PlutusData<'static> {
418 fn from(bytes: String) -> Self {
419 Self::bytes(bytes.as_bytes())
420 }
421}
422
423impl From<Vec<u8>> for PlutusData<'static> {
424 fn from(bytes: Vec<u8>) -> Self {
425 Self::bytes(bytes)
426 }
427}
428
429impl From<&[u8]> for PlutusData<'static> {
430 fn from(bytes: &[u8]) -> Self {
431 Self::bytes(bytes)
432 }
433}
434
435impl<const T: usize> From<[u8; T]> for PlutusData<'static> {
436 fn from(bytes: [u8; T]) -> Self {
437 Self::bytes(bytes)
438 }
439}
440
441impl<const T: usize> From<&[u8; T]> for PlutusData<'static> {
442 fn from(bytes: &[u8; T]) -> Self {
443 Self::bytes(bytes)
444 }
445}
446
447impl From<PlutusData<'_>> for pallas::PlutusData {
450 fn from(data: PlutusData<'_>) -> Self {
451 data.0.into_owned()
452 }
453}
454
455#[macro_export]
456macro_rules! impl_try_into_int {
457 ($($t:ty),+ $(,)?) => {
458 $(
459 impl<'a> TryFrom<&'a PlutusData<'a>> for $t {
460 type Error = anyhow::Error;
461
462 #[inline]
463 fn try_from(data: &'a PlutusData<'a>) -> anyhow::Result<Self> {
464 data.as_integer().ok_or(anyhow!("expected an integer"))
465 }
466 }
467 )+
468 };
469}
470
471impl_try_into_int!(
472 u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, BigInt,
473);
474
475impl<'a> TryFrom<&'a PlutusData<'a>> for &'a [u8] {
476 type Error = anyhow::Error;
477
478 fn try_from(data: &'a PlutusData<'a>) -> anyhow::Result<Self> {
479 data.as_bytes().ok_or(anyhow!("expected bytes"))
480 }
481}
482
483impl<'a, const T: usize> TryFrom<&'a PlutusData<'a>> for &'a [u8; T] {
484 type Error = anyhow::Error;
485
486 fn try_from(data: &'a PlutusData<'a>) -> anyhow::Result<Self> {
487 data.as_bytes()
488 .ok_or(anyhow!("expected bytes"))?
489 .try_into()
490 .map_err(|_| anyhow!("bytes length mismatch"))
491 }
492}
493
494impl<'a> TryFrom<&'a PlutusData<'a>> for Vec<PlutusData<'a>> {
495 type Error = anyhow::Error;
496
497 fn try_from(data: &'a PlutusData<'a>) -> anyhow::Result<Self> {
498 Ok(data.as_list().ok_or(anyhow!("expected a list"))?.collect())
499 }
500}
501
502impl<'a, const T: usize> TryFrom<&'a PlutusData<'a>> for [PlutusData<'a>; T] {
503 type Error = anyhow::Error;
504
505 fn try_from(data: &'a PlutusData<'a>) -> anyhow::Result<Self> {
506 let list: Vec<PlutusData<'_>> = data.try_into()?;
507 <[PlutusData<'_>; T]>::try_from(list).map_err(|_| anyhow!("tuple length mismatch"))
508 }
509}
510
511impl<'a> TryFrom<&'a PlutusData<'a>> for Vec<(PlutusData<'a>, PlutusData<'a>)> {
512 type Error = anyhow::Error;
513
514 fn try_from(data: &'a PlutusData<'a>) -> anyhow::Result<Self> {
515 Ok(data.as_map().ok_or(anyhow!("expected a map"))?.collect())
516 }
517}
518
519impl<'a> TryFrom<&'a PlutusData<'a>> for (u64, Vec<PlutusData<'a>>) {
520 type Error = anyhow::Error;
521
522 fn try_from(data: &'a PlutusData<'a>) -> anyhow::Result<Self> {
523 let (ix, fields) = data.as_constr().ok_or(anyhow!("expected a constr"))?;
524 Ok((ix, fields.collect()))
525 }
526}
527
528impl<'a, const T: usize> TryFrom<&'a PlutusData<'a>> for (u64, [PlutusData<'a>; T]) {
529 type Error = anyhow::Error;
530
531 fn try_from(data: &'a PlutusData<'a>) -> anyhow::Result<Self> {
532 let (ix, fields) = data.as_constr().ok_or(anyhow!("expected a constr"))?;
533 Ok((
534 ix,
535 <[PlutusData<'a>; T]>::try_from(fields.collect::<Vec<_>>()).map_err(|vec| {
536 anyhow!(
537 "expected a constr with {T} field(s), but found {} field(s)",
538 vec.len()
539 )
540 })?,
541 ))
542 }
543}
544
545impl<C> cbor::Encode<C> for PlutusData<'_> {
548 fn encode<W: cbor::encode::write::Write>(
549 &self,
550 e: &mut cbor::Encoder<W>,
551 ctx: &mut C,
552 ) -> Result<(), cbor::encode::Error<W::Error>> {
553 e.encode_with(self.0.as_ref(), ctx)?;
554 Ok(())
555 }
556}
557
558impl<'d, C> cbor::Decode<'d, C> for PlutusData<'static> {
559 fn decode(d: &mut cbor::Decoder<'d>, ctx: &mut C) -> Result<Self, cbor::decode::Error> {
560 Ok(Self(Cow::Owned(d.decode_with(ctx)?)))
561 }
562}