1use std::str::FromStr;
4use std::string::String;
5use std::{
6 collections::BTreeMap,
7 iter::Sum,
8 ops::{Add, Mul, Neg, Not, Sub},
9};
10use std::{fmt, ops};
11
12use anyhow::anyhow;
13use cardano_serialization_lib as csl;
14#[cfg(feature = "lbf")]
15use lbr_prelude::json::{Error, Json, JsonType};
16use nom::combinator::{map, opt};
17use nom::{
18 branch::alt,
19 character::complete::{char, space0},
20 combinator::{all_consuming, eof, map_res, success},
21 error::{context, VerboseError},
22 multi::separated_list0,
23 sequence::preceded,
24 sequence::tuple,
25 Finish, IResult,
26};
27use num_bigint::BigInt;
28use num_traits::Zero;
29#[cfg(feature = "serde")]
30use serde::{Deserialize, Serialize};
31#[cfg(feature = "lbf")]
32use serde_json;
33
34use crate as plutus_ledger_api;
35use crate::aux::{big_int, singleton, union_b_tree_maps_with, union_btree_maps_with};
36use crate::csl::csl_to_pla::FromCSL;
37use crate::csl::pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL};
38use crate::error::ConversionError;
39use crate::plutus_data::{IsPlutusData, PlutusData, PlutusDataError};
40use crate::v1::crypto::LedgerBytes;
41use crate::v1::script::{MintingPolicyHash, ScriptHash};
42
43use super::crypto::ledger_bytes;
44
45#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
52#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
53pub enum CurrencySymbol {
54 Ada,
55 NativeToken(MintingPolicyHash),
56}
57
58impl CurrencySymbol {
59 pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, ConversionError> {
60 if bytes.is_empty() {
61 Ok(CurrencySymbol::Ada)
62 } else {
63 Ok(CurrencySymbol::NativeToken(MintingPolicyHash::from_bytes(
64 bytes,
65 )?))
66 }
67 }
68
69 pub fn is_ada(&self) -> bool {
70 match self {
71 CurrencySymbol::Ada => true,
72 CurrencySymbol::NativeToken(_) => false,
73 }
74 }
75}
76
77impl fmt::Display for CurrencySymbol {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 match self {
82 CurrencySymbol::Ada => {
83 if f.alternate() {
84 write!(f, "lovelace")
85 } else {
86 write!(f, "")
87 }
88 }
89 CurrencySymbol::NativeToken(symbol) => write!(f, "{}", symbol.0 .0),
90 }
91 }
92}
93
94impl FromStr for CurrencySymbol {
95 type Err = ConversionError;
96
97 fn from_str(s: &str) -> Result<Self, Self::Err> {
98 all_consuming(currency_symbol)(s)
99 .finish()
100 .map_err(|err| {
101 ConversionError::ParseError(anyhow!(
102 "Error while parsing CurrencySymbol '{}': {}",
103 s,
104 err
105 ))
106 })
107 .map(|(_, cs)| cs)
108 }
109}
110
111impl IsPlutusData for CurrencySymbol {
112 fn to_plutus_data(&self) -> PlutusData {
113 match self {
114 CurrencySymbol::NativeToken(policy_hash) => policy_hash.to_plutus_data(),
115 CurrencySymbol::Ada => PlutusData::Bytes(Vec::with_capacity(0)),
116 }
117 }
118
119 fn from_plutus_data(data: &PlutusData) -> Result<Self, PlutusDataError> {
120 IsPlutusData::from_plutus_data(data).map(|bytes: LedgerBytes| {
121 if bytes.0.is_empty() {
122 CurrencySymbol::Ada
123 } else {
124 CurrencySymbol::NativeToken(MintingPolicyHash(ScriptHash(bytes)))
125 }
126 })
127 }
128}
129
130#[cfg(feature = "lbf")]
131impl Json for CurrencySymbol {
132 fn to_json(&self) -> serde_json::Value {
133 match self {
134 CurrencySymbol::Ada => serde_json::Value::String(String::new()),
135 CurrencySymbol::NativeToken(policy_hash) => policy_hash.to_json(),
136 }
137 }
138
139 fn from_json(value: &serde_json::Value) -> Result<Self, Error> {
140 match value.clone() {
141 serde_json::Value::String(str) => {
142 if str.is_empty() {
143 Ok(CurrencySymbol::Ada)
144 } else {
145 Ok(CurrencySymbol::NativeToken(Json::from_json(value)?))
146 }
147 }
148 _ => Err(Error::UnexpectedJsonType {
149 wanted: JsonType::String,
150 got: JsonType::from(value),
151 parser: "Plutus.V1.CurrencySymbol".to_owned(),
152 }),
153 }
154 }
155}
156
157pub(crate) fn currency_symbol(input: &str) -> IResult<&str, CurrencySymbol, VerboseError<&str>> {
160 context(
161 "currency symbol",
162 map_res(ledger_bytes, |LedgerBytes(bytes)| {
163 CurrencySymbol::from_bytes(bytes)
164 }),
165 )(input)
166}
167
168#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
174#[cfg_attr(feature = "lbf", derive(Json))]
175pub struct Value(pub BTreeMap<CurrencySymbol, BTreeMap<TokenName, BigInt>>);
176
177#[cfg(feature = "serde")]
178mod value_serde {
179 use std::collections::BTreeMap;
180
181 use num_bigint::BigInt;
182 use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize};
183
184 use super::{CurrencySymbol, TokenName, Value};
185
186 struct Assets(BTreeMap<TokenName, BigInt>);
187
188 impl Serialize for Value {
189 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
190 where
191 S: Serializer,
192 {
193 serializer.collect_seq(
194 self.0
195 .iter()
196 .map(|(cur_sym, assets)| (cur_sym, Assets(assets.to_owned()))),
197 )
198 }
199 }
200
201 impl<'de> Deserialize<'de> for Value {
202 fn deserialize<D>(deserializer: D) -> Result<Value, D::Error>
203 where
204 D: Deserializer<'de>,
205 {
206 let vec: Vec<(CurrencySymbol, Assets)> = Vec::deserialize(deserializer)?;
207
208 Ok(Value(
209 vec.into_iter().map(|(cs, assets)| (cs, assets.0)).collect(),
210 ))
211 }
212 }
213
214 impl Serialize for Assets {
215 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
216 where
217 S: Serializer,
218 {
219 serializer.collect_seq(self.0.iter())
220 }
221 }
222
223 impl<'de> Deserialize<'de> for Assets {
224 fn deserialize<D>(deserializer: D) -> Result<Assets, D::Error>
225 where
226 D: Deserializer<'de>,
227 {
228 let vec: Vec<(TokenName, BigInt)> = Vec::deserialize(deserializer)?;
229
230 Ok(Assets(vec.into_iter().collect()))
231 }
232 }
233}
234
235impl Value {
236 pub fn new() -> Self {
237 Value(BTreeMap::new())
238 }
239 pub fn ada_value(amount: &BigInt) -> Self {
241 Self::token_value(&CurrencySymbol::Ada, &TokenName::ada(), amount)
242 }
243
244 pub fn token_value(cs: &CurrencySymbol, tn: &TokenName, amount: &BigInt) -> Self {
246 Value(singleton((
247 cs.clone(),
248 singleton((tn.clone(), amount.clone())),
249 )))
250 }
251
252 pub fn get_token_amount(&self, cs: &CurrencySymbol, tn: &TokenName) -> BigInt {
254 self.0
255 .get(cs)
256 .and_then(|tn_map| tn_map.get(tn))
257 .map_or(BigInt::zero(), Clone::clone)
258 }
259
260 pub fn get_ada_amount(&self) -> BigInt {
262 self.get_token_amount(&CurrencySymbol::Ada, &TokenName::ada())
263 }
264
265 pub fn insert_token(&self, cs: &CurrencySymbol, tn: &TokenName, a: &BigInt) -> Self {
267 let mut result_map = self.0.clone();
268
269 result_map
270 .entry(cs.clone())
271 .and_modify(|tn_map| {
272 tn_map
273 .entry(tn.clone())
274 .and_modify(|old_a| {
275 old_a.clone_from(a);
276 })
277 .or_insert_with(|| a.clone());
278 })
279 .or_insert_with(|| singleton((tn.clone(), a.clone())));
280
281 Self(result_map)
282 }
283
284 pub fn is_empty(&self) -> bool {
286 self.0.is_empty()
287 }
288
289 pub fn normalize(self) -> Self {
291 self.filter(|_, _, a| a.is_zero().not())
292 }
293
294 pub fn is_subset(&self, b: &Value) -> bool {
295 (b - self)
296 .normalize()
297 .filter(|_, _, amount| amount < &BigInt::from(0u32))
299 .is_empty()
300 }
301
302 pub fn is_pure_ada(&self) -> bool {
303 self.0.iter().all(|(cs, _)| cs == &CurrencySymbol::Ada)
304 }
305
306 pub fn map_amount<F>(self, mut f: F) -> Self
308 where
309 F: FnMut(&CurrencySymbol, &TokenName, &BigInt) -> BigInt,
310 {
311 self.filter_map_amount(|cs, tn, a| Some(f(cs, tn, a)))
312 }
313
314 pub fn filter<F>(self, mut f: F) -> Self
316 where
317 F: FnMut(&CurrencySymbol, &TokenName, &BigInt) -> bool,
318 {
319 self.filter_map_amount(|cs, tn, a| f(cs, tn, a).then(|| a.clone()))
320 }
321
322 pub fn filter_map_amount<F>(self, mut f: F) -> Self
328 where
329 F: FnMut(&CurrencySymbol, &TokenName, &BigInt) -> Option<BigInt>,
330 {
331 Value(
332 (self.0)
333 .into_iter()
334 .filter_map(|(cs, tn_map)| {
335 let filtered_tn_map = tn_map
336 .into_iter()
337 .filter_map(|(tn, a)| f(&cs, &tn, &a).map(|a| (tn, a)))
338 .collect::<BTreeMap<TokenName, BigInt>>();
339
340 if filtered_tn_map.is_empty() {
341 None
342 } else {
343 Some((cs.clone(), filtered_tn_map))
344 }
345 })
346 .collect(),
347 )
348 }
349
350 pub fn flatten(&self) -> Vec<(&CurrencySymbol, &TokenName, &BigInt)> {
353 self.0
354 .iter()
355 .flat_map(|(currency_symbol, assets)| {
356 assets
357 .iter()
358 .map(move |(token_name, amount)| (currency_symbol, token_name, amount))
359 })
360 .collect()
361 }
362
363 pub fn unflatten(list: &[(CurrencySymbol, TokenName, BigInt)]) -> Self {
364 list.iter()
365 .fold(Value::new(), |v, (cs, tn, am)| v.insert_token(cs, tn, am))
366 }
367}
368
369impl fmt::Display for Value {
370 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
371 let mut it = self
372 .0
373 .iter()
374 .flat_map(|(currency_symbol, assets)| {
375 assets
376 .iter()
377 .map(move |(token_name, amount)| (currency_symbol, token_name, amount))
378 })
379 .peekable();
380 while let Some((cur_sym, tn, amount)) = it.next() {
381 if cur_sym.is_ada() {
382 amount.fmt(f)?;
383 } else if tn.is_empty() {
384 amount.fmt(f)?;
385 " ".fmt(f)?;
386 cur_sym.fmt(f)?;
387 } else {
388 amount.fmt(f)?;
389 " ".fmt(f)?;
390 cur_sym.fmt(f)?;
391 ".".fmt(f)?;
392 tn.fmt(f)?;
393 }
394 if it.peek().is_some() {
395 "+".fmt(f)?;
396 }
397 }
398
399 Ok(())
400 }
401}
402
403pub(crate) fn flat_value(
408 input: &str,
409) -> IResult<&str, (CurrencySymbol, TokenName, BigInt), VerboseError<&str>> {
410 map(
411 tuple((big_int, opt(preceded(char(' '), asset_class)))),
412 |(amount, asset_class)| match asset_class {
413 None => (CurrencySymbol::Ada, TokenName::ada(), amount),
414 Some(AssetClass {
415 currency_symbol,
416 token_name,
417 }) => (currency_symbol, token_name, amount),
418 },
419 )(input)
420}
421
422pub(crate) fn value(input: &str) -> IResult<&str, Value, VerboseError<&str>> {
426 map(
427 separated_list0(tuple((space0, char('+'))), flat_value),
428 |flat_values| Value::unflatten(&flat_values),
429 )(input)
430}
431
432impl FromStr for Value {
433 type Err = ConversionError;
434
435 fn from_str(s: &str) -> Result<Self, Self::Err> {
436 all_consuming(value)(s)
437 .finish()
438 .map_err(|err| {
439 ConversionError::ParseError(anyhow!("Error while parsing Value '{}': {}", s, err))
440 })
441 .map(|(_, cs)| cs)
442 }
443}
444
445impl Zero for Value {
446 fn zero() -> Self {
447 Default::default()
448 }
449
450 fn is_zero(&self) -> bool {
451 self.is_empty()
452 }
453}
454
455impl_op!(+ |a: &Value, b: &Value| -> Value { a.clone() + b.clone() });
456impl_op!(+ |a: &Value, b: Value| -> Value { a.clone() + b });
457impl_op!(+ |a: Value, b: &Value| -> Value { a + b.clone() });
458
459impl Add<Value> for Value {
460 type Output = Value;
461
462 fn add(self, rhs: Value) -> Self::Output {
463 Value(union_btree_maps_with(
464 |lhs, rhs| union_btree_maps_with(|lhs, rhs| lhs + rhs, lhs, rhs),
465 self.0,
466 rhs.0,
467 ))
468 }
469}
470
471impl Neg for Value {
472 type Output = Value;
473
474 fn neg(self) -> Self::Output {
475 self.map_amount(|_, _, a| a.neg())
476 }
477}
478
479impl Neg for &Value {
480 type Output = Value;
481
482 fn neg(self) -> Self::Output {
483 self.clone().neg()
484 }
485}
486
487impl_op!(-|a: &Value, b: &Value| -> Value { a.clone() - b.clone() });
488impl_op!(-|a: &Value, b: Value| -> Value { a.clone() - b });
489impl_op!(-|a: Value, b: &Value| -> Value { a - b.clone() });
490
491impl Sub<Value> for Value {
492 type Output = Value;
493
494 fn sub(self, rhs: Value) -> Self::Output {
495 self.add(rhs.neg())
496 }
497}
498
499impl_op!(*|a: &BigInt, b: &Value| -> Value { b * a });
500impl_op_commutative!(*|a: Value, b: BigInt| -> Value { &a * &b });
501impl_op_commutative!(*|a: &Value, b: BigInt| -> Value { a * &b });
502impl_op_commutative!(*|a: Value, b: &BigInt| -> Value { &a * b });
503
504impl_op_commutative!(*|a: &Value, b: i8| -> Value { a * BigInt::from(b) });
505impl_op_commutative!(*|a: &Value, b: i16| -> Value { a * BigInt::from(b) });
506impl_op_commutative!(*|a: &Value, b: i32| -> Value { a * BigInt::from(b) });
507impl_op_commutative!(*|a: &Value, b: i64| -> Value { a * BigInt::from(b) });
508
509impl_op_commutative!(*|a: &Value, b: u8| -> Value { a * BigInt::from(b) });
510impl_op_commutative!(*|a: &Value, b: u16| -> Value { a * BigInt::from(b) });
511impl_op_commutative!(*|a: &Value, b: u32| -> Value { a * BigInt::from(b) });
512impl_op_commutative!(*|a: &Value, b: u64| -> Value { a * BigInt::from(b) });
513
514impl Mul<&BigInt> for &Value {
515 type Output = Value;
516
517 fn mul(self, rhs: &BigInt) -> Self::Output {
518 Value(
519 self.0
520 .iter()
521 .map(|(cs, tn_map)| {
522 (
523 cs.clone(),
524 tn_map.iter().map(|(tn, q)| (tn.clone(), q * rhs)).collect(),
525 )
526 })
527 .collect(),
528 )
529 }
530}
531
532impl Sum<Value> for Value {
533 fn sum<I: Iterator<Item = Value>>(iter: I) -> Self {
534 iter.fold(Zero::zero(), Add::add)
535 }
536}
537
538impl<'a> Sum<&'a Value> for Value {
539 fn sum<I: Iterator<Item = &'a Value>>(iter: I) -> Self {
540 iter.fold(Zero::zero(), Add::add)
541 }
542}
543
544impl IsPlutusData for Value {
545 fn to_plutus_data(&self) -> PlutusData {
546 self.0.to_plutus_data()
547 }
548
549 fn from_plutus_data(data: &PlutusData) -> Result<Self, PlutusDataError> {
550 IsPlutusData::from_plutus_data(data).map(Self)
551 }
552}
553
554impl FromCSL<csl::Assets> for BTreeMap<TokenName, BigInt> {
555 fn from_csl(value: &csl::Assets) -> Self {
556 let keys = value.keys();
557 (0..keys.len()).fold(BTreeMap::new(), |mut acc, idx| {
558 let asset_name = keys.get(idx);
559 if let Some(quantity) = value.get(&asset_name) {
560 acc.insert(
561 TokenName::from_csl(&asset_name),
562 BigInt::from_csl(&quantity),
563 );
564 }
565 acc
566 })
567 }
568}
569
570impl TryFromPLA<BTreeMap<TokenName, BigInt>> for csl::Assets {
571 fn try_from_pla(val: &BTreeMap<TokenName, BigInt>) -> Result<Self, TryFromPLAError> {
572 val.iter().try_fold(csl::Assets::new(), |mut acc, (k, v)| {
573 acc.insert(&k.try_to_csl()?, &v.try_to_csl()?);
574 Ok(acc)
575 })
576 }
577}
578
579impl FromCSL<csl::MultiAsset> for Value {
580 fn from_csl(value: &csl::MultiAsset) -> Self {
581 let keys = value.keys();
582 Value((0..keys.len()).fold(BTreeMap::new(), |mut acc, idx| {
583 let script_hash = keys.get(idx);
584 if let Some(assets) = value.get(&script_hash) {
585 let assets = BTreeMap::from_csl(&assets);
586 acc.insert(
587 CurrencySymbol::NativeToken(MintingPolicyHash::from_csl(&script_hash)),
588 assets,
589 );
590 }
591 acc
592 }))
593 }
594}
595
596impl FromCSL<csl::Value> for Value {
597 fn from_csl(value: &csl::Value) -> Self {
598 let lovelaces = BigInt::from_csl(&value.coin());
599 let mut pla_value = Value::ada_value(&lovelaces);
600 if let Some(multi_asset) = value.multiasset() {
601 pla_value = &pla_value + &Value::from_csl(&multi_asset)
602 }
603 pla_value
604 }
605}
606
607impl TryFromPLA<Value> for csl::Value {
608 fn try_from_pla(val: &Value) -> Result<Self, TryFromPLAError> {
609 let coin: csl::Coin = val
610 .0
611 .get(&CurrencySymbol::Ada)
612 .and_then(|m| m.get(&TokenName::ada()))
613 .map_or(Ok(csl::BigNum::zero()), TryToCSL::try_to_csl)?;
614
615 let m_ass = val
616 .0
617 .iter()
618 .filter_map(|(cs, tn_map)| match &cs {
619 CurrencySymbol::Ada => None,
620 CurrencySymbol::NativeToken(h) => Some((h, tn_map)),
621 })
622 .try_fold(csl::MultiAsset::new(), |mut acc, (cs, ass)| {
623 acc.insert(&cs.try_to_csl()?, &ass.try_to_csl()?);
624 Ok(acc)
625 })?;
626
627 let mut v = csl::Value::new(&coin);
628
629 v.set_multiasset(&m_ass);
630
631 Ok(v)
632 }
633}
634
635impl FromCSL<csl::MintAssets> for BTreeMap<TokenName, BigInt> {
636 fn from_csl(m_ass: &csl::MintAssets) -> Self {
637 let keys = m_ass.keys();
638 (0..keys.len())
639 .map(|idx| {
640 let key = keys.get(idx);
641 let value = m_ass.get(&key).unwrap();
642 (TokenName::from_csl(&key), BigInt::from_csl(&value))
643 })
644 .collect()
645 }
646}
647
648impl FromCSL<csl::MintsAssets> for BTreeMap<TokenName, BigInt> {
649 fn from_csl(value: &csl::MintsAssets) -> Self {
650 (0..value.len())
651 .map(|idx| value.get(idx).unwrap())
652 .fold(BTreeMap::new(), |acc, m| {
653 let ass = BTreeMap::from_csl(&m);
654 union_b_tree_maps_with(|l, r| l + r, [&acc, &ass])
655 })
656 }
657}
658
659impl TryFromPLA<BTreeMap<TokenName, BigInt>> for csl::MintAssets {
660 fn try_from_pla(val: &BTreeMap<TokenName, BigInt>) -> Result<Self, TryFromPLAError> {
661 val.iter()
662 .try_fold(csl::MintAssets::new(), |mut acc, (k, v)| {
663 acc.insert(&k.try_to_csl()?, &v.try_to_csl()?)
664 .map_err(TryFromPLAError::CSLJsError)?;
665 Ok(acc)
666 })
667 }
668}
669
670impl FromCSL<csl::Mint> for Value {
671 fn from_csl(mint: &csl::Mint) -> Self {
672 let keys = mint.keys();
673 Value(
674 (0..keys.len())
675 .map(|idx| {
676 let sh = keys.get(idx);
677 let ass = mint.get(&sh).unwrap_or(csl::MintsAssets::new());
678 (
679 CurrencySymbol::NativeToken(MintingPolicyHash::from_csl(&sh)),
680 BTreeMap::from_csl(&ass),
681 )
682 })
683 .collect::<BTreeMap<CurrencySymbol, BTreeMap<TokenName, BigInt>>>(),
684 )
685 }
686}
687
688#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
694#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
695#[cfg_attr(feature = "lbf", derive(Json))]
696pub struct TokenName(pub LedgerBytes);
697
698impl TokenName {
699 pub fn ada() -> TokenName {
701 TokenName(LedgerBytes(Vec::with_capacity(0)))
702 }
703
704 pub fn is_empty(&self) -> bool {
705 self.0 .0.is_empty()
706 }
707
708 pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, ConversionError> {
709 if bytes.len() <= 32 {
710 Ok(TokenName(LedgerBytes(bytes)))
711 } else {
712 Err(ConversionError::invalid_bytestring_length(
713 "TokenName",
714 32,
715 "less than or equal to",
716 &bytes,
717 ))
718 }
719 }
720
721 pub fn from_string(str: &str) -> Result<Self, ConversionError> {
723 TokenName::from_bytes(String::from(str).into_bytes())
724 }
725
726 pub fn try_into_string(self) -> Result<String, std::string::FromUtf8Error> {
728 String::from_utf8(self.0 .0)
729 }
730}
731
732impl FromStr for TokenName {
733 type Err = ConversionError;
734
735 fn from_str(s: &str) -> Result<Self, Self::Err> {
736 all_consuming(token_name)(s)
737 .finish()
738 .map_err(|err| {
739 ConversionError::ParseError(anyhow!(
740 "Error while parsing TokenName '{}': {}",
741 s,
742 err
743 ))
744 })
745 .map(|(_, cs)| cs)
746 }
747}
748
749pub(crate) fn token_name(input: &str) -> IResult<&str, TokenName, VerboseError<&str>> {
752 map_res(ledger_bytes, |LedgerBytes(bytes)| {
753 TokenName::from_bytes(bytes)
754 })(input)
755}
756
757impl IsPlutusData for TokenName {
758 fn to_plutus_data(&self) -> PlutusData {
759 self.0.to_plutus_data()
760 }
761
762 fn from_plutus_data(data: &PlutusData) -> Result<Self, PlutusDataError> {
763 IsPlutusData::from_plutus_data(data).map(Self)
764 }
765}
766
767impl fmt::Display for TokenName {
771 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
772 if f.alternate() {
773 let utf8_str = std::str::from_utf8(&self.0 .0);
774
775 match utf8_str {
776 Ok(str) => write!(f, "{}", str),
777 Err(_) => write!(f, "0x{}", self.0),
778 }
779 } else {
780 write!(f, "{}", self.0)
781 }
782 }
783}
784
785impl FromCSL<csl::AssetName> for TokenName {
786 fn from_csl(value: &csl::AssetName) -> Self {
787 TokenName(LedgerBytes(value.name()))
788 }
789}
790
791impl TryFromPLA<TokenName> for csl::AssetName {
792 fn try_from_pla(val: &TokenName) -> Result<Self, TryFromPLAError> {
793 csl::AssetName::new(val.0 .0.to_owned()).map_err(TryFromPLAError::CSLJsError)
794 }
795}
796
797#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
803#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
804#[cfg_attr(feature = "lbf", derive(Json))]
805pub struct AssetClass {
806 pub currency_symbol: CurrencySymbol,
807 pub token_name: TokenName,
808}
809
810impl fmt::Display for AssetClass {
814 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
815 if self.token_name.is_empty() {
816 self.currency_symbol.fmt(f)
817 } else {
818 self.currency_symbol.fmt(f)?;
819 ".".fmt(f)?;
820 self.token_name.fmt(f)
821 }
822 }
823}
824
825impl FromStr for AssetClass {
826 type Err = ConversionError;
827
828 fn from_str(s: &str) -> Result<Self, Self::Err> {
829 all_consuming(asset_class)(s)
830 .finish()
831 .map_err(|err| {
832 ConversionError::ParseError(anyhow!(
833 "Error while parsing AssetClass '{}': {}",
834 s,
835 err
836 ))
837 })
838 .map(|(_, cs)| cs)
839 }
840}
841
842pub(crate) fn asset_class(input: &str) -> IResult<&str, AssetClass, VerboseError<&str>> {
849 let (input, cs) = currency_symbol(input)?;
850
851 let (input, tn) = alt((
852 preceded(eof, success(TokenName::ada())),
853 preceded(char('.'), token_name),
854 ))(input)?;
855
856 Ok((
857 input,
858 AssetClass {
859 currency_symbol: cs,
860 token_name: tn,
861 },
862 ))
863}
864
865#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
870#[is_plutus_data_derive_strategy = "Newtype"]
871#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
872#[cfg_attr(feature = "lbf", derive(Json))]
873pub struct Lovelace(pub BigInt);