soroban_spec_tools/
lib.rs

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