Skip to main content

meta_language/grammar/
links.rs

1use std::char;
2
3use crate::grammar::{CharClassItem, Grammar, GrammarExpr, GrammarFormat, GrammarRule, RuleKind};
4use crate::link_network::{Link, LinkId, LinkMetadata, LinkNetwork, LinkType};
5use crate::rust_codec::{FromLinks, LinksCodecError, LinksDecoder, LinksEncoder, ToLinks};
6
7const GRAMMAR: &str = "grammar::grammar";
8const RULE: &str = "grammar::rule";
9
10const EXPR_EMPTY: &str = "grammar::expr::empty";
11const EXPR_TERMINAL: &str = "grammar::expr::terminal";
12const EXPR_TERMINAL_INSENSITIVE: &str = "grammar::expr::terminal-insensitive";
13const EXPR_CHAR_RANGE: &str = "grammar::expr::char-range";
14const EXPR_CHAR_CLASS: &str = "grammar::expr::char-class";
15const EXPR_ANY_CHAR: &str = "grammar::expr::any-char";
16const EXPR_NON_TERMINAL: &str = "grammar::expr::non-terminal";
17const EXPR_CHOICE: &str = "grammar::expr::choice";
18const EXPR_SEQUENCE: &str = "grammar::expr::sequence";
19const EXPR_OPTIONAL: &str = "grammar::expr::optional";
20const EXPR_ZERO_OR_MORE: &str = "grammar::expr::zero-or-more";
21const EXPR_ONE_OR_MORE: &str = "grammar::expr::one-or-more";
22const EXPR_REPEAT: &str = "grammar::expr::repeat";
23const EXPR_AND: &str = "grammar::expr::and";
24const EXPR_NOT: &str = "grammar::expr::not";
25const EXPR_CAPTURE: &str = "grammar::expr::capture";
26
27const CHAR_CLASS_CHAR: &str = "grammar::char-class-item::char";
28const CHAR_CLASS_RANGE: &str = "grammar::char-class-item::range";
29
30const VALUE_NONE: &str = "grammar::value::none";
31const VALUE_SOME: &str = "grammar::value::some";
32const VALUE_STRING_PREFIX: &str = "grammar::value::string::";
33const VALUE_CHAR_PREFIX: &str = "grammar::value::char::";
34const VALUE_BOOL_PREFIX: &str = "grammar::value::bool::";
35const VALUE_USIZE_PREFIX: &str = "grammar::value::usize::";
36const VALUE_RULE_KIND_PREFIX: &str = "grammar::value::rule-kind::";
37const VALUE_FORMAT_PREFIX: &str = "grammar::value::format::";
38
39impl ToLinks for Grammar {
40    fn to_links(&self, encoder: &mut LinksEncoder) -> LinkId {
41        encode_grammar(encoder.network_mut(), self)
42    }
43}
44
45impl FromLinks for Grammar {
46    fn from_links(decoder: &mut LinksDecoder<'_>, link: LinkId) -> Result<Self, LinksCodecError> {
47        decode_grammar(decoder.network(), link)
48    }
49}
50
51fn encode_grammar(network: &mut LinkNetwork, grammar: &Grammar) -> LinkId {
52    let mut references = Vec::with_capacity(grammar.rules().len() + 2);
53    references.push(encode_option_format(network, grammar.source_format()));
54    references.push(encode_option_string(network, grammar.start()));
55    references.extend(
56        grammar
57            .rules()
58            .iter()
59            .map(|rule| encode_rule(network, rule)),
60    );
61    insert_grammar_node(network, GRAMMAR, &references)
62}
63
64fn decode_grammar(network: &LinkNetwork, link: LinkId) -> Result<Grammar, LinksCodecError> {
65    let references = expect_tag(network, link, GRAMMAR)?;
66    if references.len() < 2 {
67        return Err(malformed(
68            link,
69            "grammar links must contain source format and start references",
70        ));
71    }
72
73    let source_format = decode_option_format(network, references[0])?;
74    let start = decode_option_string(network, references[1])?;
75    let mut grammar = Grammar::new();
76    if let Some(source_format) = source_format {
77        grammar = grammar.with_source_format(source_format);
78    }
79    if let Some(start) = start {
80        grammar = grammar.with_start(start);
81    }
82
83    for rule in &references[2..] {
84        grammar.add_rule(decode_rule(network, *rule)?);
85    }
86    Ok(grammar)
87}
88
89fn encode_rule(network: &mut LinkNetwork, rule: &GrammarRule) -> LinkId {
90    let expr = encode_expr(network, rule.expr());
91    let references = [
92        encode_string_value(network, rule.name()),
93        expr,
94        encode_rule_kind_value(network, rule.kind()),
95        encode_option_string(network, rule.concept()),
96        encode_option_string(network, rule.doc()),
97    ];
98    insert_grammar_node(network, RULE, &references)
99}
100
101fn decode_rule(network: &LinkNetwork, link: LinkId) -> Result<GrammarRule, LinksCodecError> {
102    let references = expect_tag(network, link, RULE)?;
103    let [name, expr, kind, concept, doc] = references else {
104        return Err(malformed(
105            link,
106            "rule links must contain name, expression, kind, concept, and doc references",
107        ));
108    };
109    let mut rule = GrammarRule::new(
110        decode_string_value(network, *name)?,
111        decode_expr(network, *expr)?,
112    )
113    .with_kind(decode_rule_kind_value(network, *kind)?);
114    if let Some(concept) = decode_option_string(network, *concept)? {
115        rule = rule.with_concept(concept);
116    }
117    if let Some(doc) = decode_option_string(network, *doc)? {
118        rule = rule.with_doc(doc);
119    }
120    Ok(rule)
121}
122
123fn encode_expr(network: &mut LinkNetwork, expr: &GrammarExpr) -> LinkId {
124    match expr {
125        GrammarExpr::Empty => insert_grammar_node(network, EXPR_EMPTY, &[]),
126        GrammarExpr::Terminal(value) => {
127            let references = [encode_string_value(network, value)];
128            insert_grammar_node(network, EXPR_TERMINAL, &references)
129        }
130        GrammarExpr::TerminalInsensitive(value) => {
131            let references = [encode_string_value(network, value)];
132            insert_grammar_node(network, EXPR_TERMINAL_INSENSITIVE, &references)
133        }
134        GrammarExpr::CharRange(start, end) => {
135            let references = [
136                encode_char_value(network, *start),
137                encode_char_value(network, *end),
138            ];
139            insert_grammar_node(network, EXPR_CHAR_RANGE, &references)
140        }
141        GrammarExpr::CharClass { negated, items } => {
142            let mut references = Vec::with_capacity(items.len() + 1);
143            references.push(encode_bool_value(network, *negated));
144            references.extend(
145                items
146                    .iter()
147                    .map(|item| encode_char_class_item(network, item)),
148            );
149            insert_grammar_node(network, EXPR_CHAR_CLASS, &references)
150        }
151        GrammarExpr::AnyChar => insert_grammar_node(network, EXPR_ANY_CHAR, &[]),
152        GrammarExpr::NonTerminal(value) => {
153            let references = [encode_string_value(network, value)];
154            insert_grammar_node(network, EXPR_NON_TERMINAL, &references)
155        }
156        GrammarExpr::Choice {
157            ordered,
158            alternatives,
159        } => {
160            let mut references = Vec::with_capacity(alternatives.len() + 1);
161            references.push(encode_bool_value(network, *ordered));
162            references.extend(
163                alternatives
164                    .iter()
165                    .map(|alternative| encode_expr(network, alternative)),
166            );
167            insert_grammar_node(network, EXPR_CHOICE, &references)
168        }
169        GrammarExpr::Sequence(items) => {
170            let references = items
171                .iter()
172                .map(|item| encode_expr(network, item))
173                .collect::<Vec<_>>();
174            insert_grammar_node(network, EXPR_SEQUENCE, &references)
175        }
176        GrammarExpr::Optional(expr) => encode_unary_expr(network, EXPR_OPTIONAL, expr),
177        GrammarExpr::ZeroOrMore(expr) => encode_unary_expr(network, EXPR_ZERO_OR_MORE, expr),
178        GrammarExpr::OneOrMore(expr) => encode_unary_expr(network, EXPR_ONE_OR_MORE, expr),
179        GrammarExpr::Repeat { expr, min, max } => {
180            let references = [
181                encode_expr(network, expr),
182                encode_usize_value(network, *min),
183                encode_option_usize(network, *max),
184            ];
185            insert_grammar_node(network, EXPR_REPEAT, &references)
186        }
187        GrammarExpr::And(expr) => encode_unary_expr(network, EXPR_AND, expr),
188        GrammarExpr::Not(expr) => encode_unary_expr(network, EXPR_NOT, expr),
189        GrammarExpr::Capture { label, expr } => {
190            let references = [
191                encode_option_string(network, label.as_deref()),
192                encode_expr(network, expr),
193            ];
194            insert_grammar_node(network, EXPR_CAPTURE, &references)
195        }
196    }
197}
198
199fn decode_expr(network: &LinkNetwork, link: LinkId) -> Result<GrammarExpr, LinksCodecError> {
200    let (term, references) = grammar_link(network, link)?;
201    match term {
202        EXPR_EMPTY => {
203            expect_count(link, references, 0)?;
204            Ok(GrammarExpr::Empty)
205        }
206        EXPR_TERMINAL => {
207            let [value] = references else {
208                return Err(expected_count(link, 1, references.len()));
209            };
210            Ok(GrammarExpr::Terminal(decode_string_value(network, *value)?))
211        }
212        EXPR_TERMINAL_INSENSITIVE => {
213            let [value] = references else {
214                return Err(expected_count(link, 1, references.len()));
215            };
216            Ok(GrammarExpr::TerminalInsensitive(decode_string_value(
217                network, *value,
218            )?))
219        }
220        EXPR_CHAR_RANGE => {
221            let [start, end] = references else {
222                return Err(expected_count(link, 2, references.len()));
223            };
224            Ok(GrammarExpr::CharRange(
225                decode_char_value(network, *start)?,
226                decode_char_value(network, *end)?,
227            ))
228        }
229        EXPR_CHAR_CLASS => {
230            let Some((negated, items)) = references.split_first() else {
231                return Err(malformed(
232                    link,
233                    "character class links must contain the negated flag",
234                ));
235            };
236            Ok(GrammarExpr::CharClass {
237                negated: decode_bool_value(network, *negated)?,
238                items: items
239                    .iter()
240                    .map(|item| decode_char_class_item(network, *item))
241                    .collect::<Result<Vec<_>, _>>()?,
242            })
243        }
244        EXPR_ANY_CHAR => {
245            expect_count(link, references, 0)?;
246            Ok(GrammarExpr::AnyChar)
247        }
248        EXPR_NON_TERMINAL => {
249            let [value] = references else {
250                return Err(expected_count(link, 1, references.len()));
251            };
252            Ok(GrammarExpr::NonTerminal(decode_string_value(
253                network, *value,
254            )?))
255        }
256        EXPR_CHOICE => {
257            let Some((ordered, alternatives)) = references.split_first() else {
258                return Err(malformed(
259                    link,
260                    "choice links must contain the ordered flag",
261                ));
262            };
263            Ok(GrammarExpr::Choice {
264                ordered: decode_bool_value(network, *ordered)?,
265                alternatives: alternatives
266                    .iter()
267                    .map(|alternative| decode_expr(network, *alternative))
268                    .collect::<Result<Vec<_>, _>>()?,
269            })
270        }
271        EXPR_SEQUENCE => Ok(GrammarExpr::Sequence(
272            references
273                .iter()
274                .map(|item| decode_expr(network, *item))
275                .collect::<Result<Vec<_>, _>>()?,
276        )),
277        EXPR_OPTIONAL => decode_unary_expr(network, link, references).map(GrammarExpr::optional),
278        EXPR_ZERO_OR_MORE => {
279            decode_unary_expr(network, link, references).map(GrammarExpr::zero_or_more)
280        }
281        EXPR_ONE_OR_MORE => {
282            decode_unary_expr(network, link, references).map(GrammarExpr::one_or_more)
283        }
284        EXPR_REPEAT => {
285            let [expr, min, max] = references else {
286                return Err(expected_count(link, 3, references.len()));
287            };
288            Ok(GrammarExpr::repeat(
289                decode_expr(network, *expr)?,
290                decode_usize_value(network, *min)?,
291                decode_option_usize(network, *max)?,
292            ))
293        }
294        EXPR_AND => decode_unary_expr(network, link, references).map(GrammarExpr::and),
295        EXPR_NOT => decode_unary_expr(network, link, references).map(GrammarExpr::not),
296        EXPR_CAPTURE => {
297            let [label, expr] = references else {
298                return Err(expected_count(link, 2, references.len()));
299            };
300            Ok(GrammarExpr::Capture {
301                label: decode_option_string(network, *label)?,
302                expr: Box::new(decode_expr(network, *expr)?),
303            })
304        }
305        _ => Err(malformed(
306            link,
307            format!("unexpected grammar expression tag {term:?}"),
308        )),
309    }
310}
311
312fn encode_unary_expr(network: &mut LinkNetwork, tag: &str, expr: &GrammarExpr) -> LinkId {
313    let references = [encode_expr(network, expr)];
314    insert_grammar_node(network, tag, &references)
315}
316
317fn decode_unary_expr(
318    network: &LinkNetwork,
319    link: LinkId,
320    references: &[LinkId],
321) -> Result<GrammarExpr, LinksCodecError> {
322    let [expr] = references else {
323        return Err(expected_count(link, 1, references.len()));
324    };
325    decode_expr(network, *expr)
326}
327
328fn encode_char_class_item(network: &mut LinkNetwork, item: &CharClassItem) -> LinkId {
329    match item {
330        CharClassItem::Char(value) => {
331            let references = [encode_char_value(network, *value)];
332            insert_grammar_node(network, CHAR_CLASS_CHAR, &references)
333        }
334        CharClassItem::Range(start, end) => {
335            let references = [
336                encode_char_value(network, *start),
337                encode_char_value(network, *end),
338            ];
339            insert_grammar_node(network, CHAR_CLASS_RANGE, &references)
340        }
341    }
342}
343
344fn decode_char_class_item(
345    network: &LinkNetwork,
346    link: LinkId,
347) -> Result<CharClassItem, LinksCodecError> {
348    let (term, references) = grammar_link(network, link)?;
349    match term {
350        CHAR_CLASS_CHAR => {
351            let [value] = references else {
352                return Err(expected_count(link, 1, references.len()));
353            };
354            Ok(CharClassItem::Char(decode_char_value(network, *value)?))
355        }
356        CHAR_CLASS_RANGE => {
357            let [start, end] = references else {
358                return Err(expected_count(link, 2, references.len()));
359            };
360            Ok(CharClassItem::Range(
361                decode_char_value(network, *start)?,
362                decode_char_value(network, *end)?,
363            ))
364        }
365        _ => Err(malformed(
366            link,
367            format!("unexpected character class item tag {term:?}"),
368        )),
369    }
370}
371
372fn encode_option_string(network: &mut LinkNetwork, value: Option<&str>) -> LinkId {
373    match value {
374        Some(value) => {
375            let references = [encode_string_value(network, value)];
376            insert_grammar_node(network, VALUE_SOME, &references)
377        }
378        None => insert_grammar_node(network, VALUE_NONE, &[]),
379    }
380}
381
382fn decode_option_string(
383    network: &LinkNetwork,
384    link: LinkId,
385) -> Result<Option<String>, LinksCodecError> {
386    decode_option(network, link, decode_string_value)
387}
388
389fn encode_option_usize(network: &mut LinkNetwork, value: Option<usize>) -> LinkId {
390    match value {
391        Some(value) => {
392            let references = [encode_usize_value(network, value)];
393            insert_grammar_node(network, VALUE_SOME, &references)
394        }
395        None => insert_grammar_node(network, VALUE_NONE, &[]),
396    }
397}
398
399fn decode_option_usize(
400    network: &LinkNetwork,
401    link: LinkId,
402) -> Result<Option<usize>, LinksCodecError> {
403    decode_option(network, link, decode_usize_value)
404}
405
406fn encode_option_format(network: &mut LinkNetwork, value: Option<GrammarFormat>) -> LinkId {
407    match value {
408        Some(value) => {
409            let references = [encode_format_value(network, value)];
410            insert_grammar_node(network, VALUE_SOME, &references)
411        }
412        None => insert_grammar_node(network, VALUE_NONE, &[]),
413    }
414}
415
416fn decode_option_format(
417    network: &LinkNetwork,
418    link: LinkId,
419) -> Result<Option<GrammarFormat>, LinksCodecError> {
420    decode_option(network, link, decode_format_value)
421}
422
423fn decode_option<T>(
424    network: &LinkNetwork,
425    link: LinkId,
426    decode_value: fn(&LinkNetwork, LinkId) -> Result<T, LinksCodecError>,
427) -> Result<Option<T>, LinksCodecError> {
428    let (term, references) = grammar_link(network, link)?;
429    match term {
430        VALUE_NONE => {
431            expect_count(link, references, 0)?;
432            Ok(None)
433        }
434        VALUE_SOME => {
435            let [value] = references else {
436                return Err(expected_count(link, 1, references.len()));
437            };
438            decode_value(network, *value).map(Some)
439        }
440        _ => Err(malformed(
441            link,
442            format!("expected optional value tag, found {term:?}"),
443        )),
444    }
445}
446
447fn encode_string_value(network: &mut LinkNetwork, value: &str) -> LinkId {
448    insert_grammar_value(
449        network,
450        &format!("{VALUE_STRING_PREFIX}{}", hex_encode(value)),
451    )
452}
453
454fn decode_string_value(network: &LinkNetwork, link: LinkId) -> Result<String, LinksCodecError> {
455    let value = prefixed_value(network, link, VALUE_STRING_PREFIX, "String")?;
456    hex_decode(link, "String", value)
457}
458
459fn encode_char_value(network: &mut LinkNetwork, value: char) -> LinkId {
460    insert_grammar_value(
461        network,
462        &format!("{VALUE_CHAR_PREFIX}{:x}", u32::from(value)),
463    )
464}
465
466fn decode_char_value(network: &LinkNetwork, link: LinkId) -> Result<char, LinksCodecError> {
467    let value = prefixed_value(network, link, VALUE_CHAR_PREFIX, "char")?;
468    let code = u32::from_str_radix(value, 16)
469        .map_err(|error| invalid_value(link, "char", Some(value), &error.to_string()))?;
470    char::from_u32(code).ok_or_else(|| invalid_value(link, "char", Some(value), "invalid char"))
471}
472
473fn encode_bool_value(network: &mut LinkNetwork, value: bool) -> LinkId {
474    insert_grammar_value(network, &format!("{VALUE_BOOL_PREFIX}{value}"))
475}
476
477fn decode_bool_value(network: &LinkNetwork, link: LinkId) -> Result<bool, LinksCodecError> {
478    let value = prefixed_value(network, link, VALUE_BOOL_PREFIX, "bool")?;
479    value.parse().map_err(|error: std::str::ParseBoolError| {
480        invalid_value(link, "bool", Some(value), &error.to_string())
481    })
482}
483
484fn encode_usize_value(network: &mut LinkNetwork, value: usize) -> LinkId {
485    insert_grammar_value(network, &format!("{VALUE_USIZE_PREFIX}{value}"))
486}
487
488fn decode_usize_value(network: &LinkNetwork, link: LinkId) -> Result<usize, LinksCodecError> {
489    let value = prefixed_value(network, link, VALUE_USIZE_PREFIX, "usize")?;
490    value.parse().map_err(|error: std::num::ParseIntError| {
491        invalid_value(link, "usize", Some(value), &error.to_string())
492    })
493}
494
495fn encode_rule_kind_value(network: &mut LinkNetwork, value: RuleKind) -> LinkId {
496    insert_grammar_value(
497        network,
498        &format!("{VALUE_RULE_KIND_PREFIX}{}", value.as_str()),
499    )
500}
501
502fn decode_rule_kind_value(
503    network: &LinkNetwork,
504    link: LinkId,
505) -> Result<RuleKind, LinksCodecError> {
506    let value = prefixed_value(network, link, VALUE_RULE_KIND_PREFIX, "RuleKind")?;
507    RuleKind::from_tag(value)
508        .ok_or_else(|| invalid_value(link, "RuleKind", Some(value), "unknown rule kind"))
509}
510
511fn encode_format_value(network: &mut LinkNetwork, value: GrammarFormat) -> LinkId {
512    insert_grammar_value(network, &format!("{VALUE_FORMAT_PREFIX}{}", value.as_str()))
513}
514
515fn decode_format_value(
516    network: &LinkNetwork,
517    link: LinkId,
518) -> Result<GrammarFormat, LinksCodecError> {
519    let value = prefixed_value(network, link, VALUE_FORMAT_PREFIX, "GrammarFormat")?;
520    GrammarFormat::from_tag(value)
521        .ok_or_else(|| invalid_value(link, "GrammarFormat", Some(value), "unknown grammar format"))
522}
523
524fn insert_grammar_node(network: &mut LinkNetwork, term: &str, references: &[LinkId]) -> LinkId {
525    network.insert_dynamic_link(
526        references,
527        LinkMetadata::new()
528            .with_link_type(LinkType::Grammar)
529            .with_term(term),
530    )
531}
532
533fn insert_grammar_value(network: &mut LinkNetwork, term: &str) -> LinkId {
534    insert_grammar_node(network, term, &[])
535}
536
537fn expect_tag<'network>(
538    network: &'network LinkNetwork,
539    link: LinkId,
540    expected: &str,
541) -> Result<&'network [LinkId], LinksCodecError> {
542    let (term, references) = grammar_link(network, link)?;
543    if term == expected {
544        Ok(references)
545    } else {
546        Err(malformed(
547            link,
548            format!("expected grammar tag {expected:?}, found {term:?}"),
549        ))
550    }
551}
552
553fn grammar_link(network: &LinkNetwork, link: LinkId) -> Result<(&str, &[LinkId]), LinksCodecError> {
554    let link = network
555        .link(link)
556        .ok_or(LinksCodecError::MissingLink(link))?;
557    expect_grammar_type(link)?;
558    let Some(term) = link.metadata().term() else {
559        return Err(malformed(link.id(), "grammar link is missing its term tag"));
560    };
561    Ok((term, link.references()))
562}
563
564fn expect_grammar_type(link: &Link) -> Result<(), LinksCodecError> {
565    if link.metadata().link_type() == Some(LinkType::Grammar) {
566        return Ok(());
567    }
568    Err(malformed(
569        link.id(),
570        format!(
571            "expected LinkType::Grammar, found {:?}",
572            link.metadata().link_type()
573        ),
574    ))
575}
576
577fn prefixed_value<'network>(
578    network: &'network LinkNetwork,
579    link: LinkId,
580    prefix: &str,
581    type_name: &str,
582) -> Result<&'network str, LinksCodecError> {
583    let (term, _) = grammar_link(network, link)?;
584    term.strip_prefix(prefix)
585        .ok_or_else(|| invalid_value(link, type_name, Some(term), "wrong value prefix"))
586}
587
588fn expect_count(
589    link: LinkId,
590    references: &[LinkId],
591    expected: usize,
592) -> Result<(), LinksCodecError> {
593    if references.len() == expected {
594        Ok(())
595    } else {
596        Err(expected_count(link, expected, references.len()))
597    }
598}
599
600fn expected_count(link: LinkId, expected: usize, actual: usize) -> LinksCodecError {
601    malformed(
602        link,
603        format!("expected {expected} references, found {actual}"),
604    )
605}
606
607fn malformed(link: LinkId, reason: impl Into<String>) -> LinksCodecError {
608    LinksCodecError::MalformedObject {
609        object: link,
610        reason: reason.into(),
611    }
612}
613
614fn invalid_value(
615    link: LinkId,
616    type_name: &str,
617    value: Option<&str>,
618    reason: &str,
619) -> LinksCodecError {
620    LinksCodecError::InvalidLiteral {
621        object: link,
622        type_name: type_name.to_string(),
623        value: value.map(ToString::to_string),
624        reason: reason.to_string(),
625    }
626}
627
628fn hex_encode(value: &str) -> String {
629    const HEX: &[u8; 16] = b"0123456789abcdef";
630    let mut output = String::with_capacity(value.len() * 2);
631    for byte in value.as_bytes() {
632        output.push(HEX[(byte >> 4) as usize] as char);
633        output.push(HEX[(byte & 0x0f) as usize] as char);
634    }
635    output
636}
637
638fn hex_decode(link: LinkId, type_name: &str, value: &str) -> Result<String, LinksCodecError> {
639    if value.len() % 2 != 0 {
640        return Err(invalid_value(
641            link,
642            type_name,
643            Some(value),
644            "hex payload has odd length",
645        ));
646    }
647
648    let mut bytes = Vec::with_capacity(value.len() / 2);
649    for pair in value.as_bytes().chunks_exact(2) {
650        let high = hex_digit(pair[0])
651            .ok_or_else(|| invalid_value(link, type_name, Some(value), "invalid hex digit"))?;
652        let low = hex_digit(pair[1])
653            .ok_or_else(|| invalid_value(link, type_name, Some(value), "invalid hex digit"))?;
654        bytes.push((high << 4) | low);
655    }
656
657    String::from_utf8(bytes).map_err(|error| {
658        invalid_value(
659            link,
660            type_name,
661            Some(value),
662            &format!("invalid UTF-8: {error}"),
663        )
664    })
665}
666
667const fn hex_digit(value: u8) -> Option<u8> {
668    match value {
669        b'0'..=b'9' => Some(value - b'0'),
670        b'a'..=b'f' => Some(value - b'a' + 10),
671        b'A'..=b'F' => Some(value - b'A' + 10),
672        _ => None,
673    }
674}