1use alloc::boxed::Box;
4use alloc::string::String;
5use core::num::NonZero;
6use core::str::{self, FromStr};
7
8use super::{Error, Span, Spanned, ast, unused};
9use crate::internal_macros::bug;
10
11#[inline]
13pub(super) fn parse<'a>(
14 ast_items: impl Iterator<Item = Result<ast::Item<'a>, Error>>,
15) -> impl Iterator<Item = Result<Item<'a>, Error>> {
16 ast_items.map(|ast_item| ast_item.and_then(Item::from_ast))
17}
18
19pub(super) enum Item<'a> {
21 Literal(&'a [u8]),
23 Component(Component),
25 Optional {
27 value: Box<[Self]>,
29 span: Span,
31 },
32 First {
34 value: Box<[Box<[Self]>]>,
36 span: Span,
38 },
39}
40
41impl Item<'_> {
42 pub(super) fn from_ast(ast_item: ast::Item<'_>) -> Result<Item<'_>, Error> {
44 Ok(match ast_item {
45 ast::Item::Component {
46 _opening_bracket: _,
47 _leading_whitespace: _,
48 name,
49 modifiers,
50 _trailing_whitespace: _,
51 _closing_bracket: _,
52 } => Item::Component(component_from_ast(&name, &modifiers)?),
53 ast::Item::Literal(Spanned { value, span: _ }) => Item::Literal(value),
54 ast::Item::EscapedBracket {
55 _first: _,
56 _second: _,
57 } => Item::Literal(b"["),
58 ast::Item::Optional {
59 opening_bracket,
60 _leading_whitespace: _,
61 _optional_kw: _,
62 _whitespace: _,
63 nested_format_description,
64 closing_bracket,
65 } => {
66 let items = nested_format_description
67 .items
68 .into_vec()
69 .into_iter()
70 .map(Item::from_ast)
71 .collect::<Result<_, _>>()?;
72 Item::Optional {
73 value: items,
74 span: opening_bracket.to(closing_bracket),
75 }
76 }
77 ast::Item::First {
78 opening_bracket,
79 _leading_whitespace: _,
80 _first_kw: _,
81 _whitespace: _,
82 nested_format_descriptions,
83 closing_bracket,
84 } => {
85 let items = nested_format_descriptions
86 .into_vec()
87 .into_iter()
88 .map(|nested_format_description| {
89 nested_format_description
90 .items
91 .into_vec()
92 .into_iter()
93 .map(Item::from_ast)
94 .collect()
95 })
96 .collect::<Result<_, _>>()?;
97 Item::First {
98 value: items,
99 span: opening_bracket.to(closing_bracket),
100 }
101 }
102 })
103 }
104}
105
106impl<'a> TryFrom<Item<'a>> for crate::format_description::BorrowedFormatItem<'a> {
107 type Error = Error;
108
109 #[inline]
110 fn try_from(item: Item<'a>) -> Result<Self, Self::Error> {
111 match item {
112 #[expect(deprecated)]
113 Item::Literal(literal) => Ok(Self::Literal(literal)),
114 Item::Component(component) => Ok(Self::Component(component.into())),
115 Item::Optional { value: _, span } => Err(Error {
116 _inner: unused(span.error(
117 "optional items are not supported in runtime-parsed format descriptions",
118 )),
119 public: crate::error::InvalidFormatDescription::NotSupported {
120 what: "optional item",
121 context: "runtime-parsed format descriptions",
122 index: span.start.byte as usize,
123 },
124 }),
125 Item::First { value: _, span } => Err(Error {
126 _inner: unused(span.error(
127 "'first' items are not supported in runtime-parsed format descriptions",
128 )),
129 public: crate::error::InvalidFormatDescription::NotSupported {
130 what: "'first' item",
131 context: "runtime-parsed format descriptions",
132 index: span.start.byte as usize,
133 },
134 }),
135 }
136 }
137}
138
139impl From<Item<'_>> for crate::format_description::OwnedFormatItem {
140 #[inline]
141 fn from(item: Item<'_>) -> Self {
142 match item {
143 #[expect(deprecated)]
144 Item::Literal(literal) => Self::Literal(literal.to_vec().into_boxed_slice()),
145 Item::Component(component) => Self::Component(component.into()),
146 Item::Optional { value, span: _ } => Self::Optional(Box::new(value.into())),
147 Item::First { value, span: _ } => {
148 Self::First(value.into_vec().into_iter().map(Into::into).collect())
149 }
150 }
151 }
152}
153
154impl<'a> From<Box<[Item<'a>]>> for crate::format_description::OwnedFormatItem {
155 #[inline]
156 fn from(items: Box<[Item<'a>]>) -> Self {
157 let items = items.into_vec();
158 match <[_; 1]>::try_from(items) {
159 Ok([item]) => item.into(),
160 Err(vec) => Self::Compound(vec.into_iter().map(Into::into).collect()),
161 }
162 }
163}
164
165macro_rules! component_definition {
167 (@if_required required then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($then)* };
168 (@if_required then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($($else)*)? };
169 (@if_from_str from_str then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($then)* };
170 (@if_from_str then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($($else)*)? };
171
172 ($vis:vis enum $name:ident {$(
173 $variant:ident = $parse_variant:literal {$(
174 $(#[$required:tt])?
175 $field:ident = $parse_field:literal:
176 Option<$(#[$from_str:tt])? $field_type:ty>
177 ),* $(,)?}
178 ),* $(,)?}) => {
179 $vis enum $name {
180 $($variant($variant),)*
181 }
182
183 $($vis struct $variant {
184 $($field: Option<$field_type>),*
185 })*
186
187 $(impl $variant {
188 #[inline]
190 fn with_modifiers(
191 modifiers: &[ast::Modifier<'_>],
192 _component_span: Span,
193 ) -> Result<Self, Error>
194 {
195 #[allow(unused_mut)]
197 let mut this = Self {
198 $($field: None),*
199 };
200
201 for modifier in modifiers {
202 $(#[expect(clippy::string_lit_as_bytes)]
203 if modifier.key.eq_ignore_ascii_case($parse_field.as_bytes()) {
204 this.$field = component_definition!(@if_from_str $($from_str)?
205 then {
206 parse_from_modifier_value::<$field_type>(&modifier.value)?
207 } else {
208 <$field_type>::from_modifier_value(&modifier.value)?
209 });
210 continue;
211 })*
212 return Err(Error {
213 _inner: unused(modifier.key.span.error("invalid modifier key")),
214 public: crate::error::InvalidFormatDescription::InvalidModifier {
215 value: String::from_utf8_lossy(*modifier.key).into_owned(),
216 index: modifier.key.span.start.byte as usize,
217 }
218 });
219 }
220
221 $(component_definition! { @if_required $($required)? then {
222 if this.$field.is_none() {
223 return Err(Error {
224 _inner: unused(_component_span.error("missing required modifier")),
225 public:
226 crate::error::InvalidFormatDescription::MissingRequiredModifier {
227 name: $parse_field,
228 index: _component_span.start.byte as usize,
229 }
230 });
231 }
232 }})*
233
234 Ok(this)
235 }
236 })*
237
238 #[inline]
240 fn component_from_ast(
241 name: &Spanned<&[u8]>,
242 modifiers: &[ast::Modifier<'_>],
243 ) -> Result<Component, Error> {
244 $(#[expect(clippy::string_lit_as_bytes)]
245 if name.eq_ignore_ascii_case($parse_variant.as_bytes()) {
246 return Ok(Component::$variant($variant::with_modifiers(&modifiers, name.span)?));
247 })*
248 Err(Error {
249 _inner: unused(name.span.error("invalid component")),
250 public: crate::error::InvalidFormatDescription::InvalidComponentName {
251 name: String::from_utf8_lossy(name).into_owned(),
252 index: name.span.start.byte as usize,
253 },
254 })
255 }
256 }
257}
258
259component_definition! {
261 pub(super) enum Component {
262 Day = "day" {
263 padding = "padding": Option<Padding>,
264 },
265 End = "end" {
266 trailing_input = "trailing_input": Option<TrailingInput>,
267 },
268 Hour = "hour" {
269 padding = "padding": Option<Padding>,
270 base = "repr": Option<HourBase>,
271 },
272 Ignore = "ignore" {
273 #[required]
274 count = "count": Option<#[from_str] NonZero<u16>>,
275 },
276 Minute = "minute" {
277 padding = "padding": Option<Padding>,
278 },
279 Month = "month" {
280 padding = "padding": Option<Padding>,
281 repr = "repr": Option<MonthRepr>,
282 case_sensitive = "case_sensitive": Option<MonthCaseSensitive>,
283 },
284 OffsetHour = "offset_hour" {
285 sign_behavior = "sign": Option<SignBehavior>,
286 padding = "padding": Option<Padding>,
287 },
288 OffsetMinute = "offset_minute" {
289 padding = "padding": Option<Padding>,
290 },
291 OffsetSecond = "offset_second" {
292 padding = "padding": Option<Padding>,
293 },
294 Ordinal = "ordinal" {
295 padding = "padding": Option<Padding>,
296 },
297 Period = "period" {
298 case = "case": Option<PeriodCase>,
299 case_sensitive = "case_sensitive": Option<PeriodCaseSensitive>,
300 },
301 Second = "second" {
302 padding = "padding": Option<Padding>,
303 },
304 Subsecond = "subsecond" {
305 digits = "digits": Option<SubsecondDigits>,
306 },
307 UnixTimestamp = "unix_timestamp" {
308 precision = "precision": Option<UnixTimestampPrecision>,
309 sign_behavior = "sign": Option<SignBehavior>,
310 },
311 Weekday = "weekday" {
312 repr = "repr": Option<WeekdayRepr>,
313 one_indexed = "one_indexed": Option<WeekdayOneIndexed>,
314 case_sensitive = "case_sensitive": Option<WeekdayCaseSensitive>,
315 },
316 WeekNumber = "week_number" {
317 padding = "padding": Option<Padding>,
318 repr = "repr": Option<WeekNumberRepr>,
319 },
320 Year = "year" {
321 padding = "padding": Option<Padding>,
322 repr = "repr": Option<YearRepr>,
323 range = "range": Option<YearRange>,
324 base = "base": Option<YearBase>,
325 sign_behavior = "sign": Option<SignBehavior>,
326 },
327 }
328}
329
330impl From<Component> for crate::format_description::Component {
331 #[inline]
332 fn from(component: Component) -> Self {
333 use crate::format_description::modifier;
334 match component {
335 Component::Day(Day { padding }) => Self::Day(modifier::Day {
336 padding: padding.unwrap_or_default().into(),
337 }),
338 Component::End(End { trailing_input }) => Self::End(modifier::End {
339 trailing_input: trailing_input.unwrap_or_default().into(),
340 }),
341 Component::Hour(Hour { padding, base }) => match base.unwrap_or_default() {
342 HourBase::Twelve => Self::Hour12(modifier::Hour12 {
343 padding: padding.unwrap_or_default().into(),
344 }),
345 HourBase::TwentyFour => Self::Hour24(modifier::Hour24 {
346 padding: padding.unwrap_or_default().into(),
347 }),
348 },
349 Component::Ignore(Ignore { count }) => Self::Ignore(modifier::Ignore {
350 count: match count {
351 Some(value) => value,
352 None => bug!("required modifier was not set"),
353 },
354 }),
355 Component::Minute(Minute { padding }) => Self::Minute(modifier::Minute {
356 padding: padding.unwrap_or_default().into(),
357 }),
358 Component::Month(Month {
359 padding,
360 repr,
361 case_sensitive,
362 }) => match repr.unwrap_or_default() {
363 MonthRepr::Numerical => Self::MonthNumerical(modifier::MonthNumerical {
364 padding: padding.unwrap_or_default().into(),
365 }),
366 MonthRepr::Long => Self::MonthLong(modifier::MonthLong {
367 case_sensitive: case_sensitive.unwrap_or_default().into(),
368 }),
369 MonthRepr::Short => Self::MonthShort(modifier::MonthShort {
370 case_sensitive: case_sensitive.unwrap_or_default().into(),
371 }),
372 },
373 Component::OffsetHour(OffsetHour {
374 sign_behavior,
375 padding,
376 }) => Self::OffsetHour(modifier::OffsetHour {
377 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
378 padding: padding.unwrap_or_default().into(),
379 }),
380 Component::OffsetMinute(OffsetMinute { padding }) => {
381 Self::OffsetMinute(modifier::OffsetMinute {
382 padding: padding.unwrap_or_default().into(),
383 })
384 }
385 Component::OffsetSecond(OffsetSecond { padding }) => {
386 Self::OffsetSecond(modifier::OffsetSecond {
387 padding: padding.unwrap_or_default().into(),
388 })
389 }
390 Component::Ordinal(Ordinal { padding }) => Self::Ordinal(modifier::Ordinal {
391 padding: padding.unwrap_or_default().into(),
392 }),
393 Component::Period(Period {
394 case,
395 case_sensitive,
396 }) => Self::Period(modifier::Period {
397 is_uppercase: case.unwrap_or_default().into(),
398 case_sensitive: case_sensitive.unwrap_or_default().into(),
399 }),
400 Component::Second(Second { padding }) => Self::Second(modifier::Second {
401 padding: padding.unwrap_or_default().into(),
402 }),
403 Component::Subsecond(Subsecond { digits }) => Self::Subsecond(modifier::Subsecond {
404 digits: digits.unwrap_or_default().into(),
405 }),
406 Component::UnixTimestamp(UnixTimestamp {
407 precision,
408 sign_behavior,
409 }) => match precision.unwrap_or_default() {
410 UnixTimestampPrecision::Second => {
411 Self::UnixTimestampSecond(modifier::UnixTimestampSecond {
412 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
413 })
414 }
415 UnixTimestampPrecision::Millisecond => {
416 Self::UnixTimestampMillisecond(modifier::UnixTimestampMillisecond {
417 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
418 })
419 }
420 UnixTimestampPrecision::Microsecond => {
421 Self::UnixTimestampMicrosecond(modifier::UnixTimestampMicrosecond {
422 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
423 })
424 }
425 UnixTimestampPrecision::Nanosecond => {
426 Self::UnixTimestampNanosecond(modifier::UnixTimestampNanosecond {
427 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
428 })
429 }
430 },
431 Component::Weekday(Weekday {
432 repr,
433 one_indexed,
434 case_sensitive,
435 }) => match repr.unwrap_or_default() {
436 WeekdayRepr::Short => Self::WeekdayShort(modifier::WeekdayShort {
437 case_sensitive: case_sensitive.unwrap_or_default().into(),
438 }),
439 WeekdayRepr::Long => Self::WeekdayLong(modifier::WeekdayLong {
440 case_sensitive: case_sensitive.unwrap_or_default().into(),
441 }),
442 WeekdayRepr::Sunday => Self::WeekdaySunday(modifier::WeekdaySunday {
443 one_indexed: one_indexed.unwrap_or_default().into(),
444 }),
445 WeekdayRepr::Monday => Self::WeekdayMonday(modifier::WeekdayMonday {
446 one_indexed: one_indexed.unwrap_or_default().into(),
447 }),
448 },
449 Component::WeekNumber(WeekNumber { padding, repr }) => match repr.unwrap_or_default() {
450 WeekNumberRepr::Iso => Self::WeekNumberIso(modifier::WeekNumberIso {
451 padding: padding.unwrap_or_default().into(),
452 }),
453 WeekNumberRepr::Sunday => Self::WeekNumberSunday(modifier::WeekNumberSunday {
454 padding: padding.unwrap_or_default().into(),
455 }),
456 WeekNumberRepr::Monday => Self::WeekNumberMonday(modifier::WeekNumberMonday {
457 padding: padding.unwrap_or_default().into(),
458 }),
459 },
460 Component::Year(Year {
461 padding,
462 repr,
463 range,
464 base,
465 sign_behavior,
466 }) => match (
467 base.unwrap_or_default(),
468 repr.unwrap_or_default(),
469 range.unwrap_or_default(),
470 ) {
471 #[cfg(feature = "large-dates")]
472 (YearBase::Calendar, YearRepr::Full, YearRange::Extended) => {
473 Self::CalendarYearFullExtendedRange(modifier::CalendarYearFullExtendedRange {
474 padding: padding.unwrap_or_default().into(),
475 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
476 })
477 }
478 (YearBase::Calendar, YearRepr::Full, _) => {
479 Self::CalendarYearFullStandardRange(modifier::CalendarYearFullStandardRange {
480 padding: padding.unwrap_or_default().into(),
481 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
482 })
483 }
484 #[cfg(feature = "large-dates")]
485 (YearBase::Calendar, YearRepr::Century, YearRange::Extended) => {
486 Self::CalendarYearCenturyExtendedRange(
487 modifier::CalendarYearCenturyExtendedRange {
488 padding: padding.unwrap_or_default().into(),
489 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
490 },
491 )
492 }
493 (YearBase::Calendar, YearRepr::Century, _) => {
494 Self::CalendarYearCenturyStandardRange(
495 modifier::CalendarYearCenturyStandardRange {
496 padding: padding.unwrap_or_default().into(),
497 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
498 },
499 )
500 }
501 #[cfg(feature = "large-dates")]
502 (YearBase::IsoWeek, YearRepr::Full, YearRange::Extended) => {
503 Self::IsoYearFullExtendedRange(modifier::IsoYearFullExtendedRange {
504 padding: padding.unwrap_or_default().into(),
505 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
506 })
507 }
508 (YearBase::IsoWeek, YearRepr::Full, _) => {
509 Self::IsoYearFullStandardRange(modifier::IsoYearFullStandardRange {
510 padding: padding.unwrap_or_default().into(),
511 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
512 })
513 }
514 #[cfg(feature = "large-dates")]
515 (YearBase::IsoWeek, YearRepr::Century, YearRange::Extended) => {
516 Self::IsoYearCenturyExtendedRange(modifier::IsoYearCenturyExtendedRange {
517 padding: padding.unwrap_or_default().into(),
518 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
519 })
520 }
521 (YearBase::IsoWeek, YearRepr::Century, _) => {
522 Self::IsoYearCenturyStandardRange(modifier::IsoYearCenturyStandardRange {
523 padding: padding.unwrap_or_default().into(),
524 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
525 })
526 }
527 (YearBase::Calendar, YearRepr::LastTwo, _) => {
528 Self::CalendarYearLastTwo(modifier::CalendarYearLastTwo {
529 padding: padding.unwrap_or_default().into(),
530 })
531 }
532 (YearBase::IsoWeek, YearRepr::LastTwo, _) => {
533 Self::IsoYearLastTwo(modifier::IsoYearLastTwo {
534 padding: padding.unwrap_or_default().into(),
535 })
536 }
537 },
538 }
539 }
540}
541
542macro_rules! target_ty {
544 ($name:ident $type:ty) => {
545 $type
546 };
547 ($name:ident) => {
548 $crate::format_description::modifier::$name
549 };
550}
551
552macro_rules! target_value {
554 ($name:ident $variant:ident $value:expr) => {
555 $value
556 };
557 ($name:ident $variant:ident) => {
558 $crate::format_description::modifier::$name::$variant
559 };
560}
561
562macro_rules! modifier {
580 ($(
581 $(#[expect($expect_inner:meta)])?
582 enum $name:ident $(($target_ty:ty))? {
583 $(
584 $(#[$attr:meta])?
585 $variant:ident $(($target_value:expr))? = $parse_variant:literal
586 ),* $(,)?
587 }
588 )+) => {$(
589 #[derive(Default)]
590 enum $name {
591 $($(#[$attr])? $variant),*
592 }
593
594 impl $name {
595 #[inline]
597 fn from_modifier_value(value: &Spanned<&[u8]>) -> Result<Option<Self>, Error> {
598 $(if value.eq_ignore_ascii_case($parse_variant) {
599 return Ok(Some(Self::$variant));
600 })*
601 Err(Error {
602 _inner: unused(value.span.error("invalid modifier value")),
603 public: crate::error::InvalidFormatDescription::InvalidModifier {
604 value: String::from_utf8_lossy(value).into_owned(),
605 index: value.span.start.byte as usize,
606 },
607 })
608 }
609 }
610
611 $(#[expect($expect_inner)])?
612 impl From<$name> for target_ty!($name $($target_ty)?) {
613 #[inline]
614 fn from(modifier: $name) -> Self {
615 match modifier {
616 $($name::$variant => target_value!($name $variant $($target_value)?)),*
617 }
618 }
619 }
620 )+};
621}
622
623modifier! {
625 enum HourBase(bool) {
626 Twelve(true) = b"12",
627 #[default]
628 TwentyFour(false) = b"24",
629 }
630
631 enum MonthCaseSensitive(bool) {
632 False(false) = b"false",
633 #[default]
634 True(true) = b"true",
635 }
636
637 #[expect(deprecated)]
638 enum MonthRepr {
639 #[default]
640 Numerical = b"numerical",
641 Long = b"long",
642 Short = b"short",
643 }
644
645 enum Padding {
646 Space = b"space",
647 #[default]
648 Zero = b"zero",
649 None = b"none",
650 }
651
652 enum PeriodCase(bool) {
653 Lower(false) = b"lower",
654 #[default]
655 Upper(true) = b"upper",
656 }
657
658 enum PeriodCaseSensitive(bool) {
659 False(false) = b"false",
660 #[default]
661 True(true) = b"true",
662 }
663
664 enum SignBehavior(bool) {
665 #[default]
666 Automatic(false) = b"automatic",
667 Mandatory(true) = b"mandatory",
668 }
669
670 enum SubsecondDigits {
671 One = b"1",
672 Two = b"2",
673 Three = b"3",
674 Four = b"4",
675 Five = b"5",
676 Six = b"6",
677 Seven = b"7",
678 Eight = b"8",
679 Nine = b"9",
680 #[default]
681 OneOrMore = b"1+",
682 }
683
684 enum TrailingInput {
685 #[default]
686 Prohibit = b"prohibit",
687 Discard = b"discard",
688 }
689
690 #[expect(deprecated)]
691 enum UnixTimestampPrecision {
692 #[default]
693 Second = b"second",
694 Millisecond = b"millisecond",
695 Microsecond = b"microsecond",
696 Nanosecond = b"nanosecond",
697 }
698
699 #[expect(deprecated)]
700 enum WeekNumberRepr {
701 #[default]
702 Iso = b"iso",
703 Sunday = b"sunday",
704 Monday = b"monday",
705 }
706
707 enum WeekdayCaseSensitive(bool) {
708 False(false) = b"false",
709 #[default]
710 True(true) = b"true",
711 }
712
713 enum WeekdayOneIndexed(bool) {
714 False(false) = b"false",
715 #[default]
716 True(true) = b"true",
717 }
718
719 #[expect(deprecated)]
720 enum WeekdayRepr {
721 Short = b"short",
722 #[default]
723 Long = b"long",
724 Sunday = b"sunday",
725 Monday = b"monday",
726 }
727
728 enum YearBase(bool) {
729 #[default]
730 Calendar(false) = b"calendar",
731 IsoWeek(true) = b"iso_week",
732 }
733
734 #[expect(deprecated)]
735 enum YearRepr {
736 #[default]
737 Full = b"full",
738 Century = b"century",
739 LastTwo = b"last_two",
740 }
741
742 #[expect(deprecated)]
743 enum YearRange {
744 Standard = b"standard",
745 #[default]
746 Extended = b"extended",
747 }
748}
749
750#[inline]
752fn parse_from_modifier_value<T>(value: &Spanned<&[u8]>) -> Result<Option<T>, Error>
753where
754 T: FromStr,
755{
756 str::from_utf8(value)
757 .ok()
758 .and_then(|val| val.parse::<T>().ok())
759 .map(|val| Some(val))
760 .ok_or_else(|| Error {
761 _inner: unused(value.span.error("invalid modifier value")),
762 public: crate::error::InvalidFormatDescription::InvalidModifier {
763 value: String::from_utf8_lossy(value).into_owned(),
764 index: value.span.start.byte as usize,
765 },
766 })
767}