1use std::{
6 collections::BTreeMap,
7 fmt::{self, Display, Formatter, Write},
8 str::FromStr,
9};
10
11use colored::Colorize;
12use iota_macros::EnumVariantOrder;
13use iota_types::{
14 base_types::{IotaAddress, ObjectID},
15 error::{IotaError, UserInputError},
16 iota_serde::IotaStructTag,
17};
18use itertools::Itertools;
19use move_binary_format::{
20 file_format::{Ability, AbilitySet, DatatypeTyParameter, Visibility},
21 normalized::{
22 self, Enum as NormalizedEnum, Field as NormalizedField, Function as NormalizedFunction,
23 Module as NormalizedModule, Struct as NormalizedStruct, Type as NormalizedType,
24 },
25};
26use move_core_types::{
27 annotated_value::{MoveStruct, MoveValue, MoveVariant},
28 identifier::Identifier,
29 language_storage::StructTag,
30};
31use schemars::JsonSchema;
32use serde::{Deserialize, Serialize};
33use serde_json::{Value, json};
34use serde_with::serde_as;
35use tracing::warn;
36
37pub type IotaMoveTypeParameterIndex = u16;
38
39#[cfg(test)]
40#[path = "unit_tests/iota_move_tests.rs"]
41mod iota_move_tests;
42
43#[derive(Serialize, Deserialize, Copy, Clone, Debug, JsonSchema, PartialEq)]
44pub enum IotaMoveAbility {
45 Copy,
46 Drop,
47 Store,
48 Key,
49}
50
51#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)]
52pub struct IotaMoveAbilitySet {
53 pub abilities: Vec<IotaMoveAbility>,
54}
55
56#[derive(Serialize, Deserialize, Copy, Clone, Debug, JsonSchema, PartialEq)]
57pub enum IotaMoveVisibility {
58 Private,
59 Public,
60 Friend,
61}
62
63#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)]
64#[serde(rename_all = "camelCase")]
65pub struct IotaMoveStructTypeParameter {
66 pub constraints: IotaMoveAbilitySet,
67 pub is_phantom: bool,
68}
69
70#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)]
71pub struct IotaMoveNormalizedField {
72 pub name: String,
73 #[serde(rename = "type")]
74 pub type_: IotaMoveNormalizedType,
75}
76
77#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)]
78#[serde(rename_all = "camelCase")]
79pub struct IotaMoveNormalizedStruct {
80 pub abilities: IotaMoveAbilitySet,
81 pub type_parameters: Vec<IotaMoveStructTypeParameter>,
82 pub fields: Vec<IotaMoveNormalizedField>,
83}
84
85#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)]
86#[serde(rename_all = "camelCase")]
87pub struct IotaMoveNormalizedEnum {
88 pub abilities: IotaMoveAbilitySet,
89 pub type_parameters: Vec<IotaMoveStructTypeParameter>,
90 pub variants: BTreeMap<String, Vec<IotaMoveNormalizedField>>,
91}
92
93#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)]
94pub enum IotaMoveNormalizedType {
95 Bool,
96 U8,
97 U16,
98 U32,
99 U64,
100 U128,
101 U256,
102 Address,
103 Signer,
104 Struct {
105 #[serde(flatten)]
106 inner: Box<IotaMoveNormalizedStructType>,
107 },
108 Vector(Box<IotaMoveNormalizedType>),
109 TypeParameter(IotaMoveTypeParameterIndex),
110 Reference(Box<IotaMoveNormalizedType>),
111 MutableReference(Box<IotaMoveNormalizedType>),
112}
113
114#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone, PartialEq)]
115#[serde(rename_all = "camelCase")]
116pub struct IotaMoveNormalizedStructType {
117 pub address: String,
118 pub module: String,
119 pub name: String,
120 pub type_arguments: Vec<IotaMoveNormalizedType>,
121}
122#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)]
123#[serde(rename_all = "camelCase")]
124pub struct IotaMoveNormalizedFunction {
125 pub visibility: IotaMoveVisibility,
126 pub is_entry: bool,
127 pub type_parameters: Vec<IotaMoveAbilitySet>,
128 pub parameters: Vec<IotaMoveNormalizedType>,
129 pub return_: Vec<IotaMoveNormalizedType>,
130}
131
132#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)]
133pub struct IotaMoveModuleId {
134 address: String,
135 name: String,
136}
137
138#[serde_as]
140#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
141#[serde(rename_all = "camelCase")]
142pub struct MoveFunctionName {
143 pub package: ObjectID,
145 pub module: String,
147 pub function: String,
149}
150
151impl FromStr for MoveFunctionName {
152 type Err = IotaError;
153
154 fn from_str(s: &str) -> Result<Self, Self::Err> {
155 let (module, name) =
156 iota_types::parse_iota_fq_name(s).map_err(|e| UserInputError::InvalidIdentifier {
157 error: e.to_string(),
158 })?;
159 let package = ObjectID::from_address(*module.address());
160 Ok(Self {
161 package,
162 module: module.name().to_string(),
163 function: name.to_string(),
164 })
165 }
166}
167
168#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)]
169#[serde(rename_all = "camelCase")]
170pub struct IotaMoveNormalizedModule {
171 pub file_format_version: u32,
172 pub address: String,
173 pub name: String,
174 pub friends: Vec<IotaMoveModuleId>,
175 pub structs: BTreeMap<String, IotaMoveNormalizedStruct>,
176 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
177 pub enums: BTreeMap<String, IotaMoveNormalizedEnum>,
178 pub exposed_functions: BTreeMap<String, IotaMoveNormalizedFunction>,
179}
180
181impl PartialEq for IotaMoveNormalizedModule {
182 fn eq(&self, other: &Self) -> bool {
183 self.file_format_version == other.file_format_version
184 && self.address == other.address
185 && self.name == other.name
186 }
187}
188
189impl<S: std::hash::Hash + Eq + ToString> From<&NormalizedModule<S>> for IotaMoveNormalizedModule {
190 fn from(module: &NormalizedModule<S>) -> Self {
191 Self {
192 file_format_version: module.file_format_version,
193 address: module.address().to_hex_literal(),
194 name: module.name().to_string(),
195 friends: module
196 .friends
197 .iter()
198 .map(|module_id| IotaMoveModuleId {
199 address: module_id.address.to_hex_literal(),
200 name: module_id.name.to_string(),
201 })
202 .collect::<Vec<IotaMoveModuleId>>(),
203 structs: module
204 .structs
205 .iter()
206 .map(|(name, struct_)| {
207 (name.to_string(), IotaMoveNormalizedStruct::from(&**struct_))
208 })
209 .collect::<BTreeMap<String, IotaMoveNormalizedStruct>>(),
210 enums: module
211 .enums
212 .iter()
213 .map(|(name, enum_)| (name.to_string(), IotaMoveNormalizedEnum::from(&**enum_)))
214 .collect(),
215 exposed_functions: module
216 .functions
217 .iter()
218 .filter(|(_name, function)| {
219 function.is_entry || function.visibility != Visibility::Private
220 })
221 .map(|(name, function)| {
222 (
225 name.to_string(),
226 IotaMoveNormalizedFunction::from(&**function),
227 )
228 })
229 .collect::<BTreeMap<String, IotaMoveNormalizedFunction>>(),
230 }
231 }
232}
233
234impl<S: ToString> From<&NormalizedFunction<S>> for IotaMoveNormalizedFunction {
235 fn from(function: &NormalizedFunction<S>) -> Self {
236 Self {
237 visibility: match function.visibility {
238 Visibility::Private => IotaMoveVisibility::Private,
239 Visibility::Public => IotaMoveVisibility::Public,
240 Visibility::Friend => IotaMoveVisibility::Friend,
241 },
242 is_entry: function.is_entry,
243 type_parameters: function
244 .type_parameters
245 .iter()
246 .copied()
247 .map(|a| a.into())
248 .collect::<Vec<IotaMoveAbilitySet>>(),
249 parameters: function
250 .parameters
251 .iter()
252 .map(|t| IotaMoveNormalizedType::from(&**t))
253 .collect::<Vec<IotaMoveNormalizedType>>(),
254 return_: function
255 .return_
256 .iter()
257 .map(|t| IotaMoveNormalizedType::from(&**t))
258 .collect::<Vec<IotaMoveNormalizedType>>(),
259 }
260 }
261}
262
263impl<S: ToString> From<&NormalizedStruct<S>> for IotaMoveNormalizedStruct {
264 fn from(struct_: &NormalizedStruct<S>) -> Self {
265 Self {
266 abilities: struct_.abilities.into(),
267 type_parameters: struct_
268 .type_parameters
269 .iter()
270 .copied()
271 .map(IotaMoveStructTypeParameter::from)
272 .collect::<Vec<IotaMoveStructTypeParameter>>(),
273 fields: struct_
274 .fields
275 .iter()
276 .map(|f| IotaMoveNormalizedField::from(&**f))
277 .collect::<Vec<IotaMoveNormalizedField>>(),
278 }
279 }
280}
281
282impl<S: ToString> From<&NormalizedEnum<S>> for IotaMoveNormalizedEnum {
283 fn from(value: &NormalizedEnum<S>) -> Self {
284 Self {
285 abilities: value.abilities.into(),
286 type_parameters: value
287 .type_parameters
288 .iter()
289 .copied()
290 .map(Into::into)
291 .collect(),
292 variants: value
293 .variants
294 .iter()
295 .map(|variant| {
296 (
297 variant.name.to_string(),
298 variant.fields.iter().map(Into::into).collect(),
299 )
300 })
301 .collect(),
302 }
303 }
304}
305
306impl From<DatatypeTyParameter> for IotaMoveStructTypeParameter {
307 fn from(type_parameter: DatatypeTyParameter) -> Self {
308 Self {
309 constraints: type_parameter.constraints.into(),
310 is_phantom: type_parameter.is_phantom,
311 }
312 }
313}
314
315impl<S: ToString> From<&NormalizedField<S>> for IotaMoveNormalizedField {
316 fn from(normalized_field: &NormalizedField<S>) -> Self {
317 Self {
318 name: normalized_field.name.to_string(),
319 type_: IotaMoveNormalizedType::from(&normalized_field.type_),
320 }
321 }
322}
323
324impl<S: ToString> From<&NormalizedType<S>> for IotaMoveNormalizedType {
325 fn from(type_: &NormalizedType<S>) -> Self {
326 match type_ {
327 NormalizedType::Bool => IotaMoveNormalizedType::Bool,
328 NormalizedType::U8 => IotaMoveNormalizedType::U8,
329 NormalizedType::U16 => IotaMoveNormalizedType::U16,
330 NormalizedType::U32 => IotaMoveNormalizedType::U32,
331 NormalizedType::U64 => IotaMoveNormalizedType::U64,
332 NormalizedType::U128 => IotaMoveNormalizedType::U128,
333 NormalizedType::U256 => IotaMoveNormalizedType::U256,
334 NormalizedType::Address => IotaMoveNormalizedType::Address,
335 NormalizedType::Signer => IotaMoveNormalizedType::Signer,
336 NormalizedType::Datatype(dt) => {
337 let normalized::Datatype {
338 module,
339 name,
340 type_arguments,
341 } = &**dt;
342 IotaMoveNormalizedType::new_struct(
343 module.address.to_hex_literal(),
344 module.name.to_string(),
345 name.to_string(),
346 type_arguments
347 .iter()
348 .map(IotaMoveNormalizedType::from)
349 .collect::<Vec<IotaMoveNormalizedType>>(),
350 )
351 }
352 NormalizedType::Vector(v) => {
353 IotaMoveNormalizedType::Vector(Box::new(IotaMoveNormalizedType::from(&**v)))
354 }
355 NormalizedType::TypeParameter(t) => IotaMoveNormalizedType::TypeParameter(*t),
356 NormalizedType::Reference(false, r) => {
357 IotaMoveNormalizedType::Reference(Box::new(IotaMoveNormalizedType::from(&**r)))
358 }
359 NormalizedType::Reference(true, mr) => IotaMoveNormalizedType::MutableReference(
360 Box::new(IotaMoveNormalizedType::from(&**mr)),
361 ),
362 }
363 }
364}
365
366impl From<AbilitySet> for IotaMoveAbilitySet {
367 fn from(set: AbilitySet) -> IotaMoveAbilitySet {
368 Self {
369 abilities: set
370 .into_iter()
371 .map(|a| match a {
372 Ability::Copy => IotaMoveAbility::Copy,
373 Ability::Drop => IotaMoveAbility::Drop,
374 Ability::Key => IotaMoveAbility::Key,
375 Ability::Store => IotaMoveAbility::Store,
376 })
377 .collect::<Vec<IotaMoveAbility>>(),
378 }
379 }
380}
381
382impl IotaMoveNormalizedType {
383 pub fn new_struct(
384 address: String,
385 module: String,
386 name: String,
387 type_arguments: Vec<IotaMoveNormalizedType>,
388 ) -> Self {
389 IotaMoveNormalizedType::Struct {
390 inner: Box::new(IotaMoveNormalizedStructType {
391 address,
392 module,
393 name,
394 type_arguments,
395 }),
396 }
397 }
398}
399
400#[derive(Serialize, Deserialize, Copy, Clone, Debug, JsonSchema, PartialEq)]
401pub enum ObjectValueKind {
402 ByImmutableReference,
403 ByMutableReference,
404 ByValue,
405}
406
407#[derive(Serialize, Deserialize, Copy, Clone, Debug, JsonSchema, PartialEq)]
408pub enum MoveFunctionArgType {
409 Pure,
410 Object(ObjectValueKind),
411}
412
413#[serde_as]
414#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Eq, PartialEq, EnumVariantOrder)]
415#[serde(untagged, rename = "MoveValue")]
416pub enum IotaMoveValue {
417 Number(u32),
419 Bool(bool),
420 Address(IotaAddress),
421 Vector(Vec<IotaMoveValue>),
422 String(String),
423 UID { id: ObjectID },
424 Struct(IotaMoveStruct),
425 Option(Box<Option<IotaMoveValue>>),
426 Variant(IotaMoveVariant),
427}
428
429impl IotaMoveValue {
430 pub fn to_json_value(self) -> Value {
432 match self {
433 IotaMoveValue::Struct(move_struct) => move_struct.to_json_value(),
434 IotaMoveValue::Vector(values) => IotaMoveStruct::Runtime(values).to_json_value(),
435 IotaMoveValue::Number(v) => json!(v),
436 IotaMoveValue::Bool(v) => json!(v),
437 IotaMoveValue::Address(v) => json!(v),
438 IotaMoveValue::String(v) => json!(v),
439 IotaMoveValue::UID { id } => json!({ "id": id }),
440 IotaMoveValue::Option(v) => json!(v),
441 IotaMoveValue::Variant(v) => v.to_json_value(),
442 }
443 }
444}
445
446impl Display for IotaMoveValue {
447 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
448 let mut writer = String::new();
449 match self {
450 IotaMoveValue::Number(value) => write!(writer, "{value}")?,
451 IotaMoveValue::Bool(value) => write!(writer, "{value}")?,
452 IotaMoveValue::Address(value) => write!(writer, "{value}")?,
453 IotaMoveValue::String(value) => write!(writer, "{value}")?,
454 IotaMoveValue::UID { id } => write!(writer, "{id}")?,
455 IotaMoveValue::Struct(value) => write!(writer, "{value}")?,
456 IotaMoveValue::Option(value) => write!(writer, "{value:?}")?,
457 IotaMoveValue::Vector(vec) => {
458 write!(
459 writer,
460 "{}",
461 vec.iter().map(|value| format!("{value}")).join(",\n")
462 )?;
463 }
464 IotaMoveValue::Variant(value) => write!(writer, "{value}")?,
465 }
466 write!(f, "{}", writer.trim_end_matches('\n'))
467 }
468}
469
470impl From<MoveValue> for IotaMoveValue {
471 fn from(value: MoveValue) -> Self {
472 match value {
473 MoveValue::U8(value) => IotaMoveValue::Number(value.into()),
474 MoveValue::U16(value) => IotaMoveValue::Number(value.into()),
475 MoveValue::U32(value) => IotaMoveValue::Number(value),
476 MoveValue::U64(value) => IotaMoveValue::String(format!("{value}")),
477 MoveValue::U128(value) => IotaMoveValue::String(format!("{value}")),
478 MoveValue::U256(value) => IotaMoveValue::String(format!("{value}")),
479 MoveValue::Bool(value) => IotaMoveValue::Bool(value),
480 MoveValue::Vector(values) => {
481 IotaMoveValue::Vector(values.into_iter().map(|value| value.into()).collect())
482 }
483 MoveValue::Struct(value) => {
484 let MoveStruct { type_, fields } = &value;
486 if let Some(value) = try_convert_type(type_, fields) {
487 return value;
488 }
489 IotaMoveValue::Struct(value.into())
490 }
491 MoveValue::Signer(value) | MoveValue::Address(value) => {
492 IotaMoveValue::Address(IotaAddress::from(ObjectID::from(value)))
493 }
494 MoveValue::Variant(MoveVariant {
495 type_,
496 variant_name,
497 tag: _,
498 fields,
499 }) => IotaMoveValue::Variant(IotaMoveVariant {
500 type_: type_.clone(),
501 variant: variant_name.to_string(),
502 fields: fields
503 .into_iter()
504 .map(|(id, value)| (id.into_string(), value.into()))
505 .collect::<BTreeMap<_, _>>(),
506 }),
507 }
508 }
509}
510
511fn to_bytearray(value: &[MoveValue]) -> Option<Vec<u8>> {
512 if value.iter().all(|value| matches!(value, MoveValue::U8(_))) {
513 let bytearray = value
514 .iter()
515 .flat_map(|value| {
516 if let MoveValue::U8(u8) = value {
517 Some(*u8)
518 } else {
519 None
520 }
521 })
522 .collect::<Vec<_>>();
523 Some(bytearray)
524 } else {
525 None
526 }
527}
528
529#[serde_as]
530#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Eq, PartialEq)]
531#[serde(rename = "MoveVariant")]
532pub struct IotaMoveVariant {
533 #[schemars(with = "String")]
534 #[serde(rename = "type")]
535 #[serde_as(as = "IotaStructTag")]
536 pub type_: StructTag,
537 pub variant: String,
538 pub fields: BTreeMap<String, IotaMoveValue>,
539}
540
541impl IotaMoveVariant {
542 pub fn to_json_value(self) -> Value {
543 let fields = self
546 .fields
547 .into_iter()
548 .map(|(key, value)| (key, value.to_json_value()))
549 .collect::<BTreeMap<_, _>>();
550 json!({
551 "variant": self.variant,
552 "fields": fields,
553 })
554 }
555}
556
557impl Display for IotaMoveVariant {
558 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
559 let mut writer = String::new();
560 let IotaMoveVariant {
561 type_,
562 variant,
563 fields,
564 } = self;
565 writeln!(writer)?;
566 writeln!(writer, " {}: {type_}", "type".bold().bright_black())?;
567 writeln!(writer, " {}: {variant}", "variant".bold().bright_black())?;
568 for (name, value) in fields {
569 let value = format!("{value}");
570 let value = if value.starts_with('\n') {
571 indent(&value, 2)
572 } else {
573 value
574 };
575 writeln!(writer, " {}: {value}", name.bold().bright_black())?;
576 }
577
578 write!(f, "{}", writer.trim_end_matches('\n'))
579 }
580}
581
582#[serde_as]
583#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Eq, PartialEq, EnumVariantOrder)]
584#[serde(untagged, rename = "MoveStruct")]
585pub enum IotaMoveStruct {
586 Runtime(Vec<IotaMoveValue>),
587 WithTypes {
588 #[schemars(with = "String")]
589 #[serde(rename = "type")]
590 #[serde_as(as = "IotaStructTag")]
591 type_: StructTag,
592 fields: BTreeMap<String, IotaMoveValue>,
593 },
594 WithFields(BTreeMap<String, IotaMoveValue>),
595}
596
597impl IotaMoveStruct {
598 pub fn to_json_value(self) -> Value {
600 match self {
602 IotaMoveStruct::Runtime(values) => {
603 let values = values
604 .into_iter()
605 .map(|value| value.to_json_value())
606 .collect::<Vec<_>>();
607 json!(values)
608 }
609 IotaMoveStruct::WithTypes { type_: _, fields } | IotaMoveStruct::WithFields(fields) => {
612 let fields = fields
613 .into_iter()
614 .map(|(key, value)| (key, value.to_json_value()))
615 .collect::<BTreeMap<_, _>>();
616 json!(fields)
617 }
618 }
619 }
620
621 pub fn read_dynamic_field_value(&self, field_name: &str) -> Option<IotaMoveValue> {
622 match self {
623 IotaMoveStruct::WithFields(fields) => fields.get(field_name).cloned(),
624 IotaMoveStruct::WithTypes { type_: _, fields } => fields.get(field_name).cloned(),
625 _ => None,
626 }
627 }
628}
629
630impl Display for IotaMoveStruct {
631 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
632 let mut writer = String::new();
633 match self {
634 IotaMoveStruct::Runtime(_) => {}
635 IotaMoveStruct::WithFields(fields) => {
636 for (name, value) in fields {
637 writeln!(writer, "{}: {value}", name.bold().bright_black())?;
638 }
639 }
640 IotaMoveStruct::WithTypes { type_, fields } => {
641 writeln!(writer)?;
642 writeln!(writer, " {}: {type_}", "type".bold().bright_black())?;
643 for (name, value) in fields {
644 let value = format!("{value}");
645 let value = if value.starts_with('\n') {
646 indent(&value, 2)
647 } else {
648 value
649 };
650 writeln!(writer, " {}: {value}", name.bold().bright_black())?;
651 }
652 }
653 }
654 write!(f, "{}", writer.trim_end_matches('\n'))
655 }
656}
657
658fn indent<T: Display>(d: &T, indent: usize) -> String {
659 d.to_string()
660 .lines()
661 .map(|line| format!("{:indent$}{line}", ""))
662 .join("\n")
663}
664
665fn try_convert_type(
666 type_: &StructTag,
667 fields: &[(Identifier, MoveValue)],
668) -> Option<IotaMoveValue> {
669 let struct_name = format!(
670 "0x{}::{}::{}",
671 type_.address.short_str_lossless(),
672 type_.module,
673 type_.name
674 );
675 let mut values = fields
676 .iter()
677 .map(|(id, value)| (id.to_string(), value))
678 .collect::<BTreeMap<_, _>>();
679 match struct_name.as_str() {
680 "0x1::string::String" | "0x1::ascii::String" => {
681 if let Some(MoveValue::Vector(bytes)) = values.remove("bytes") {
682 return to_bytearray(bytes)
683 .and_then(|bytes| String::from_utf8(bytes).ok())
684 .map(IotaMoveValue::String);
685 }
686 }
687 "0x2::url::Url" => {
688 return values.remove("url").cloned().map(IotaMoveValue::from);
689 }
690 "0x2::object::ID" => {
691 return values.remove("bytes").cloned().map(IotaMoveValue::from);
692 }
693 "0x2::object::UID" => {
694 let id = values.remove("id").cloned().map(IotaMoveValue::from);
695 if let Some(IotaMoveValue::Address(address)) = id {
696 return Some(IotaMoveValue::UID {
697 id: ObjectID::from(address),
698 });
699 }
700 }
701 "0x2::balance::Balance" => {
702 return values.remove("value").cloned().map(IotaMoveValue::from);
703 }
704 "0x1::option::Option" => {
705 if let Some(MoveValue::Vector(values)) = values.remove("vec") {
706 return Some(IotaMoveValue::Option(Box::new(
707 values.first().cloned().map(IotaMoveValue::from),
709 )));
710 }
711 }
712 _ => return None,
713 }
714 warn!(
715 fields =? fields,
716 "failed to convert {struct_name} to IotaMoveValue"
717 );
718 None
719}
720
721impl From<MoveStruct> for IotaMoveStruct {
722 fn from(move_struct: MoveStruct) -> Self {
723 IotaMoveStruct::WithTypes {
724 type_: move_struct.type_,
725 fields: move_struct
726 .fields
727 .into_iter()
728 .map(|(id, value)| (id.into_string(), value.into()))
729 .collect(),
730 }
731 }
732}
733
734#[test]
735fn enum_size() {
736 assert_eq!(std::mem::size_of::<IotaMoveNormalizedType>(), 16);
737}