sui_display/v2/
value.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::borrow::Cow;
5use std::fmt::Write as _;
6use std::str;
7
8use async_trait::async_trait;
9use base64::engine::Engine;
10use chrono::DateTime;
11use move_core_types::account_address::AccountAddress;
12use move_core_types::annotated_value as A;
13use move_core_types::annotated_value::MoveTypeLayout;
14use move_core_types::language_storage::StructTag;
15use move_core_types::language_storage::TypeTag;
16use move_core_types::u256::U256;
17use serde::Serialize;
18use serde::ser::SerializeSeq as _;
19use serde::ser::SerializeTuple as _;
20use serde::ser::SerializeTupleVariant;
21use sui_types::base_types::ObjectID;
22use sui_types::base_types::RESOLVED_UTF8_STR;
23use sui_types::base_types::SuiAddress;
24use sui_types::base_types::move_ascii_str_layout;
25use sui_types::base_types::move_utf8_str_layout;
26use sui_types::base_types::type_name_layout;
27use sui_types::base_types::url_layout;
28use sui_types::derived_object::derive_object_id;
29use sui_types::dynamic_field::DynamicFieldInfo;
30use sui_types::dynamic_field::derive_dynamic_field_id;
31use sui_types::id::ID;
32use sui_types::id::UID;
33use sui_types::object::rpc_visitor as RV;
34use sui_types::object::rpc_visitor::Meter as _;
35
36use crate::v2::error::FormatError;
37use crate::v2::parser::Base64Modifier;
38use crate::v2::parser::Transform;
39use crate::v2::writer;
40
41/// Dynamically load objects by their ID, returning the object's owned data.
42///
43/// The `Store` trait is responsible only for fetching object data -- lifetime management
44/// and caching are handled by the `Interpreter`. The interpreter can potentially issue racing
45/// requests for the same object, and it is the store's responsibility to handle this correctly
46/// (e.g. by deduplicating in-flight requests).
47#[async_trait]
48pub trait Store {
49    async fn object(&self, id: AccountAddress) -> anyhow::Result<Option<OwnedSlice>>;
50}
51
52/// Result of evaluating a single strand of a Display v2 format string.
53#[derive(Clone)]
54pub enum Strand<'s> {
55    Text(&'s str),
56    Value {
57        offset: usize,
58        value: Value<'s>,
59        transform: Transform,
60    },
61}
62
63/// Value representation used during evaluation by the Display v2 interpreter.
64#[derive(Clone)]
65pub enum Value<'s> {
66    Address(AccountAddress),
67    Bool(bool),
68    Bytes(Cow<'s, [u8]>),
69    Enum(Enum<'s>),
70    Slice(Slice<'s>),
71    String(Cow<'s, [u8]>),
72    Struct(Struct<'s>),
73    U8(u8),
74    U16(u16),
75    U32(u32),
76    U64(u64),
77    U128(u128),
78    U256(U256),
79    Vector(Vector<'s>),
80}
81
82/// Non-aggregate values that can be formatted during string interpolation.
83#[derive(Debug, PartialEq, Eq)]
84pub enum Atom<'s> {
85    Address(AccountAddress),
86    Bool(bool),
87    Bytes(Cow<'s, [u8]>),
88    U8(u8),
89    U16(u16),
90    U32(u32),
91    U64(u64),
92    U128(u128),
93    U256(U256),
94}
95
96/// A single step in a chain of accesses, with its inner expression (if there is one) evaluated.
97pub enum Accessor<'s> {
98    Field(&'s str),
99    Positional(u8),
100    Index(Value<'s>),
101    DFIndex(Value<'s>),
102    DOFIndex(Value<'s>),
103    Derived(Value<'s>),
104}
105
106/// Bytes extracted from the serialized representation of a Move value, along with its layout.
107#[derive(Copy, Clone)]
108pub struct Slice<'s> {
109    pub(crate) layout: &'s MoveTypeLayout,
110    pub(crate) bytes: &'s [u8],
111}
112
113/// An owned version of `Slice`.
114#[derive(Clone)]
115pub struct OwnedSlice {
116    pub layout: MoveTypeLayout,
117    pub bytes: Vec<u8>,
118}
119
120/// An evaluated vector literal.
121#[derive(Clone)]
122pub struct Vector<'s> {
123    pub(crate) type_: Cow<'s, TypeTag>,
124    pub(crate) elements: Vec<Value<'s>>,
125}
126
127/// An evaluated struct literal.
128#[derive(Clone)]
129pub struct Struct<'s> {
130    pub(crate) type_: &'s StructTag,
131    pub(crate) fields: Fields<'s>,
132}
133
134/// An evaluated enum/variant literal.
135#[derive(Clone)]
136pub struct Enum<'s> {
137    pub(crate) type_: &'s StructTag,
138    pub(crate) variant_name: Option<&'s str>,
139    pub(crate) variant_index: u16,
140    pub(crate) fields: Fields<'s>,
141}
142
143/// Evaluated fields that are part of a struct or enum literal.
144#[derive(Clone)]
145pub enum Fields<'s> {
146    Positional(Vec<Value<'s>>),
147    Named(Vec<(&'s str, Value<'s>)>),
148}
149
150impl Value<'_> {
151    /// Treat this value as a dynamic field name, and derive the ID of its `Field<K, V>` object,
152    /// under the given `parent` address.
153    pub fn derive_dynamic_field_id(
154        &self,
155        parent: impl Into<SuiAddress>,
156    ) -> Result<ObjectID, FormatError> {
157        let bytes = bcs::to_bytes(self)?;
158        let type_ = self.type_();
159
160        Ok(derive_dynamic_field_id(parent, &type_, &bytes)?)
161    }
162
163    /// Treat this value as a dynamic object field name, and derive the ID of its `Field<K, V>`
164    /// object, under the given `parent` address.
165    pub fn derive_dynamic_object_field_id(
166        &self,
167        parent: impl Into<SuiAddress>,
168    ) -> Result<ObjectID, FormatError> {
169        let bytes = bcs::to_bytes(self)?;
170        let type_ = DynamicFieldInfo::dynamic_object_field_wrapper(self.type_()).into();
171
172        Ok(derive_dynamic_field_id(parent, &type_, &bytes)?)
173    }
174
175    /// Treat this value as a derived object key and derive the corresponding object ID under the
176    /// given parent address.
177    pub fn derive_object_id(&self, parent: impl Into<SuiAddress>) -> Result<ObjectID, FormatError> {
178        let bytes = bcs::to_bytes(self)?;
179        let type_ = self.type_();
180
181        Ok(derive_object_id(parent, &type_, &bytes)?)
182    }
183
184    /// Write out a formatted representation of this value, transformed by `transform`, to the
185    /// provided writer.
186    ///
187    /// This operation can fail if the transform is not supported for this value, or if the output
188    /// is too large. If it succeds, `w` will be modified to include the newly written data.
189    pub(crate) fn format(
190        self,
191        transform: Transform,
192        w: &mut writer::StringWriter<'_>,
193    ) -> Result<(), FormatError> {
194        match transform {
195            Transform::Base64(xmod) => Atom::try_from(self)?.format_as_base64(xmod.engine(), w),
196            Transform::Bcs(xmod) => {
197                let bytes = bcs::to_bytes(&self)?;
198                Ok(write!(w, "{}", xmod.engine().encode(bytes))?)
199            }
200
201            Transform::Hex => Atom::try_from(self)?.format_as_hex(w),
202            Transform::Json => Err(FormatError::TransformInvalid("unexpected 'json' in string")),
203            Transform::Str => Atom::try_from(self)?.format_as_str(w),
204            Transform::Timestamp => Atom::try_from(self)?.format_as_timestamp(w),
205            Transform::Url => Atom::try_from(self)?.format_as_url(w),
206        }
207    }
208
209    /// Write out a formatted representation of this value as JSON, using the provided meter.
210    ///
211    /// This operation can fail if the output is too large. If it succeeds, `meter` will be
212    /// modified to account for the size of the written data.
213    pub(crate) fn format_json<F: RV::Format>(
214        self,
215        mut meter: writer::Meter<'_>,
216    ) -> Result<F, FormatError> {
217        match self {
218            Value::Address(a) => Ok(F::string(&mut meter, a.to_canonical_string(true))?),
219            Value::Bool(b) => Ok(F::bool(&mut meter, b)?),
220            Value::U8(n) => Ok(F::number(&mut meter, n as u32)?),
221            Value::U16(n) => Ok(F::number(&mut meter, n as u32)?),
222            Value::U32(n) => Ok(F::number(&mut meter, n)?),
223            Value::U64(n) => Ok(F::string(&mut meter, n.to_string())?),
224            Value::U128(n) => Ok(F::string(&mut meter, n.to_string())?),
225            Value::U256(n) => Ok(F::string(&mut meter, n.to_string())?),
226
227            Value::Bytes(bs) => {
228                let b64 = Base64Modifier::EMPTY.engine().encode(&bs);
229                Ok(F::string(&mut meter, b64)?)
230            }
231
232            Value::String(bs) => {
233                let s = str::from_utf8(&bs)
234                    .map_err(|_| FormatError::TransformInvalid("expected utf8 bytes"))?;
235                Ok(F::string(&mut meter, s.to_owned())?)
236            }
237
238            Value::Struct(s) => s.format_json(meter),
239            Value::Enum(e) => e.format_json(meter),
240            Value::Vector(v) => v.format_json(meter),
241            Value::Slice(s) => s.format_json(meter),
242        }
243    }
244
245    /// The Move type of this value.
246    pub(crate) fn type_(&self) -> TypeTag {
247        match self {
248            Value::Address(_) => TypeTag::Address,
249            Value::Bool(_) => TypeTag::Bool,
250            Value::Bytes(_) => TypeTag::Vector(Box::new(TypeTag::U8)),
251            Value::U8(_) => TypeTag::U8,
252            Value::U16(_) => TypeTag::U16,
253            Value::U32(_) => TypeTag::U32,
254            Value::U64(_) => TypeTag::U64,
255            Value::U128(_) => TypeTag::U128,
256            Value::U256(_) => TypeTag::U256,
257
258            Value::Enum(e) => e.type_.clone().into(),
259            Value::Struct(s) => s.type_.clone().into(),
260
261            Value::Slice(s) => s.layout.into(),
262
263            Value::String(_) => {
264                let (&address, module, name) = RESOLVED_UTF8_STR;
265                TypeTag::Struct(Box::new(StructTag {
266                    address,
267                    module: module.to_owned(),
268                    name: name.to_owned(),
269                    type_params: vec![],
270                }))
271            }
272
273            Value::Vector(v) => v.type_(),
274        }
275    }
276
277    /// Attempt to coerce this value into a `u64` if that's possible. This works for any numeric
278    /// value that can be represented within 64 bits.
279    pub(crate) fn as_u64(&self) -> Option<u64> {
280        use MoveTypeLayout as L;
281        use Value as V;
282
283        match self {
284            // Numeric literals in Display
285            V::U8(n) => Some(*n as u64),
286            V::U16(n) => Some(*n as u64),
287            V::U32(n) => Some(*n as u64),
288            V::U64(n) => Some(*n),
289            V::U128(n) => u64::try_from(*n).ok(),
290            V::U256(n) => u64::try_from(*n).ok(),
291
292            // Numeric values sliced out of Move values
293            V::Slice(Slice {
294                layout,
295                bytes: data,
296            }) => match layout {
297                L::U8 => Some(bcs::from_bytes::<u8>(data).ok()?.into()),
298                L::U16 => Some(bcs::from_bytes::<u16>(data).ok()?.into()),
299                L::U32 => Some(bcs::from_bytes::<u32>(data).ok()?.into()),
300                L::U64 => bcs::from_bytes::<u64>(data).ok(),
301                L::U128 => bcs::from_bytes::<u128>(data).ok()?.try_into().ok(),
302                L::U256 => bcs::from_bytes::<U256>(data).ok()?.try_into().ok(),
303                L::Address | L::Bool | L::Enum(_) | L::Signer | L::Struct(_) | L::Vector(_) => None,
304            },
305
306            // Everything else cannot be coerced to u64
307            V::Address(_)
308            | V::Bool(_)
309            | V::Bytes(_)
310            | V::Enum(_)
311            | V::String(_)
312            | V::Struct(_)
313            | V::Vector(_) => None,
314        }
315    }
316}
317
318impl Atom<'_> {
319    /// Format the atom as a hexadecimal string.
320    fn format_as_hex(&self, w: &mut writer::StringWriter<'_>) -> Result<(), FormatError> {
321        match self {
322            Atom::Bool(b) => write!(w, "{:02x}", *b as u8)?,
323            Atom::U8(n) => write!(w, "{n:02x}")?,
324            Atom::U16(n) => write!(w, "{n:04x}")?,
325            Atom::U32(n) => write!(w, "{n:08x}")?,
326            Atom::U64(n) => write!(w, "{n:016x}")?,
327            Atom::U128(n) => write!(w, "{n:032x}")?,
328            Atom::U256(n) => write!(w, "{n:064x}")?,
329
330            Atom::Address(a) => {
331                for b in a.into_bytes() {
332                    write!(w, "{b:02x}")?;
333                }
334            }
335
336            Atom::Bytes(bs) => {
337                for b in bs.iter() {
338                    write!(w, "{b:02x}")?;
339                }
340            }
341        }
342
343        Ok(())
344    }
345
346    /// Format the atom as a string.
347    fn format_as_str(&self, w: &mut writer::StringWriter<'_>) -> Result<(), FormatError> {
348        match self {
349            Atom::Address(a) => write!(w, "{}", a.to_canonical_display(true))?,
350            Atom::Bool(b) => write!(w, "{b}")?,
351            Atom::U8(n) => write!(w, "{n}")?,
352            Atom::U16(n) => write!(w, "{n}")?,
353            Atom::U32(n) => write!(w, "{n}")?,
354            Atom::U64(n) => write!(w, "{n}")?,
355            Atom::U128(n) => write!(w, "{n}")?,
356            Atom::U256(n) => write!(w, "{n}")?,
357            Atom::Bytes(bs) => {
358                let s = str::from_utf8(bs)
359                    .map_err(|_| FormatError::TransformInvalid("expected utf8 bytes"))?;
360                write!(w, "{s}")?;
361            }
362        }
363
364        Ok(())
365    }
366
367    /// Coerce the atom into an `i64`, interpreted as an offset in milliseconds since the Unix
368    /// epoch, and format it as an ISO8601 timestamp.
369    fn format_as_timestamp(&self, w: &mut writer::StringWriter<'_>) -> Result<(), FormatError> {
370        let ts = self
371            .as_i64()
372            .and_then(DateTime::from_timestamp_millis)
373            .ok_or_else(|| {
374                FormatError::TransformInvalid("expected unix timestamp in milliseconds")
375            })?;
376
377        write!(w, "{ts:?}")?;
378        Ok(())
379    }
380
381    /// Like string formatting, but percent-encoding reserved URL characters.
382    fn format_as_url(&self, w: &mut writer::StringWriter<'_>) -> Result<(), FormatError> {
383        match self {
384            Atom::Address(a) => write!(w, "{}", a.to_canonical_display(true))?,
385            Atom::Bool(b) => write!(w, "{b}")?,
386            Atom::U8(n) => write!(w, "{n}")?,
387            Atom::U16(n) => write!(w, "{n}")?,
388            Atom::U32(n) => write!(w, "{n}")?,
389            Atom::U64(n) => write!(w, "{n}")?,
390            Atom::U128(n) => write!(w, "{n}")?,
391            Atom::U256(n) => write!(w, "{n}")?,
392            Atom::Bytes(bs) => {
393                for b in bs.iter() {
394                    match *b {
395                        b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'.' | b'_' | b'~' => {
396                            write!(w, "{}", *b as char)?
397                        }
398                        b => write!(w, "%{b:02X}")?,
399                    }
400                }
401            }
402        }
403
404        Ok(())
405    }
406
407    /// Base64-encode the byte representation of this atom.
408    fn format_as_base64(
409        &self,
410        e: &impl Engine,
411        w: &mut writer::StringWriter<'_>,
412    ) -> Result<(), FormatError> {
413        let base64 = match self {
414            Atom::Address(a) => e.encode(a.into_bytes()),
415            Atom::Bool(b) => e.encode([*b as u8]),
416            Atom::U8(n) => e.encode([*n]),
417            Atom::U16(n) => e.encode(n.to_le_bytes()),
418            Atom::U32(n) => e.encode(n.to_le_bytes()),
419            Atom::U64(n) => e.encode(n.to_le_bytes()),
420            Atom::U128(n) => e.encode(n.to_le_bytes()),
421            Atom::U256(n) => e.encode(n.to_le_bytes()),
422            Atom::Bytes(bs) => e.encode(bs),
423        };
424
425        write!(w, "{base64}")?;
426        Ok(())
427    }
428
429    /// Attempt to coerce this atom into an `i64`, if possible.
430    fn as_i64(&self) -> Option<i64> {
431        match self {
432            Atom::U8(n) => Some(*n as i64),
433            Atom::U16(n) => Some(*n as i64),
434            Atom::U32(n) => Some(*n as i64),
435            Atom::U64(n) => i64::try_from(*n).ok(),
436            Atom::U128(n) => i64::try_from(*n).ok(),
437            Atom::U256(n) => u64::try_from(*n).ok().and_then(|v| i64::try_from(v).ok()),
438            _ => None,
439        }
440    }
441}
442
443impl<'s> Accessor<'s> {
444    /// Coerce this accessor into a numeric index, if possible, and returns its value.
445    ///
446    /// Coercion works for all integer literals, as well as `Slice` literals with a numeric layout,
447    /// as long as their numeric values fit into a `u64`.
448    pub(crate) fn as_numeric_index(&self) -> Option<u64> {
449        use Accessor as A;
450
451        match self {
452            A::Index(value) => value.as_u64(),
453            // All other index types don't represent a numeric index.
454            A::DFIndex(_) | A::DOFIndex(_) | A::Derived(_) | A::Field(_) | A::Positional(_) => None,
455        }
456    }
457
458    /// Coerce this accessor into a field name, if possible, and return its name.
459    pub(crate) fn as_field_name(&self) -> Option<Cow<'s, str>> {
460        use Accessor as A;
461        match self {
462            A::Field(f) => Some(Cow::Borrowed(*f)),
463            A::Positional(i) => Some(Cow::Owned(format!("pos{i}"))),
464            A::Index(_) | A::DFIndex(_) | A::DOFIndex(_) | A::Derived(_) => None,
465        }
466    }
467}
468
469impl OwnedSlice {
470    pub(crate) fn as_slice(&self) -> Slice<'_> {
471        Slice {
472            layout: &self.layout,
473            bytes: &self.bytes,
474        }
475    }
476}
477
478impl Slice<'_> {
479    fn format_json<F: RV::Format>(self, meter: writer::Meter<'_>) -> Result<F, FormatError> {
480        Ok(A::MoveValue::visit_deserialize(
481            self.bytes,
482            self.layout,
483            &mut RV::RpcVisitor::new(meter),
484        )?)
485    }
486}
487
488impl Value<'_> {
489    /// Convert this value into an owned slice.
490    ///
491    /// This operation returns `None` if the value contains compound literals (struct, enum, vector
492    /// literals), since their layouts are not guaranteed to be valid.
493    pub fn into_owned_slice(self) -> Option<OwnedSlice> {
494        let layout = self.layout()?;
495        let bytes = bcs::to_bytes(&self).ok()?;
496        Some(OwnedSlice { layout, bytes })
497    }
498
499    /// Compute the type layout for this value, if possible.
500    ///
501    /// Returns `None` for compound literals (Struct, Enum, Vector) since we cannot reliably
502    /// compute their layouts without access to the full type information.
503    fn layout(&self) -> Option<MoveTypeLayout> {
504        use MoveTypeLayout as L;
505
506        match self {
507            Value::Slice(s) => Some(s.layout.clone()),
508
509            Value::Address(_) => Some(L::Address),
510            Value::Bool(_) => Some(L::Bool),
511            Value::U8(_) => Some(L::U8),
512            Value::U16(_) => Some(L::U16),
513            Value::U32(_) => Some(L::U32),
514            Value::U64(_) => Some(L::U64),
515            Value::U128(_) => Some(L::U128),
516            Value::U256(_) => Some(L::U256),
517
518            Value::Bytes(_) => Some(L::Vector(Box::new(L::U8))),
519            Value::String(_) => Some(L::Struct(Box::new(move_utf8_str_layout()))),
520
521            // Compound literals: cannot compute layout
522            Value::Enum(_) | Value::Struct(_) | Value::Vector(_) => None,
523        }
524    }
525}
526
527impl Vector<'_> {
528    fn type_(&self) -> TypeTag {
529        TypeTag::Vector(Box::new(self.type_.clone().into_owned()))
530    }
531
532    fn format_json<F: RV::Format>(self, mut meter: writer::Meter<'_>) -> Result<F, FormatError> {
533        let mut elems = F::Vec::default();
534        let mut nested = meter.nest()?;
535        for e in self.elements {
536            let json = e.format_json(nested.reborrow())?;
537            F::vec_push_element(&mut nested, &mut elems, json)?;
538        }
539
540        Ok(F::vec(&mut meter, elems)?)
541    }
542}
543
544impl Struct<'_> {
545    fn format_json<F: RV::Format>(self, mut meter: writer::Meter<'_>) -> Result<F, FormatError> {
546        let mut map = F::Map::default();
547        let nested = meter.nest()?;
548        self.fields.format_json::<F>(nested, &mut map)?;
549
550        Ok(F::map(&mut meter, map)?)
551    }
552}
553
554impl Enum<'_> {
555    fn format_json<F: RV::Format>(self, mut meter: writer::Meter<'_>) -> Result<F, FormatError> {
556        let mut map = F::Map::default();
557        let mut nested = meter.nest()?;
558
559        let name = match self.variant_name {
560            Some(name) => F::string(&mut nested, name.to_owned())?,
561            None => F::number(&mut nested, self.variant_index as u32)?,
562        };
563
564        F::map_push_field(&mut nested, &mut map, "@variant".to_owned(), name)?;
565        self.fields.format_json::<F>(nested, &mut map)?;
566
567        Ok(F::map(&mut meter, map)?)
568    }
569}
570
571impl<'s> Fields<'s> {
572    /// Attempt to fetch a particular field  from a struct or enum literal's fields based on the
573    /// given accessor.
574    pub(crate) fn get(self, accessor: &Accessor<'s>) -> Option<Value<'s>> {
575        match (self, accessor) {
576            (Fields::Positional(mut fs), Accessor::Positional(i)) => {
577                let i = *i as usize;
578                if i < fs.len() {
579                    Some(fs.swap_remove(i))
580                } else {
581                    None
582                }
583            }
584
585            (Fields::Named(mut fs), Accessor::Field(f)) => {
586                let i = fs.iter().position(|(n, _)| n == f)?;
587                Some(fs.swap_remove(i).1)
588            }
589
590            _ => None,
591        }
592    }
593
594    fn len(&self) -> usize {
595        match self {
596            Fields::Positional(fs) => fs.len(),
597            Fields::Named(fs) => fs.len(),
598        }
599    }
600
601    fn format_json<F: RV::Format>(
602        self,
603        mut meter: writer::Meter<'_>,
604        map: &mut F::Map,
605    ) -> Result<(), FormatError> {
606        match self {
607            Fields::Positional(values) => {
608                for (i, value) in values.into_iter().enumerate() {
609                    let json = value.format_json(meter.reborrow())?;
610                    F::map_push_field(&mut meter, map, format!("pos{i}"), json)?;
611                }
612            }
613
614            Fields::Named(items) => {
615                for (field, value) in items {
616                    let json = value.format_json(meter.reborrow())?;
617                    F::map_push_field(&mut meter, map, field.to_owned(), json)?;
618                }
619            }
620        }
621
622        Ok(())
623    }
624}
625
626/// Serialize implementation for Value to support serializing the Value to BCS bytes.
627impl Serialize for Value<'_> {
628    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
629    where
630        S: serde::Serializer,
631    {
632        match self {
633            Value::Address(a) => a.serialize(serializer),
634            Value::Bool(b) => b.serialize(serializer),
635            Value::Bytes(b) => b.serialize(serializer),
636            Value::Enum(e) => e.serialize(serializer),
637            Value::Slice(s) => s.serialize(serializer),
638            Value::String(s) => s.serialize(serializer),
639            Value::Struct(s) => s.serialize(serializer),
640            Value::U8(n) => n.serialize(serializer),
641            Value::U16(n) => n.serialize(serializer),
642            Value::U32(n) => n.serialize(serializer),
643            Value::U64(n) => n.serialize(serializer),
644            Value::U128(n) => n.serialize(serializer),
645            Value::U256(n) => n.serialize(serializer),
646            Value::Vector(v) => v.serialize(serializer),
647        }
648    }
649}
650
651/// This implementation makes it so that serializing a `Slice` to BCS bytes produces the bytes
652/// unchanged (but this property is not guaranteed for any other format).
653impl Serialize for Slice<'_> {
654    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
655    where
656        S: serde::Serializer,
657    {
658        let mut s = serializer.serialize_tuple(self.bytes.len())?;
659        for b in self.bytes {
660            s.serialize_element(b)?;
661        }
662
663        s.end()
664    }
665}
666
667impl Serialize for Vector<'_> {
668    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
669    where
670        S: serde::Serializer,
671    {
672        let mut s = serializer.serialize_seq(Some(self.elements.len()))?;
673        for e in &self.elements {
674            s.serialize_element(e)?;
675        }
676
677        s.end()
678    }
679}
680
681impl Serialize for Struct<'_> {
682    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
683    where
684        S: serde::Serializer,
685    {
686        // Serialize the struct as a tuple, regardless of whether it has named or positional
687        // fields, because `serde`'s field names need to be `&'static str`, which we don't have
688        // (and we don't need).
689        let mut s = serializer.serialize_tuple(self.fields.len())?;
690
691        match &self.fields {
692            // Move values cannot serialize to an empty byte stream, so if there are no fields,
693            // `dummy_field: bool = false` is injected.
694            Fields::Positional(fs) if fs.is_empty() => {
695                s.serialize_element(&false)?;
696            }
697
698            Fields::Named(fs) if fs.is_empty() => {
699                s.serialize_element(&false)?;
700            }
701
702            Fields::Positional(fs) => {
703                for f in fs {
704                    s.serialize_element(f)?;
705                }
706            }
707            Fields::Named(fs) => {
708                for (_, f) in fs {
709                    s.serialize_element(f)?;
710                }
711            }
712        }
713
714        s.end()
715    }
716}
717
718impl Serialize for Enum<'_> {
719    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
720    where
721        S: serde::Serializer,
722    {
723        // Serialize the enum as a tuple, with empty names, for similar reasons as `Struct`, above.
724        let mut s = serializer.serialize_tuple_variant(
725            "",
726            self.variant_index as u32,
727            "",
728            self.fields.len(),
729        )?;
730
731        match &self.fields {
732            Fields::Positional(fs) => {
733                for f in fs {
734                    s.serialize_field(f)?;
735                }
736            }
737            Fields::Named(fs) => {
738                for (_, f) in fs {
739                    s.serialize_field(f)?;
740                }
741            }
742        }
743
744        s.end()
745    }
746}
747
748impl<'s> TryFrom<Value<'s>> for Atom<'s> {
749    type Error = FormatError;
750
751    fn try_from(value: Value<'s>) -> Result<Atom<'s>, FormatError> {
752        use Atom as A;
753        use MoveTypeLayout as L;
754        use TypeTag as T;
755        use Value as V;
756
757        Ok(match value {
758            V::Address(a) => A::Address(a),
759            V::Bool(b) => A::Bool(b),
760            V::U8(n) => A::U8(n),
761            V::U16(n) => A::U16(n),
762            V::U32(n) => A::U32(n),
763            V::U64(n) => A::U64(n),
764            V::U128(n) => A::U128(n),
765            V::U256(n) => A::U256(n),
766
767            // Byte arrays and strings are indistinguishable at the Atom level
768            V::Bytes(bs) | V::String(bs) => A::Bytes(bs),
769
770            V::Enum(_) => return Err(FormatError::TransformInvalid("unexpected enum")),
771            V::Struct(_) => return Err(FormatError::TransformInvalid("unexpected struct")),
772
773            // Vector literals are supported if they are byte vectors.
774            V::Vector(Vector { type_, elements }) => {
775                if *type_ != T::U8 {
776                    return Err(FormatError::TransformInvalid("unexpected vector"));
777                }
778
779                let bytes: Result<Vec<_>, _> = elements
780                    .into_iter()
781                    .map(|e| match e {
782                        V::U8(b) => Ok(b),
783                        V::Slice(Slice { layout, bytes }) if layout == &L::U8 => {
784                            Ok(bcs::from_bytes(bytes)?)
785                        }
786                        _ => Err(FormatError::TransformInvalid("unexpected vector")),
787                    })
788                    .collect();
789
790                A::Bytes(Cow::Owned(bytes?))
791            }
792
793            V::Slice(Slice { layout, bytes }) => match layout {
794                L::Address => A::Address(bcs::from_bytes(bytes)?),
795                L::Bool => A::Bool(bcs::from_bytes(bytes)?),
796                L::U8 => A::U8(bcs::from_bytes(bytes)?),
797                L::U16 => A::U16(bcs::from_bytes(bytes)?),
798                L::U32 => A::U32(bcs::from_bytes(bytes)?),
799                L::U64 => A::U64(bcs::from_bytes(bytes)?),
800                L::U128 => A::U128(bcs::from_bytes(bytes)?),
801                L::U256 => A::U256(bcs::from_bytes(bytes)?),
802
803                L::Vector(layout) if layout.as_ref() == &L::U8 => {
804                    A::Bytes(Cow::Borrowed(bcs::from_bytes(bytes)?))
805                }
806
807                L::Struct(layout)
808                    if [
809                        move_ascii_str_layout(),
810                        move_utf8_str_layout(),
811                        type_name_layout(),
812                        url_layout(),
813                    ]
814                    .contains(layout.as_ref()) =>
815                {
816                    A::Bytes(Cow::Borrowed(bcs::from_bytes(bytes)?))
817                }
818
819                L::Struct(layout) if [UID::layout(), ID::layout()].contains(layout.as_ref()) => {
820                    A::Address(bcs::from_bytes(bytes)?)
821                }
822
823                L::Signer => return Err(FormatError::TransformInvalid("unexpected signer")),
824                L::Enum(_) => return Err(FormatError::TransformInvalid("unexpected enum")),
825                L::Struct(_) => return Err(FormatError::TransformInvalid("unexpected struct")),
826                L::Vector(_) => return Err(FormatError::TransformInvalid("unexpected vector")),
827            },
828        })
829    }
830}
831
832#[cfg(test)]
833pub(crate) mod tests {
834    use std::collections::BTreeMap;
835    use std::str::FromStr;
836    use std::sync::atomic::AtomicUsize;
837
838    use move_core_types::annotated_value::MoveEnumLayout;
839    use move_core_types::annotated_value::MoveFieldLayout;
840    use move_core_types::annotated_value::MoveStructLayout;
841    use move_core_types::annotated_value::MoveTypeLayout as L;
842    use move_core_types::identifier::Identifier;
843    use serde_json::Value as Json;
844    use serde_json::json;
845    use sui_types::MOVE_STDLIB_ADDRESS;
846    use sui_types::base_types::STD_ASCII_MODULE_NAME;
847    use sui_types::base_types::STD_ASCII_STRUCT_NAME;
848    use sui_types::derived_object::derive_object_id;
849    use sui_types::dynamic_field::DynamicFieldInfo;
850    use sui_types::dynamic_field::Field;
851    use sui_types::dynamic_field::derive_dynamic_field_id;
852    use sui_types::id::ID;
853    use sui_types::id::UID;
854
855    use super::*;
856
857    /// Mock Store implementation for testing.
858    #[derive(Default, Clone)]
859    pub struct MockStore {
860        data: BTreeMap<AccountAddress, OwnedSlice>,
861    }
862
863    impl MockStore {
864        /// Add objects representing a dynamic field to the store.
865        ///
866        /// The dynamic field is owned by `parent` and has the given `name` and `value`, with their
867        /// respective layouts.
868        pub(crate) fn with_dynamic_field<N: Serialize, V: Serialize>(
869            mut self,
870            parent: AccountAddress,
871            name: N,
872            name_layout: MoveTypeLayout,
873            value: V,
874            value_layout: MoveTypeLayout,
875        ) -> Self {
876            use Identifier as I;
877            use MoveFieldLayout as F;
878            use MoveStructLayout as S;
879
880            let name_bytes = bcs::to_bytes(&name).unwrap();
881            let name_type = TypeTag::from(&name_layout);
882            let value_type = TypeTag::from(&value_layout);
883            let df_id = derive_dynamic_field_id(parent, &name_type, &name_bytes).unwrap();
884
885            let bytes = bcs::to_bytes(&Field {
886                id: UID::new(df_id),
887                name,
888                value,
889            })
890            .unwrap();
891
892            let layout = L::Struct(Box::new(S {
893                type_: DynamicFieldInfo::dynamic_field_type(name_type, value_type),
894                fields: vec![
895                    F::new(I::new("id").unwrap(), L::Struct(Box::new(UID::layout()))),
896                    F::new(I::new("name").unwrap(), name_layout),
897                    F::new(I::new("value").unwrap(), value_layout),
898                ],
899            }));
900
901            self.data.insert(df_id.into(), OwnedSlice { layout, bytes });
902            self
903        }
904
905        /// Add objects representing a dynamic object field to the store.
906        ///
907        /// The dynamic object field is owned by `parent` and has the given `name` and `value`,
908        /// with their respective layouts. `value` is expected to start with a UID, as it must be
909        /// an object (its type must have `key`).
910        pub(crate) fn with_dynamic_object_field<N: Serialize, V: Serialize>(
911            mut self,
912            parent: AccountAddress,
913            name: N,
914            name_layout: MoveTypeLayout,
915            value: V,
916            value_layout: MoveTypeLayout,
917        ) -> Self {
918            use AccountAddress as A;
919            use Identifier as I;
920            use MoveFieldLayout as F;
921            use MoveStructLayout as S;
922
923            let name_bytes = bcs::to_bytes(&name).unwrap();
924            let value_bytes = bcs::to_bytes(&value).unwrap();
925            let name_type = TypeTag::from(&name_layout);
926            let wrap_type = DynamicFieldInfo::dynamic_object_field_wrapper(name_type);
927            let val_id = A::from_bytes(&value_bytes[0..AccountAddress::LENGTH]).unwrap();
928            let dof_id =
929                derive_dynamic_field_id(parent, &wrap_type.clone().into(), &name_bytes).unwrap();
930
931            let field_bytes = bcs::to_bytes(&Field {
932                id: UID::new(dof_id),
933                name,
934                value: val_id,
935            })
936            .unwrap();
937
938            let wrapper_layout = L::Struct(Box::new(S {
939                type_: wrap_type.clone(),
940                fields: vec![F::new(I::new("name").unwrap(), name_layout)],
941            }));
942
943            let field_layout = L::Struct(Box::new(S {
944                type_: DynamicFieldInfo::dynamic_field_type(wrap_type.into(), ID::type_().into()),
945                fields: vec![
946                    F::new(I::new("id").unwrap(), L::Struct(Box::new(UID::layout()))),
947                    F::new(I::new("name").unwrap(), wrapper_layout),
948                    F::new(I::new("value").unwrap(), L::Struct(Box::new(ID::layout()))),
949                ],
950            }));
951
952            let field = OwnedSlice {
953                layout: field_layout,
954                bytes: field_bytes,
955            };
956
957            let value = OwnedSlice {
958                layout: value_layout,
959                bytes: value_bytes,
960            };
961
962            self.data.insert(dof_id.into(), field);
963            self.data.insert(val_id, value);
964            self
965        }
966
967        /// Add a derived object to the store.
968        pub(crate) fn with_derived_object<N: Serialize, V: Serialize>(
969            mut self,
970            parent: AccountAddress,
971            name: N,
972            name_layout: MoveTypeLayout,
973            value: V,
974            value_layout: MoveTypeLayout,
975        ) -> Self {
976            let name_bytes = bcs::to_bytes(&name).unwrap();
977            let name_type = TypeTag::from(&name_layout);
978            let id = derive_object_id(parent, &name_type, &name_bytes).unwrap();
979
980            self.data.insert(
981                id.into(),
982                OwnedSlice {
983                    layout: value_layout,
984                    bytes: bcs::to_bytes(&value).unwrap(),
985                },
986            );
987            self
988        }
989    }
990
991    #[async_trait]
992    impl Store for MockStore {
993        async fn object(&self, id: AccountAddress) -> anyhow::Result<Option<OwnedSlice>> {
994            Ok(self.data.get(&id).cloned())
995        }
996    }
997
998    pub fn struct_(type_: &str, fields: Vec<(&str, MoveTypeLayout)>) -> MoveTypeLayout {
999        let type_: StructTag = type_.parse().unwrap();
1000        let fields = fields
1001            .into_iter()
1002            .map(|(name, layout)| MoveFieldLayout::new(Identifier::new(name).unwrap(), layout))
1003            .collect();
1004
1005        MoveTypeLayout::Struct(Box::new(MoveStructLayout { type_, fields }))
1006    }
1007
1008    pub fn enum_(
1009        type_: &str,
1010        variants: Vec<(&str, Vec<(&str, MoveTypeLayout)>)>,
1011    ) -> MoveTypeLayout {
1012        let type_: StructTag = type_.parse().unwrap();
1013        let variants = variants
1014            .into_iter()
1015            .enumerate()
1016            .map(|(tag, (name, fields))| {
1017                let fields = fields
1018                    .into_iter()
1019                    .map(|(name, layout)| {
1020                        MoveFieldLayout::new(Identifier::new(name).unwrap(), layout)
1021                    })
1022                    .collect();
1023
1024                ((Identifier::new(name).unwrap(), tag as u16), fields)
1025            })
1026            .collect();
1027
1028        MoveTypeLayout::Enum(Box::new(MoveEnumLayout { type_, variants }))
1029    }
1030
1031    pub fn vector_(layout: MoveTypeLayout) -> MoveTypeLayout {
1032        MoveTypeLayout::Vector(Box::new(layout))
1033    }
1034
1035    pub fn optional_(layout: MoveTypeLayout) -> MoveTypeLayout {
1036        let type_ = TypeTag::from(&layout);
1037        struct_(
1038            &format!("0x1::option::Option<{type_}>"),
1039            vec![("vec", vector_(layout))],
1040        )
1041    }
1042
1043    pub fn vec_map(key: MoveTypeLayout, value: MoveTypeLayout) -> MoveTypeLayout {
1044        let key_type = TypeTag::from(&key);
1045        let value_type = TypeTag::from(&value);
1046
1047        struct_(
1048            &format!("0x2::vec_map::VecMap<{key_type}, {value_type}>"),
1049            vec![(
1050                "contents",
1051                vector_(struct_(
1052                    &format!("0x2::vec_map::Entry<{key_type}, {value_type}>"),
1053                    vec![("key", key), ("value", value)],
1054                )),
1055            )],
1056        )
1057    }
1058
1059    #[test]
1060    fn test_slice_serialize_roundtrip() {
1061        let bytes = &[0x01, 0x02, 0x03, 0x04];
1062        let slice = Slice {
1063            layout: &L::U64,
1064            bytes,
1065        };
1066
1067        let serialized = bcs::to_bytes(&slice).unwrap();
1068        assert_eq!(serialized, bytes);
1069    }
1070
1071    #[test]
1072    fn test_serialize_bool() {
1073        assert_eq!(
1074            bcs::to_bytes(&Value::Bool(true)).unwrap(),
1075            bcs::to_bytes(&true).unwrap()
1076        );
1077        assert_eq!(
1078            bcs::to_bytes(&Value::Bool(false)).unwrap(),
1079            bcs::to_bytes(&false).unwrap()
1080        );
1081    }
1082
1083    #[test]
1084    fn test_serialize_u8() {
1085        assert_eq!(
1086            bcs::to_bytes(&Value::U8(42)).unwrap(),
1087            bcs::to_bytes(&42u8).unwrap()
1088        );
1089    }
1090
1091    #[test]
1092    fn test_serialize_u16() {
1093        assert_eq!(
1094            bcs::to_bytes(&Value::U16(1234)).unwrap(),
1095            bcs::to_bytes(&1234u16).unwrap()
1096        );
1097    }
1098
1099    #[test]
1100    fn test_serialize_u32() {
1101        assert_eq!(
1102            bcs::to_bytes(&Value::U32(123456)).unwrap(),
1103            bcs::to_bytes(&123456u32).unwrap()
1104        );
1105    }
1106
1107    #[test]
1108    fn test_serialize_u64() {
1109        assert_eq!(
1110            bcs::to_bytes(&Value::U64(12345678901234)).unwrap(),
1111            bcs::to_bytes(&12345678901234u64).unwrap()
1112        );
1113    }
1114
1115    #[test]
1116    fn test_serialize_u128() {
1117        assert_eq!(
1118            bcs::to_bytes(&Value::U128(123456789012345678901234567890)).unwrap(),
1119            bcs::to_bytes(&123456789012345678901234567890u128).unwrap()
1120        );
1121    }
1122
1123    #[test]
1124    fn test_serialize_u256() {
1125        let val = U256::from(42u64);
1126        assert_eq!(
1127            bcs::to_bytes(&Value::U256(val)).unwrap(),
1128            bcs::to_bytes(&val).unwrap()
1129        );
1130    }
1131
1132    #[test]
1133    fn test_serialize_address() {
1134        let addr: AccountAddress = "0x1".parse().unwrap();
1135        assert_eq!(
1136            bcs::to_bytes(&Value::Address(addr)).unwrap(),
1137            bcs::to_bytes(&addr).unwrap()
1138        );
1139    }
1140
1141    #[test]
1142    fn test_serialize_string() {
1143        assert_eq!(
1144            bcs::to_bytes(&Value::String(Cow::Borrowed("hello".as_bytes()))).unwrap(),
1145            bcs::to_bytes("hello").unwrap()
1146        );
1147    }
1148
1149    #[test]
1150    fn test_serialize_bytes() {
1151        let bytes = vec![1u8, 2, 3, 4, 5];
1152        assert_eq!(
1153            bcs::to_bytes(&Value::Bytes(Cow::Borrowed(&bytes))).unwrap(),
1154            bcs::to_bytes(&bytes).unwrap()
1155        );
1156    }
1157
1158    #[test]
1159    fn test_serialize_positional_struct() {
1160        let type_ = &"0x2::foo::Bar".parse().unwrap();
1161        let struct_ = Value::Struct(Struct {
1162            type_,
1163            fields: Fields::Positional(vec![
1164                Value::U64(42),
1165                Value::Bool(true),
1166                Value::String(Cow::Borrowed("test".as_bytes())),
1167            ]),
1168        });
1169
1170        assert_eq!(
1171            bcs::to_bytes(&struct_).unwrap(),
1172            bcs::to_bytes(&(42u64, true, "test")).unwrap()
1173        );
1174    }
1175
1176    #[test]
1177    fn test_serialize_named_struct() {
1178        let type_ = &"0x2::foo::Bar".parse().unwrap();
1179        let addr = "0x300".parse().unwrap();
1180        let struct_ = Value::Struct(Struct {
1181            type_,
1182            fields: Fields::Named(vec![
1183                ("x", Value::U32(100)),
1184                ("y", Value::U32(200)),
1185                ("z", Value::Address(addr)),
1186            ]),
1187        });
1188
1189        assert_eq!(
1190            bcs::to_bytes(&struct_).unwrap(),
1191            bcs::to_bytes(&(100u32, 200u32, addr)).unwrap()
1192        );
1193    }
1194
1195    #[test]
1196    fn test_serialize_empty_struct() {
1197        let type_ = &"0x2::foo::Empty".parse().unwrap();
1198
1199        let positional = Value::Struct(Struct {
1200            type_,
1201            fields: Fields::Positional(vec![]),
1202        });
1203
1204        let named = Value::Struct(Struct {
1205            type_,
1206            fields: Fields::Named(vec![]),
1207        });
1208
1209        assert_eq!(
1210            bcs::to_bytes(&positional).unwrap(),
1211            bcs::to_bytes(&false).unwrap()
1212        );
1213
1214        assert_eq!(
1215            bcs::to_bytes(&named).unwrap(),
1216            bcs::to_bytes(&false).unwrap()
1217        );
1218    }
1219
1220    #[test]
1221    fn test_serialize_enum() {
1222        #[derive(Serialize)]
1223        enum E {
1224            A(u64, bool),
1225            B { x: u32, y: u32 },
1226            C,
1227        }
1228
1229        let type_: StructTag = "0x1::m::E".parse().unwrap();
1230        let enum_ = Value::Enum(Enum {
1231            type_: &type_,
1232            variant_name: Some("A"),
1233            variant_index: 0,
1234            fields: Fields::Positional(vec![Value::U64(42), Value::Bool(true)]),
1235        });
1236
1237        assert_eq!(
1238            bcs::to_bytes(&enum_).unwrap(),
1239            bcs::to_bytes(&E::A(42, true)).unwrap()
1240        );
1241
1242        // Test enum with named fields
1243        let enum_ = Value::Enum(Enum {
1244            type_: &type_,
1245            variant_name: Some("B"),
1246            variant_index: 1,
1247            fields: Fields::Named(vec![("x", Value::U32(100)), ("y", Value::U32(200))]),
1248        });
1249
1250        assert_eq!(
1251            bcs::to_bytes(&enum_).unwrap(),
1252            bcs::to_bytes(&E::B { x: 100, y: 200 }).unwrap()
1253        );
1254
1255        // Test enum with no fields
1256        let enum_ = Value::Enum(Enum {
1257            type_: &type_,
1258            variant_name: Some("C"),
1259            variant_index: 2,
1260            fields: Fields::Positional(vec![]),
1261        });
1262
1263        assert_eq!(
1264            bcs::to_bytes(&enum_).unwrap(),
1265            bcs::to_bytes(&E::C).unwrap()
1266        );
1267    }
1268
1269    #[test]
1270    fn test_serialize_vector() {
1271        let vec = Value::Vector(Vector {
1272            type_: Cow::Owned(TypeTag::U64),
1273            elements: vec![Value::U64(10), Value::U64(20), Value::U64(30)],
1274        });
1275
1276        assert_eq!(
1277            bcs::to_bytes(&vec).unwrap(),
1278            bcs::to_bytes(&vec![10u64, 20, 30]).unwrap()
1279        );
1280
1281        // Test vector of strings
1282        let vec = Value::Vector(Vector {
1283            type_: Cow::Owned(TypeTag::Struct(Box::new(StructTag {
1284                address: MOVE_STDLIB_ADDRESS,
1285                module: STD_ASCII_MODULE_NAME.to_owned(),
1286                name: STD_ASCII_STRUCT_NAME.to_owned(),
1287                type_params: vec![],
1288            }))),
1289            elements: vec![
1290                Value::String(Cow::Borrowed("hello".as_bytes())),
1291                Value::String(Cow::Borrowed("world".as_bytes())),
1292            ],
1293        });
1294
1295        assert_eq!(
1296            bcs::to_bytes(&vec).unwrap(),
1297            bcs::to_bytes(&vec!["hello", "world"]).unwrap()
1298        );
1299
1300        // Test empty vector
1301        let vec = Value::Vector(Vector {
1302            type_: Cow::Owned(TypeTag::U64),
1303            elements: vec![],
1304        });
1305
1306        assert_eq!(bcs::to_bytes(&vec).unwrap(), &[0x00]);
1307    }
1308
1309    #[test]
1310    fn test_literal_to_atom_conversion() {
1311        let values = vec![
1312            Value::Bool(true),
1313            Value::U8(42),
1314            Value::U16(1234),
1315            Value::U32(123456),
1316            Value::U64(12345678),
1317            Value::U128(123456),
1318            Value::U256(U256::from(42u64)),
1319            Value::Address("0x42".parse().unwrap()),
1320            Value::String(Cow::Borrowed("hello".as_bytes())),
1321            Value::Bytes(Cow::Borrowed(&[1, 2, 3])),
1322            Value::Vector(Vector {
1323                type_: Cow::Owned(TypeTag::U8),
1324                elements: vec![
1325                    Value::U8(4),
1326                    Value::U8(5),
1327                    Value::Slice(Slice {
1328                        layout: &L::U8,
1329                        bytes: &[6],
1330                    }),
1331                ],
1332            }),
1333        ];
1334
1335        let atoms = vec![
1336            Atom::Bool(true),
1337            Atom::U8(42),
1338            Atom::U16(1234),
1339            Atom::U32(123456),
1340            Atom::U64(12345678),
1341            Atom::U128(123456),
1342            Atom::U256(U256::from(42u64)),
1343            Atom::Address("0x42".parse().unwrap()),
1344            Atom::Bytes(Cow::Borrowed("hello".as_bytes())),
1345            Atom::Bytes(Cow::Borrowed(&[1, 2, 3])),
1346            Atom::Bytes(Cow::Borrowed(&[4, 5, 6])),
1347        ];
1348
1349        assert_eq!(values.len(), atoms.len());
1350        for (value, expect) in values.into_iter().zip(atoms.into_iter()) {
1351            let actual = Atom::try_from(value).unwrap();
1352            assert_eq!(actual, expect);
1353        }
1354    }
1355
1356    #[test]
1357    fn test_slice_to_atom_converion() {
1358        let bool_bytes = bcs::to_bytes(&true).unwrap();
1359        let u8_bytes = bcs::to_bytes(&42u8).unwrap();
1360        let u16_bytes = bcs::to_bytes(&1234u16).unwrap();
1361        let u32_bytes = bcs::to_bytes(&123456u32).unwrap();
1362        let u64_bytes = bcs::to_bytes(&12345678u64).unwrap();
1363        let u128_bytes = bcs::to_bytes(&123456u128).unwrap();
1364        let u256_bytes = bcs::to_bytes(&U256::from(42u64)).unwrap();
1365        let addr_bytes = bcs::to_bytes(&AccountAddress::from_str("0x42").unwrap()).unwrap();
1366        let str_bytes = bcs::to_bytes("hello").unwrap();
1367        let type_name_bytes = bcs::to_bytes("0000000000000000000000000000000000000000000000000000000000000002::coin::Coin<0000000000000000000000000000000000000000000000000000000000000002::sui::SUI>").unwrap();
1368        let vec_bytes = bcs::to_bytes(&vec![1u8, 2, 3]).unwrap();
1369
1370        let str_layout = L::Struct(Box::new(move_utf8_str_layout()));
1371        let type_name_layout = L::Struct(Box::new(type_name_layout()));
1372        let vec_layout = L::Vector(Box::new(L::U8));
1373
1374        let values = vec![
1375            Value::Slice(Slice {
1376                layout: &L::Bool,
1377                bytes: &bool_bytes,
1378            }),
1379            Value::Slice(Slice {
1380                layout: &L::U8,
1381                bytes: &u8_bytes,
1382            }),
1383            Value::Slice(Slice {
1384                layout: &L::U16,
1385                bytes: &u16_bytes,
1386            }),
1387            Value::Slice(Slice {
1388                layout: &L::U32,
1389                bytes: &u32_bytes,
1390            }),
1391            Value::Slice(Slice {
1392                layout: &L::U64,
1393                bytes: &u64_bytes,
1394            }),
1395            Value::Slice(Slice {
1396                layout: &L::U128,
1397                bytes: &u128_bytes,
1398            }),
1399            Value::Slice(Slice {
1400                layout: &L::U256,
1401                bytes: &u256_bytes,
1402            }),
1403            Value::Slice(Slice {
1404                layout: &L::Address,
1405                bytes: &addr_bytes,
1406            }),
1407            Value::Slice(Slice {
1408                layout: &str_layout,
1409                bytes: &str_bytes,
1410            }),
1411            Value::Slice(Slice {
1412                layout: &type_name_layout,
1413                bytes: &type_name_bytes,
1414            }),
1415            Value::Slice(Slice {
1416                layout: &vec_layout,
1417                bytes: &vec_bytes,
1418            }),
1419        ];
1420
1421        let atoms = vec![
1422            Atom::Bool(true),
1423            Atom::U8(42),
1424            Atom::U16(1234),
1425            Atom::U32(123456),
1426            Atom::U64(12345678),
1427            Atom::U128(123456),
1428            Atom::U256(U256::from(42u64)),
1429            Atom::Address(AccountAddress::from_str("0x42").unwrap()),
1430            Atom::Bytes(Cow::Borrowed("hello".as_bytes())),
1431            Atom::Bytes(Cow::Borrowed("0000000000000000000000000000000000000000000000000000000000000002::coin::Coin<0000000000000000000000000000000000000000000000000000000000000002::sui::SUI>".as_bytes())),
1432            Atom::Bytes(Cow::Borrowed(&[1, 2, 3])),
1433        ];
1434
1435        for (value, expect) in values.into_iter().zip(atoms.into_iter()) {
1436            let actual = Atom::try_from(value).unwrap();
1437            assert_eq!(actual, expect);
1438        }
1439    }
1440
1441    #[test]
1442    fn test_basic_json_formatting() {
1443        let values = vec![
1444            Value::Bool(true),
1445            Value::U8(42),
1446            Value::U16(43),
1447            Value::U32(44),
1448            Value::U64(45),
1449            Value::U128(46),
1450            Value::U256(U256::from(47u64)),
1451            Value::Address("0x48".parse().unwrap()),
1452            Value::String(Cow::Borrowed("hello".as_bytes())),
1453            Value::Bytes(Cow::Borrowed(&[1, 2, 3])),
1454        ];
1455
1456        let json = vec![
1457            json!(true),
1458            json!(42u8),
1459            json!(43u8),
1460            json!(44u8),
1461            json!("45"),
1462            json!("46"),
1463            json!("47"),
1464            json!("0x0000000000000000000000000000000000000000000000000000000000000048"),
1465            json!("hello"),
1466            json!("AQID"),
1467        ];
1468
1469        assert_eq!(values.len(), json.len());
1470        for (value, expect) in values.into_iter().zip(json.into_iter()) {
1471            let used = AtomicUsize::new(0);
1472            let meter = writer::Meter::new(&used, usize::MAX, usize::MAX);
1473            let actual = value.format_json::<Json>(meter).unwrap();
1474            assert_eq!(actual, expect);
1475        }
1476    }
1477
1478    #[test]
1479    fn test_struct_json_formatting() {
1480        let lit = Value::Struct(Struct {
1481            type_: &"0x2::foo::Bar".parse().unwrap(),
1482            fields: Fields::Named(vec![
1483                ("x", Value::U32(100)),
1484                ("y", Value::U32(200)),
1485                ("z", Value::Address("0x300".parse().unwrap())),
1486            ]),
1487        });
1488
1489        let slice = Value::Slice(Slice {
1490            layout: &struct_(
1491                "0x2::foo::Bar",
1492                vec![("x", L::U32), ("y", L::U32), ("z", L::Address)],
1493            ),
1494            bytes: &bcs::to_bytes(&(100u32, 200u32, "0x300".parse::<AccountAddress>().unwrap()))
1495                .unwrap(),
1496        });
1497
1498        let expect = json!({
1499            "x": 100u32,
1500            "y": 200u32,
1501            "z": "0x0000000000000000000000000000000000000000000000000000000000000300"
1502        });
1503
1504        let used = AtomicUsize::new(0);
1505        let mut meter = writer::Meter::new(&used, usize::MAX, usize::MAX);
1506        assert_eq!(expect, lit.format_json::<Json>(meter.reborrow()).unwrap());
1507        assert_eq!(expect, slice.format_json::<Json>(meter.reborrow()).unwrap());
1508    }
1509
1510    #[test]
1511    fn test_enum_named_variant_json_formatting() {
1512        let lit = Value::Enum(Enum {
1513            type_: &"0x1::m::E".parse().unwrap(),
1514            variant_name: Some("A"),
1515            variant_index: 0,
1516            fields: Fields::Named(vec![("b", Value::U64(42)), ("c", Value::Bool(true))]),
1517        });
1518
1519        let slice = Value::Slice(Slice {
1520            layout: &enum_(
1521                "0x1::m::E",
1522                vec![("A", vec![("b", L::U64), ("c", L::Bool)])],
1523            ),
1524            bytes: &bcs::to_bytes(&(0u8, 42u64, true)).unwrap(),
1525        });
1526
1527        let expect = json!({
1528            "@variant": "A",
1529            "b": "42",
1530            "c": true
1531        });
1532
1533        let used = AtomicUsize::new(0);
1534        let mut meter = writer::Meter::new(&used, usize::MAX, usize::MAX);
1535        assert_eq!(expect, lit.format_json::<Json>(meter.reborrow()).unwrap());
1536        assert_eq!(expect, slice.format_json::<Json>(meter.reborrow()).unwrap());
1537    }
1538
1539    #[test]
1540    fn test_enum_numeric_variant_json_formatting() {
1541        let literal = Value::Enum(Enum {
1542            type_: &"0x1::m::E".parse().unwrap(),
1543            variant_name: None,
1544            variant_index: 0,
1545            fields: Fields::Named(vec![("b", Value::U64(42)), ("c", Value::Bool(true))]),
1546        });
1547
1548        let expect = json!({
1549            "@variant": 0,
1550            "b": "42",
1551            "c": true
1552        });
1553
1554        let used = AtomicUsize::new(0);
1555        let meter = writer::Meter::new(&used, usize::MAX, usize::MAX);
1556        assert_eq!(expect, literal.format_json::<Json>(meter).unwrap());
1557    }
1558}