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}