Skip to main content

scale_serialization/
textfmt.rs

1use crate::error::Error;
2use crate::registry::*;
3use crate::value::{sequence_size, Cursor, Value};
4use alloc::vec::Vec;
5use core::fmt::{self, Write};
6
7/// Maximum nesting depth to prevent stack overflow from recursive types.
8const MAX_DEPTH: usize = 64;
9
10/// Format a [`Value`] as a compact, URL-safe text string.
11pub fn to_text(value: &Value<'_>) -> Result<alloc::string::String, Error> {
12    let mut out = alloc::string::String::new();
13    fmt_value(value, &mut out, MAX_DEPTH)?;
14    Ok(out)
15}
16
17/// Parse a text-encoded string back into SCALE bytes, guided by the type registry.
18pub fn from_text(input: &str, registry: &Registry, ty_id: TypeId) -> Result<Vec<u8>, Error> {
19    let mut parser = Parser {
20        input,
21        pos: 0,
22        registry,
23        depth: MAX_DEPTH,
24    };
25    let mut out = Vec::new();
26    parser.parse_value(ty_id, &mut out)?;
27    if parser.pos != parser.input.len() {
28        let trailing = &input[parser.pos..];
29        let truncated = if trailing.len() > 40 {
30            alloc::format!("{}...", &trailing[..40])
31        } else {
32            alloc::string::String::from(trailing)
33        };
34        return Err(Error::BadInput(alloc::format!(
35            "unexpected trailing input: '{truncated}'"
36        )));
37    }
38    Ok(out)
39}
40
41// ── Display ────────────────────────────────────────────────────────────
42
43impl fmt::Display for Value<'_> {
44    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45        fmt_value(self, f, MAX_DEPTH).map_err(|_| fmt::Error)
46    }
47}
48
49// ── Formatter ──────────────────────────────────────────────────────────
50
51fn fmt_value(value: &Value<'_>, out: &mut impl Write, depth: usize) -> Result<(), Error> {
52    let depth = depth.checked_sub(1).ok_or_else(|| {
53        Error::BadInput("maximum nesting depth exceeded".into())
54    })?;
55
56    let ty = value.ty().ok_or(Error::TypeNotFound(value.ty_id))?;
57    let data = value.data;
58    let reg = value.registry;
59
60    match ty {
61        TypeDef::Bool => {
62            write!(out, "{}", value.as_bool().ok_or(Error::Eof)?)?;
63        }
64        TypeDef::U8 => write!(out, "{}", value.as_u8().ok_or(Error::Eof)?)?,
65        TypeDef::U16 => write!(out, "{}", value.as_u16().ok_or(Error::Eof)?)?,
66        TypeDef::U32 => write!(out, "{}", value.as_u32().ok_or(Error::Eof)?)?,
67        TypeDef::U64 => write!(out, "{}", value.as_u64().ok_or(Error::Eof)?)?,
68        TypeDef::U128 => write!(out, "{}", value.as_u128().ok_or(Error::Eof)?)?,
69        TypeDef::I8 => write!(out, "{}", value.as_i8().ok_or(Error::Eof)?)?,
70        TypeDef::I16 => write!(out, "{}", value.as_i16().ok_or(Error::Eof)?)?,
71        TypeDef::I32 => write!(out, "{}", value.as_i32().ok_or(Error::Eof)?)?,
72        TypeDef::I64 => write!(out, "{}", value.as_i64().ok_or(Error::Eof)?)?,
73        TypeDef::I128 => write!(out, "{}", value.as_i128().ok_or(Error::Eof)?)?,
74        TypeDef::Char => {
75            let c = value.as_char().ok_or(Error::Eof)?;
76            write!(out, "{c}")?;
77        }
78        TypeDef::Str => {
79            let s = value.as_str().ok_or(Error::Eof)?;
80            out.write_char('\'')?;
81            for c in s.chars() {
82                if c == '\'' {
83                    out.write_str("''")?;
84                } else {
85                    out.write_char(c)?;
86                }
87            }
88            out.write_char('\'')?;
89        }
90        TypeDef::Bytes => {
91            let (len, prefix) = sequence_size(data)?;
92            let bytes = data.get(prefix..prefix + len).ok_or(Error::Eof)?;
93            write_hex(bytes, out)?;
94        }
95        TypeDef::Compact(_) => {
96            let (val, _) = sequence_size(data)?;
97            write!(out, "{val}")?;
98        }
99        TypeDef::Tuple(tys) => {
100            out.write_char('(')?;
101            let mut cursor = Cursor::new(data, reg);
102            for (i, ty_id) in tys.iter().enumerate() {
103                if i > 0 {
104                    out.write_char(';')?;
105                }
106                fmt_value(&cursor.next_value(*ty_id)?, out, depth)?;
107            }
108            out.write_char(')')?;
109        }
110        TypeDef::StructUnit => {}
111        TypeDef::StructNewType(inner) => {
112            fmt_value(&Value::new(data, *inner, reg), out, depth)?;
113        }
114        TypeDef::StructTuple(tys) => {
115            out.write_char('(')?;
116            let mut cursor = Cursor::new(data, reg);
117            for (i, ty_id) in tys.iter().enumerate() {
118                if i > 0 {
119                    out.write_char(';')?;
120                }
121                fmt_value(&cursor.next_value(*ty_id)?, out, depth)?;
122            }
123            out.write_char(')')?;
124        }
125        TypeDef::Struct(fields) => {
126            out.write_char('(')?;
127            let mut cursor = Cursor::new(data, reg);
128            for (i, f) in fields.iter().enumerate() {
129                if i > 0 {
130                    out.write_char(';')?;
131                }
132                out.write_str(&f.name)?;
133                out.write_char(':')?;
134                fmt_value(&cursor.next_value(f.ty)?, out, depth)?;
135            }
136            out.write_char(')')?;
137        }
138        TypeDef::Variant(vdef) => {
139            let idx = *data.first().ok_or(Error::Eof)?;
140            let var = vdef.variant(idx)?;
141            out.write_str(&vdef.name)?;
142            out.write_str("::")?;
143            out.write_str(&var.name)?;
144            let inner = &data[1..];
145            match &var.fields {
146                Fields::Unit => {}
147                Fields::NewType(ty_id) => {
148                    out.write_char('(')?;
149                    fmt_value(&Value::new(inner, *ty_id, reg), out, depth)?;
150                    out.write_char(')')?;
151                }
152                Fields::Tuple(tys) => {
153                    out.write_char('(')?;
154                    let mut cursor = Cursor::new(inner, reg);
155                    for (i, ty_id) in tys.iter().enumerate() {
156                        if i > 0 {
157                            out.write_char(';')?;
158                        }
159                        fmt_value(&cursor.next_value(*ty_id)?, out, depth)?;
160                    }
161                    out.write_char(')')?;
162                }
163                Fields::Struct(fields) => {
164                    out.write_char('(')?;
165                    let mut cursor = Cursor::new(inner, reg);
166                    for (i, f) in fields.iter().enumerate() {
167                        if i > 0 {
168                            out.write_char(';')?;
169                        }
170                        out.write_str(&f.name)?;
171                        out.write_char(':')?;
172                        fmt_value(&cursor.next_value(f.ty)?, out, depth)?;
173                    }
174                    out.write_char(')')?;
175                }
176            }
177        }
178        TypeDef::Sequence(inner_ty) => {
179            let (len, prefix) = sequence_size(data)?;
180            out.write_str("..")?;
181            let mut cursor = Cursor::new(&data[prefix..], reg);
182            for i in 0..len {
183                if i > 0 {
184                    out.write_char(';')?;
185                }
186                fmt_value(&cursor.next_value(*inner_ty)?, out, depth)?;
187            }
188            out.write_char('.')?;
189        }
190        TypeDef::Array(inner_ty, len) => {
191            out.write_str("..")?;
192            let mut cursor = Cursor::new(data, reg);
193            for i in 0..*len {
194                if i > 0 {
195                    out.write_char(';')?;
196                }
197                fmt_value(&cursor.next_value(*inner_ty)?, out, depth)?;
198            }
199            out.write_char('.')?;
200        }
201        TypeDef::Map(ty_k, ty_v) => {
202            let (len, prefix) = sequence_size(data)?;
203            out.write_str("..")?;
204            let mut cursor = Cursor::new(&data[prefix..], reg);
205            for i in 0..len {
206                if i > 0 {
207                    out.write_char(';')?;
208                }
209                out.write_char('(')?;
210                fmt_value(&cursor.next_value(*ty_k)?, out, depth)?;
211                out.write_char(';')?;
212                fmt_value(&cursor.next_value(*ty_v)?, out, depth)?;
213                out.write_char(')')?;
214            }
215            out.write_char('.')?;
216        }
217        TypeDef::BitSequence(_, _) => {
218            let (bit_len, prefix) = sequence_size(data)?;
219            let byte_len = bit_len.div_ceil(8);
220            let bytes = data.get(prefix..prefix + byte_len).ok_or(Error::Eof)?;
221            write_hex(bytes, out)?;
222        }
223    }
224    Ok(())
225}
226
227/// Write bytes as `0x`-prefixed lowercase hex directly to a `fmt::Write`,
228/// without allocating an intermediate string.
229fn write_hex(bytes: &[u8], out: &mut impl Write) -> fmt::Result {
230    out.write_str("0x")?;
231    for &b in bytes {
232        write!(out, "{:02x}", b)?;
233    }
234    Ok(())
235}
236
237/// Returns true if `c` can immediately follow a bare keyword (true/false)
238/// or identifier without being part of it.
239fn is_delimiter(c: char) -> bool {
240    matches!(c, ';' | ')' | '.' | ':' | '(')
241}
242
243// ── Parser ─────────────────────────────────────────────────────────────
244
245struct Parser<'a> {
246    input: &'a str,
247    pos: usize,
248    registry: &'a Registry,
249    depth: usize,
250}
251
252impl<'a> Parser<'a> {
253    fn remaining(&self) -> &'a str {
254        &self.input[self.pos..]
255    }
256
257    fn peek(&self) -> Option<char> {
258        self.remaining().chars().next()
259    }
260
261    fn advance(&mut self, n: usize) {
262        self.pos += n;
263    }
264
265    fn expect(&mut self, c: char) -> Result<(), Error> {
266        if self.peek() == Some(c) {
267            self.advance(c.len_utf8());
268            Ok(())
269        } else {
270            Err(Error::BadInput(alloc::format!(
271                "expected '{}' at position {}",
272                c, self.pos
273            )))
274        }
275    }
276
277    fn consume(&mut self, s: &str) -> bool {
278        if self.remaining().starts_with(s) {
279            self.advance(s.len());
280            true
281        } else {
282            false
283        }
284    }
285
286    fn take_while(&mut self, pred: impl Fn(char) -> bool) -> &'a str {
287        let start = self.pos;
288        while self.peek().is_some_and(&pred) {
289            self.advance(self.peek().unwrap().len_utf8());
290        }
291        &self.input[start..self.pos]
292    }
293
294    /// A single `.` not followed by another `.` closes a sequence.
295    fn at_seq_close(&self) -> bool {
296        let r = self.remaining();
297        r.starts_with('.') && !r.starts_with("..")
298    }
299
300    /// Check that the next char (if any) is a structural delimiter,
301    /// ensuring a keyword like `true` wasn't parsed from `truer`.
302    fn expect_delimiter(&self) -> Result<(), Error> {
303        match self.peek() {
304            None => Ok(()),
305            Some(c) if is_delimiter(c) => Ok(()),
306            Some(c) => Err(Error::BadInput(alloc::format!(
307                "unexpected character '{c}' at position {}",
308                self.pos
309            ))),
310        }
311    }
312
313    fn parse_uint(&mut self) -> Result<u128, Error> {
314        let s = self.take_while(|c| c.is_ascii_digit());
315        if s.is_empty() {
316            return Err(Error::BadInput(alloc::format!(
317                "expected number at position {}",
318                self.pos
319            )));
320        }
321        s.parse::<u128>()
322            .map_err(|e| Error::BadInput(alloc::string::ToString::to_string(&e)))
323    }
324
325    fn parse_uint_checked<T: TryFrom<u128>>(&mut self) -> Result<T, Error> {
326        let n = self.parse_uint()?;
327        T::try_from(n).map_err(|_| Error::BadInput(alloc::format!("{n} out of range")))
328    }
329
330    fn parse_int(&mut self) -> Result<i128, Error> {
331        let neg = self.consume("-");
332        let n = self.parse_uint()?;
333        let n = n as i128;
334        Ok(if neg { -n } else { n })
335    }
336
337    fn parse_int_checked<T: TryFrom<i128>>(&mut self) -> Result<T, Error> {
338        let n = self.parse_int()?;
339        T::try_from(n).map_err(|_| Error::BadInput(alloc::format!("{n} out of range")))
340    }
341
342    fn parse_hex_bytes(&mut self) -> Result<Vec<u8>, Error> {
343        if !self.consume("0x") {
344            return Err(Error::BadInput("expected '0x' prefix".into()));
345        }
346        let hex_str = self.take_while(|c| c.is_ascii_hexdigit());
347        crate::decode_hex(hex_str)
348    }
349
350    fn parse_value(&mut self, ty_id: TypeId, out: &mut Vec<u8>) -> Result<(), Error> {
351        self.depth = self.depth.checked_sub(1).ok_or_else(|| {
352            Error::BadInput("maximum nesting depth exceeded".into())
353        })?;
354
355        let result = self.parse_value_inner(ty_id, out);
356
357        self.depth += 1;
358        result
359    }
360
361    fn parse_value_inner(&mut self, ty_id: TypeId, out: &mut Vec<u8>) -> Result<(), Error> {
362        let reg = self.registry;
363        let ty = reg.resolve(ty_id).ok_or(Error::TypeNotFound(ty_id))?;
364
365        match ty {
366            TypeDef::Bool => {
367                if self.consume("true") {
368                    self.expect_delimiter()?;
369                    out.push(1);
370                } else if self.consume("false") {
371                    self.expect_delimiter()?;
372                    out.push(0);
373                } else {
374                    return Err(Error::BadInput("expected 'true' or 'false'".into()));
375                }
376            }
377            TypeDef::U8 => {
378                let n: u8 = self.parse_uint_checked()?;
379                out.push(n);
380            }
381            TypeDef::U16 => {
382                let n: u16 = self.parse_uint_checked()?;
383                out.extend_from_slice(&n.to_le_bytes());
384            }
385            TypeDef::U32 => {
386                let n: u32 = self.parse_uint_checked()?;
387                out.extend_from_slice(&n.to_le_bytes());
388            }
389            TypeDef::U64 => {
390                let n: u64 = self.parse_uint_checked()?;
391                out.extend_from_slice(&n.to_le_bytes());
392            }
393            TypeDef::U128 => {
394                let n = self.parse_uint()?;
395                out.extend_from_slice(&n.to_le_bytes());
396            }
397            TypeDef::I8 => {
398                let n: i8 = self.parse_int_checked()?;
399                out.push(n as u8);
400            }
401            TypeDef::I16 => {
402                let n: i16 = self.parse_int_checked()?;
403                out.extend_from_slice(&n.to_le_bytes());
404            }
405            TypeDef::I32 => {
406                let n: i32 = self.parse_int_checked()?;
407                out.extend_from_slice(&n.to_le_bytes());
408            }
409            TypeDef::I64 => {
410                let n: i64 = self.parse_int_checked()?;
411                out.extend_from_slice(&n.to_le_bytes());
412            }
413            TypeDef::I128 => {
414                let n = self.parse_int()?;
415                out.extend_from_slice(&n.to_le_bytes());
416            }
417            TypeDef::Char => {
418                let c = self.peek().ok_or(Error::Eof)?;
419                self.advance(c.len_utf8());
420                out.extend_from_slice(&(c as u32).to_le_bytes());
421            }
422            TypeDef::Str => {
423                self.expect('\'')?;
424                // Parse string content, handling '' escape for literal single quotes
425                let mut s = Vec::new();
426                loop {
427                    match self.peek() {
428                        None => return Err(Error::BadInput("unterminated string".into())),
429                        Some('\'') => {
430                            self.advance(1);
431                            // '' is an escaped single quote, lone ' closes the string
432                            if self.peek() == Some('\'') {
433                                self.advance(1);
434                                s.push(b'\'');
435                            } else {
436                                break;
437                            }
438                        }
439                        Some(c) => {
440                            self.advance(c.len_utf8());
441                            let mut buf = [0u8; 4];
442                            s.extend_from_slice(c.encode_utf8(&mut buf).as_bytes());
443                        }
444                    }
445                }
446                crate::compact_encode(s.len() as u128, &mut *out);
447                out.extend_from_slice(&s);
448            }
449            TypeDef::Bytes => {
450                let bytes = self.parse_hex_bytes()?;
451                crate::compact_encode(bytes.len() as u128, &mut *out);
452                out.extend_from_slice(&bytes);
453            }
454            TypeDef::Compact(_) => {
455                let n = self.parse_uint()?;
456                crate::compact_encode(n, out);
457            }
458            TypeDef::Tuple(tys) => {
459                self.expect('(')?;
460                for (i, ty_id) in tys.iter().enumerate() {
461                    if i > 0 {
462                        self.expect(';')?;
463                    }
464                    self.parse_value(*ty_id, out)?;
465                }
466                self.expect(')')?;
467            }
468            TypeDef::StructUnit => {}
469            TypeDef::StructNewType(inner) => {
470                let inner = *inner;
471                self.parse_value(inner, out)?;
472            }
473            TypeDef::StructTuple(tys) => {
474                self.expect('(')?;
475                for (i, ty_id) in tys.iter().enumerate() {
476                    if i > 0 {
477                        self.expect(';')?;
478                    }
479                    self.parse_value(*ty_id, out)?;
480                }
481                self.expect(')')?;
482            }
483            TypeDef::Struct(fields) => {
484                self.expect('(')?;
485                for (i, f) in fields.iter().enumerate() {
486                    if i > 0 {
487                        self.expect(';')?;
488                    }
489                    if !self.consume(&f.name) {
490                        return Err(Error::BadInput(alloc::format!(
491                            "expected field '{}'",
492                            f.name
493                        )));
494                    }
495                    self.expect(':')?;
496                    self.parse_value(f.ty, out)?;
497                }
498                self.expect(')')?;
499            }
500            TypeDef::Variant(vdef) => {
501                if !self.remaining().starts_with(vdef.name.as_str()) {
502                    return Err(Error::BadInput(alloc::format!(
503                        "expected enum '{}'",
504                        vdef.name
505                    )));
506                }
507                self.advance(vdef.name.len());
508                self.expect(':')?;
509                self.expect(':')?;
510                let var_name = self.take_while(|c| c.is_alphanumeric() || c == '_');
511                let var = vdef
512                    .variants
513                    .iter()
514                    .find(|v| v.name == var_name)
515                    .ok_or_else(|| {
516                        Error::BadInput(alloc::format!("unknown variant '{var_name}'"))
517                    })?;
518                out.push(var.index);
519                match &var.fields {
520                    Fields::Unit => {}
521                    Fields::NewType(ty_id) => {
522                        self.expect('(')?;
523                        self.parse_value(*ty_id, out)?;
524                        self.expect(')')?;
525                    }
526                    Fields::Tuple(tys) => {
527                        self.expect('(')?;
528                        for (i, ty_id) in tys.iter().enumerate() {
529                            if i > 0 {
530                                self.expect(';')?;
531                            }
532                            self.parse_value(*ty_id, out)?;
533                        }
534                        self.expect(')')?;
535                    }
536                    Fields::Struct(fields) => {
537                        self.expect('(')?;
538                        for (i, f) in fields.iter().enumerate() {
539                            if i > 0 {
540                                self.expect(';')?;
541                            }
542                            if !self.consume(&f.name) {
543                                return Err(Error::BadInput(alloc::format!(
544                                    "expected field '{}'",
545                                    f.name
546                                )));
547                            }
548                            self.expect(':')?;
549                            self.parse_value(f.ty, out)?;
550                        }
551                        self.expect(')')?;
552                    }
553                }
554            }
555            TypeDef::Sequence(inner_ty) => {
556                let inner_ty = *inner_ty;
557                if !self.consume("..") {
558                    return Err(Error::BadInput("expected '..' for sequence".into()));
559                }
560                let mut items = Vec::new();
561                let mut count = 0usize;
562                if !self.at_seq_close() {
563                    loop {
564                        self.parse_value(inner_ty, &mut items)?;
565                        count += 1;
566                        if !self.consume(";") || self.at_seq_close() {
567                            break;
568                        }
569                    }
570                }
571                self.expect('.')?;
572                crate::compact_encode(count as u128, &mut *out);
573                out.extend_from_slice(&items);
574            }
575            TypeDef::Array(inner_ty, len) => {
576                let (inner_ty, len) = (*inner_ty, *len);
577                if !self.consume("..") {
578                    return Err(Error::BadInput("expected '..' for array".into()));
579                }
580                for i in 0..len {
581                    if i > 0 {
582                        self.expect(';')?;
583                    }
584                    self.parse_value(inner_ty, out)?;
585                }
586                self.expect('.')?;
587            }
588            TypeDef::Map(ty_k, ty_v) => {
589                let (ty_k, ty_v) = (*ty_k, *ty_v);
590                if !self.consume("..") {
591                    return Err(Error::BadInput("expected '..' for map".into()));
592                }
593                let mut items = Vec::new();
594                let mut count = 0usize;
595                if !self.at_seq_close() {
596                    loop {
597                        self.expect('(')?;
598                        self.parse_value(ty_k, &mut items)?;
599                        self.expect(';')?;
600                        self.parse_value(ty_v, &mut items)?;
601                        self.expect(')')?;
602                        count += 1;
603                        if !self.consume(";") || self.at_seq_close() {
604                            break;
605                        }
606                    }
607                }
608                self.expect('.')?;
609                crate::compact_encode(count as u128, &mut *out);
610                out.extend_from_slice(&items);
611            }
612            TypeDef::BitSequence(_, _) => {
613                let bytes = self.parse_hex_bytes()?;
614                let bit_len = bytes.len() * 8;
615                crate::compact_encode(bit_len as u128, &mut *out);
616                out.extend_from_slice(&bytes);
617            }
618        }
619        Ok(())
620    }
621}