iota_types/object/
option_visitor.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2025 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5//! Shared OptionVisitor implementation for deserializing Move Option types.
6
7use move_core_types::{
8    account_address::AccountAddress, annotated_value::MoveTypeLayout, annotated_visitor as AV,
9    u256::U256,
10};
11
12use crate::base_types::RESOLVED_STD_OPTION;
13
14/// Error type for OptionVisitor.
15#[derive(thiserror::Error, Debug)]
16#[error("Unexpected type")]
17pub struct Error;
18
19/// A visitor that deserializes an `Option<T>` by interpreting an empty vector
20/// as `None` and a single-element vector as `Some(T)`.
21pub struct OptionVisitor<'a, T>(pub &'a mut T);
22
23impl<'b, 'l, T, E> AV::Visitor<'b, 'l> for OptionVisitor<'_, T>
24where
25    T: AV::Visitor<'b, 'l, Error = E>,
26    E: From<Error> + From<AV::Error>,
27{
28    type Value = Option<T::Value>;
29    type Error = E;
30
31    fn visit_vector(
32        &mut self,
33        driver: &mut AV::VecDriver<'_, 'b, 'l>,
34    ) -> Result<Self::Value, Self::Error> {
35        match driver.len() {
36            0 => Ok(None),
37            1 => driver.next_element(self.0),
38            _ => Err(Error.into()),
39        }
40    }
41
42    fn visit_struct(
43        &mut self,
44        driver: &mut AV::StructDriver<'_, 'b, 'l>,
45    ) -> Result<Self::Value, Self::Error> {
46        if is_option(driver.struct_layout()) {
47            driver
48                .next_field(self)?
49                .ok_or_else(|| Error.into())
50                .map(|(_, option)| option)
51        } else {
52            Err(Error.into())
53        }
54    }
55
56    // === Empty/default cases ===
57
58    fn visit_u8(
59        &mut self,
60        _: &AV::ValueDriver<'_, 'b, 'l>,
61        _: u8,
62    ) -> Result<Self::Value, Self::Error> {
63        Err(Error.into())
64    }
65
66    fn visit_u16(
67        &mut self,
68        _: &AV::ValueDriver<'_, 'b, 'l>,
69        _: u16,
70    ) -> Result<Self::Value, Self::Error> {
71        Err(Error.into())
72    }
73
74    fn visit_u32(
75        &mut self,
76        _: &AV::ValueDriver<'_, 'b, 'l>,
77        _: u32,
78    ) -> Result<Self::Value, Self::Error> {
79        Err(Error.into())
80    }
81
82    fn visit_u64(
83        &mut self,
84        _: &AV::ValueDriver<'_, 'b, 'l>,
85        _: u64,
86    ) -> Result<Self::Value, Self::Error> {
87        Err(Error.into())
88    }
89
90    fn visit_u128(
91        &mut self,
92        _: &AV::ValueDriver<'_, 'b, 'l>,
93        _: u128,
94    ) -> Result<Self::Value, Self::Error> {
95        Err(Error.into())
96    }
97
98    fn visit_u256(
99        &mut self,
100        _: &AV::ValueDriver<'_, 'b, 'l>,
101        _: U256,
102    ) -> Result<Self::Value, Self::Error> {
103        Err(Error.into())
104    }
105
106    fn visit_bool(
107        &mut self,
108        _: &AV::ValueDriver<'_, 'b, 'l>,
109        _: bool,
110    ) -> Result<Self::Value, Self::Error> {
111        Err(Error.into())
112    }
113
114    fn visit_address(
115        &mut self,
116        _: &AV::ValueDriver<'_, 'b, 'l>,
117        _: AccountAddress,
118    ) -> Result<Self::Value, Self::Error> {
119        Err(Error.into())
120    }
121
122    fn visit_signer(
123        &mut self,
124        _: &AV::ValueDriver<'_, 'b, 'l>,
125        _: AccountAddress,
126    ) -> Result<Self::Value, Self::Error> {
127        Err(Error.into())
128    }
129
130    fn visit_variant(
131        &mut self,
132        _: &mut AV::VariantDriver<'_, 'b, 'l>,
133    ) -> Result<Self::Value, Self::Error> {
134        Err(Error.into())
135    }
136}
137
138/// Check if a struct layout represents a Move Option type.
139fn is_option(struct_layout: &move_core_types::annotated_value::MoveStructLayout) -> bool {
140    let ty = &struct_layout.type_;
141
142    if (&ty.address, ty.module.as_ref(), ty.name.as_ref()) != RESOLVED_STD_OPTION {
143        return false;
144    }
145
146    if ty.type_params.len() != 1 {
147        return false;
148    }
149
150    let Some(type_param) = ty.type_params.first() else {
151        return false;
152    };
153
154    if struct_layout.fields.len() != 1 {
155        return false;
156    }
157
158    let Some(field) = struct_layout.fields.first() else {
159        return false;
160    };
161
162    if field.name.as_str() != "vec" {
163        return false;
164    }
165
166    match &field.layout {
167        MoveTypeLayout::Vector(elem) => {
168            if !elem.is_type(type_param) {
169                return false;
170            }
171        }
172        _ => return false,
173    }
174
175    true
176}