Skip to main content

uv_resolver/lock/
deserialize.rs

1//! Deserializes the canonical lockfile layout without building a TOML tree.
2//!
3//! This parser is deliberately limited to the syntax emitted by the lockfile
4//! serializer. The public entry point falls back to the general TOML parser
5//! when a lock uses another valid representation.
6
7use std::borrow::Cow;
8use std::fmt;
9
10use memchr::memchr3;
11use rustc_hash::{FxBuildHasher, FxHashSet};
12use serde::Deserialize;
13use serde::de::{self, DeserializeSeed, EnumAccess, MapAccess, SeqAccess, VariantAccess, Visitor};
14use serde::forward_to_deserialize_any;
15use smallvec::SmallVec;
16use toml_parser::decoder::{Encoding, IntegerRadix, ScalarKind};
17use toml_parser::{Raw, Span};
18
19use super::Lock;
20
21/// Parses a canonical lock directly into its existing, validated wire format.
22pub(super) fn from_str(input: &str) -> Result<Lock, Error> {
23    let mut cursor = Cursor::new(input);
24    let lock = Lock::deserialize(DocumentDeserializer {
25        cursor: &mut cursor,
26    })?;
27    cursor.skip_whitespace();
28    if cursor.peek().is_some() {
29        return Err(cursor.unsupported("unexpected trailing input"));
30    }
31    Ok(lock)
32}
33
34/// An error returned when parsing a lockfile without the general TOML fallback.
35#[derive(Debug, thiserror::Error)]
36pub enum Error {
37    #[error("unsupported canonical lock syntax at byte {offset}: {message}")]
38    Unsupported {
39        offset: usize,
40        message: &'static str,
41    },
42    #[error("invalid canonical lock syntax at byte {offset}: {message}")]
43    Invalid { offset: usize, message: String },
44    #[error("failed to deserialize canonical lock: {0}")]
45    Deserialize(String),
46}
47
48impl de::Error for Error {
49    fn custom<T: fmt::Display>(message: T) -> Self {
50        Self::Deserialize(message.to_string())
51    }
52}
53
54struct Cursor<'de> {
55    input: &'de str,
56    offset: usize,
57    container_depth: u8,
58}
59
60impl<'de> Cursor<'de> {
61    fn new(input: &'de str) -> Self {
62        Self {
63            input,
64            offset: 0,
65            container_depth: 0,
66        }
67    }
68
69    fn peek(&self) -> Option<u8> {
70        self.input.as_bytes().get(self.offset).copied()
71    }
72
73    fn unsupported(&self, message: &'static str) -> Error {
74        Error::Unsupported {
75            offset: self.offset,
76            message,
77        }
78    }
79
80    fn invalid(&self, error: &toml_parser::ParseError) -> Error {
81        Error::Invalid {
82            offset: error
83                .unexpected()
84                .or_else(|| error.context())
85                .map_or(self.offset, |span| span.start()),
86            message: error.description().to_owned(),
87        }
88    }
89
90    fn skip_whitespace(&mut self) {
91        loop {
92            match self.peek() {
93                Some(b' ' | b'\t' | b'\n') => self.offset += 1,
94                Some(b'\r') if self.input.as_bytes().get(self.offset + 1) == Some(&b'\n') => {
95                    self.offset += 2;
96                }
97                _ => break,
98            }
99        }
100    }
101
102    fn skip_horizontal_whitespace(&mut self) {
103        while matches!(self.peek(), Some(b' ' | b'\t')) {
104            self.offset += 1;
105        }
106    }
107
108    fn consume(&mut self, expected: u8) -> Result<(), Error> {
109        if self.peek() == Some(expected) {
110            self.offset += 1;
111            Ok(())
112        } else {
113            Err(self.unsupported("unexpected delimiter"))
114        }
115    }
116
117    fn header(&self) -> Result<&'de str, Error> {
118        if self.peek() != Some(b'[') {
119            return Err(self.unsupported("expected a table header"));
120        }
121        let remaining = &self.input[self.offset..];
122        let length = remaining.find('\n').unwrap_or(remaining.len());
123        Ok(remaining[..length].trim_end_matches('\r'))
124    }
125
126    fn consume_header(&mut self, expected: &'static str) -> Result<(), Error> {
127        if self.header()? != expected {
128            return Err(self.unsupported("unknown or noncanonical table header"));
129        }
130        self.offset += expected.len();
131
132        match self.peek() {
133            Some(b'\r') => {
134                self.offset += 1;
135                self.consume(b'\n')
136            }
137            Some(b'\n') => {
138                self.offset += 1;
139                Ok(())
140            }
141            None => Ok(()),
142            _ => Err(self.unsupported("expected the end of a table header")),
143        }
144    }
145
146    fn assignment_key(&mut self) -> Result<&'de str, Error> {
147        let start = self.offset;
148        while matches!(
149            self.peek(),
150            Some(b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_' | b'-')
151        ) {
152            self.offset += 1;
153        }
154        if self.offset == start {
155            return Err(self.unsupported("expected a canonical bare key"));
156        }
157        let key = &self.input[start..self.offset];
158        self.skip_horizontal_whitespace();
159        self.consume(b'=')?;
160        self.skip_horizontal_whitespace();
161        Ok(key)
162    }
163
164    fn finish_assignment(&mut self) -> Result<(), Error> {
165        self.skip_horizontal_whitespace();
166
167        if self.peek() == Some(b'#') {
168            self.offset += 1;
169
170            while let Some(byte) = self.peek() {
171                match byte {
172                    b'\n' | b'\r' => break,
173                    b'\t' | b' '..=b'~' | 0x80..=0xff => self.offset += 1,
174                    _ => return Err(self.unsupported("invalid character in a TOML comment")),
175                }
176            }
177        }
178
179        match self.peek() {
180            Some(b'\r') => {
181                self.offset += 1;
182                self.consume(b'\n')
183            }
184            Some(b'\n') => {
185                self.offset += 1;
186                Ok(())
187            }
188            None => Ok(()),
189            _ => Err(self.unsupported("expected the end of an assignment")),
190        }
191    }
192
193    fn string(&mut self) -> Result<Cow<'de, str>, Error> {
194        let start = self.offset;
195        self.consume(b'"')?;
196        if self.input[self.offset..].starts_with("\"\"") {
197            return Err(self.unsupported("multiline strings require the TOML fallback"));
198        }
199
200        let mut escaped = false;
201        loop {
202            let remaining = &self.input.as_bytes()[self.offset..];
203            let Some(delimiter) = memchr3(b'"', b'\\', 0x7f, remaining) else {
204                return Err(self.unsupported("unterminated basic string"));
205            };
206
207            let content = &remaining[..delimiter];
208            // The minimum is SIMD-vectorizable; locate the exact offset only for invalid input.
209            if content
210                .iter()
211                .copied()
212                .min()
213                .is_some_and(|byte| byte < 0x20)
214                && let Some(control) = content.iter().position(|byte| *byte < 0x20)
215            {
216                self.offset += control;
217                return Err(self.unsupported("control character in a basic string"));
218            }
219
220            self.offset += delimiter;
221            match self.peek() {
222                Some(b'"') => {
223                    let end = self.offset;
224                    self.offset += 1;
225                    if escaped {
226                        let encoded = &self.input[start..self.offset];
227                        let raw = Raw::new_unchecked(
228                            encoded,
229                            Some(Encoding::BasicString),
230                            Span::new_unchecked(start, self.offset),
231                        );
232                        let mut decoded = Cow::Borrowed("");
233                        let mut error = None;
234                        let kind = raw.decode_scalar(&mut decoded, &mut error);
235                        debug_assert_eq!(kind, ScalarKind::String);
236
237                        if let Some(error) = error {
238                            return Err(self.invalid(&error));
239                        }
240
241                        return Ok(decoded);
242                    }
243                    return Ok(Cow::Borrowed(&self.input[start + 1..end]));
244                }
245                Some(b'\\') => {
246                    escaped = true;
247                    self.offset += 1;
248                    if self.peek().is_none() {
249                        return Err(self.unsupported("unterminated string escape"));
250                    }
251                    self.offset += 1;
252                }
253                _ => return Err(self.unsupported("control character in a basic string")),
254            }
255        }
256    }
257
258    fn number(&mut self) -> Result<Cow<'de, str>, Error> {
259        let start = self.offset;
260        while matches!(
261            self.peek(),
262            Some(b'0'..=b'9' | b'-' | b'+' | b'.' | b'e' | b'E' | b'_')
263        ) {
264            self.offset += 1;
265        }
266        if self.offset == start {
267            return Err(self.unsupported("expected a canonical number"));
268        }
269
270        let encoded = &self.input[start..self.offset];
271        let raw = Raw::new_unchecked(encoded, None, Span::new_unchecked(start, self.offset));
272        let mut decoded = Cow::Borrowed("");
273        let mut error = None;
274        let kind = raw.decode_scalar(&mut decoded, &mut error);
275
276        if let Some(error) = error {
277            return Err(self.invalid(&error));
278        }
279
280        if !matches!(
281            kind,
282            ScalarKind::Float | ScalarKind::Integer(IntegerRadix::Dec)
283        ) {
284            return Err(self.unsupported("expected a canonical number"));
285        }
286
287        Ok(decoded)
288    }
289
290    fn literal(&mut self, literal: &'static str) -> Result<(), Error> {
291        if self.input[self.offset..].starts_with(literal) {
292            self.offset += literal.len();
293            Ok(())
294        } else {
295            Err(self.unsupported("expected a canonical boolean"))
296        }
297    }
298
299    fn with_container<T>(
300        &mut self,
301        deserialize: impl FnOnce(&mut Self) -> Result<T, Error>,
302    ) -> Result<T, Error> {
303        if self.container_depth == 80 {
304            return Err(self.unsupported("maximum TOML nesting depth exceeded"));
305        }
306
307        self.container_depth += 1;
308        let result = deserialize(self);
309        self.container_depth -= 1;
310        result
311    }
312}
313
314struct DocumentDeserializer<'a, 'de> {
315    cursor: &'a mut Cursor<'de>,
316}
317
318impl<'de> de::Deserializer<'de> for DocumentDeserializer<'_, 'de> {
319    type Error = Error;
320
321    fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
322        visitor.visit_map(DocumentMapAccess {
323            cursor: self.cursor,
324            kind: MapKind::Root,
325            pending: None,
326            seen_keys: SeenKeys::default(),
327        })
328    }
329
330    forward_to_deserialize_any! {
331        bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes
332        byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map
333        struct enum identifier ignored_any
334    }
335}
336
337#[derive(Clone, Copy, Debug, Eq, PartialEq)]
338enum MapKind {
339    Root,
340    Options,
341    OptionsExcludeNewerPackage,
342    Manifest,
343    ManifestDependencyGroups,
344    ManifestDependencyMetadata,
345    Package,
346    PackageOptionalDependencies,
347    PackageDevDependencies,
348    PackageMetadata,
349    PackageMetadataRequiresDev,
350}
351
352#[derive(Clone, Copy, Debug, Eq, PartialEq)]
353enum SequenceKind {
354    Packages,
355    ManifestDependencyMetadata,
356}
357
358#[derive(Clone, Copy, Debug, Eq, PartialEq)]
359enum Pending {
360    Value,
361    Map(MapKind),
362    Sequence(SequenceKind),
363}
364
365/// Keeps common maps allocation-free and wide maps linear in the number of keys.
366#[derive(Default)]
367struct SeenKeys<'de> {
368    inline: SmallVec<[&'de str; 8]>,
369    overflow: Option<FxHashSet<&'de str>>,
370}
371
372impl<'de> SeenKeys<'de> {
373    fn insert(&mut self, key: &'de str) -> bool {
374        if let Some(overflow) = &mut self.overflow {
375            return overflow.insert(key);
376        }
377
378        if self.inline.contains(&key) {
379            return false;
380        }
381
382        if self.inline.len() < self.inline.inline_size() {
383            self.inline.push(key);
384            return true;
385        }
386
387        let mut overflow =
388            FxHashSet::with_capacity_and_hasher(self.inline.len() + 1, FxBuildHasher);
389        overflow.extend(self.inline.iter().copied());
390        let inserted = overflow.insert(key);
391        self.overflow = Some(overflow);
392        inserted
393    }
394}
395
396struct DocumentMapAccess<'a, 'de> {
397    cursor: &'a mut Cursor<'de>,
398    kind: MapKind,
399    pending: Option<Pending>,
400    seen_keys: SeenKeys<'de>,
401}
402
403impl<'de> MapAccess<'de> for DocumentMapAccess<'_, 'de> {
404    type Error = Error;
405
406    fn next_key_seed<K: DeserializeSeed<'de>>(
407        &mut self,
408        seed: K,
409    ) -> Result<Option<K::Value>, Error> {
410        self.cursor.skip_whitespace();
411        match self.cursor.peek() {
412            None => Ok(None),
413            Some(b'[') => self.section_key(seed),
414            Some(b'#') => Err(self
415                .cursor
416                .unsupported("comments require the TOML fallback")),
417            Some(_) => {
418                let key = self.cursor.assignment_key()?;
419                self.track_key(key)?;
420                self.pending = Some(Pending::Value);
421                seed.deserialize(de::value::BorrowedStrDeserializer::new(key))
422                    .map(Some)
423            }
424        }
425    }
426
427    fn next_value_seed<V: DeserializeSeed<'de>>(&mut self, seed: V) -> Result<V::Value, Error> {
428        let Some(pending) = self.pending.take() else {
429            return Err(self.cursor.unsupported("map value has no matching key"));
430        };
431
432        match pending {
433            Pending::Value => {
434                let value = seed.deserialize(ValueDeserializer {
435                    cursor: self.cursor,
436                })?;
437                self.cursor.finish_assignment()?;
438                Ok(value)
439            }
440            Pending::Map(kind) => seed.deserialize(SectionDeserializer {
441                cursor: self.cursor,
442                kind,
443            }),
444            Pending::Sequence(kind) => seed.deserialize(SectionSequenceDeserializer {
445                cursor: self.cursor,
446                kind,
447            }),
448        }
449    }
450}
451
452impl<'de> DocumentMapAccess<'_, 'de> {
453    fn track_key(&mut self, key: &'de str) -> Result<(), Error> {
454        if !self.seen_keys.insert(key) {
455            return Err(self.cursor.unsupported("duplicate TOML key"));
456        }
457        Ok(())
458    }
459
460    fn section_key<K: DeserializeSeed<'de>>(&mut self, seed: K) -> Result<Option<K::Value>, Error> {
461        let header = self.cursor.header()?;
462        let child = match (self.kind, header) {
463            (MapKind::Root, "[options]") => {
464                Some(("options", Pending::Map(MapKind::Options), "[options]"))
465            }
466            (MapKind::Root, "[manifest]") => {
467                Some(("manifest", Pending::Map(MapKind::Manifest), "[manifest]"))
468            }
469            (MapKind::Root, "[[package]]") => Some((
470                "package",
471                Pending::Sequence(SequenceKind::Packages),
472                "[[package]]",
473            )),
474            (MapKind::Options, "[options.exclude-newer-package]") => Some((
475                "exclude-newer-package",
476                Pending::Map(MapKind::OptionsExcludeNewerPackage),
477                "[options.exclude-newer-package]",
478            )),
479            (MapKind::Manifest, "[manifest.dependency-groups]") => Some((
480                "dependency-groups",
481                Pending::Map(MapKind::ManifestDependencyGroups),
482                "[manifest.dependency-groups]",
483            )),
484            (MapKind::Manifest, "[[manifest.dependency-metadata]]") => Some((
485                "dependency-metadata",
486                Pending::Sequence(SequenceKind::ManifestDependencyMetadata),
487                "[[manifest.dependency-metadata]]",
488            )),
489            (MapKind::Package, "[package.optional-dependencies]") => Some((
490                "optional-dependencies",
491                Pending::Map(MapKind::PackageOptionalDependencies),
492                "[package.optional-dependencies]",
493            )),
494            (MapKind::Package, "[package.dev-dependencies]") => Some((
495                "dev-dependencies",
496                Pending::Map(MapKind::PackageDevDependencies),
497                "[package.dev-dependencies]",
498            )),
499            (MapKind::Package, "[package.metadata]") => Some((
500                "metadata",
501                Pending::Map(MapKind::PackageMetadata),
502                "[package.metadata]",
503            )),
504            (MapKind::PackageMetadata, "[package.metadata.requires-dev]") => Some((
505                "requires-dev",
506                Pending::Map(MapKind::PackageMetadataRequiresDev),
507                "[package.metadata.requires-dev]",
508            )),
509            (MapKind::Root, _) => {
510                return Err(self
511                    .cursor
512                    .unsupported("unknown or noncanonical lock table"));
513            }
514            _ => None,
515        };
516
517        let Some((key, pending, expected)) = child else {
518            return Ok(None);
519        };
520        self.track_key(key)?;
521        self.cursor.consume_header(expected)?;
522        self.pending = Some(pending);
523        seed.deserialize(de::value::BorrowedStrDeserializer::new(key))
524            .map(Some)
525    }
526}
527
528struct SectionDeserializer<'a, 'de> {
529    cursor: &'a mut Cursor<'de>,
530    kind: MapKind,
531}
532
533impl<'de> de::Deserializer<'de> for SectionDeserializer<'_, 'de> {
534    type Error = Error;
535
536    fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
537        visitor.visit_map(DocumentMapAccess {
538            cursor: self.cursor,
539            kind: self.kind,
540            pending: None,
541            seen_keys: SeenKeys::default(),
542        })
543    }
544
545    forward_to_deserialize_any! {
546        bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes
547        byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map
548        struct enum identifier ignored_any
549    }
550}
551
552struct SectionSequenceDeserializer<'a, 'de> {
553    cursor: &'a mut Cursor<'de>,
554    kind: SequenceKind,
555}
556
557impl<'de> de::Deserializer<'de> for SectionSequenceDeserializer<'_, 'de> {
558    type Error = Error;
559
560    fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
561        visitor.visit_seq(SectionSequenceAccess {
562            cursor: self.cursor,
563            kind: self.kind,
564            started: false,
565        })
566    }
567
568    forward_to_deserialize_any! {
569        bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes
570        byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map
571        struct enum identifier ignored_any
572    }
573}
574
575struct SectionSequenceAccess<'a, 'de> {
576    cursor: &'a mut Cursor<'de>,
577    kind: SequenceKind,
578    started: bool,
579}
580
581impl<'de> SeqAccess<'de> for SectionSequenceAccess<'_, 'de> {
582    type Error = Error;
583
584    fn next_element_seed<T: DeserializeSeed<'de>>(
585        &mut self,
586        seed: T,
587    ) -> Result<Option<T::Value>, Error> {
588        if self.started {
589            self.cursor.skip_whitespace();
590            if self.cursor.peek() != Some(b'[') {
591                return Ok(None);
592            }
593
594            let expected = match self.kind {
595                SequenceKind::Packages => "[[package]]",
596                SequenceKind::ManifestDependencyMetadata => "[[manifest.dependency-metadata]]",
597            };
598            if self.cursor.header()? != expected {
599                return Ok(None);
600            }
601            self.cursor.consume_header(expected)?;
602        }
603
604        self.started = true;
605        let kind = match self.kind {
606            SequenceKind::Packages => MapKind::Package,
607            SequenceKind::ManifestDependencyMetadata => MapKind::ManifestDependencyMetadata,
608        };
609        seed.deserialize(SectionDeserializer {
610            cursor: self.cursor,
611            kind,
612        })
613        .map(Some)
614    }
615}
616
617struct ValueDeserializer<'a, 'de> {
618    cursor: &'a mut Cursor<'de>,
619}
620
621impl<'de> de::Deserializer<'de> for ValueDeserializer<'_, 'de> {
622    type Error = Error;
623
624    fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
625        match self.cursor.peek() {
626            Some(b'"') => match self.cursor.string()? {
627                Cow::Borrowed(value) => visitor.visit_borrowed_str(value),
628                Cow::Owned(value) => visitor.visit_string(value),
629            },
630            Some(b'{') => self.cursor.with_container(|cursor| {
631                cursor.consume(b'{')?;
632                visitor.visit_map(InlineMapAccess {
633                    cursor,
634                    started: false,
635                    seen_keys: SeenKeys::default(),
636                })
637            }),
638            Some(b'[') => self.cursor.with_container(|cursor| {
639                cursor.consume(b'[')?;
640                visitor.visit_seq(InlineSequenceAccess {
641                    cursor,
642                    started: false,
643                })
644            }),
645            Some(b't') => {
646                self.cursor.literal("true")?;
647                visitor.visit_bool(true)
648            }
649            Some(b'f') => {
650                self.cursor.literal("false")?;
651                visitor.visit_bool(false)
652            }
653            Some(b'-' | b'0'..=b'9') => {
654                let number = self.cursor.number()?;
655                if number.contains(['.', 'e', 'E']) {
656                    let value = number
657                        .parse::<f64>()
658                        .map_err(|_| self.cursor.unsupported("invalid floating-point value"))?;
659                    visitor.visit_f64(value)
660                } else if number.starts_with('-') {
661                    let value = number
662                        .parse::<i64>()
663                        .map_err(|_| self.cursor.unsupported("invalid signed integer"))?;
664                    visitor.visit_i64(value)
665                } else {
666                    let value = number
667                        .parse::<u64>()
668                        .map_err(|_| self.cursor.unsupported("invalid unsigned integer"))?;
669                    visitor.visit_u64(value)
670                }
671            }
672            _ => Err(self.cursor.unsupported("unsupported canonical lock value")),
673        }
674    }
675
676    fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
677        visitor.visit_some(self)
678    }
679
680    fn deserialize_newtype_struct<V: Visitor<'de>>(
681        self,
682        _name: &'static str,
683        visitor: V,
684    ) -> Result<V::Value, Error> {
685        visitor.visit_newtype_struct(self)
686    }
687
688    fn deserialize_enum<V: Visitor<'de>>(
689        self,
690        _name: &'static str,
691        _variants: &'static [&'static str],
692        visitor: V,
693    ) -> Result<V::Value, Error> {
694        match self.cursor.peek() {
695            Some(b'"') => match self.cursor.string()? {
696                Cow::Borrowed(value) => {
697                    visitor.visit_enum(de::value::BorrowedStrDeserializer::new(value))
698                }
699                Cow::Owned(value) => visitor.visit_enum(de::value::StringDeserializer::new(value)),
700            },
701            Some(b'{') => self.cursor.with_container(|cursor| {
702                cursor.consume(b'{')?;
703                visitor.visit_enum(InlineEnumAccess { cursor })
704            }),
705            _ => Err(self.cursor.unsupported("expected a canonical enum value")),
706        }
707    }
708
709    forward_to_deserialize_any! {
710        bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes
711        byte_buf unit unit_struct seq tuple tuple_struct map struct identifier ignored_any
712    }
713}
714
715struct InlineMapAccess<'a, 'de> {
716    cursor: &'a mut Cursor<'de>,
717    started: bool,
718    seen_keys: SeenKeys<'de>,
719}
720
721impl<'de> MapAccess<'de> for InlineMapAccess<'_, 'de> {
722    type Error = Error;
723
724    fn next_key_seed<K: DeserializeSeed<'de>>(
725        &mut self,
726        seed: K,
727    ) -> Result<Option<K::Value>, Error> {
728        self.cursor.skip_whitespace();
729        if self.started {
730            if self.cursor.peek() == Some(b'}') {
731                self.cursor.consume(b'}')?;
732                return Ok(None);
733            }
734            self.cursor.consume(b',')?;
735            self.cursor.skip_whitespace();
736        } else if self.cursor.peek() == Some(b'}') {
737            self.cursor.consume(b'}')?;
738            return Ok(None);
739        }
740
741        let key = self.cursor.assignment_key()?;
742        if !self.seen_keys.insert(key) {
743            return Err(self.cursor.unsupported("duplicate TOML key"));
744        }
745        self.started = true;
746        seed.deserialize(de::value::BorrowedStrDeserializer::new(key))
747            .map(Some)
748    }
749
750    fn next_value_seed<V: DeserializeSeed<'de>>(&mut self, seed: V) -> Result<V::Value, Error> {
751        seed.deserialize(ValueDeserializer {
752            cursor: self.cursor,
753        })
754    }
755}
756
757struct InlineSequenceAccess<'a, 'de> {
758    cursor: &'a mut Cursor<'de>,
759    started: bool,
760}
761
762impl<'de> SeqAccess<'de> for InlineSequenceAccess<'_, 'de> {
763    type Error = Error;
764
765    fn next_element_seed<T: DeserializeSeed<'de>>(
766        &mut self,
767        seed: T,
768    ) -> Result<Option<T::Value>, Error> {
769        self.cursor.skip_whitespace();
770        if self.started {
771            if self.cursor.peek() == Some(b']') {
772                self.cursor.consume(b']')?;
773                return Ok(None);
774            }
775            self.cursor.consume(b',')?;
776            self.cursor.skip_whitespace();
777        }
778
779        if self.cursor.peek() == Some(b']') {
780            self.cursor.consume(b']')?;
781            return Ok(None);
782        }
783
784        self.started = true;
785        seed.deserialize(ValueDeserializer {
786            cursor: self.cursor,
787        })
788        .map(Some)
789    }
790}
791
792struct InlineEnumAccess<'a, 'de> {
793    cursor: &'a mut Cursor<'de>,
794}
795
796impl<'a, 'de> EnumAccess<'de> for InlineEnumAccess<'a, 'de> {
797    type Error = Error;
798    type Variant = InlineVariantAccess<'a, 'de>;
799
800    fn variant_seed<V: DeserializeSeed<'de>>(
801        self,
802        seed: V,
803    ) -> Result<(V::Value, Self::Variant), Error> {
804        self.cursor.skip_whitespace();
805        let key = self.cursor.assignment_key()?;
806        let variant = seed.deserialize(de::value::BorrowedStrDeserializer::new(key))?;
807        Ok((
808            variant,
809            InlineVariantAccess {
810                cursor: self.cursor,
811            },
812        ))
813    }
814}
815
816struct InlineVariantAccess<'a, 'de> {
817    cursor: &'a mut Cursor<'de>,
818}
819
820impl<'de> VariantAccess<'de> for InlineVariantAccess<'_, 'de> {
821    type Error = Error;
822
823    fn unit_variant(self) -> Result<(), Error> {
824        Err(self
825            .cursor
826            .unsupported("inline tables cannot contain unit variants"))
827    }
828
829    fn newtype_variant_seed<T: DeserializeSeed<'de>>(self, seed: T) -> Result<T::Value, Error> {
830        let value = seed.deserialize(ValueDeserializer {
831            cursor: self.cursor,
832        })?;
833        self.cursor.skip_whitespace();
834        self.cursor.consume(b'}')?;
835        Ok(value)
836    }
837
838    fn tuple_variant<V: Visitor<'de>>(self, length: usize, visitor: V) -> Result<V::Value, Error> {
839        let value = de::Deserializer::deserialize_tuple(
840            ValueDeserializer {
841                cursor: self.cursor,
842            },
843            length,
844            visitor,
845        )?;
846        self.cursor.skip_whitespace();
847        self.cursor.consume(b'}')?;
848        Ok(value)
849    }
850
851    fn struct_variant<V: Visitor<'de>>(
852        self,
853        fields: &'static [&'static str],
854        visitor: V,
855    ) -> Result<V::Value, Error> {
856        let value = de::Deserializer::deserialize_struct(
857            ValueDeserializer {
858                cursor: self.cursor,
859            },
860            "",
861            fields,
862            visitor,
863        )?;
864        self.cursor.skip_whitespace();
865        self.cursor.consume(b'}')?;
866        Ok(value)
867    }
868}
869
870#[cfg(test)]
871mod tests {
872    use std::fmt::Write as _;
873
874    use serde::Deserialize;
875
876    use super::{Cursor, Error, Lock, ValueDeserializer, from_str};
877
878    const CANONICAL_LOCK: &str = r#"version = 1
879revision = 3
880requires-python = ">=3.12"
881
882[[package]]
883name = "dependency"
884version = "1.0.0"
885source = { registry = "https://example.com/simple" }
886
887[[package]]
888name = "project"
889version = "0.1.0"
890source = { virtual = "." }
891dependencies = [
892    { name = "dependency" },
893]
894"#;
895
896    #[test]
897    fn canonical_lock_matches_toml() {
898        let expected: Lock = toml::from_str(CANONICAL_LOCK).expect("valid TOML lock");
899        let actual = from_str(CANONICAL_LOCK).expect("valid canonical lock");
900
901        assert_eq!(actual, expected);
902    }
903
904    #[test]
905    fn repository_lock_matches_toml() {
906        let input = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../uv.lock"));
907        let expected: Lock = toml::from_str(input).expect("valid repository lock");
908        let actual = from_str(input).expect("valid canonical repository lock");
909
910        assert_eq!(actual, expected);
911    }
912
913    #[test]
914    fn nested_lock_sections_match_toml() {
915        let input = r#"version = 1
916revision = 3
917requires-python = ">=3.12"
918
919[options]
920resolution-mode = "highest"
921
922[options.exclude-newer-package]
923dependency = false
924
925[manifest]
926members = ["project"]
927requirements = [{ name = "dependency", specifier = ">=1" }]
928
929[manifest.dependency-groups]
930dev = [{ name = "dependency", specifier = ">=1" }]
931
932[[manifest.dependency-metadata]]
933name = "dependency"
934version = "1.0.0"
935
936[[package]]
937name = "dependency"
938version = "1.0.0"
939source = { registry = "https://example.com/simple" }
940
941[[package]]
942name = "project"
943version = "0.1.0"
944source = { virtual = "." }
945dependencies = [{ name = "dependency" }]
946
947[package.optional-dependencies]
948feature = [{ name = "dependency" }]
949
950[package.dev-dependencies]
951dev = [{ name = "dependency" }]
952
953[package.metadata]
954requires-dist = [{ name = "dependency", specifier = ">=1" }]
955provides-extras = ["feature"]
956
957[package.metadata.requires-dev]
958dev = [{ name = "dependency", specifier = ">=1" }]
959"#;
960        let expected: Lock = toml::from_str(input).expect("valid nested TOML lock");
961        let actual = from_str(input).expect("valid nested canonical lock");
962
963        assert_eq!(actual, expected);
964    }
965
966    #[test]
967    fn escaped_strings_match_toml() {
968        let input = CANONICAL_LOCK.replace(
969            "https://example.com/simple",
970            "https://example.com/simple?name=quoted\\\"value",
971        );
972        let expected: Lock = toml::from_str(&input).expect("valid TOML lock");
973        let actual = from_str(&input).expect("valid canonical lock");
974
975        assert_eq!(actual, expected);
976    }
977
978    #[test]
979    fn long_basic_strings_deserialize_directly() {
980        let source = format!("https://example.com/{}", "a".repeat(512));
981        let input = CANONICAL_LOCK.replace("https://example.com/simple", &source);
982        let expected: Lock = toml::from_str(&input).expect("valid TOML lock with a long string");
983        let actual = from_str(&input).expect("long basic string uses the direct parser");
984
985        assert_eq!(actual, expected);
986    }
987
988    #[test]
989    fn empty_basic_string_deserializes_directly() {
990        let mut cursor = Cursor::new(r#""""#);
991        let actual = String::deserialize(ValueDeserializer {
992            cursor: &mut cursor,
993        })
994        .expect("empty basic string deserializes directly");
995
996        assert_eq!(actual, "");
997    }
998
999    #[test]
1000    fn noncanonical_lock_falls_back() {
1001        let input = CANONICAL_LOCK
1002            .replace("requires-python = \">=3.12\"", "requires-python = '>=3.12'")
1003            .replace("[[package]]", "# A hand-edited package.\n[[package]]");
1004        let expected: Lock = toml::from_str(&input).expect("valid noncanonical lock");
1005
1006        assert!(
1007            Lock::from_canonical_toml(&input).is_err(),
1008            "the canonical lock parser must not fall back"
1009        );
1010        assert_eq!(
1011            Lock::from_toml(&input).expect("noncanonical lock falls back"),
1012            expected
1013        );
1014    }
1015
1016    #[test]
1017    fn invalid_lock_preserves_toml_error() {
1018        let input = CANONICAL_LOCK.replace(
1019            "source = { registry = \"https://example.com/simple\" }",
1020            "source = { registry = \"https://example.com/simple\", registry = \"https://other.example/simple\" }",
1021        );
1022        let expected = toml::from_str::<Lock>(&input).expect_err("duplicate source is invalid");
1023        let actual = Lock::from_toml(&input).expect_err("duplicate source remains invalid");
1024
1025        assert_eq!(actual.to_string(), expected.to_string());
1026    }
1027
1028    #[test]
1029    fn syntax_errors_preserve_real_byte_offsets() {
1030        let input =
1031            CANONICAL_LOCK.replace("requires-python = \">=3.12\"", "requires-python = '>=3.12'");
1032        let unsupported = from_str(&input).expect_err("literal strings require the TOML fallback");
1033
1034        assert!(
1035            matches!(&unsupported, Error::Unsupported { offset, .. } if *offset > 0),
1036            "unsupported syntax must retain its real byte offset: {unsupported}"
1037        );
1038
1039        let input = CANONICAL_LOCK.replace("revision = 3", "revision = 03");
1040        let invalid = from_str(&input).expect_err("TOML rejects a leading zero");
1041
1042        assert!(
1043            matches!(&invalid, Error::Invalid { offset, .. } if *offset > 0),
1044            "invalid TOML scalars must retain their real byte offset: {invalid}"
1045        );
1046    }
1047
1048    #[test]
1049    fn inline_unit_variants_without_values_preserve_toml_error() {
1050        for variant in ["highest", "lowest", "lowest-direct"] {
1051            let input = CANONICAL_LOCK.replacen(
1052                "\n\n[[package]]",
1053                &format!("\n\n[options]\nresolution-mode = {{ {variant} = }}\n\n[[package]]"),
1054                1,
1055            );
1056            let expected = toml::from_str::<Lock>(&input)
1057                .expect_err("TOML rejects an inline unit variant without a value");
1058
1059            assert!(
1060                from_str(&input).is_err(),
1061                "the direct parser must reject an inline `{variant}` without a value"
1062            );
1063
1064            let actual = Lock::from_toml(&input)
1065                .expect_err("the lock reader rejects an inline unit variant without a value");
1066
1067            assert_eq!(actual.to_string(), expected.to_string());
1068        }
1069    }
1070
1071    #[test]
1072    fn leading_zero_integers_preserve_toml_error() {
1073        for (valid, invalid) in [
1074            ("version = 1", "version = 01"),
1075            ("revision = 3", "revision = 03"),
1076        ] {
1077            let input = CANONICAL_LOCK.replace(valid, invalid);
1078            let expected = toml::from_str::<Lock>(&input)
1079                .expect_err("TOML rejects decimal integers with leading zeros");
1080
1081            assert!(
1082                from_str(&input).is_err(),
1083                "the direct parser must reject `{invalid}`"
1084            );
1085
1086            let actual = Lock::from_toml(&input)
1087                .expect_err("the lock reader rejects decimal integers with leading zeros");
1088
1089            assert_eq!(actual.to_string(), expected.to_string());
1090        }
1091    }
1092
1093    #[test]
1094    fn invalid_numeric_syntax_preserves_toml_error() {
1095        for number in [
1096            "-01", "00.1", "1.", "1.e2", "1e", "1e+", "1__0", "1_", "0_1",
1097        ] {
1098            let input = CANONICAL_LOCK.replace(
1099                "requires-python = \">=3.12\"",
1100                &format!("requires-python = \">=3.12\"\nunknown-number = {number}"),
1101            );
1102            let expected = toml::from_str::<Lock>(&input)
1103                .expect_err("TOML rejects invalid decimal number syntax");
1104
1105            assert!(
1106                from_str(&input).is_err(),
1107                "the direct parser must reject `{number}`"
1108            );
1109
1110            let actual = Lock::from_toml(&input)
1111                .expect_err("the lock reader rejects invalid decimal number syntax");
1112
1113            assert_eq!(actual.to_string(), expected.to_string());
1114        }
1115    }
1116
1117    #[test]
1118    fn json_only_string_escapes_preserve_toml_error() {
1119        for source in [
1120            r"https:\/\/example.com\/simple",
1121            r"https://example.com/\uD83D\uDE00",
1122        ] {
1123            let input = CANONICAL_LOCK.replace("https://example.com/simple", source);
1124            let expected =
1125                toml::from_str::<Lock>(&input).expect_err("TOML rejects JSON-only string escapes");
1126
1127            assert!(
1128                from_str(&input).is_err(),
1129                "the direct parser must reject JSON-only escapes in `{source}`"
1130            );
1131
1132            let actual = Lock::from_toml(&input)
1133                .expect_err("the lock reader rejects JSON-only string escapes");
1134
1135            assert_eq!(actual.to_string(), expected.to_string());
1136        }
1137    }
1138
1139    #[test]
1140    fn unicode_escapes_match_toml() {
1141        let input = CANONICAL_LOCK.replace(
1142            "https://example.com/simple",
1143            r"https://example.com/\u0073imple",
1144        );
1145        let expected: Lock = toml::from_str(&input).expect("valid TOML Unicode escape");
1146        let actual = from_str(&input).expect("valid canonical Unicode escape");
1147
1148        assert_eq!(actual, expected);
1149    }
1150
1151    #[test]
1152    fn toml_only_string_escapes_deserialize_directly() {
1153        for source in [
1154            r"https://example.com/simpl\x65",
1155            r"https://example.com/simpl\U00000065",
1156        ] {
1157            let input = CANONICAL_LOCK.replace("https://example.com/simple", source);
1158            let expected: Lock = toml::from_str(&input).expect("valid TOML-only string escape");
1159
1160            assert_eq!(
1161                from_str(&input).expect("valid TOML-only string escape uses the direct parser"),
1162                expected,
1163                "the TOML decoder correctly handles escapes in `{source}`"
1164            );
1165        }
1166    }
1167
1168    #[test]
1169    fn underscored_integers_deserialize_directly() {
1170        let input = CANONICAL_LOCK.replace("revision = 3", "revision = 3_0");
1171        let expected: Lock = toml::from_str(&input).expect("valid TOML integer separator");
1172        let actual = from_str(&input).expect("valid TOML integer separator uses the direct parser");
1173
1174        assert_eq!(actual, expected);
1175    }
1176
1177    #[test]
1178    fn toml_escape_character_deserializes_directly() {
1179        let input = CANONICAL_LOCK.replace(
1180            "revision = 3\n",
1181            "revision = 3\nignored = \"TOML\\eescape\"\n",
1182        );
1183        let expected: Lock = toml::from_str(&input).expect("valid TOML escape character");
1184        let actual = from_str(&input).expect("valid TOML escape character uses the direct parser");
1185
1186        assert_eq!(actual, expected);
1187    }
1188
1189    #[test]
1190    fn canonical_relative_exclude_newer_uses_fast_path() {
1191        let input = CANONICAL_LOCK.replace(
1192            "requires-python = \">=3.12\"\n",
1193            concat!(
1194                "requires-python = \">=3.12\"\n\n",
1195                "[options]\n",
1196                "exclude-newer = \"0001-01-01T00:00:00Z\" ",
1197                "# This has no effect and is included for backwards compatibility ",
1198                "when using relative exclude-newer values.\n",
1199                "exclude-newer-span = \"P3W\"\n",
1200            ),
1201        );
1202        let expected: Lock =
1203            toml::from_str(&input).expect("canonical relative exclude-newer lock is valid TOML");
1204        let actual =
1205            from_str(&input).expect("canonical relative exclude-newer lock uses the direct parser");
1206
1207        assert_eq!(actual, expected);
1208    }
1209
1210    #[test]
1211    fn inline_comments_match_toml() {
1212        let input = CANONICAL_LOCK
1213            .replace("version = 1\n", "version = 1 # root comment\n")
1214            .replace(
1215                "source = { virtual = \".\" }\n",
1216                "source = { virtual = \".\" } # package comment\n",
1217            );
1218        let expected: Lock = toml::from_str(&input).expect("inline comments are valid TOML");
1219        let actual = from_str(&input).expect("inline comments use the direct parser");
1220
1221        assert_eq!(actual, expected);
1222    }
1223
1224    #[test]
1225    fn invalid_comment_characters_preserve_toml_error() {
1226        for character in ['\u{0}', '\u{7}', '\u{b}', '\u{7f}'] {
1227            let input = CANONICAL_LOCK.replace(
1228                "version = 1\n",
1229                &format!("version = 1 # invalid{character}comment\n"),
1230            );
1231            let expected = toml::from_str::<Lock>(&input)
1232                .expect_err("TOML rejects control characters in comments");
1233
1234            assert!(
1235                from_str(&input).is_err(),
1236                "the direct parser must reject U+{:04X} in a comment",
1237                u32::from(character)
1238            );
1239
1240            let actual = Lock::from_toml(&input)
1241                .expect_err("the lock reader rejects control characters in comments");
1242
1243            assert_eq!(actual.to_string(), expected.to_string());
1244        }
1245    }
1246
1247    #[test]
1248    fn bare_carriage_returns_preserve_toml_error() {
1249        for input in [
1250            format!("\r{CANONICAL_LOCK}"),
1251            format!("{CANONICAL_LOCK}\r"),
1252            CANONICAL_LOCK.replace("revision = 3\n", "revision = 3\n\r"),
1253            CANONICAL_LOCK.replace("dependencies = [\n", "dependencies = [\r"),
1254            format!("{CANONICAL_LOCK}[options]\r"),
1255        ] {
1256            let expected = toml::from_str::<Lock>(&input)
1257                .expect_err("TOML rejects standalone carriage returns");
1258
1259            assert!(
1260                from_str(&input).is_err(),
1261                "the direct parser must reject standalone carriage returns"
1262            );
1263
1264            let actual = Lock::from_toml(&input)
1265                .expect_err("the lock reader rejects standalone carriage returns");
1266
1267            assert_eq!(actual.to_string(), expected.to_string());
1268        }
1269    }
1270
1271    #[test]
1272    fn unescaped_delete_preserves_toml_error() {
1273        let input = CANONICAL_LOCK.replace(
1274            "requires-python = \">=3.12\"\n",
1275            "requires-python = \">=3.12\"\nunknown = \"invalid\u{7f}string\"\n",
1276        );
1277        let expected =
1278            toml::from_str::<Lock>(&input).expect_err("TOML rejects unescaped ASCII DELETE");
1279
1280        assert!(
1281            from_str(&input).is_err(),
1282            "the direct parser must reject unescaped ASCII DELETE"
1283        );
1284
1285        let actual =
1286            Lock::from_toml(&input).expect_err("the lock reader rejects unescaped ASCII DELETE");
1287
1288        assert_eq!(actual.to_string(), expected.to_string());
1289    }
1290
1291    #[test]
1292    fn unescaped_string_control_characters_preserve_toml_error() {
1293        for prefix_length in [0, 7, 8, 15, 16, 31, 32, 63, 64] {
1294            let prefix = "a".repeat(prefix_length);
1295
1296            for character in ['\u{0}', '\u{7}', '\u{8}', '\n', '\r', '\u{1f}'] {
1297                let input = CANONICAL_LOCK.replace(
1298                    "requires-python = \">=3.12\"\n",
1299                    &format!(
1300                        "requires-python = \">=3.12\"\nunknown = \"{prefix}{character}string\"\n"
1301                    ),
1302                );
1303                let expected = toml::from_str::<Lock>(&input)
1304                    .expect_err("TOML rejects unescaped control characters in basic strings");
1305
1306                assert!(
1307                    from_str(&input).is_err(),
1308                    "the direct parser must reject U+{:04X} after {prefix_length} bytes",
1309                    u32::from(character)
1310                );
1311
1312                let actual = Lock::from_toml(&input)
1313                    .expect_err("the lock reader rejects unescaped string control characters");
1314
1315                assert_eq!(actual.to_string(), expected.to_string());
1316            }
1317        }
1318    }
1319
1320    #[test]
1321    fn duplicate_keys_preserve_toml_error() {
1322        for (kind, input) in [
1323            (
1324                "unknown root keys",
1325                CANONICAL_LOCK.replace(
1326                    "requires-python = \">=3.12\"\n",
1327                    "requires-python = \">=3.12\"\nunknown = 1\nunknown = 2\n",
1328                ),
1329            ),
1330            (
1331                "unknown inline-table keys",
1332                CANONICAL_LOCK.replace(
1333                    "requires-python = \">=3.12\"\n",
1334                    "requires-python = \">=3.12\"\nunknown = { nested = 1, nested = 2 }\n",
1335                ),
1336            ),
1337            (
1338                "dependency-group keys",
1339                format!("{CANONICAL_LOCK}\n[package.dev-dependencies]\ndev = []\ndev = []\n"),
1340            ),
1341            (
1342                "section headers",
1343                format!(
1344                    "{CANONICAL_LOCK}\n[package.metadata]\nunknown = 1\n\n[package.metadata]\nunknown = 2\n"
1345                ),
1346            ),
1347        ] {
1348            let expected = toml::from_str::<Lock>(&input).expect_err("TOML rejects duplicate keys");
1349
1350            assert!(
1351                from_str(&input).is_err(),
1352                "the direct parser must reject duplicate {kind}"
1353            );
1354
1355            let actual =
1356                Lock::from_toml(&input).expect_err("the lock reader rejects duplicate keys");
1357
1358            assert_eq!(actual.to_string(), expected.to_string());
1359        }
1360    }
1361
1362    #[test]
1363    fn wide_maps_match_toml() {
1364        let mut assignments = String::new();
1365        let mut inline_entries = String::new();
1366
1367        for index in 0..4_096 {
1368            writeln!(assignments, "ignored-{index:04} = true")
1369                .expect("writing to a string cannot fail");
1370
1371            if !inline_entries.is_empty() {
1372                inline_entries.push_str(", ");
1373            }
1374            write!(inline_entries, "ignored-{index:04} = true")
1375                .expect("writing to a string cannot fail");
1376        }
1377
1378        for (kind, entries) in [
1379            ("root", assignments),
1380            ("inline", format!("ignored = {{ {inline_entries} }}\n")),
1381        ] {
1382            let input = CANONICAL_LOCK.replace(
1383                "requires-python = \">=3.12\"\n",
1384                &format!("requires-python = \">=3.12\"\n{entries}"),
1385            );
1386            let expected: Lock = toml::from_str(&input).expect("wide lock is valid TOML");
1387            let actual = from_str(&input).expect("wide lock uses the direct parser");
1388
1389            assert_eq!(actual, expected, "wide {kind} map matches TOML");
1390        }
1391    }
1392
1393    #[test]
1394    fn duplicates_after_inline_capacity_preserve_toml_error() {
1395        let mut assignments = String::new();
1396        let mut inline_entries = String::new();
1397
1398        for index in 0..16 {
1399            writeln!(assignments, "ignored-{index:02} = {index}")
1400                .expect("writing to a string cannot fail");
1401
1402            if !inline_entries.is_empty() {
1403                inline_entries.push_str(", ");
1404            }
1405            write!(inline_entries, "ignored-{index:02} = {index}")
1406                .expect("writing to a string cannot fail");
1407        }
1408
1409        assignments.push_str("ignored-08 = 8\n");
1410        inline_entries.push_str(", ignored-08 = 8");
1411
1412        for (kind, entries) in [
1413            ("root", assignments),
1414            ("inline", format!("ignored = {{ {inline_entries} }}\n")),
1415        ] {
1416            let input = CANONICAL_LOCK.replace(
1417                "requires-python = \">=3.12\"\n",
1418                &format!("requires-python = \">=3.12\"\n{entries}"),
1419            );
1420            let expected = toml::from_str::<Lock>(&input)
1421                .expect_err("TOML rejects duplicate keys in wide maps");
1422
1423            assert!(
1424                from_str(&input).is_err(),
1425                "the direct parser rejects duplicate keys in a wide {kind} map"
1426            );
1427
1428            let actual = Lock::from_toml(&input)
1429                .expect_err("the lock reader rejects duplicate keys in wide maps");
1430
1431            assert_eq!(actual.to_string(), expected.to_string());
1432        }
1433    }
1434
1435    #[test]
1436    fn canonical_mutations_match_toml() {
1437        const MUTATIONS: &[char] = &[
1438            '\0', '\t', '\n', '\r', ' ', '#', '"', '\'', '\\', '=', ',', '.', '{', '}', '[', ']',
1439            '_', '0', 'é', '🦀', '\u{85}', '\u{2028}',
1440        ];
1441
1442        for offset in 0..CANONICAL_LOCK.len() {
1443            for &mutation in MUTATIONS {
1444                let mut input = CANONICAL_LOCK.to_owned();
1445                input.insert(offset, mutation);
1446
1447                let Ok(actual) = from_str(&input) else {
1448                    continue;
1449                };
1450
1451                let expected = toml::from_str::<Lock>(&input);
1452                assert!(
1453                    expected.is_ok(),
1454                    "direct parser accepted invalid TOML after inserting {mutation:?} at {offset}: {expected:?}"
1455                );
1456
1457                assert_eq!(
1458                    actual,
1459                    expected.expect("direct parser accepted valid TOML"),
1460                    "direct parser disagreed with TOML after inserting {mutation:?} at {offset}"
1461                );
1462            }
1463        }
1464    }
1465
1466    #[test]
1467    fn excessive_container_depth_preserves_toml_error() {
1468        for nested in [
1469            format!("{}0{}", "[".repeat(81), "]".repeat(81)),
1470            (0..81).fold(String::from("0"), |nested, index| {
1471                if index % 2 == 0 {
1472                    format!("[{nested}]")
1473                } else {
1474                    format!("{{ nested = {nested} }}")
1475                }
1476            }),
1477        ] {
1478            let input = CANONICAL_LOCK.replace(
1479                "requires-python = \">=3.12\"\n",
1480                &format!("requires-python = \">=3.12\"\nunknown = {nested}\n"),
1481            );
1482            let expected =
1483                toml::from_str::<Lock>(&input).expect_err("TOML rejects excessive nesting");
1484
1485            assert!(
1486                from_str(&input).is_err(),
1487                "the direct parser must reject excessive inline-container nesting"
1488            );
1489
1490            let actual = Lock::from_toml(&input)
1491                .expect_err("the lock reader rejects excessive inline-container nesting");
1492
1493            assert_eq!(actual.to_string(), expected.to_string());
1494        }
1495    }
1496
1497    #[test]
1498    fn supported_container_depth_matches_toml() {
1499        let nested = format!("{}0{}", "[".repeat(80), "]".repeat(80));
1500        let input = CANONICAL_LOCK.replace(
1501            "requires-python = \">=3.12\"\n",
1502            &format!("requires-python = \">=3.12\"\nunknown = {nested}\n"),
1503        );
1504        let expected: Lock = toml::from_str(&input).expect("TOML supports 80 nested containers");
1505        let actual = from_str(&input).expect("the direct parser supports 80 nested containers");
1506
1507        assert_eq!(actual, expected);
1508    }
1509
1510    #[test]
1511    fn canonical_round_trip_uses_fast_path() {
1512        let lock: Lock = toml::from_str(CANONICAL_LOCK).expect("valid TOML lock");
1513        let canonical = lock.to_toml().expect("lock serializes canonically");
1514
1515        assert_eq!(
1516            Lock::from_canonical_toml(&canonical).expect("writer output uses fast path"),
1517            lock
1518        );
1519    }
1520}