scale_value/string_impls/
from_string.rs

1// Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io)
2// This file is a part of the scale-value crate.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//         http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16#![allow(clippy::enum_variant_names)]
17
18use super::string_helpers;
19use crate::prelude::*;
20use crate::value_type::{BitSequence, Composite, Primitive, Value, Variant};
21use core::num::ParseIntError;
22use yap::{types::StrTokens, IntoTokens, TokenLocation, Tokens};
23
24/// A struct which will try to parse a string into a [`Value`].
25/// This can be configured with custom parsers to extend what we're able
26/// to parse into a [`Value`].
27pub struct FromStrBuilder {
28    custom_parsers: Vec<CustomParser>,
29}
30
31type CustomParser = Box<dyn Fn(&mut &str) -> Option<Result<Value<()>, ParseError>> + 'static>;
32
33impl FromStrBuilder {
34    pub(crate) fn new() -> Self {
35        FromStrBuilder { custom_parsers: Vec::new() }
36    }
37
38    /// Add a custom parser. A custom parser is a function which is given a mutable string
39    /// reference and will:
40    ///
41    /// - return `None` if the string given is not applicable,
42    /// - return `Some(Ok(value))` if we can successfully parse a value from the string. In
43    ///   this case, the parser should update the `&mut str` it's given to consume however
44    ///   much was parsed.
45    /// - return `Some(Err(error))` if the string given looks like a match, but something went
46    ///   wrong in trying to properly parse it. In this case, parsing will stop immediately and
47    ///   this error will be returned, so use this sparingly, as other parsers may be able to
48    ///   successfully parse what this one has failed to parse. No additional tokens will be consumed
49    ///   if an error occurs.
50    pub fn add_custom_parser<F>(mut self, f: F) -> Self
51    where
52        F: Fn(&mut &str) -> Option<Result<Value<()>, ParseError>> + 'static,
53    {
54        self.custom_parsers.push(Box::new(f));
55        self
56    }
57
58    /// Attempt to parse the string provided into a value, returning any error that occurred while
59    /// attempting to do so, as well as the rest of the string that was not consumed by this parsing.
60    pub fn parse<'a>(&self, s: &'a str) -> (Result<Value<()>, ParseError>, &'a str) {
61        let mut tokens = s.into_tokens();
62        let res = parse_value(&mut tokens, &self.custom_parsers);
63        let remaining = tokens.remaining();
64        (res, remaining)
65    }
66}
67
68/// An error parsing the provided string into a Value
69#[derive(Debug, PartialEq, Eq)]
70pub struct ParseError {
71    /// Byte offset into the provided string that the error begins.
72    pub start_loc: usize,
73    /// Byte offset into the provided string that the error ends. Many errors begin at some
74    /// point but do not have a known end position.
75    pub end_loc: Option<usize>,
76    /// Details about the error that occurred.
77    pub err: ParseErrorKind,
78}
79
80impl core::error::Error for ParseError {}
81
82impl ParseError {
83    /// Construct a new `ParseError` for tokens at the given location.
84    pub fn new_at<E: Into<ParseErrorKind>>(err: E, loc: usize) -> Self {
85        Self { start_loc: loc, end_loc: None, err: err.into() }
86    }
87    /// Construct a new `ParseError` for tokens between the given locations.
88    pub fn new_between<E: Into<ParseErrorKind>>(err: E, start: usize, end: usize) -> Self {
89        Self { start_loc: start, end_loc: Some(end), err: err.into() }
90    }
91}
92
93impl core::fmt::Display for ParseError {
94    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
95        if let Some(end_loc) = self.end_loc {
96            write!(f, "Error from char {} to {}: {}", self.start_loc, end_loc, self.err)
97        } else {
98            write!(f, "Error at char {}: {}", self.start_loc, self.err)
99        }
100    }
101}
102
103// Add handy helper methods to error-kinds
104macro_rules! at_between {
105    ($ty:ident) => {
106        impl $ty {
107            /// Error at a specific location with no specific end
108            pub fn at(self, loc: usize) -> ParseError {
109                ParseError::new_at(self, loc)
110            }
111            /// Error at a specific location for the next character
112            pub fn at_one(self, loc: usize) -> ParseError {
113                ParseError::new_between(self, loc, loc + 1)
114            }
115            /// Error between two locations.
116            pub fn between(self, start: usize, end: usize) -> ParseError {
117                ParseError::new_between(self, start, end)
118            }
119        }
120    };
121}
122
123/// Details about the error that occurred.
124#[derive(Debug, PartialEq, Eq, thiserror::Error)]
125#[allow(missing_docs)]
126pub enum ParseErrorKind {
127    #[error("Expected a value")]
128    ExpectedValue,
129    #[error(transparent)]
130    Complex(#[from] ParseComplexError),
131    #[error(transparent)]
132    Char(#[from] ParseCharError),
133    #[error(transparent)]
134    String(#[from] ParseStringError),
135    #[error(transparent)]
136    Number(#[from] ParseNumberError),
137    #[error(transparent)]
138    BitSequence(#[from] ParseBitSequenceError),
139    #[error("Custom error: {0}")]
140    Custom(String),
141}
142at_between!(ParseErrorKind);
143
144impl ParseErrorKind {
145    /// Construct a custom error from a type implementing [`core::fmt::Display`].
146    pub fn custom<T: core::fmt::Display>(value: T) -> Self {
147        ParseErrorKind::Custom(format!("{value}"))
148    }
149
150    /// Construct a custom error from a type implementing [`Debug`].
151    /// Prefer [`ParseErrorKind::custom`] where possible.
152    pub fn custom_debug<T: core::fmt::Debug>(value: T) -> Self {
153        ParseErrorKind::Custom(format!("{value:?}"))
154    }
155}
156
157impl From<String> for ParseErrorKind {
158    fn from(s: String) -> Self {
159        ParseErrorKind::Custom(s)
160    }
161}
162
163#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
164#[allow(missing_docs)]
165pub enum ParseComplexError {
166    #[error("The first character in a field name should be alphabetic")]
167    InvalidStartingCharacterInIdent,
168    #[error(
169        "Field name is not valid (it should begin with an alphabetical char and then consist only of alphanumeric chars)"
170    )]
171    InvalidFieldName,
172    #[error("Missing field separator; expected {0}")]
173    MissingFieldSeparator(char),
174    #[error("Missing closing '{0}'")]
175    ExpectedCloserToMatch(char, usize),
176}
177at_between!(ParseComplexError);
178
179#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
180#[allow(missing_docs)]
181pub enum ParseCharError {
182    #[error("Expected a single character")]
183    ExpectedValidCharacter,
184    #[error("Expected an escape code to follow the '\\'")]
185    ExpectedValidEscapeCode,
186    #[error("Expected a closing quote to match the opening quote at position {0}")]
187    ExpectedClosingQuoteToMatch(usize),
188}
189at_between!(ParseCharError);
190
191#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
192#[allow(missing_docs)]
193pub enum ParseStringError {
194    #[error("Expected a closing quote to match the opening quote at position {0}")]
195    ExpectedClosingQuoteToMatch(usize),
196    #[error("Expected an escape code to follow the '\\'")]
197    ExpectedValidEscapeCode,
198}
199at_between!(ParseStringError);
200
201#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
202#[allow(missing_docs)]
203pub enum ParseNumberError {
204    #[error("Expected one or more digits")]
205    ExpectedDigit,
206    #[error("Failed to parse digits into an integer: {0}")]
207    ParsingFailed(#[from] ParseIntError),
208}
209at_between!(ParseNumberError);
210
211#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
212#[allow(missing_docs)]
213pub enum ParseBitSequenceError {
214    #[error("Expected a closing bracket ('>') to match the opening one at position {_0}")]
215    ExpectedClosingBracketToMatch(usize),
216    #[error("Invalid character; expecting a 0 or 1")]
217    InvalidCharacter,
218}
219at_between!(ParseBitSequenceError);
220
221// Parse a value.
222fn parse_value(
223    t: &mut StrTokens,
224    custom_parsers: &[CustomParser],
225) -> Result<Value<()>, ParseError> {
226    // Try any custom parsers first.
227    if !custom_parsers.is_empty() {
228        let s = t.remaining();
229        let start_offset = t.offset();
230        let cursor = &mut &*s;
231
232        for parser in custom_parsers {
233            if let Some(res) = parser(cursor) {
234                match res {
235                    Ok(value) => {
236                        // Wind our StrTokens forward to take into account anything that
237                        // was consumed. We do this rather than replacing it because we want
238                        // to preserve the full string and offsets.
239                        for _ in cursor.len()..s.len() {
240                            t.next();
241                        }
242                        return Ok(value);
243                    }
244                    Err(e) => {
245                        // Adjust locations in error to be relative to the big string,
246                        // not the smaller slice that was passed to the custom parser.
247                        return Err(ParseError {
248                            start_loc: start_offset + e.start_loc,
249                            end_loc: e.end_loc.map(|l| start_offset + l),
250                            err: e.err,
251                        });
252                    }
253                }
254            }
255        }
256    }
257
258    // Our parsers return `Result<Thing, Option<ParseError>>`, but in order to know
259    // whether to try the next item, `one_of` expects `Option<T>`, so we transpose_err
260    // to convert to the right shape.
261    let val = yap::one_of!(t;
262        transpose_err(parse_bool(t).map(Value::bool).ok_or(None)),
263        transpose_err(parse_char(t).map(Value::char)),
264        transpose_err(parse_string(t).map(Value::string)),
265        transpose_err(parse_number(t).map(Value::primitive)),
266        transpose_err(parse_named_composite(t, custom_parsers).map(|v| v.into())),
267        transpose_err(parse_unnamed_composite(t, custom_parsers).map(|v| v.into())),
268        transpose_err(parse_bit_sequence(t).map(Value::bit_sequence)),
269        transpose_err(parse_variant(t, custom_parsers).map(|v| v.into())),
270    );
271
272    match val {
273        Some(Ok(val)) => Ok(val),
274        Some(Err(e)) => Err(e),
275        None => Err(ParseError::new_at(ParseErrorKind::ExpectedValue, t.offset())),
276    }
277}
278
279// Parse a named composite value like `{ foo: 123 }`.
280//
281// As with most of the parsers here, the error is optional. A `Some` error indicates that
282// we're midway through parsing something and have run into an error. a `None` error indicates
283// that we can see up front that the characters we're parsing aren't going to be the right shape,
284// and can attempt to parse the characters into a different thing if we wish.
285fn parse_named_composite(
286    t: &mut StrTokens,
287    custom_parsers: &[CustomParser],
288) -> Result<Composite<()>, Option<ParseError>> {
289    let start = t.offset();
290    if !t.token('{') {
291        return Err(None);
292    }
293    skip_whitespace(t);
294
295    // No values? bail early.
296    if t.token('}') {
297        return Ok(Composite::Named(vec![]));
298    }
299
300    let vals = t
301        .sep_by_err(
302            |t| parse_field_name_and_value(t, custom_parsers),
303            |t| skip_spaced_separator(t, ','),
304        )
305        .collect::<Result<_, _>>()?;
306
307    skip_whitespace(t);
308    if !t.token('}') {
309        return Err(Some(ParseComplexError::ExpectedCloserToMatch('}', start).at_one(t.offset())));
310    }
311    Ok(Composite::Named(vals))
312}
313
314// Parse an unnamed composite value like `(true, 123)`
315fn parse_unnamed_composite(
316    t: &mut StrTokens,
317    custom_parsers: &[CustomParser],
318) -> Result<Composite<()>, Option<ParseError>> {
319    let start = t.offset();
320    if !t.token('(') {
321        return Err(None);
322    }
323    skip_whitespace(t);
324
325    // No values? bail early.
326    if t.token(')') {
327        return Ok(Composite::Unnamed(vec![]));
328    }
329
330    let vals = t
331        .sep_by_err(|t| parse_value(t, custom_parsers), |t| skip_spaced_separator(t, ','))
332        .collect::<Result<_, _>>()?;
333
334    skip_whitespace(t);
335    if !t.token(')') {
336        return Err(Some(ParseComplexError::ExpectedCloserToMatch(')', start).at_one(t.offset())));
337    }
338    Ok(Composite::Unnamed(vals))
339}
340
341// Parse a variant like `Variant { hello: "there" }` or `Foo (123, true)`
342fn parse_variant(
343    t: &mut StrTokens,
344    custom_parsers: &[CustomParser],
345) -> Result<Variant<()>, Option<ParseError>> {
346    let ident = match parse_optional_variant_ident(t) {
347        Some(ident) => ident,
348        None => return Err(None),
349    };
350
351    skip_whitespace(t);
352
353    let composite = yap::one_of!(t;
354        transpose_err(parse_named_composite(t, custom_parsers)),
355        transpose_err(parse_unnamed_composite(t, custom_parsers))
356    );
357
358    match composite {
359        Some(Ok(values)) => Ok(Variant { name: ident, values }),
360        Some(Err(e)) => Err(Some(e)),
361        None => Err(None),
362    }
363}
364
365// Parse a sequence of bits like `<01101>` or `<>` into a bit sequence.
366fn parse_bit_sequence(t: &mut StrTokens) -> Result<BitSequence, Option<ParseError>> {
367    let start = t.offset();
368    if !t.token('<') {
369        return Err(None);
370    }
371    let bits = t.take_while(|&c| c == '0' || c == '1').into_iter().map(|c| c == '1');
372    let mut seq = BitSequence::new();
373    for bit in bits {
374        seq.push(bit);
375    }
376    if !t.token('>') {
377        return Err(Some(
378            ParseBitSequenceError::ExpectedClosingBracketToMatch(start)
379                .between(t.offset(), t.offset() + 1),
380        ));
381    }
382    Ok(seq)
383}
384
385// Parse a bool (`true` or `false`)
386fn parse_bool(t: &mut StrTokens) -> Option<bool> {
387    if t.tokens("true".chars()) {
388        Some(true)
389    } else if t.tokens("false".chars()) {
390        Some(false)
391    } else {
392        None
393    }
394}
395
396// Parse a char like `'a'`
397fn parse_char(t: &mut StrTokens) -> Result<char, Option<ParseError>> {
398    let start = t.offset();
399    if !t.token('\'') {
400        return Err(None);
401    }
402    let char = match t.next() {
403        None => return Err(Some(ParseCharError::ExpectedValidCharacter.at_one(t.offset()))),
404        Some(c) => c,
405    };
406
407    // If char is a backslash, it's an escape code and we
408    // need to unescape it to find our inner char:
409    let char = if char == '\\' {
410        let escape_code = match t.next() {
411            None => return Err(Some(ParseCharError::ExpectedValidEscapeCode.at_one(t.offset()))),
412            Some(c) => c,
413        };
414        match string_helpers::from_escape_code(escape_code) {
415            None => return Err(Some(ParseCharError::ExpectedValidEscapeCode.at_one(t.offset()))),
416            Some(c) => c,
417        }
418    } else {
419        char
420    };
421
422    if !t.token('\'') {
423        return Err(Some(ParseCharError::ExpectedClosingQuoteToMatch(start).at_one(t.offset())));
424    }
425    Ok(char)
426}
427
428// Parse a number like `-123_456` or `234` or `+1234_5`
429fn parse_number(t: &mut StrTokens) -> Result<Primitive, Option<ParseError>> {
430    let start_loc = t.offset();
431    let is_positive = t.token('+') || !t.token('-');
432
433    // When we iterate numeric digits, prefix a sign as needed:
434    let sign = if is_positive { "".chars() } else { "-".chars() };
435
436    // Now, we expect a digit and then a mix of digits and underscores:
437    let mut seen_n = false;
438    let digits = t
439        .take_while(|c| {
440            if c.is_ascii_digit() {
441                seen_n = true;
442                true
443            } else {
444                seen_n && *c == '_'
445            }
446        })
447        .into_iter()
448        .filter(|c| c.is_ascii_digit());
449
450    // Chain sign to digits and attempt to parse into a number.
451    let n_str: String = sign.chain(digits).collect();
452    let end_loc = t.offset();
453
454    // Nothing was parsed; Return None.
455    if end_loc == start_loc {
456        return Err(None);
457    }
458
459    // No digits were parsed but a sign was; err.
460    if !seen_n {
461        return Err(Some(ParseNumberError::ExpectedDigit.between(end_loc, end_loc + 1)));
462    }
463
464    // Parse into a number as best we can:
465    if is_positive {
466        n_str
467            .parse::<u128>()
468            .map(Primitive::u128)
469            .map_err(|e| Some(ParseNumberError::ParsingFailed(e).between(start_loc, end_loc)))
470    } else {
471        n_str
472            .parse::<i128>()
473            .map(Primitive::i128)
474            .map_err(|e| Some(ParseNumberError::ParsingFailed(e).between(start_loc, end_loc)))
475    }
476}
477
478// Parse a string like `"hello\n there"`
479fn parse_string(t: &mut StrTokens) -> Result<String, Option<ParseError>> {
480    let start = t.offset();
481    if !t.token('"') {
482        return Err(None);
483    }
484
485    let mut out: String = String::new();
486    let mut next_is_escaped = false;
487    loop {
488        let pos = t.offset();
489        let char = match t.next() {
490            Some(c) => c,
491            None => {
492                return Err(Some(
493                    ParseStringError::ExpectedClosingQuoteToMatch(start).at_one(t.offset()),
494                ))
495            }
496        };
497
498        match char {
499            // Escape a char:
500            '\\' if !next_is_escaped => {
501                next_is_escaped = true;
502            }
503            // Handle escaped chars:
504            c if next_is_escaped => match string_helpers::from_escape_code(c) {
505                Some(c) => {
506                    out.push(c);
507                    next_is_escaped = false;
508                }
509                None => {
510                    return Err(Some(
511                        ParseStringError::ExpectedValidEscapeCode.between(pos, pos + 1),
512                    ))
513                }
514            },
515            // String has closed
516            '"' => {
517                break; // closing quote seen; done!
518            }
519            // All other chars pushed as-is.
520            c => {
521                out.push(c);
522            }
523        }
524    }
525
526    Ok(out)
527}
528
529// Parse a field in a named composite like `foo: 123` or `"hello there": 123`
530fn parse_field_name_and_value(
531    t: &mut StrTokens,
532    custom_parsers: &[CustomParser],
533) -> Result<(String, Value<()>), ParseError> {
534    let name = parse_field_name(t)?;
535    if !skip_spaced_separator(t, ':') {
536        return Err(ParseComplexError::MissingFieldSeparator(':').at_one(t.offset()));
537    }
538    let value = parse_value(t, custom_parsers)?;
539    Ok((name, value))
540}
541
542// Parse a field name in a named composite like `foo` or `"hello there"`
543fn parse_field_name(t: &mut StrTokens) -> Result<String, ParseError> {
544    let field_name = yap::one_of!(t;
545        transpose_err(parse_string(t)),
546        Some(parse_ident(t)),
547    );
548
549    match field_name {
550        Some(Ok(name)) => Ok(name),
551        Some(Err(e)) => Err(e),
552        None => Err(ParseComplexError::InvalidFieldName.at(t.offset())),
553    }
554}
555
556// Parse an ident used for the variant name, like `MyVariant` or the special case
557// `i"My variant name"` for idents that are not normally valid variant names, but
558// can be set in Value variants (this is to ensure that we can display and then parse
559// as many user-generated Values as possible).
560fn parse_optional_variant_ident(t: &mut StrTokens) -> Option<String> {
561    fn parse_i_string(t: &mut StrTokens) -> Option<String> {
562        if t.next()? != 'v' {
563            return None;
564        }
565        parse_string(t).ok()
566    }
567
568    yap::one_of!(t;
569        parse_i_string(t),
570        parse_ident(t).ok()
571    )
572}
573
574// Parse an ident like `foo` or `MyVariant`
575fn parse_ident(t: &mut StrTokens) -> Result<String, ParseError> {
576    let start = t.location();
577    if t.skip_while(|c| c.is_alphabetic()) == 0 {
578        return Err(ParseComplexError::InvalidStartingCharacterInIdent.at_one(start.offset()));
579    }
580    t.skip_while(|c| c.is_alphanumeric() || *c == '_');
581    let end = t.location();
582
583    let ident_str = t.slice(start, end).as_iter().collect();
584    Ok(ident_str)
585}
586
587// Skip any whitespace characters
588fn skip_whitespace(t: &mut StrTokens) {
589    t.skip_while(|c| c.is_whitespace());
590}
591
592// Skip a provided separator, with optional spaces on either side
593fn skip_spaced_separator(t: &mut StrTokens, s: char) -> bool {
594    skip_whitespace(t);
595    let is_sep = t.token(s);
596    skip_whitespace(t);
597    is_sep
598}
599
600// Turn a ` Result<T, Option<E>>` into `Option<Result<T, E>>`.
601fn transpose_err<T, E>(r: Result<T, Option<E>>) -> Option<Result<T, E>> {
602    match r {
603        Ok(val) => Some(Ok(val)),
604        Err(Some(e)) => Some(Err(e)),
605        Err(None) => None,
606    }
607}
608
609#[cfg(test)]
610mod test {
611    use crate::value;
612
613    use super::*;
614
615    fn from(s: &str) -> Result<Value<()>, ParseError> {
616        let (res, remaining) = FromStrBuilder::new().parse(s);
617        match res {
618            Ok(value) => {
619                assert_eq!(remaining.len(), 0, "was not expecting any unparsed output");
620                Ok(value)
621            }
622            Err(e) => Err(e),
623        }
624    }
625
626    #[test]
627    fn parse_bools() {
628        assert_eq!(from("true"), Ok(Value::bool(true)));
629        assert_eq!(from("false"), Ok(Value::bool(false)));
630    }
631
632    #[test]
633    fn parse_numbers() {
634        assert_eq!(from("123"), Ok(Value::u128(123)));
635        assert_eq!(from("1_234_56"), Ok(Value::u128(123_456)));
636        assert_eq!(from("+1_234_56"), Ok(Value::u128(123_456)));
637        assert_eq!(from("-123_4"), Ok(Value::i128(-1234)));
638        assert_eq!(from("-abc"), Err(ParseNumberError::ExpectedDigit.between(1, 2)));
639    }
640
641    #[test]
642    fn parse_chars() {
643        assert_eq!(from("'a'"), Ok(Value::char('a')));
644        assert_eq!(from("'😀'"), Ok(Value::char('😀')));
645        assert_eq!(from("'\\n'"), Ok(Value::char('\n')));
646        assert_eq!(from("'\\t'"), Ok(Value::char('\t')));
647        assert_eq!(from("'\\\"'"), Ok(Value::char('"')));
648        assert_eq!(from("'\\\''"), Ok(Value::char('\'')));
649        assert_eq!(from("'\\r'"), Ok(Value::char('\r')));
650        assert_eq!(from("'\\\\'"), Ok(Value::char('\\')));
651        assert_eq!(from("'\\0'"), Ok(Value::char('\0')));
652        assert_eq!(from("'a"), Err(ParseCharError::ExpectedClosingQuoteToMatch(0).at_one(2)));
653    }
654
655    #[test]
656    fn parse_strings() {
657        assert_eq!(from("\"\\n \\r \\t \\0 \\\"\""), Ok(Value::string("\n \r \t \0 \"")));
658        assert_eq!(from("\"Hello there 😀\""), Ok(Value::string("Hello there 😀")));
659        assert_eq!(from("\"Hello\\n\\t there\""), Ok(Value::string("Hello\n\t there")));
660        assert_eq!(from("\"Hello\\\\ there\""), Ok(Value::string("Hello\\ there")));
661        assert_eq!(
662            from("\"Hello\\p there\""),
663            Err(ParseStringError::ExpectedValidEscapeCode.between(7, 8))
664        );
665        assert_eq!(from("\"Hi"), Err(ParseStringError::ExpectedClosingQuoteToMatch(0).at_one(3)));
666    }
667
668    #[test]
669    fn parse_unnamed_composites() {
670        assert_eq!(
671            from("(  true, 1234 ,\t\n\t \"Hello!\" )"),
672            Ok(value!((true, 1234u32, "Hello!")))
673        );
674        assert_eq!(from("()"), Ok(value!(())));
675        assert_eq!(from("(\n\n\t\t\n)"), Ok(value!(())));
676    }
677
678    #[test]
679    fn parse_named_composites() {
680        assert_eq!(
681            from(
682                "{
683            hello: true,
684            foo: 1234,
685            \"Hello there 😀\": \"Hello!\"
686        }"
687            ),
688            Ok(Value::named_composite([
689                ("hello", Value::bool(true)),
690                ("foo", Value::u128(1234)),
691                ("Hello there 😀", Value::string("Hello!"))
692            ]))
693        );
694    }
695
696    #[test]
697    fn parse_variants() {
698        assert_eq!(
699            from(
700                "MyVariant {
701            hello: true,
702            foo: 1234,
703            \"Hello there 😀\": \"Hello!\"
704        }"
705            ),
706            Ok(Value::named_variant(
707                "MyVariant",
708                [
709                    ("hello", Value::bool(true)),
710                    ("foo", Value::u128(1234)),
711                    ("Hello there 😀", Value::string("Hello!"))
712                ]
713            ))
714        );
715
716        assert_eq!(
717            from("Foo (  true, 1234 ,\t\n\t \"Hello!\" )"),
718            Ok(value!(Foo(true, 1234u32, "Hello!")))
719        );
720
721        assert_eq!(from("Foo()"), Ok(value!(Foo())));
722        assert_eq!(from("Foo{}"), Ok(value!(Foo {})));
723        assert_eq!(from("Foo( \t)"), Ok(value!(Foo())));
724        assert_eq!(from("Foo{  }"), Ok(value!(Foo {})));
725
726        // Parsing special "v" strings:
727        assert_eq!(
728            from("v\"variant name\" {  }"),
729            Ok(Value::named_variant::<_, String, _>("variant name", []))
730        );
731    }
732
733    #[test]
734    fn parse_bit_sequences() {
735        use scale_bits::bits;
736        assert_eq!(
737            from("<011010110101101>"),
738            Ok(Value::bit_sequence(bits![0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1]))
739        );
740        assert_eq!(from("<01101>"), Ok(Value::bit_sequence(bits![0, 1, 1, 0, 1])));
741        assert_eq!(from("<0>"), Ok(Value::bit_sequence(bits![0])));
742        assert_eq!(from("<>"), Ok(Value::bit_sequence(bits![])));
743    }
744
745    #[test]
746    fn custom_parsers() {
747        let custom_parser = FromStrBuilder::new()
748            // We add the ability to parse custom hex strings:
749            .add_custom_parser(|s| {
750                let mut toks = s.into_tokens();
751
752                // Return None if this clearly isn't hex.
753                if !toks.tokens("0x".chars()) {
754                    return None;
755                }
756
757                let from = toks.location();
758                let num_hex_chars = toks.skip_while(|c| {
759                    c.is_numeric()
760                        || ['a', 'b', 'c', 'd', 'e', 'f'].contains(&c.to_ascii_lowercase())
761                });
762
763                // Return an error if is _looks_ like hex but something isn't right about it.
764                if num_hex_chars % 2 != 0 {
765                    let e = ParseErrorKind::custom("Wrong number hex")
766                        .between(from.offset(), toks.offset());
767                    return Some(Err(e));
768                }
769
770                // For testing, we just dump the hex chars into a string.
771                let hex: String = toks.slice(from, toks.location()).as_iter().collect();
772                // Since we parsed stuff, we need to update the cursor to consume what we parsed.
773                *s = toks.remaining();
774
775                Some(Ok(Value::string(hex)))
776            });
777
778        let expected = [
779            // Hex can be parsed as a part of values now!
780            ("(1, 0x1234, true)", (Ok(value!((1u8, "1234", true))), "")),
781            // Invalid hex emits the expected custom error:
782            (
783                "0x12345zzz",
784                (Err(ParseErrorKind::custom("Wrong number hex").between(2, 7)), "0x12345zzz"),
785            ),
786            // Custom error locations are relative to the entire string:
787            (
788                "(true, 0x12345)",
789                (Err(ParseErrorKind::custom("Wrong number hex").between(9, 14)), ", 0x12345)"),
790            ),
791        ];
792
793        for (s, v) in expected {
794            let (expected_res, expected_leftover) = (v.0, v.1);
795            let (res, leftover) = custom_parser.parse(s);
796            assert_eq!(res, expected_res, "result isn't what we expected for: {s}");
797            assert_eq!(leftover, expected_leftover, "wrong number of leftover bytes for: {s}");
798        }
799    }
800}