soroban_spec_tools/
lib.rs

1#![allow(clippy::missing_errors_doc, clippy::must_use_candidate)]
2use std::fmt::Write;
3use std::str::FromStr;
4
5use itertools::Itertools;
6use serde_json::{json, Value};
7use stellar_xdr::curr::{
8    AccountId, BytesM, ContractExecutable, ContractId, Error as XdrError, Hash, Int128Parts,
9    Int256Parts, MuxedEd25519Account, PublicKey, ScAddress, ScBytes, ScContractInstance, ScMap,
10    ScMapEntry, ScNonceKey, ScSpecEntry, ScSpecEventV0, ScSpecFunctionV0, ScSpecTypeDef as ScType,
11    ScSpecTypeMap, ScSpecTypeOption, ScSpecTypeResult, ScSpecTypeTuple, ScSpecTypeUdt,
12    ScSpecTypeVec, ScSpecUdtEnumV0, ScSpecUdtErrorEnumCaseV0, ScSpecUdtErrorEnumV0,
13    ScSpecUdtStructV0, ScSpecUdtUnionCaseTupleV0, ScSpecUdtUnionCaseV0, ScSpecUdtUnionCaseVoidV0,
14    ScSpecUdtUnionV0, ScString, ScSymbol, ScVal, ScVec, StringM, UInt128Parts, UInt256Parts,
15    Uint256, VecM,
16};
17
18pub mod contract;
19pub mod utils;
20
21#[derive(thiserror::Error, Debug)]
22pub enum Error {
23    #[error("an unknown error occurred")]
24    Unknown,
25    #[error("Invalid pair {0:#?} {1:#?}")]
26    InvalidPair(ScVal, ScType),
27    #[error("value is not parseable to {0:#?}")]
28    InvalidValue(Option<ScType>),
29    #[error("Unknown case {0} for {1}")]
30    EnumCase(String, String),
31    #[error("Enum {0} missing value for type {1}")]
32    EnumMissingSecondValue(String, String),
33    #[error("Enum {0} is illformed")]
34    IllFormedEnum(String),
35    #[error("Unknown const case {0}")]
36    EnumConst(u32),
37    #[error("Enum const value must be a u32 or smaller")]
38    EnumConstTooLarge(u64),
39    #[error("Missing Entry {0}")]
40    MissingEntry(String),
41    #[error("Missing Spec")]
42    MissingSpec,
43    #[error(transparent)]
44    Xdr(XdrError),
45    #[error(transparent)]
46    Serde(#[from] serde_json::Error),
47    #[error(transparent)]
48    Ethnum(#[from] core::num::ParseIntError),
49
50    #[error("Missing key {0} in map")]
51    MissingKey(String),
52    #[error("Failed to convert {0} to number")]
53    FailedNumConversion(serde_json::Number),
54    #[error("First argument in an enum must be a sybmol")]
55    EnumFirstValueNotSymbol,
56    #[error("Failed to find enum case {0}")]
57    FailedToFindEnumCase(String),
58    #[error(transparent)]
59    FailedSilceToByte(#[from] std::array::TryFromSliceError),
60    #[error(transparent)]
61    Infallible(#[from] std::convert::Infallible),
62    #[error("Missing Error case {0}")]
63    MissingErrorCase(u32),
64    #[error(transparent)]
65    Spec(#[from] soroban_spec::read::FromWasmError),
66    #[error(transparent)]
67    Base64Spec(#[from] soroban_spec::read::ParseSpecBase64Error),
68}
69
70#[derive(Default, Clone)]
71pub struct Spec(pub Option<Vec<ScSpecEntry>>);
72
73impl TryInto<Spec> for &[u8] {
74    type Error = soroban_spec::read::FromWasmError;
75
76    fn try_into(self) -> Result<Spec, Self::Error> {
77        let spec = soroban_spec::read::from_wasm(self)?;
78        Ok(Spec::new(spec.as_slice()))
79    }
80}
81
82impl Spec {
83    pub fn new(entries: &[ScSpecEntry]) -> Self {
84        Self(Some(entries.to_vec()))
85    }
86
87    pub fn from_wasm(wasm: &[u8]) -> Result<Spec, Error> {
88        let spec = soroban_spec::read::from_wasm(wasm)?;
89        Ok(Spec::new(spec.as_slice()))
90    }
91
92    pub fn parse_base64(base64: &str) -> Result<Spec, Error> {
93        let spec = soroban_spec::read::parse_base64(base64.as_bytes())?;
94        Ok(Spec::new(spec.as_slice()))
95    }
96}
97
98impl Spec {
99    /// # Errors
100    /// Could fail to find User Defined Type
101    pub fn doc(&self, name: &str, type_: &ScType) -> Result<Option<&'static str>, Error> {
102        let mut str = match type_ {
103            ScType::Val
104            | ScType::U64
105            | ScType::I64
106            | ScType::U128
107            | ScType::I128
108            | ScType::U32
109            | ScType::I32
110            | ScType::Result(_)
111            | ScType::Vec(_)
112            | ScType::Map(_)
113            | ScType::Tuple(_)
114            | ScType::BytesN(_)
115            | ScType::Symbol
116            | ScType::Error
117            | ScType::Bytes
118            | ScType::Void
119            | ScType::Timepoint
120            | ScType::Duration
121            | ScType::U256
122            | ScType::I256
123            | ScType::String
124            | ScType::Bool => String::new(),
125            ScType::MuxedAddress => {
126                String::from("Can be public key (G13..), contract ID (C13...), or a muxed account (M13..), or an identity")
127            }
128            ScType::Address => String::from(
129                "Can be public key (G13..), a contract ID (C13...) or an identity (alice), ",
130            ),
131            ScType::Option(type_) => return self.doc(name, &type_.value_type),
132            ScType::Udt(ScSpecTypeUdt { name }) => {
133                let spec_type = self.find(&name.to_utf8_string_lossy())?;
134                match spec_type {
135                    ScSpecEntry::FunctionV0(ScSpecFunctionV0 { doc, .. })
136                    | ScSpecEntry::UdtStructV0(ScSpecUdtStructV0 { doc, .. })
137                    | ScSpecEntry::UdtUnionV0(ScSpecUdtUnionV0 { doc, .. })
138                    | ScSpecEntry::UdtEnumV0(ScSpecUdtEnumV0 { doc, .. })
139                    | ScSpecEntry::UdtErrorEnumV0(ScSpecUdtErrorEnumV0 { doc, .. })
140                    | ScSpecEntry::EventV0(ScSpecEventV0 { doc, .. }) => doc,
141                }
142                .to_utf8_string_lossy()
143            }
144        };
145        if let Some(mut ex) = self.example(type_) {
146            if ex.contains(' ') {
147                ex = format!("'{ex}'");
148            } else if ex.contains('"') {
149                ex = ex.replace('"', "");
150            }
151            if matches!(type_, ScType::Bool) {
152                ex = String::new();
153            }
154            let sep = if str.is_empty() { "" } else { "\n" };
155            str = format!("{str}{sep}Example:\n  --{name} {ex}");
156            if ex.contains('"') {}
157        }
158        if str.is_empty() {
159            Ok(None)
160        } else {
161            Ok(Some(Box::leak(str.into_boxed_str())))
162        }
163    }
164
165    /// # Errors
166    ///
167    /// Might return errors
168    pub fn find(&self, name: &str) -> Result<&ScSpecEntry, Error> {
169        self.0
170            .as_ref()
171            .and_then(|specs| {
172                specs.iter().find(|e| {
173                    let entry_name = match e {
174                        ScSpecEntry::FunctionV0(x) => x.name.to_utf8_string_lossy(),
175                        ScSpecEntry::UdtStructV0(x) => x.name.to_utf8_string_lossy(),
176                        ScSpecEntry::UdtUnionV0(x) => x.name.to_utf8_string_lossy(),
177                        ScSpecEntry::UdtEnumV0(x) => x.name.to_utf8_string_lossy(),
178                        ScSpecEntry::UdtErrorEnumV0(x) => x.name.to_utf8_string_lossy(),
179                        ScSpecEntry::EventV0(x) => x.name.to_utf8_string_lossy(),
180                    };
181                    name == entry_name
182                })
183            })
184            .ok_or_else(|| Error::MissingEntry(name.to_owned()))
185    }
186
187    /// # Errors
188    ///
189    /// Might return errors
190    pub fn find_function(&self, name: &str) -> Result<&ScSpecFunctionV0, Error> {
191        match self.find(name)? {
192            ScSpecEntry::FunctionV0(f) => Ok(f),
193            _ => Err(Error::MissingEntry(name.to_owned())),
194        }
195    }
196    //
197    /// # Errors
198    ///
199    pub fn find_functions(&self) -> Result<impl Iterator<Item = &ScSpecFunctionV0>, Error> {
200        Ok(self
201            .0
202            .as_deref()
203            .ok_or(Error::MissingSpec)?
204            .iter()
205            .filter_map(|e| match e {
206                ScSpecEntry::FunctionV0(x) => Some(x),
207                _ => None,
208            }))
209    }
210
211    /// # Errors
212    ///
213    pub fn find_error_type(&self, value: u32) -> Result<&ScSpecUdtErrorEnumCaseV0, Error> {
214        if let ScSpecEntry::UdtErrorEnumV0(ScSpecUdtErrorEnumV0 { cases, .. }) =
215            self.find("Error")?
216        {
217            if let Some(case) = cases.iter().find(|case| value == case.value) {
218                return Ok(case);
219            }
220        }
221        Err(Error::MissingErrorCase(value))
222    }
223
224    /// # Errors
225    ///
226    /// Might return errors
227    pub fn from_string_primitive(s: &str, t: &ScType) -> Result<ScVal, Error> {
228        Self::default().from_string(s, t)
229    }
230
231    /// # Errors
232    ///
233    /// Might return errors
234    #[allow(clippy::wrong_self_convention)]
235    pub fn from_string(&self, s: &str, t: &ScType) -> Result<ScVal, Error> {
236        if let ScType::Option(b) = t {
237            if s == "null" {
238                return Ok(ScVal::Void);
239            }
240            let ScSpecTypeOption { value_type } = b.as_ref().clone();
241            let v = value_type.as_ref().clone();
242            return self.from_string(s, &v);
243        }
244        // Parse as string and for special types assume Value::String
245        serde_json::from_str(s)
246            .map_or_else(
247                |e| match t {
248                    ScType::Symbol
249                    | ScType::String
250                    | ScType::Bytes
251                    | ScType::BytesN(_)
252                    | ScType::U256
253                    | ScType::I256
254                    | ScType::U128
255                    | ScType::I128
256                    | ScType::Address
257                    | ScType::MuxedAddress => Ok(Value::String(s.to_owned())),
258                    ScType::Udt(ScSpecTypeUdt { name })
259                        if matches!(
260                            self.find(&name.to_utf8_string_lossy())?,
261                            ScSpecEntry::UdtUnionV0(_) | ScSpecEntry::UdtStructV0(_)
262                        ) =>
263                    {
264                        Ok(Value::String(s.to_owned()))
265                    }
266                    _ => Err(Error::Serde(e)),
267                },
268                |val| match t {
269                    ScType::U128 | ScType::I128 | ScType::U256 | ScType::I256 => {
270                        Ok(Value::String(s.to_owned()))
271                    }
272                    _ => Ok(val),
273                },
274            )
275            .and_then(|raw| self.from_json(&raw, t))
276    }
277
278    /// # Errors
279    ///
280    /// Might return errors
281    #[allow(clippy::wrong_self_convention)]
282    pub fn from_json(&self, v: &Value, t: &ScType) -> Result<ScVal, Error> {
283        let val: ScVal = match (t, v) {
284            (
285                ScType::Bool
286                | ScType::U128
287                | ScType::I128
288                | ScType::U256
289                | ScType::I256
290                | ScType::I32
291                | ScType::I64
292                | ScType::U32
293                | ScType::U64
294                | ScType::String
295                | ScType::Symbol
296                | ScType::Address
297                | ScType::MuxedAddress
298                | ScType::Bytes
299                | ScType::BytesN(_),
300                _,
301            ) => from_json_primitives(v, t)?,
302
303            // Vec parsing
304            (ScType::Vec(elem), Value::Array(raw)) => {
305                let converted: ScVec = raw
306                    .iter()
307                    .map(|item| self.from_json(item, &elem.element_type))
308                    .collect::<Result<Vec<ScVal>, Error>>()?
309                    .try_into()
310                    .map_err(Error::Xdr)?;
311                ScVal::Vec(Some(converted))
312            }
313
314            // Map parsing
315            (ScType::Map(map), Value::Object(raw)) => self.parse_map(map, raw)?,
316
317            // Option parsing
318            (ScType::Option(_), Value::Null) => ScVal::Void,
319            (ScType::Option(elem), v) => self.from_json(v, &elem.value_type)?,
320
321            // Tuple parsing
322            (ScType::Tuple(elem), Value::Array(raw)) => self.parse_tuple(t, elem, raw)?,
323
324            // User defined types parsing
325            (ScType::Udt(ScSpecTypeUdt { name }), _) => self.parse_udt(name, v)?,
326
327            // TODO: Implement the rest of these
328            (_, raw) => serde_json::from_value(raw.clone()).map_err(Error::Serde)?,
329        };
330        Ok(val)
331    }
332
333    fn parse_udt(&self, name: &StringM<60>, value: &Value) -> Result<ScVal, Error> {
334        let name = &name.to_utf8_string_lossy();
335        match (self.find(name)?, value) {
336            (ScSpecEntry::UdtStructV0(strukt), Value::Object(map)) => {
337                if strukt
338                    .fields
339                    .iter()
340                    .any(|f| f.name.to_utf8_string_lossy() == "0")
341                {
342                    self.parse_tuple_strukt(
343                        strukt,
344                        &(0..map.len())
345                            .map(|i| map.get(&i.to_string()).unwrap().clone())
346                            .collect::<Vec<_>>(),
347                    )
348                } else {
349                    self.parse_strukt(strukt, map)
350                }
351            }
352            (ScSpecEntry::UdtStructV0(strukt), Value::Array(arr)) => {
353                self.parse_tuple_strukt(strukt, arr)
354            }
355            (
356                ScSpecEntry::UdtUnionV0(union),
357                val @ (Value::Array(_) | Value::String(_) | Value::Object(_)),
358            ) => self.parse_union(union, val),
359            (ScSpecEntry::UdtEnumV0(enum_), Value::Number(num)) => parse_const_enum(num, enum_),
360            (s, v) => todo!("Not implemented for {s:#?} {v:#?}"),
361        }
362    }
363
364    fn parse_tuple_strukt(
365        &self,
366        strukt: &ScSpecUdtStructV0,
367        array: &[Value],
368    ) -> Result<ScVal, Error> {
369        let items = strukt
370            .fields
371            .to_vec()
372            .iter()
373            .zip(array.iter())
374            .map(|(f, v)| {
375                let val = self.from_json(v, &f.type_)?;
376                Ok(val)
377            })
378            .collect::<Result<Vec<_>, Error>>()?;
379        Ok(ScVal::Vec(Some(items.try_into().map_err(Error::Xdr)?)))
380    }
381
382    fn parse_strukt(
383        &self,
384        strukt: &ScSpecUdtStructV0,
385        map: &serde_json::Map<String, Value>,
386    ) -> Result<ScVal, Error> {
387        let items = strukt
388            .fields
389            .to_vec()
390            .iter()
391            .map(|f| {
392                let name = &f.name.to_utf8_string_lossy();
393                let v = map
394                    .get(name)
395                    .ok_or_else(|| Error::MissingKey(name.clone()))?;
396                let val = self.from_json(v, &f.type_)?;
397                let key = StringM::from_str(name).unwrap();
398                Ok(ScMapEntry {
399                    key: ScVal::Symbol(key.into()),
400                    val,
401                })
402            })
403            .collect::<Result<Vec<_>, Error>>()?;
404        let map = ScMap::sorted_from(items).map_err(Error::Xdr)?;
405        Ok(ScVal::Map(Some(map)))
406    }
407
408    fn parse_union(&self, union: &ScSpecUdtUnionV0, value: &Value) -> Result<ScVal, Error> {
409        let (enum_case, rest) = match value {
410            Value::String(s) => (s, None),
411            Value::Object(o) if o.len() == 1 => {
412                let res = o.values().next().map(|v| match v {
413                    Value::Object(obj) if obj.contains_key("0") => {
414                        let len = obj.len();
415                        Value::Array(
416                            (0..len)
417                                .map(|i| obj.get(&i.to_string()).unwrap().clone())
418                                .collect::<Vec<_>>(),
419                        )
420                    }
421                    _ => v.clone(),
422                });
423                (o.keys().next().unwrap(), res)
424            }
425            _ => todo!(),
426        };
427        let case = union
428            .cases
429            .iter()
430            .find(|c| {
431                let name = match c {
432                    ScSpecUdtUnionCaseV0::VoidV0(v) => &v.name,
433                    ScSpecUdtUnionCaseV0::TupleV0(v) => &v.name,
434                };
435                enum_case == &name.to_utf8_string_lossy()
436            })
437            .ok_or_else(|| {
438                Error::EnumCase(enum_case.to_string(), union.name.to_utf8_string_lossy())
439            })?;
440
441        let mut res = vec![ScVal::Symbol(ScSymbol(
442            enum_case.try_into().map_err(Error::Xdr)?,
443        ))];
444
445        match (case, rest) {
446            (ScSpecUdtUnionCaseV0::VoidV0(_), _) | (ScSpecUdtUnionCaseV0::TupleV0(_), None) => (),
447            (ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 { type_, .. }), Some(arr))
448                if type_.len() == 1 =>
449            {
450                res.push(self.from_json(&arr, &type_[0])?);
451            }
452            (
453                ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 { type_, .. }),
454                Some(Value::Array(arr)),
455            ) => {
456                res.extend(
457                    arr.iter()
458                        .zip(type_.iter())
459                        .map(|(elem, ty)| self.from_json(elem, ty))
460                        .collect::<Result<Vec<_>, _>>()?,
461                );
462            }
463            (ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 { .. }), Some(_)) => {}
464        }
465        Ok(ScVal::Vec(Some(res.try_into().map_err(Error::Xdr)?)))
466    }
467
468    fn parse_tuple(
469        &self,
470        t: &ScType,
471        tuple: &ScSpecTypeTuple,
472        items: &[Value],
473    ) -> Result<ScVal, Error> {
474        let ScSpecTypeTuple { value_types } = tuple;
475        if items.len() != value_types.len() {
476            return Err(Error::InvalidValue(Some(t.clone())));
477        }
478        let parsed: Result<Vec<ScVal>, Error> = items
479            .iter()
480            .zip(value_types.iter())
481            .map(|(item, t)| self.from_json(item, t))
482            .collect();
483        let converted: ScVec = parsed?.try_into().map_err(Error::Xdr)?;
484        Ok(ScVal::Vec(Some(converted)))
485    }
486
487    fn parse_map(
488        &self,
489        map: &ScSpecTypeMap,
490        value_map: &serde_json::Map<String, Value>,
491    ) -> Result<ScVal, Error> {
492        let ScSpecTypeMap {
493            key_type,
494            value_type,
495        } = map;
496        // TODO: What do we do if the expected key_type is not a string or symbol?
497        let parsed: Result<Vec<ScMapEntry>, Error> = value_map
498            .iter()
499            .map(|(k, v)| -> Result<ScMapEntry, Error> {
500                let key = self.from_string(k, key_type)?;
501                let val = self.from_json(v, value_type)?;
502                Ok(ScMapEntry { key, val })
503            })
504            .collect();
505        Ok(ScVal::Map(Some(
506            ScMap::sorted_from(parsed?).map_err(Error::Xdr)?,
507        )))
508    }
509}
510
511impl Spec {
512    /// # Errors
513    ///
514    /// Might return `Error::InvalidValue`
515    ///
516    /// # Panics
517    ///
518    /// May panic
519    pub fn xdr_to_json(&self, val: &ScVal, output: &ScType) -> Result<Value, Error> {
520        Ok(match (val, output) {
521            (ScVal::Void, ScType::Val | ScType::Option(_) | ScType::Tuple(_))
522            | (ScVal::Map(None) | ScVal::Vec(None), ScType::Option(_)) => Value::Null,
523            (ScVal::Bool(_), ScType::Bool)
524            | (ScVal::Void, ScType::Void)
525            | (ScVal::String(_), ScType::String)
526            | (ScVal::Symbol(_), ScType::Symbol)
527            | (ScVal::U64(_), ScType::U64)
528            | (ScVal::I64(_), ScType::I64)
529            | (ScVal::U32(_), ScType::U32)
530            | (ScVal::I32(_), ScType::I32)
531            | (ScVal::U128(_), ScType::U128)
532            | (ScVal::I128(_), ScType::I128)
533            | (ScVal::U256(_), ScType::U256)
534            | (ScVal::I256(_), ScType::I256)
535            | (ScVal::Duration(_), ScType::Duration)
536            | (ScVal::Timepoint(_), ScType::Timepoint)
537            | (
538                ScVal::ContractInstance(_)
539                | ScVal::LedgerKeyContractInstance
540                | ScVal::LedgerKeyNonce(_),
541                _,
542            )
543            | (ScVal::Address(_), ScType::Address | ScType::MuxedAddress)
544            | (ScVal::Bytes(_), ScType::Bytes | ScType::BytesN(_)) => to_json(val)?,
545
546            (val, ScType::Result(inner)) => self.xdr_to_json(val, &inner.ok_type)?,
547
548            (val, ScType::Option(inner)) => self.xdr_to_json(val, &inner.value_type)?,
549            (ScVal::Map(Some(_)) | ScVal::Vec(Some(_)) | ScVal::U32(_), type_) => {
550                self.sc_object_to_json(val, type_)?
551            }
552
553            (ScVal::Error(_), ScType::Error) => todo!(),
554            (v, typed) => todo!("{v:#?} doesn't have a matching {typed:#?}"),
555        })
556    }
557
558    /// # Errors
559    ///
560    /// Might return an error
561    pub fn vec_m_to_json<const MAX: u32>(
562        &self,
563        vec_m: &VecM<ScVal, MAX>,
564        type_: &ScType,
565    ) -> Result<Value, Error> {
566        Ok(Value::Array(
567            vec_m
568                .to_vec()
569                .iter()
570                .map(|sc_val| self.xdr_to_json(sc_val, type_))
571                .collect::<Result<Vec<_>, Error>>()?,
572        ))
573    }
574
575    /// # Errors
576    ///
577    /// Might return an error
578    pub fn sc_map_to_json(&self, sc_map: &ScMap, type_: &ScSpecTypeMap) -> Result<Value, Error> {
579        let v = sc_map
580            .iter()
581            .map(|ScMapEntry { key, val }| {
582                let key_s = self.xdr_to_json(key, &type_.key_type)?.to_string();
583                let val_value = self.xdr_to_json(val, &type_.value_type)?;
584                Ok((key_s, val_value))
585            })
586            .collect::<Result<serde_json::Map<String, Value>, Error>>()?;
587        Ok(Value::Object(v))
588    }
589
590    /// # Errors
591    ///
592    /// Might return an error
593    ///
594    /// # Panics
595    ///
596    /// May panic
597    pub fn udt_to_json(&self, name: &StringM<60>, sc_obj: &ScVal) -> Result<Value, Error> {
598        let name = &name.to_utf8_string_lossy();
599        let udt = self.find(name)?;
600        Ok(match (sc_obj, udt) {
601            (ScVal::Map(Some(map)), ScSpecEntry::UdtStructV0(strukt)) => serde_json::Value::Object(
602                strukt
603                    .fields
604                    .iter()
605                    .zip(map.iter())
606                    .map(|(field, entry)| {
607                        let val = self.xdr_to_json(&entry.val, &field.type_)?;
608                        Ok((field.name.to_utf8_string_lossy(), val))
609                    })
610                    .collect::<Result<serde_json::Map<String, _>, Error>>()?,
611            ),
612            (ScVal::Vec(Some(vec_)), ScSpecEntry::UdtStructV0(strukt)) => Value::Array(
613                strukt
614                    .fields
615                    .iter()
616                    .zip(vec_.iter())
617                    .map(|(field, entry)| self.xdr_to_json(entry, &field.type_))
618                    .collect::<Result<Vec<_>, Error>>()?,
619            ),
620            (ScVal::Vec(Some(vec_)), ScSpecEntry::UdtUnionV0(union)) => {
621                let v = vec_.to_vec();
622                // let val = &v[0];
623                let (first, rest) = match v.split_at(1) {
624                    ([first], []) => (first, None),
625                    ([first], rest) => (first, Some(rest)),
626                    _ => return Err(Error::IllFormedEnum(union.name.to_utf8_string_lossy())),
627                };
628
629                let ScVal::Symbol(case_name) = first else {
630                    return Err(Error::EnumFirstValueNotSymbol);
631                };
632                let case = union
633                    .cases
634                    .iter()
635                    .find(|case| {
636                        let name = match case {
637                            ScSpecUdtUnionCaseV0::VoidV0(v) => &v.name,
638                            ScSpecUdtUnionCaseV0::TupleV0(v) => &v.name,
639                        };
640                        name.as_vec() == case_name.as_vec()
641                    })
642                    .ok_or_else(|| Error::FailedToFindEnumCase(case_name.to_utf8_string_lossy()))?;
643
644                let case_name = case_name.to_utf8_string_lossy();
645                match case {
646                    ScSpecUdtUnionCaseV0::TupleV0(v) => {
647                        let rest = rest.ok_or_else(|| {
648                            Error::EnumMissingSecondValue(
649                                union.name.to_utf8_string_lossy(),
650                                case_name.clone(),
651                            )
652                        })?;
653                        let val = if v.type_.len() == 1 {
654                            self.xdr_to_json(&rest[0], &v.type_[0])?
655                        } else {
656                            Value::Array(
657                                v.type_
658                                    .iter()
659                                    .zip(rest.iter())
660                                    .map(|(type_, val)| self.xdr_to_json(val, type_))
661                                    .collect::<Result<Vec<_>, Error>>()?,
662                            )
663                        };
664
665                        Value::Object([(case_name, val)].into_iter().collect())
666                    }
667                    ScSpecUdtUnionCaseV0::VoidV0(_) => Value::String(case_name),
668                }
669            }
670            (ScVal::U32(v), ScSpecEntry::UdtEnumV0(_enum_)) => {
671                Value::Number(serde_json::Number::from(*v))
672            }
673            (s, v) => todo!("Not implemented for {s:#?} {v:#?}"),
674        })
675    }
676
677    /// # Errors
678    ///
679    /// Might return an error
680    ///
681    /// # Panics
682    ///
683    /// Some types are not yet supported and will cause a panic if supplied
684    pub fn sc_object_to_json(&self, val: &ScVal, spec_type: &ScType) -> Result<Value, Error> {
685        Ok(match (val, spec_type) {
686            (ScVal::Vec(Some(ScVec(vec_m))), ScType::Vec(type_)) => {
687                self.vec_m_to_json(vec_m, &type_.element_type)?
688            }
689            (ScVal::Vec(Some(ScVec(vec_m))), ScType::Tuple(tuple_type)) => Value::Array(
690                vec_m
691                    .iter()
692                    .zip(tuple_type.value_types.iter())
693                    .map(|(v, t)| self.xdr_to_json(v, t))
694                    .collect::<Result<Vec<_>, _>>()?,
695            ),
696            (
697                sc_obj @ (ScVal::Vec(_) | ScVal::Map(_) | ScVal::U32(_)),
698                ScType::Udt(ScSpecTypeUdt { name }),
699            ) => self.udt_to_json(name, sc_obj)?,
700
701            (ScVal::Map(Some(map)), ScType::Map(map_type)) => self.sc_map_to_json(map, map_type)?,
702
703            (ScVal::U64(u64_), ScType::U64) => Value::Number(serde_json::Number::from(*u64_)),
704
705            (ScVal::I64(i64_), ScType::I64) => Value::Number(serde_json::Number::from(*i64_)),
706            (int @ ScVal::U128(_), ScType::U128) => {
707                // Always output u128s as strings
708                let v: u128 = int
709                    .clone()
710                    .try_into()
711                    .map_err(|()| Error::InvalidValue(Some(ScType::U128)))?;
712                Value::String(v.to_string())
713            }
714
715            (int @ ScVal::I128(_), ScType::I128) => {
716                // Always output u128s as strings
717                let v: i128 = int
718                    .clone()
719                    .try_into()
720                    .map_err(|()| Error::InvalidValue(Some(ScType::I128)))?;
721                Value::String(v.to_string())
722            }
723
724            (ScVal::Bytes(v), ScType::Bytes | ScType::BytesN(_)) => {
725                Value::String(to_lower_hex(v.as_slice()))
726            }
727
728            (ScVal::Bytes(_), ScType::Udt(_)) => todo!(),
729
730            (ScVal::ContractInstance(_), _) => todo!(),
731
732            (ScVal::Address(v), ScType::Address | ScType::MuxedAddress) => sc_address_to_json(v),
733
734            (ok_val, ScType::Result(result_type)) => {
735                let ScSpecTypeResult { ok_type, .. } = result_type.as_ref();
736                self.xdr_to_json(ok_val, ok_type)?
737            }
738
739            (x, y) => return Err(Error::InvalidPair(x.clone(), y.clone())),
740        })
741    }
742}
743
744/// # Errors
745///
746/// Might return an error
747pub fn from_string_primitive(s: &str, t: &ScType) -> Result<ScVal, Error> {
748    Spec::from_string_primitive(s, t)
749}
750
751fn parse_const_enum(num: &serde_json::Number, enum_: &ScSpecUdtEnumV0) -> Result<ScVal, Error> {
752    let num = num
753        .as_u64()
754        .ok_or_else(|| Error::FailedNumConversion(num.clone()))?;
755    let num = u32::try_from(num).map_err(|_| Error::EnumConstTooLarge(num))?;
756    enum_
757        .cases
758        .iter()
759        .find(|c| c.value == num)
760        .ok_or(Error::EnumConst(num))
761        .map(|c| ScVal::U32(c.value))
762}
763
764/// # Errors
765///
766/// Might return an error
767#[allow(clippy::too_many_lines)]
768pub fn from_json_primitives(v: &Value, t: &ScType) -> Result<ScVal, Error> {
769    let val: ScVal = match (t, v) {
770        // Boolean parsing
771        (ScType::Bool, Value::Bool(true)) => ScVal::Bool(true),
772        (ScType::Bool, Value::Bool(false)) => ScVal::Bool(false),
773
774        // Number parsing
775        (ScType::U128, Value::String(s)) => {
776            let val: u128 = u128::from_str(s).map_err(|_| Error::InvalidValue(Some(t.clone())))?;
777            let bytes = val.to_be_bytes();
778            let (hi, lo) = bytes.split_at(8);
779            ScVal::U128(UInt128Parts {
780                hi: u64::from_be_bytes(hi.try_into()?),
781                lo: u64::from_be_bytes(lo.try_into()?),
782            })
783        }
784
785        (ScType::I128, Value::String(s)) => {
786            let val: i128 = i128::from_str(s).map_err(|_| Error::InvalidValue(Some(t.clone())))?;
787            let bytes = val.to_be_bytes();
788            let (hi, lo) = bytes.split_at(8);
789            ScVal::I128(Int128Parts {
790                hi: i64::from_be_bytes(hi.try_into()?),
791                lo: u64::from_be_bytes(lo.try_into()?),
792            })
793        }
794
795        // Number parsing
796        (ScType::U256, Value::String(s)) => {
797            let (hi, lo) = ethnum::U256::from_str_prefixed(s)?.into_words();
798            let hi_bytes = hi.to_be_bytes();
799            let (hi_hi, hi_lo) = hi_bytes.split_at(8);
800            let lo_bytes = lo.to_be_bytes();
801            let (lo_hi, lo_lo) = lo_bytes.split_at(8);
802            ScVal::U256(UInt256Parts {
803                hi_hi: u64::from_be_bytes(hi_hi.try_into()?),
804                hi_lo: u64::from_be_bytes(hi_lo.try_into()?),
805                lo_hi: u64::from_be_bytes(lo_hi.try_into()?),
806                lo_lo: u64::from_be_bytes(lo_lo.try_into()?),
807            })
808        }
809        (ScType::I256, Value::String(s)) => {
810            let (hi, lo) = ethnum::I256::from_str_prefixed(s)?.into_words();
811            let hi_bytes = hi.to_be_bytes();
812            let (hi_hi, hi_lo) = hi_bytes.split_at(8);
813            let lo_bytes = lo.to_be_bytes();
814            let (lo_hi, lo_lo) = lo_bytes.split_at(8);
815            ScVal::I256(Int256Parts {
816                hi_hi: i64::from_be_bytes(hi_hi.try_into()?),
817                hi_lo: u64::from_be_bytes(hi_lo.try_into()?),
818                lo_hi: u64::from_be_bytes(lo_hi.try_into()?),
819                lo_lo: u64::from_be_bytes(lo_lo.try_into()?),
820            })
821        }
822
823        (ScType::I32, Value::Number(n)) => ScVal::I32(
824            n.as_i64()
825                .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?
826                .try_into()
827                .map_err(|_| Error::InvalidValue(Some(t.clone())))?,
828        ),
829        (ScType::U32, Value::Number(n)) => ScVal::U32(
830            n.as_u64()
831                .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?
832                .try_into()
833                .map_err(|_| Error::InvalidValue(Some(t.clone())))?,
834        ),
835        (ScType::I64, Value::Number(n)) => ScVal::I64(
836            n.as_i64()
837                .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?,
838        ),
839        (ScType::U64 | ScType::Timepoint | ScType::Duration, Value::Number(n)) => ScVal::U64(
840            n.as_u64()
841                .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?,
842        ),
843
844        // Symbol parsing
845        (ScType::Symbol, Value::String(s)) => ScVal::Symbol(ScSymbol(
846            s.as_bytes()
847                .try_into()
848                .map_err(|_| Error::InvalidValue(Some(t.clone())))?,
849        )),
850
851        (ScType::Address, Value::String(s)) => sc_address_from_json(s)?,
852
853        (ScType::MuxedAddress, Value::String(s)) => sc_address_from_json(s)?,
854
855        // Bytes parsing
856        (bytes @ ScType::BytesN(_), Value::Number(n)) => {
857            from_json_primitives(&Value::String(format!("{n}")), bytes)?
858        }
859        (ScType::BytesN(bytes), Value::String(s)) => ScVal::Bytes(ScBytes({
860            if bytes.n == 32 {
861                // Bytes might be a strkey, try parsing it as one. Contract devs should use the new
862                // proper Address type, but for backwards compatibility some contracts might use a
863                // BytesN<32> to represent an Address.
864                if let Ok(key) = sc_address_from_json(s) {
865                    return Ok(key);
866                }
867            }
868            // Bytes are not an address, just parse as a hex string
869            utils::padded_hex_from_str(s, bytes.n as usize)
870                .map_err(|_| Error::InvalidValue(Some(t.clone())))?
871                .try_into()
872                .map_err(|_| Error::InvalidValue(Some(t.clone())))?
873        })),
874        (ScType::Bytes, Value::Number(n)) => {
875            from_json_primitives(&Value::String(format!("{n}")), &ScType::Bytes)?
876        }
877        (ScType::Bytes, Value::String(s)) => ScVal::Bytes(
878            hex::decode(s)
879                .map_err(|_| Error::InvalidValue(Some(t.clone())))?
880                .try_into()
881                .map_err(|_| Error::InvalidValue(Some(t.clone())))?,
882        ),
883        (ScType::Bytes | ScType::BytesN(_), Value::Array(raw)) => {
884            let b: Result<Vec<u8>, Error> = raw
885                .iter()
886                .map(|item| {
887                    item.as_u64()
888                        .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?
889                        .try_into()
890                        .map_err(|_| Error::InvalidValue(Some(t.clone())))
891                })
892                .collect();
893            let converted: BytesM<{ u32::MAX }> = b?.try_into().map_err(Error::Xdr)?;
894            ScVal::Bytes(ScBytes(converted))
895        }
896
897        (ScType::String, Value::String(s)) => ScVal::String(ScString(
898            s.try_into()
899                .map_err(|_| Error::InvalidValue(Some(t.clone())))?,
900        )),
901        // Todo make proper error Which shouldn't exist
902        (_, raw) => serde_json::from_value(raw.clone())?,
903    };
904    Ok(val)
905}
906
907/// # Errors
908///
909/// Might return an error
910pub fn to_string(v: &ScVal) -> Result<String, Error> {
911    #[allow(clippy::match_same_arms)]
912    Ok(match v {
913        // If symbols are a top-level thing we omit the wrapping quotes
914        // TODO: Decide if this is a good idea or not.
915        ScVal::Symbol(v) => std::str::from_utf8(v.as_slice())
916            .map_err(|_| Error::InvalidValue(Some(ScType::Symbol)))?
917            .to_string(),
918        ScVal::LedgerKeyContractInstance => "LedgerKeyContractInstance".to_string(),
919        _ => serde_json::to_string(&to_json(v)?)?,
920    })
921}
922
923/// # Errors
924///
925/// Might return an error
926#[allow(clippy::too_many_lines)]
927pub fn to_json(v: &ScVal) -> Result<Value, Error> {
928    #[allow(clippy::match_same_arms)]
929    let val: Value = match v {
930        ScVal::Bool(b) => Value::Bool(*b),
931        ScVal::Void => Value::Null,
932        ScVal::LedgerKeyContractInstance => Value::String("LedgerKeyContractInstance".to_string()),
933        ScVal::U64(v) => Value::Number(serde_json::Number::from(*v)),
934        ScVal::Timepoint(tp) => Value::Number(serde_json::Number::from(tp.0)),
935        ScVal::Duration(d) => Value::Number(serde_json::Number::from(d.0)),
936        ScVal::I64(v) => Value::Number(serde_json::Number::from(*v)),
937        ScVal::U32(v) => Value::Number(serde_json::Number::from(*v)),
938        ScVal::I32(v) => Value::Number(serde_json::Number::from(*v)),
939        ScVal::Symbol(v) => Value::String(
940            std::str::from_utf8(v.as_slice())
941                .map_err(|_| Error::InvalidValue(Some(ScType::Symbol)))?
942                .to_string(),
943        ),
944        ScVal::String(v) => Value::String(
945            std::str::from_utf8(v.as_slice())
946                .map_err(|_| Error::InvalidValue(Some(ScType::Symbol)))?
947                .to_string(),
948        ),
949        ScVal::Vec(v) => {
950            let values: Result<Vec<Value>, Error> = v.as_ref().map_or_else(
951                || Ok(vec![]),
952                |v| {
953                    v.iter()
954                        .map(|item| -> Result<Value, Error> { to_json(item) })
955                        .collect()
956                },
957            );
958            Value::Array(values?)
959        }
960        ScVal::Map(None) => Value::Object(serde_json::Map::with_capacity(0)),
961        ScVal::Map(Some(v)) => {
962            // TODO: What do we do if the key is not a string?
963            let mut m = serde_json::Map::<String, Value>::with_capacity(v.len());
964            for ScMapEntry { key, val } in v.iter() {
965                let k: String = to_string(key)?;
966                let v: Value = to_json(val).map_err(|_| Error::InvalidValue(None))?;
967                m.insert(k, v);
968            }
969            Value::Object(m)
970        }
971        ScVal::Bytes(v) => Value::String(to_lower_hex(v.as_slice())),
972        ScVal::Address(v) => sc_address_to_json(v),
973        ScVal::U128(n) => {
974            let hi: [u8; 8] = n.hi.to_be_bytes();
975            let lo: [u8; 8] = n.lo.to_be_bytes();
976            let bytes = [hi, lo].concat();
977            // Always output u128s as strings
978            let v = u128::from_be_bytes(
979                bytes
980                    .as_slice()
981                    .try_into()
982                    .map_err(|_| Error::InvalidValue(Some(ScType::I128)))?,
983            )
984            .to_string();
985            Value::String(v)
986        }
987        ScVal::I128(n) => {
988            let hi: [u8; 8] = n.hi.to_be_bytes();
989            let lo: [u8; 8] = n.lo.to_be_bytes();
990            let bytes = [hi, lo].concat();
991            // Always output u128s as strings
992            let v = i128::from_be_bytes(
993                bytes
994                    .as_slice()
995                    .try_into()
996                    .map_err(|_| Error::InvalidValue(Some(ScType::I128)))?,
997            )
998            .to_string();
999            Value::String(v)
1000        }
1001        ScVal::U256(u256parts) => {
1002            let bytes = [
1003                u256parts.hi_hi.to_be_bytes(),
1004                u256parts.hi_lo.to_be_bytes(),
1005                u256parts.lo_hi.to_be_bytes(),
1006                u256parts.lo_lo.to_be_bytes(),
1007            ]
1008            .concat();
1009            let u256 = ethnum::U256::from_be_bytes(
1010                bytes
1011                    .as_slice()
1012                    .try_into()
1013                    .map_err(|_| Error::InvalidValue(Some(ScType::U256)))?,
1014            );
1015            Value::String(u256.to_string())
1016        }
1017        ScVal::I256(i256parts) => {
1018            let bytes = [
1019                i256parts.hi_hi.to_be_bytes(),
1020                i256parts.hi_lo.to_be_bytes(),
1021                i256parts.lo_hi.to_be_bytes(),
1022                i256parts.lo_lo.to_be_bytes(),
1023            ]
1024            .concat();
1025            let i256 = ethnum::I256::from_be_bytes(
1026                bytes
1027                    .as_slice()
1028                    .try_into()
1029                    .map_err(|_| Error::InvalidValue(Some(ScType::I256)))?,
1030            );
1031            Value::String(i256.to_string())
1032        }
1033        ScVal::ContractInstance(ScContractInstance {
1034            executable: ContractExecutable::Wasm(hash),
1035            ..
1036        }) => json!({ "hash": hash }),
1037        ScVal::ContractInstance(ScContractInstance {
1038            executable: ContractExecutable::StellarAsset,
1039            ..
1040        }) => json!({"SAC": true}),
1041        ScVal::LedgerKeyNonce(ScNonceKey { nonce }) => {
1042            Value::Number(serde_json::Number::from(*nonce))
1043        }
1044        ScVal::Error(e) => serde_json::to_value(e)?,
1045    };
1046    Ok(val)
1047}
1048
1049fn sc_address_to_json(v: &ScAddress) -> Value {
1050    match v {
1051        ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(k)))) => {
1052            Value::String(stellar_strkey::ed25519::PublicKey(*k).to_string())
1053        }
1054        ScAddress::Contract(ContractId(h)) => {
1055            Value::String(stellar_strkey::Contract(h.clone().into()).to_string())
1056        }
1057        ScAddress::MuxedAccount(MuxedEd25519Account { ed25519, id }) => Value::String(
1058            stellar_strkey::ed25519::MuxedAccount {
1059                ed25519: ed25519.0,
1060                id: *id,
1061            }
1062            .to_string(),
1063        ),
1064        ScAddress::ClaimableBalance(_) => todo!("ClaimableBalance is not supported"),
1065        ScAddress::LiquidityPool(_) => todo!("LiquidityPool is not supported"),
1066    }
1067}
1068
1069fn sc_address_from_json(s: &str) -> Result<ScVal, Error> {
1070    stellar_strkey::Strkey::from_string(s)
1071        .map_err(|_| Error::InvalidValue(Some(ScType::Address)))
1072        .map(|parsed| match parsed {
1073            stellar_strkey::Strkey::PublicKeyEd25519(p) => Some(ScVal::Address(
1074                ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(p.0)))),
1075            )),
1076            stellar_strkey::Strkey::Contract(c) => {
1077                Some(ScVal::Address(ScAddress::Contract(ContractId(Hash(c.0)))))
1078            }
1079            stellar_strkey::Strkey::MuxedAccountEd25519(m) => Some(ScVal::Address(
1080                ScAddress::MuxedAccount(MuxedEd25519Account {
1081                    ed25519: Uint256(m.ed25519),
1082                    id: m.id,
1083                }),
1084            )),
1085            _ => None,
1086        })?
1087        .ok_or(Error::InvalidValue(Some(ScType::Address)))
1088}
1089
1090fn to_lower_hex(bytes: &[u8]) -> String {
1091    let mut res = String::with_capacity(bytes.len());
1092    for b in bytes {
1093        let _ = write!(res, "{b:02x}");
1094    }
1095    res
1096}
1097
1098impl Spec {
1099    #[must_use]
1100    pub fn arg_value_name(&self, type_: &ScType, depth: usize) -> Option<String> {
1101        match type_ {
1102            ScType::U64 => Some("u64".to_string()),
1103            ScType::I64 => Some("i64".to_string()),
1104            ScType::U128 => Some("u128".to_string()),
1105            ScType::I128 => Some("i128".to_string()),
1106            ScType::U32 => Some("u32".to_string()),
1107            ScType::I32 => Some("i32".to_string()),
1108            ScType::Bool => Some("bool".to_string()),
1109            ScType::Symbol => Some("Symbol".to_string()),
1110            ScType::Error => Some("Error".to_string()),
1111            ScType::Bytes => Some("hex_bytes".to_string()),
1112            ScType::Address => Some("Address".to_string()),
1113            ScType::Void => Some("Null".to_string()),
1114            ScType::Timepoint => Some("Timepoint".to_string()),
1115            ScType::Duration => Some("Duration".to_string()),
1116            ScType::U256 => Some("u256".to_string()),
1117            ScType::I256 => Some("i256".to_string()),
1118            ScType::String => Some("String".to_string()),
1119            ScType::MuxedAddress => Some("MuxedAddress".to_string()),
1120            ScType::Option(val) => {
1121                let ScSpecTypeOption { value_type } = val.as_ref();
1122                let inner = self.arg_value_name(value_type.as_ref(), depth + 1)?;
1123                Some(format!("Option<{inner}>"))
1124            }
1125            ScType::Vec(val) => {
1126                let ScSpecTypeVec { element_type } = val.as_ref();
1127                let inner = self.arg_value_name(element_type.as_ref(), depth + 1)?;
1128                Some(format!("Array<{inner}>"))
1129            }
1130            ScType::Result(val) => {
1131                let ScSpecTypeResult {
1132                    ok_type,
1133                    error_type,
1134                } = val.as_ref();
1135                let ok = self.arg_value_name(ok_type.as_ref(), depth + 1)?;
1136                let error = self.arg_value_name(error_type.as_ref(), depth + 1)?;
1137                Some(format!("Result<{ok}, {error}>"))
1138            }
1139            ScType::Tuple(val) => {
1140                let ScSpecTypeTuple { value_types } = val.as_ref();
1141                let names = value_types
1142                    .iter()
1143                    .map(|t| self.arg_value_name(t, depth + 1))
1144                    .collect::<Option<Vec<_>>>()?
1145                    .join(", ");
1146                Some(format!("Tuple<{names}>"))
1147            }
1148            ScType::Map(val) => {
1149                let ScSpecTypeMap {
1150                    key_type,
1151                    value_type,
1152                } = val.as_ref();
1153                let (key, val) = (
1154                    self.arg_value_name(key_type.as_ref(), depth + 1)?,
1155                    self.arg_value_name(value_type.as_ref(), depth + 1)?,
1156                );
1157                Some(format!("Map<{key}, {val}>"))
1158            }
1159            ScType::BytesN(t) => Some(format!("{}_hex_bytes", t.n)),
1160            ScType::Udt(ScSpecTypeUdt { name }) => {
1161                match self.find(&name.to_utf8_string_lossy()).ok()? {
1162                    ScSpecEntry::UdtStructV0(ScSpecUdtStructV0 { fields, .. })
1163                        if fields
1164                            .first()
1165                            .is_some_and(|f| f.name.to_utf8_string_lossy() == "0") =>
1166                    {
1167                        let fields = fields
1168                            .iter()
1169                            .map(|t| self.arg_value_name(&t.type_, depth + 1))
1170                            .collect::<Option<Vec<_>>>()?
1171                            .join(", ");
1172                        Some(format!("[{fields}]"))
1173                    }
1174                    ScSpecEntry::UdtStructV0(strukt) => self.arg_value_udt(strukt, depth),
1175                    ScSpecEntry::UdtUnionV0(union) => self.arg_value_union(union, depth),
1176                    ScSpecEntry::UdtEnumV0(enum_) => Some(arg_value_enum(enum_)),
1177                    ScSpecEntry::FunctionV0(_)
1178                    | ScSpecEntry::UdtErrorEnumV0(_)
1179                    | ScSpecEntry::EventV0(_) => None,
1180                }
1181            }
1182            // No specific value name for these yet.
1183            ScType::Val => None,
1184        }
1185    }
1186
1187    fn arg_value_udt(&self, strukt: &ScSpecUdtStructV0, depth: usize) -> Option<String> {
1188        let inner = strukt
1189            .fields
1190            .iter()
1191            .map(|f| (f.name.to_utf8_string_lossy(), &f.type_))
1192            .map(|(name, type_)| {
1193                let type_ = self.arg_value_name(type_, depth + 1)?;
1194                Some(format!("{name}: {type_}"))
1195            })
1196            .collect::<Option<Vec<_>>>()?
1197            .join(", ");
1198        Some(format!("{{ {inner} }}"))
1199    }
1200
1201    fn arg_value_union(&self, union: &ScSpecUdtUnionV0, depth: usize) -> Option<String> {
1202        union
1203            .cases
1204            .iter()
1205            .map(|f| {
1206                Some(match f {
1207                    ScSpecUdtUnionCaseV0::VoidV0(ScSpecUdtUnionCaseVoidV0 { name, .. }) => {
1208                        name.to_utf8_string_lossy()
1209                    }
1210                    ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 {
1211                        name,
1212                        type_,
1213                        ..
1214                    }) => format!(
1215                        "{}({})",
1216                        name.to_utf8_string_lossy(),
1217                        type_
1218                            .iter()
1219                            .map(|type_| self.arg_value_name(type_, depth + 1))
1220                            .collect::<Option<Vec<String>>>()?
1221                            .join(",")
1222                    ),
1223                })
1224            })
1225            .collect::<Option<Vec<_>>>()
1226            .map(|v| v.join(" | "))
1227    }
1228}
1229
1230fn arg_value_enum(enum_: &ScSpecUdtEnumV0) -> String {
1231    enum_
1232        .cases
1233        .iter()
1234        .map(|case| case.value.to_string())
1235        .join(" | ")
1236}
1237
1238// Example implementation
1239impl Spec {
1240    #[must_use]
1241    pub fn example(&self, type_: &ScType) -> Option<String> {
1242        #[allow(clippy::match_same_arms)]
1243        match type_ {
1244            ScType::U64 => Some("1".to_string()),
1245            ScType::I64 => Some("1".to_string()),
1246            ScType::U128 => Some("\"1\"".to_string()),
1247            ScType::I128 => Some("\"1\"".to_string()),
1248            ScType::U32 => Some("1".to_string()),
1249            ScType::I32 => Some("1".to_string()),
1250            ScType::Bool => Some("true".to_string()),
1251            ScType::Symbol => Some("\"hello\"".to_string()),
1252            ScType::Error => Some("Error".to_string()),
1253            ScType::Bytes => Some("\"0000000000000000\"".to_string()),
1254            ScType::Address => {
1255                Some("\"GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF\"".to_string())
1256            }
1257            ScType::Void => Some("null".to_string()),
1258            ScType::Timepoint => Some("1743010492".to_string()),
1259            ScType::Duration => Some("1".to_string()),
1260            ScType::U256 => Some("\"1\"".to_string()),
1261            ScType::I256 => Some("\"1\"".to_string()),
1262            ScType::String => Some("\"hello world\"".to_string()),
1263            ScType::Option(val) => {
1264                let ScSpecTypeOption { value_type } = val.as_ref();
1265                self.example(value_type.as_ref())
1266            }
1267            ScType::Vec(val) => {
1268                let ScSpecTypeVec { element_type } = val.as_ref();
1269                let inner = self.example(element_type.as_ref())?;
1270                Some(format!("[ {inner} ]"))
1271            }
1272            ScType::Result(val) => {
1273                let ScSpecTypeResult {
1274                    ok_type,
1275                    error_type,
1276                } = val.as_ref();
1277                let ok = self.example(ok_type.as_ref())?;
1278                let error = self.example(error_type.as_ref())?;
1279                Some(format!("Result<{ok}, {error}>"))
1280            }
1281            ScType::Tuple(val) => {
1282                let ScSpecTypeTuple { value_types } = val.as_ref();
1283                let names = value_types
1284                    .iter()
1285                    .map(|t| self.example(t))
1286                    .collect::<Option<Vec<_>>>()?
1287                    .join(", ");
1288                Some(format!("[{names}]"))
1289            }
1290            ScType::Map(map) => {
1291                let ScSpecTypeMap {
1292                    key_type,
1293                    value_type,
1294                } = map.as_ref();
1295                let (mut key, val) = (
1296                    self.example(key_type.as_ref())?,
1297                    self.example(value_type.as_ref())?,
1298                );
1299                if !matches!(key_type.as_ref(), ScType::Symbol) {
1300                    key = format!("\"{key}\"");
1301                }
1302                Some(format!("{{ {key}: {val} }}"))
1303            }
1304            ScType::BytesN(n) => {
1305                let n = n.n as usize;
1306                let res = "00".repeat(n);
1307                Some(format!("\"{res}\""))
1308            }
1309            ScType::Udt(ScSpecTypeUdt { name }) => {
1310                self.example_udts(name.to_utf8_string_lossy().as_ref())
1311            }
1312            ScType::MuxedAddress => {
1313                Some("\"GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF\"".to_string())
1314            }
1315            // No specific value name for these yet.
1316            ScType::Val => None,
1317        }
1318    }
1319
1320    fn example_udts(&self, name: &str) -> Option<String> {
1321        match self.find(name).ok() {
1322            Some(ScSpecEntry::UdtStructV0(strukt)) => {
1323                // Check if a tuple strukt
1324                if !strukt.fields.is_empty() && strukt.fields[0].name.to_utf8_string_lossy() == "0"
1325                {
1326                    let value_types = strukt
1327                        .fields
1328                        .iter()
1329                        .map(|f| f.type_.clone())
1330                        .collect::<Vec<_>>()
1331                        .try_into()
1332                        .ok()?;
1333                    return self.example(&ScType::Tuple(Box::new(ScSpecTypeTuple { value_types })));
1334                }
1335                let inner = strukt
1336                    .fields
1337                    .iter()
1338                    .map(|f| (f.name.to_utf8_string_lossy(), &f.type_))
1339                    .map(|(name, type_)| {
1340                        let type_ = self.example(type_)?;
1341                        let name = format!(r#""{name}""#);
1342                        Some(format!("{name}: {type_}"))
1343                    })
1344                    .collect::<Option<Vec<_>>>()?
1345                    .join(", ");
1346                Some(format!(r"{{ {inner} }}"))
1347            }
1348            Some(ScSpecEntry::UdtUnionV0(union)) => self.example_union(union),
1349            Some(ScSpecEntry::UdtEnumV0(enum_)) => {
1350                enum_.cases.iter().next().map(|c| c.value.to_string())
1351            }
1352            Some(
1353                ScSpecEntry::FunctionV0(_)
1354                | ScSpecEntry::UdtErrorEnumV0(_)
1355                | ScSpecEntry::EventV0(_),
1356            )
1357            | None => None,
1358        }
1359    }
1360
1361    fn example_union(&self, union: &ScSpecUdtUnionV0) -> Option<String> {
1362        let res = union
1363            .cases
1364            .iter()
1365            .map(|case| match case {
1366                ScSpecUdtUnionCaseV0::VoidV0(ScSpecUdtUnionCaseVoidV0 { name, .. }) => {
1367                    Some(format!("\"{}\"", name.to_utf8_string_lossy()))
1368                }
1369                ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 {
1370                    name, type_, ..
1371                }) => {
1372                    if type_.len() == 1 {
1373                        let single = self.example(&type_[0])?;
1374                        Some(format!("{{\"{}\":{single}}}", name.to_utf8_string_lossy()))
1375                    } else {
1376                        let names = type_
1377                            .iter()
1378                            .map(|t| self.example(t))
1379                            .collect::<Option<Vec<_>>>()?
1380                            .join(", ");
1381                        Some(format!("{{\"{}\":[{names}]}}", name.to_utf8_string_lossy()))
1382                    }
1383                }
1384            })
1385            .collect::<Option<Vec<_>>>()?;
1386        Some(res.join("|"))
1387    }
1388}
1389
1390#[cfg(test)]
1391mod tests {
1392    use super::*;
1393
1394    use stellar_xdr::curr::ScSpecTypeBytesN;
1395
1396    #[test]
1397    fn from_json_primitives_bytesn() {
1398        // TODO: Add test for parsing addresses
1399
1400        // Check it parses hex-encoded bytes
1401        let b = from_json_primitives(
1402            &Value::String("beefface".to_string()),
1403            &ScType::BytesN(ScSpecTypeBytesN { n: 4 }),
1404        )
1405        .unwrap();
1406        assert_eq!(
1407            b,
1408            ScVal::Bytes(ScBytes(vec![0xbe, 0xef, 0xfa, 0xce].try_into().unwrap()))
1409        );
1410
1411        // Check it parses hex-encoded bytes when they are all numbers. Normally the json would
1412        // interpret the CLI arg as a number, so we need a special case there.
1413        let b = from_json_primitives(
1414            &Value::Number(4554.into()),
1415            &ScType::BytesN(ScSpecTypeBytesN { n: 2 }),
1416        )
1417        .unwrap();
1418        assert_eq!(
1419            b,
1420            ScVal::Bytes(ScBytes(vec![0x45, 0x54].try_into().unwrap()))
1421        );
1422    }
1423
1424    #[test]
1425    fn from_json_primitives_bytes() {
1426        // Check it parses hex-encoded bytes
1427        let b =
1428            from_json_primitives(&Value::String("beefface".to_string()), &ScType::Bytes).unwrap();
1429        assert_eq!(
1430            b,
1431            ScVal::Bytes(ScBytes(vec![0xbe, 0xef, 0xfa, 0xce].try_into().unwrap()))
1432        );
1433
1434        // Check it parses hex-encoded bytes when they are all numbers. Normally the json would
1435        // interpret the CLI arg as a number, so we need a special case there.
1436        let b = from_json_primitives(&Value::Number(4554.into()), &ScType::Bytes).unwrap();
1437        assert_eq!(
1438            b,
1439            ScVal::Bytes(ScBytes(vec![0x45, 0x54].try_into().unwrap()))
1440        );
1441    }
1442
1443    #[test]
1444    fn test_sc_address_from_json_strkey() {
1445        // All zero contract address
1446        match sc_address_from_json("CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSC4") {
1447            Ok(addr) => assert_eq!(
1448                addr,
1449                ScVal::Address(ScAddress::Contract(ContractId(Hash([0; 32]))))
1450            ),
1451            Err(e) => panic!("Unexpected error: {e}"),
1452        }
1453
1454        // Real contract address
1455        match sc_address_from_json("CA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAXE") {
1456            Ok(addr) => assert_eq!(
1457                addr,
1458                ScVal::Address(ScAddress::Contract(ContractId::from(Hash([
1459                    0x36, 0x3e, 0xaa, 0x38, 0x67, 0x84, 0x1f, 0xba, 0xd0, 0xf4, 0xed, 0x88, 0xc7,
1460                    0x79, 0xe4, 0xfe, 0x66, 0xe5, 0x6a, 0x24, 0x70, 0xdc, 0x98, 0xc0, 0xec, 0x9c,
1461                    0x07, 0x3d, 0x05, 0xc7, 0xb1, 0x03,
1462                ]))))
1463            ),
1464            Err(e) => panic!("Unexpected error: {e}"),
1465        }
1466
1467        // All zero user account address
1468        match sc_address_from_json("GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF") {
1469            Ok(addr) => assert_eq!(
1470                addr,
1471                ScVal::Address(ScAddress::Account(AccountId(
1472                    PublicKey::PublicKeyTypeEd25519([0; 32].into())
1473                )))
1474            ),
1475            Err(e) => panic!("Unexpected error: {e}"),
1476        }
1477
1478        // Real user account address
1479        match sc_address_from_json("GA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQHES5") {
1480            Ok(addr) => assert_eq!(
1481                addr,
1482                ScVal::Address(ScAddress::Account(AccountId(
1483                    PublicKey::PublicKeyTypeEd25519(
1484                        [
1485                            0x36, 0x3e, 0xaa, 0x38, 0x67, 0x84, 0x1f, 0xba, 0xd0, 0xf4, 0xed, 0x88,
1486                            0xc7, 0x79, 0xe4, 0xfe, 0x66, 0xe5, 0x6a, 0x24, 0x70, 0xdc, 0x98, 0xc0,
1487                            0xec, 0x9c, 0x07, 0x3d, 0x05, 0xc7, 0xb1, 0x03,
1488                        ]
1489                        .into()
1490                    )
1491                )))
1492            ),
1493            Err(e) => panic!("Unexpected error: {e}"),
1494        }
1495
1496        // MuxedAddress test
1497        match sc_address_from_json(
1498            "MA5XIGA5C7QTPTWXQHY6MCJRMTRZDOSHR6EFIBNDQTCQHG262N4GGGC2XZXD7G7EWH7U6",
1499        ) {
1500            Ok(addr) => {
1501                // This should parse successfully now
1502                assert!(matches!(addr, ScVal::Address(ScAddress::MuxedAccount(_))));
1503            }
1504            Err(e) => panic!("Unexpected error parsing MuxedAddress: {e}"),
1505        }
1506    }
1507
1508    #[test]
1509    fn test_sc_address_json_strkey_muxed_roundtrip() {
1510        let muxed_address_str =
1511            "MA5XIGA5C7QTPTWXQHY6MCJRMTRZDOSHR6EFIBNDQTCQHG262N4GGGC2XZXD7G7EWH7U6";
1512
1513        // First convert from JSON strkey to ScAddress, then convert back to JSON strkey using sc_address_to_json
1514        match sc_address_from_json(muxed_address_str) {
1515            Ok(ScVal::Address(address)) => {
1516                let json_result = sc_address_to_json(&address);
1517                assert_eq!(json_result, Value::String(muxed_address_str.to_string()));
1518            }
1519            Ok(_) => panic!("Expected ScVal::Address"),
1520            Err(e) => panic!("Unexpected error: {e}"),
1521        }
1522    }
1523
1524    #[test]
1525    fn test_sc_muxed_address_from_json() {
1526        let expected_ed25519 = Uint256([
1527            0x3b, 0x74, 0x18, 0x1d, 0x17, 0xe1, 0x37, 0xce, 0xd7, 0x81, 0xf1, 0xe6, 0x09, 0x31,
1528            0x64, 0xe3, 0x91, 0xba, 0x47, 0x8f, 0x88, 0x54, 0x05, 0xa3, 0x84, 0xc5, 0x03, 0x9b,
1529            0x5e, 0xd3, 0x78, 0x63,
1530        ]);
1531        let expected_id = 1_754_924_385_537_090_737_u64;
1532
1533        // Generate the muxed address from the expected values
1534        let muxed_addr = stellar_strkey::ed25519::MuxedAccount {
1535            ed25519: expected_ed25519.0,
1536            id: expected_id,
1537        }
1538        .to_string();
1539
1540        // Test muxed address parsing
1541        match sc_address_from_json(&muxed_addr) {
1542            Ok(ScVal::Address(ScAddress::MuxedAccount(MuxedEd25519Account { ed25519, id }))) => {
1543                // Verify the actual content of the MuxedEd25519Account
1544                assert_eq!(ed25519, expected_ed25519);
1545                assert_eq!(id, expected_id);
1546            }
1547            Ok(_) => panic!("Expected ScVal::Address(ScAddress::MuxedAccount(_))"),
1548            Err(e) => panic!("Unexpected error parsing MuxedAddress: {e}"),
1549        }
1550    }
1551
1552    #[test]
1553    fn test_u128_conversions() {
1554        // Test basic positive value
1555        let val = 42u128;
1556        let json_val = Value::String(val.to_string());
1557        let scval = from_json_primitives(&json_val, &ScType::U128).unwrap();
1558        assert_eq!(to_json(&scval).unwrap(), json_val);
1559
1560        // Test zero
1561        let val = 0u128;
1562        let json_val = Value::String(val.to_string());
1563        let scval = from_json_primitives(&json_val, &ScType::U128).unwrap();
1564        assert_eq!(to_json(&scval).unwrap(), json_val);
1565
1566        // Test maximum value
1567        let val = u128::MAX;
1568        let json_val = Value::String(val.to_string());
1569        let scval = from_json_primitives(&json_val, &ScType::U128).unwrap();
1570        assert_eq!(to_json(&scval).unwrap(), json_val);
1571
1572        // Test large value
1573        let val = 340_282_366_920_938_463_463_374_607_431_768_211_455u128;
1574        let json_val = Value::String(val.to_string());
1575        let scval = from_json_primitives(&json_val, &ScType::U128).unwrap();
1576        assert_eq!(to_json(&scval).unwrap(), json_val);
1577    }
1578
1579    #[test]
1580    fn test_i128_conversions() {
1581        // Test basic positive value
1582        let val = 42i128;
1583        let json_val = Value::String(val.to_string());
1584        let scval = from_json_primitives(&json_val, &ScType::I128).unwrap();
1585        assert_eq!(to_json(&scval).unwrap(), json_val);
1586
1587        // Test basic negative value
1588        let val = -42i128;
1589        let json_val = Value::String(val.to_string());
1590        let scval = from_json_primitives(&json_val, &ScType::I128).unwrap();
1591        assert_eq!(to_json(&scval).unwrap(), json_val);
1592
1593        // Test zero
1594        let val = 0i128;
1595        let json_val = Value::String(val.to_string());
1596        let scval = from_json_primitives(&json_val, &ScType::I128).unwrap();
1597        assert_eq!(to_json(&scval).unwrap(), json_val);
1598
1599        // Test maximum value
1600        let val = i128::MAX;
1601        let json_val = Value::String(val.to_string());
1602        let scval = from_json_primitives(&json_val, &ScType::I128).unwrap();
1603        assert_eq!(to_json(&scval).unwrap(), json_val);
1604
1605        // Test minimum value
1606        let val = i128::MIN;
1607        let json_val = Value::String(val.to_string());
1608        let scval = from_json_primitives(&json_val, &ScType::I128).unwrap();
1609        assert_eq!(to_json(&scval).unwrap(), json_val);
1610
1611        // Test large negative value
1612        let val = -170_141_183_460_469_231_731_687_303_715_884_105_728i128;
1613        let json_val = Value::String(val.to_string());
1614        let scval = from_json_primitives(&json_val, &ScType::I128).unwrap();
1615        assert_eq!(to_json(&scval).unwrap(), json_val);
1616    }
1617
1618    #[test]
1619    fn test_u256_conversions() {
1620        // Test basic positive value
1621        let val = "42";
1622        let json_val = Value::String(val.to_string());
1623        let scval = from_json_primitives(&json_val, &ScType::U256).unwrap();
1624        assert_eq!(to_json(&scval).unwrap(), json_val);
1625
1626        // Test zero
1627        let val = "0";
1628        let json_val = Value::String(val.to_string());
1629        let scval = from_json_primitives(&json_val, &ScType::U256).unwrap();
1630        assert_eq!(to_json(&scval).unwrap(), json_val);
1631
1632        // Test large value
1633        let val = "115792089237316195423570985008687907853269984665640564039457584007913129639935";
1634        let json_val = Value::String(val.to_string());
1635        let scval = from_json_primitives(&json_val, &ScType::U256).unwrap();
1636        assert_eq!(to_json(&scval).unwrap(), json_val);
1637
1638        // Test hex format
1639        let val = "0xff";
1640        let json_val = Value::String(val.to_string());
1641        let scval = from_json_primitives(&json_val, &ScType::U256).unwrap();
1642        let expected = Value::String("255".to_string());
1643        assert_eq!(to_json(&scval).unwrap(), expected);
1644
1645        // Test hex format with 0x prefix
1646        let val = "0x1234567890abcdef";
1647        let json_val = Value::String(val.to_string());
1648        let scval = from_json_primitives(&json_val, &ScType::U256).unwrap();
1649        let expected = Value::String("1311768467294899695".to_string());
1650        assert_eq!(to_json(&scval).unwrap(), expected);
1651    }
1652
1653    #[test]
1654    fn test_i256_conversions() {
1655        // Test basic positive value
1656        let val = "42";
1657        let json_val = Value::String(val.to_string());
1658        let scval = from_json_primitives(&json_val, &ScType::I256).unwrap();
1659        assert_eq!(to_json(&scval).unwrap(), json_val);
1660
1661        // Test basic negative value
1662        let val = "-42";
1663        let json_val = Value::String(val.to_string());
1664        let scval = from_json_primitives(&json_val, &ScType::I256).unwrap();
1665        assert_eq!(to_json(&scval).unwrap(), json_val);
1666
1667        // Test zero
1668        let val = "0";
1669        let json_val = Value::String(val.to_string());
1670        let scval = from_json_primitives(&json_val, &ScType::I256).unwrap();
1671        assert_eq!(to_json(&scval).unwrap(), json_val);
1672
1673        // Test large positive value
1674        let val = "57896044618658097711785492504343953926634992332820282019728792003956564819967";
1675        let json_val = Value::String(val.to_string());
1676        let scval = from_json_primitives(&json_val, &ScType::I256).unwrap();
1677        assert_eq!(to_json(&scval).unwrap(), json_val);
1678
1679        // Test large negative value
1680        let val = "-57896044618658097711785492504343953926634992332820282019728792003956564819968";
1681        let json_val = Value::String(val.to_string());
1682        let scval = from_json_primitives(&json_val, &ScType::I256).unwrap();
1683        assert_eq!(to_json(&scval).unwrap(), json_val);
1684
1685        // Test hex format
1686        let val = "0xff";
1687        let json_val = Value::String(val.to_string());
1688        let scval = from_json_primitives(&json_val, &ScType::I256).unwrap();
1689        let expected = Value::String("255".to_string());
1690        assert_eq!(to_json(&scval).unwrap(), expected);
1691
1692        // Test negative hex format
1693        let val = "-0xff";
1694        let json_val = Value::String(val.to_string());
1695        let scval = from_json_primitives(&json_val, &ScType::I256).unwrap();
1696        let expected = Value::String("-255".to_string());
1697        assert_eq!(to_json(&scval).unwrap(), expected);
1698    }
1699
1700    #[test]
1701    fn test_integer_conversion_edge_cases() {
1702        // Test invalid string for U128
1703        let json_val = Value::String("not_a_number".to_string());
1704        let result = from_json_primitives(&json_val, &ScType::U128);
1705        assert!(result.is_err());
1706
1707        // Test invalid string for I128
1708        let json_val = Value::String("not_a_number".to_string());
1709        let result = from_json_primitives(&json_val, &ScType::I128);
1710        assert!(result.is_err());
1711
1712        // Test invalid string for U256
1713        let json_val = Value::String("not_a_number".to_string());
1714        let result = from_json_primitives(&json_val, &ScType::U256);
1715        assert!(result.is_err());
1716
1717        // Test invalid string for I256
1718        let json_val = Value::String("not_a_number".to_string());
1719        let result = from_json_primitives(&json_val, &ScType::I256);
1720        assert!(result.is_err());
1721
1722        // Test negative value for U128 (should fail)
1723        let json_val = Value::String("-42".to_string());
1724        let result = from_json_primitives(&json_val, &ScType::U128);
1725        assert!(result.is_err());
1726
1727        // Test negative value for U256 (should fail)
1728        let json_val = Value::String("-42".to_string());
1729        let result = from_json_primitives(&json_val, &ScType::U256);
1730        assert!(result.is_err());
1731    }
1732
1733    #[test]
1734    fn test_basic_numeric_conversions() {
1735        // Test U32
1736        let val = 42u32;
1737        let json_val = Value::Number(val.into());
1738        let scval = from_json_primitives(&json_val, &ScType::U32).unwrap();
1739        assert_eq!(to_json(&scval).unwrap(), json_val);
1740
1741        // Test I32
1742        let val = -42i32;
1743        let json_val = Value::Number(val.into());
1744        let scval = from_json_primitives(&json_val, &ScType::I32).unwrap();
1745        assert_eq!(to_json(&scval).unwrap(), json_val);
1746
1747        // Test U64
1748        let val = 42u64;
1749        let json_val = Value::Number(val.into());
1750        let scval = from_json_primitives(&json_val, &ScType::U64).unwrap();
1751        assert_eq!(to_json(&scval).unwrap(), json_val);
1752
1753        // Test I64
1754        let val = -42i64;
1755        let json_val = Value::Number(val.into());
1756        let scval = from_json_primitives(&json_val, &ScType::I64).unwrap();
1757        assert_eq!(to_json(&scval).unwrap(), json_val);
1758
1759        // Test boolean
1760        let scval = from_json_primitives(&Value::Bool(true), &ScType::Bool).unwrap();
1761        assert_eq!(to_json(&scval).unwrap(), Value::Bool(true));
1762
1763        let scval = from_json_primitives(&Value::Bool(false), &ScType::Bool).unwrap();
1764        assert_eq!(to_json(&scval).unwrap(), Value::Bool(false));
1765    }
1766
1767    #[test]
1768    fn test_numeric_extremes() {
1769        // Test U32 maximum
1770        let val = u32::MAX;
1771        let json_val = Value::Number(val.into());
1772        let scval = from_json_primitives(&json_val, &ScType::U32).unwrap();
1773        assert_eq!(to_json(&scval).unwrap(), json_val);
1774
1775        // Test I32 minimum and maximum
1776        let val = i32::MIN;
1777        let json_val = Value::Number(val.into());
1778        let scval = from_json_primitives(&json_val, &ScType::I32).unwrap();
1779        assert_eq!(to_json(&scval).unwrap(), json_val);
1780
1781        let val = i32::MAX;
1782        let json_val = Value::Number(val.into());
1783        let scval = from_json_primitives(&json_val, &ScType::I32).unwrap();
1784        assert_eq!(to_json(&scval).unwrap(), json_val);
1785
1786        // Test U64 maximum
1787        let val = u64::MAX;
1788        let json_val = Value::Number(val.into());
1789        let scval = from_json_primitives(&json_val, &ScType::U64).unwrap();
1790        assert_eq!(to_json(&scval).unwrap(), json_val);
1791
1792        // Test I64 minimum and maximum
1793        let val = i64::MIN;
1794        let json_val = Value::Number(val.into());
1795        let scval = from_json_primitives(&json_val, &ScType::I64).unwrap();
1796        assert_eq!(to_json(&scval).unwrap(), json_val);
1797
1798        let val = i64::MAX;
1799        let json_val = Value::Number(val.into());
1800        let scval = from_json_primitives(&json_val, &ScType::I64).unwrap();
1801        assert_eq!(to_json(&scval).unwrap(), json_val);
1802    }
1803
1804    #[test]
1805    fn test_string_primitive_integration() {
1806        // Test that from_string_primitive works with various types
1807        // U128
1808        let val = "42";
1809        let scval = from_string_primitive(val, &ScType::U128).unwrap();
1810        assert_eq!(to_string(&scval).unwrap(), "\"42\"");
1811
1812        // I128
1813        let val = "-42";
1814        let scval = from_string_primitive(val, &ScType::I128).unwrap();
1815        assert_eq!(to_string(&scval).unwrap(), "\"-42\"");
1816
1817        // U256
1818        let val = "12345678901234567890123456789012345678901234567890";
1819        let scval = from_string_primitive(val, &ScType::U256).unwrap();
1820        assert_eq!(
1821            to_string(&scval).unwrap(),
1822            "\"12345678901234567890123456789012345678901234567890\""
1823        );
1824
1825        // I256
1826        let val = "-12345678901234567890123456789012345678901234567890";
1827        let scval = from_string_primitive(val, &ScType::I256).unwrap();
1828        assert_eq!(
1829            to_string(&scval).unwrap(),
1830            "\"-12345678901234567890123456789012345678901234567890\""
1831        );
1832
1833        // Boolean
1834        let scval = from_string_primitive("true", &ScType::Bool).unwrap();
1835        assert_eq!(to_string(&scval).unwrap(), "true");
1836
1837        let scval = from_string_primitive("false", &ScType::Bool).unwrap();
1838        assert_eq!(to_string(&scval).unwrap(), "false");
1839    }
1840}