1use core::fmt;
4
5use miden_field_repr::{FeltReader, FromFeltRepr};
6
7use crate::{
8 CodecRegistry, Error, Felt, NoteStorage, PrimitiveType, Result, SchemaType, SchemaTypeKind,
9 schema::normalize_name,
10};
11
12#[derive(Clone, Debug, Eq, PartialEq)]
14pub struct DecodedValue {
15 name: Option<String>,
16 docs: Option<String>,
17 fqn: Option<String>,
18 kind: DecodedValueKind,
19}
20
21impl DecodedValue {
22 pub fn name(&self) -> Option<&str> {
24 self.name.as_deref()
25 }
26
27 pub fn docs(&self) -> Option<&str> {
29 self.docs.as_deref()
30 }
31
32 pub fn fqn(&self) -> Option<&str> {
34 self.fqn.as_deref()
35 }
36
37 pub const fn kind(&self) -> &DecodedValueKind {
39 &self.kind
40 }
41
42 pub fn field(&self, name: &str) -> Option<&Self> {
44 let DecodedValueKind::Record(fields) = &self.kind else {
45 return None;
46 };
47 let name = normalize_name(name);
48 fields.iter().find(|field| field.name.as_deref() == Some(name.as_str()))
49 }
50}
51
52#[derive(Clone, Debug, Eq, PartialEq)]
54pub enum DecodedValueKind {
55 Leaf {
57 felts: Vec<Felt>,
59 display: String,
61 },
62 Record(Vec<DecodedValue>),
64 Option(Option<Box<DecodedValue>>),
66 Variant {
68 case: String,
70 value: Option<Box<DecodedValue>>,
72 },
73}
74
75impl fmt::Display for DecodedValue {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 match &self.kind {
78 DecodedValueKind::Leaf { display, .. } => f.write_str(display),
79 DecodedValueKind::Record(fields) => {
80 f.write_str("{")?;
81 for (index, field) in fields.iter().enumerate() {
82 if index != 0 {
83 f.write_str(", ")?;
84 }
85 f.write_str(field.name.as_deref().unwrap_or("<unnamed>"))?;
86 f.write_str(": ")?;
87 field.fmt(f)?;
88 }
89 f.write_str("}")
90 }
91 DecodedValueKind::Option(None) => f.write_str("none"),
92 DecodedValueKind::Option(Some(value)) => write!(f, "some({value})"),
93 DecodedValueKind::Variant { case, value: None } => f.write_str(case),
94 DecodedValueKind::Variant {
95 case,
96 value: Some(value),
97 } => write!(f, "{case}({value})"),
98 }
99 }
100}
101
102pub(crate) fn decode(
104 root: &SchemaType,
105 storage: &NoteStorage,
106 registry: &CodecRegistry,
107) -> Result<DecodedValue> {
108 let (value, consumed) = decode_type(
109 root,
110 storage.items(),
111 Some(registry),
112 root.name().map(str::to_owned),
113 root.docs().map(str::to_owned),
114 )?;
115 if consumed != storage.items().len() {
116 return Err(Error::new(format!(
117 "note storage has {} trailing felt(s) after the schema root",
118 storage.items().len() - consumed
119 )));
120 }
121 Ok(value)
122}
123
124pub(crate) fn validate_encoding(ty: &SchemaType, felts: &[Felt]) -> Result<()> {
126 let (_, consumed) = decode_type(ty, felts, None, None, None)?;
127 if consumed != felts.len() {
128 return Err(Error::new(format!(
129 "value has {} trailing felt(s) after its structural encoding",
130 felts.len() - consumed
131 )));
132 }
133 Ok(())
134}
135
136fn decode_type(
138 ty: &SchemaType,
139 input: &[Felt],
140 registry: Option<&CodecRegistry>,
141 name: Option<String>,
142 docs: Option<String>,
143) -> Result<(DecodedValue, usize)> {
144 if let (Some(registry), Some(fqn)) = (registry, ty.fqn())
145 && let Some(codec) = registry.codec(fqn)
146 {
147 let (_, consumed) = decode_type(ty, input, None, None, None)?;
148 let felts = input[..consumed].to_vec();
149 codec
150 .validate(&felts)
151 .map_err(|err| err.context(format!("codec `{fqn}` rejected decoded value")))?;
152 let display = codec
153 .display(&felts)
154 .map_err(|err| err.context(format!("codec `{fqn}` failed to display decoded value")))?;
155 return Ok((
156 DecodedValue {
157 name,
158 docs,
159 fqn: Some(fqn.to_owned()),
160 kind: DecodedValueKind::Leaf { display, felts },
161 },
162 consumed,
163 ));
164 }
165
166 let fqn = ty.fqn().map(str::to_owned);
167 let (kind, consumed) = match ty.kind() {
168 SchemaTypeKind::Felt => {
169 let mut reader = FeltReader::new(input);
170 let felt = reader
171 .read()
172 .map_err(|err| Error::new(format!("invalid felt representation: {err}")))?;
173 (
174 DecodedValueKind::Leaf {
175 felts: vec![felt],
176 display: felt.as_canonical_u64().to_string(),
177 },
178 reader.pos(),
179 )
180 }
181 SchemaTypeKind::Primitive(primitive) => decode_primitive(*primitive, input)?,
182 SchemaTypeKind::Record(fields) => {
183 let mut decoded = Vec::with_capacity(fields.len());
184 let mut offset = 0usize;
185 for field in fields {
186 let (value, consumed) = decode_type(
187 field.ty(),
188 &input[offset..],
189 registry,
190 Some(field.name().to_owned()),
191 field.docs().map(str::to_owned),
192 )
193 .map_err(|err| err.context(format!("field `{}`", field.name())))?;
194 offset = offset
195 .checked_add(consumed)
196 .ok_or_else(|| Error::new("decoded record width is too large"))?;
197 decoded.push(value);
198 }
199 (DecodedValueKind::Record(decoded), offset)
200 }
201 SchemaTypeKind::Option(payload) => {
202 let mut reader = FeltReader::new(input);
203 let tag = reader
204 .read()
205 .map_err(|err| Error::new(format!("invalid option representation: {err}")))?
206 .as_canonical_u64();
207 match tag {
208 0 => (DecodedValueKind::Option(None), reader.pos()),
209 1 => {
210 let (value, consumed) =
211 decode_type(payload, &input[reader.pos()..], registry, None, None)?;
212 (DecodedValueKind::Option(Some(Box::new(value))), reader.pos() + consumed)
213 }
214 tag => {
215 return Err(Error::new(format!("invalid option tag {tag}; expected 0 or 1")));
216 }
217 }
218 }
219 SchemaTypeKind::Variant(cases) => {
220 let mut reader = FeltReader::new(input);
221 let tag = reader
222 .read_u32()
223 .map_err(|err| Error::new(format!("invalid variant representation: {err}")))?
224 as usize;
225 let case = cases.get(tag).ok_or_else(|| {
226 Error::new(format!(
227 "invalid variant tag {tag}; expected a declaration ordinal below {}",
228 cases.len()
229 ))
230 })?;
231 let (value, consumed) = match case.payload() {
232 Some(payload) => {
233 let (value, consumed) =
234 decode_type(payload, &input[reader.pos()..], registry, None, None)?;
235 (Some(Box::new(value)), reader.pos() + consumed)
236 }
237 None => (None, reader.pos()),
238 };
239 (
240 DecodedValueKind::Variant {
241 case: case.name().to_owned(),
242 value,
243 },
244 consumed,
245 )
246 }
247 };
248
249 Ok((
250 DecodedValue {
251 name,
252 docs,
253 fqn,
254 kind,
255 },
256 consumed,
257 ))
258}
259
260fn decode_primitive(primitive: PrimitiveType, input: &[Felt]) -> Result<(DecodedValueKind, usize)> {
262 match primitive {
263 PrimitiveType::U64 => read_primitive::<u64>(input),
264 PrimitiveType::U32 => read_primitive::<u32>(input),
265 PrimitiveType::U8 => read_primitive::<u8>(input),
266 PrimitiveType::Bool => read_primitive::<bool>(input),
267 }
268}
269
270fn read_primitive<T>(input: &[Felt]) -> Result<(DecodedValueKind, usize)>
272where
273 T: FromFeltRepr + fmt::Display,
274{
275 let mut reader = FeltReader::new(input);
276 let value = T::from_felt_repr(&mut reader)
277 .map_err(|err| Error::new(format!("invalid primitive representation: {err}")))?;
278 Ok((
279 DecodedValueKind::Leaf {
280 felts: input[..reader.pos()].to_vec(),
281 display: value.to_string(),
282 },
283 reader.pos(),
284 ))
285}