1#![allow(clippy::cast_possible_wrap)]
10#![allow(clippy::cast_sign_loss)]
11
12use alloc::{
13 borrow::{Cow, ToOwned},
14 collections::VecDeque,
15 string::String,
16 vec::Vec,
17};
18use core::char;
19
20use crate::{
21 char_traits::{
22 as_hex, find_non_printable, is_anchor_char, is_blank_or_breakz, is_bom, is_break,
23 is_breakz, is_flow, is_hex, is_printable, is_tag_char, is_uri_char, is_yaml_non_space,
24 is_z,
25 },
26 error::{ErrorKind, ScanError},
27 input::{BorrowedInput, SkipTabs},
28 Options,
29};
30
31#[derive(Clone, Copy, PartialEq, Debug, Eq, Hash, PartialOrd, Ord)]
33pub enum ScalarStyle {
34 Plain,
36 SingleQuoted,
38 DoubleQuoted,
40
41 Literal,
47 Folded,
54}
55
56#[derive(Clone, Copy, Debug, Default)]
63struct MarkerOffsets {
64 chars: usize,
66 bytes: Option<usize>,
68}
69
70impl PartialEq for MarkerOffsets {
71 fn eq(&self, other: &Self) -> bool {
72 self.chars == other.chars
76 }
77}
78
79impl Eq for MarkerOffsets {}
80
81#[derive(Clone, Copy, PartialEq, Debug, Eq, Default)]
83pub struct Marker {
84 offsets: MarkerOffsets,
86 line: usize,
88 col: usize,
90}
91
92impl Marker {
93 #[must_use]
95 pub fn new(index: usize, line: usize, col: usize) -> Marker {
96 Marker {
97 offsets: MarkerOffsets {
98 chars: index,
99 bytes: None,
100 },
101 line,
102 col,
103 }
104 }
105
106 #[must_use]
108 pub fn with_byte_offset(mut self, byte_offset: Option<usize>) -> Marker {
109 self.offsets.bytes = byte_offset;
110 self
111 }
112
113 #[must_use]
115 pub fn index(&self) -> usize {
116 self.offsets.chars
117 }
118
119 #[must_use]
121 pub fn byte_offset(&self) -> Option<usize> {
122 self.offsets.bytes
123 }
124
125 #[must_use]
127 pub fn line(&self) -> usize {
128 self.line
129 }
130
131 #[must_use]
133 pub fn col(&self) -> usize {
134 self.col
135 }
136}
137
138#[derive(Clone, Copy, PartialEq, Debug, Eq, Default)]
140pub struct Span {
141 pub start: Marker,
143 pub end: Marker,
145
146 pub indent: Option<usize>,
151
152 pub tag_start: Option<Marker>,
161}
162
163impl Span {
164 #[must_use]
166 pub fn new(start: Marker, end: Marker) -> Span {
167 Span {
168 start,
169 end,
170 indent: None,
171 tag_start: None,
172 }
173 }
174
175 #[must_use]
182 pub fn empty(mark: Marker) -> Span {
183 Span {
184 start: mark,
185 end: mark,
186 indent: None,
187 tag_start: None,
188 }
189 }
190
191 #[must_use]
193 pub fn with_indent(mut self, indent: Option<usize>) -> Span {
194 self.indent = indent;
195 self
196 }
197
198 #[must_use]
200 pub fn with_tag_start(mut self, tag_start: Option<Marker>) -> Span {
201 self.tag_start = tag_start;
202 self
203 }
204
205 #[must_use]
211 pub fn tag_start(&self) -> Option<Marker> {
212 self.tag_start
213 }
214
215 #[track_caller]
220 #[must_use]
221 pub fn len(&self) -> usize {
222 self.end.index() - self.start.index()
223 }
224
225 #[track_caller]
230 #[must_use]
231 pub fn is_empty(&self) -> bool {
232 self.len() == 0
233 }
234
235 #[must_use]
237 pub fn byte_range(&self) -> Option<core::ops::Range<usize>> {
238 let start = self.start.byte_offset()?;
239 let end = self.end.byte_offset()?;
240 Some(start..end)
241 }
242
243 #[must_use]
246 pub fn slice<'source>(&self, source: &'source str) -> Option<&'source str> {
247 source.get(self.byte_range()?)
248 }
249}
250
251#[non_exhaustive]
266#[derive(Clone, Copy, PartialEq, Debug, Eq, Default)]
267pub enum Placement {
268 Above,
274 Right,
277 #[default]
283 Free,
284 Last,
289}
290
291#[derive(Clone, PartialEq, Debug, Eq)]
296pub struct Comment<'input> {
297 text: Cow<'input, str>,
301 placement: Placement,
303}
304
305impl<'input> Comment<'input> {
306 #[must_use]
311 pub fn new(text: impl Into<Cow<'input, str>>) -> Self {
312 Self {
313 text: text.into(),
314 placement: Placement::Free,
315 }
316 }
317
318 #[must_use]
320 pub fn with_placement(mut self, placement: Placement) -> Self {
321 self.placement = placement;
322 self
323 }
324
325 #[must_use]
327 pub fn text(&self) -> &str {
328 self.text.as_ref()
329 }
330
331 #[must_use]
333 pub const fn placement(&self) -> Placement {
334 self.placement
335 }
336
337 #[must_use]
339 pub fn into_text(self) -> Cow<'input, str> {
340 self.text
341 }
342
343 #[must_use]
347 pub fn trimmed_text(&self) -> &str {
348 self.text.trim()
349 }
350}
351
352impl AsRef<str> for Comment<'_> {
353 fn as_ref(&self) -> &str {
354 self.text.as_ref()
355 }
356}
357
358#[non_exhaustive]
360#[derive(Clone, PartialEq, Debug, Eq)]
361pub enum TokenType<'input> {
362 StreamStart,
364 StreamEnd,
366 VersionDirective(
368 u32,
370 u32,
372 ),
373 TagDirective(
375 Cow<'input, str>,
377 Cow<'input, str>,
379 ),
380 DocumentStart,
382 DocumentEnd,
384 BlockSequenceStart,
388 BlockMappingStart,
392 BlockEnd,
394 FlowSequenceStart,
396 FlowSequenceEnd,
398 FlowMappingStart,
400 FlowMappingEnd,
402 BlockEntry,
404 FlowEntry,
406 Key,
408 Value,
410 Alias(Cow<'input, str>),
412 Anchor(Cow<'input, str>),
414 Tag(
416 Cow<'input, str>,
418 Cow<'input, str>,
420 ),
421 Scalar(ScalarStyle, Cow<'input, str>),
423 Comment(
429 Comment<'input>,
431 ),
432 ReservedDirective(
434 String,
436 Vec<String>,
438 ),
439}
440
441#[derive(Clone, PartialEq, Debug, Eq)]
443pub struct Token<'input>(Span, TokenType<'input>);
444
445impl<'input> Token<'input> {
446 #[must_use]
448 pub const fn new(span: Span, token_type: TokenType<'input>) -> Self {
449 Self(span, token_type)
450 }
451
452 #[must_use]
454 pub const fn span(&self) -> Span {
455 self.0
456 }
457
458 #[must_use]
460 pub const fn token_type(&self) -> &TokenType<'input> {
461 &self.1
462 }
463
464 #[must_use]
466 pub fn into_parts(self) -> (Span, TokenType<'input>) {
467 (self.0, self.1)
468 }
469}
470
471#[derive(Clone, PartialEq, Debug, Eq)]
475pub(crate) enum QueuedTokenType<'input> {
476 StreamStart,
477 StreamEnd,
478 VersionDirective(u32, u32),
479 TagDirective(Cow<'input, str>, Cow<'input, str>),
480 DocumentStart,
481 DocumentEnd,
482 BlockSequenceStart,
483 BlockMappingStart,
484 BlockEnd,
485 FlowSequenceStart,
486 FlowSequenceEnd,
487 FlowMappingStart,
488 FlowMappingEnd,
489 BlockEntry,
490 FlowEntry,
491 Key,
492 Value,
493 Alias(Cow<'input, str>),
494 Anchor(Cow<'input, str>),
495 Tag(Cow<'input, str>, Cow<'input, str>),
496 Scalar(ScalarStyle, Cow<'input, str>),
497 Comment(Comment<'input>),
498 ReservedDirective(String, Vec<String>),
499}
500
501impl<'input> QueuedTokenType<'input> {
502 fn into_public(self) -> TokenType<'input> {
503 match self {
504 Self::StreamStart => TokenType::StreamStart,
505 Self::StreamEnd => TokenType::StreamEnd,
506 Self::VersionDirective(major, minor) => TokenType::VersionDirective(major, minor),
507 Self::TagDirective(handle, prefix) => TokenType::TagDirective(handle, prefix),
508 Self::DocumentStart => TokenType::DocumentStart,
509 Self::DocumentEnd => TokenType::DocumentEnd,
510 Self::BlockSequenceStart => TokenType::BlockSequenceStart,
511 Self::BlockMappingStart => TokenType::BlockMappingStart,
512 Self::BlockEnd => TokenType::BlockEnd,
513 Self::FlowSequenceStart => TokenType::FlowSequenceStart,
514 Self::FlowSequenceEnd => TokenType::FlowSequenceEnd,
515 Self::FlowMappingStart => TokenType::FlowMappingStart,
516 Self::FlowMappingEnd => TokenType::FlowMappingEnd,
517 Self::BlockEntry => TokenType::BlockEntry,
518 Self::FlowEntry => TokenType::FlowEntry,
519 Self::Key => TokenType::Key,
520 Self::Value => TokenType::Value,
521 Self::Alias(name) => TokenType::Alias(name),
522 Self::Anchor(name) => TokenType::Anchor(name),
523 Self::Tag(handle, suffix) => TokenType::Tag(handle, suffix),
524 Self::Scalar(style, value) => TokenType::Scalar(style, value),
525 Self::Comment(comment) => TokenType::Comment(comment),
526 Self::ReservedDirective(name, params) => TokenType::ReservedDirective(name, params),
527 }
528 }
529}
530
531impl<'input> From<TokenType<'input>> for QueuedTokenType<'input> {
532 fn from(token: TokenType<'input>) -> Self {
533 match token {
534 TokenType::StreamStart => Self::StreamStart,
535 TokenType::StreamEnd => Self::StreamEnd,
536 TokenType::VersionDirective(major, minor) => Self::VersionDirective(major, minor),
537 TokenType::TagDirective(handle, prefix) => Self::TagDirective(handle, prefix),
538 TokenType::DocumentStart => Self::DocumentStart,
539 TokenType::DocumentEnd => Self::DocumentEnd,
540 TokenType::BlockSequenceStart => Self::BlockSequenceStart,
541 TokenType::BlockMappingStart => Self::BlockMappingStart,
542 TokenType::BlockEnd => Self::BlockEnd,
543 TokenType::FlowSequenceStart => Self::FlowSequenceStart,
544 TokenType::FlowSequenceEnd => Self::FlowSequenceEnd,
545 TokenType::FlowMappingStart => Self::FlowMappingStart,
546 TokenType::FlowMappingEnd => Self::FlowMappingEnd,
547 TokenType::BlockEntry => Self::BlockEntry,
548 TokenType::FlowEntry => Self::FlowEntry,
549 TokenType::Key => Self::Key,
550 TokenType::Value => Self::Value,
551 TokenType::Alias(name) => Self::Alias(name),
552 TokenType::Anchor(name) => Self::Anchor(name),
553 TokenType::Tag(handle, suffix) => Self::Tag(handle, suffix),
554 TokenType::Scalar(style, value) => Self::Scalar(style, value),
555 TokenType::Comment(comment) => Self::Comment(comment),
556 TokenType::ReservedDirective(name, params) => Self::ReservedDirective(name, params),
557 }
558 }
559}
560
561#[derive(Clone, PartialEq, Debug, Eq)]
563pub(crate) struct QueuedToken<'input>(pub(crate) Span, pub(crate) QueuedTokenType<'input>);
564
565impl<'input> QueuedToken<'input> {
566 fn into_public(self) -> Token<'input> {
567 Token(self.0, self.1.into_public())
568 }
569}
570
571impl<'input> From<Token<'input>> for QueuedToken<'input> {
572 fn from(token: Token<'input>) -> Self {
573 Self(token.0, token.1.into())
574 }
575}
576
577#[derive(Clone, Copy, PartialEq, Debug, Eq)]
612struct SimpleKey {
613 possible: bool,
626 required: bool,
635 token_number: usize,
641 mark: Marker,
643}
644
645impl SimpleKey {
646 fn new(mark: Marker) -> SimpleKey {
648 SimpleKey {
649 possible: false,
650 required: false,
651 token_number: 0,
652 mark,
653 }
654 }
655}
656
657#[derive(Clone, Debug, Default)]
659struct Indent {
660 indent: isize,
662 needs_block_end: bool,
680}
681
682#[derive(Debug, PartialEq)]
704enum ImplicitMappingState {
705 Possible,
710 Inside(usize),
714}
715
716#[derive(Debug)]
729#[allow(clippy::struct_excessive_bools)]
730pub struct Scanner<'input, T> {
731 input: T,
735 options: Options,
737 mark: Marker,
739 tokens: VecDeque<QueuedToken<'input>>,
746 failed: bool,
748 deferred_error: Option<ScanError>,
750 comments_possible: bool,
756
757 stream_start_produced: bool,
759 stream_end_produced: bool,
761 document_prefix_allowed: bool,
767 adjacent_value_allowed_at: usize,
770 simple_key_allowed: bool,
774 simple_keys: smallvec::SmallVec<[SimpleKey; 8]>,
779 first_simple_key: Option<usize>,
785 indent: isize,
787 indents: smallvec::SmallVec<[Indent; 8]>,
789 flow_level: usize,
791 block_level: usize,
793 tokens_parsed: usize,
797 token_available: bool,
799 leading_whitespace: bool,
801 flow_mapping_started: smallvec::SmallVec<[bool; 8]>,
808 implicit_flow_mapping_states: smallvec::SmallVec<[ImplicitMappingState; 8]>,
821 interrupted_plain_by_comment: Option<Marker>,
824 explicit_key_tab_check_pending: bool,
829 flow_markers: smallvec::SmallVec<[(Marker, char); 8]>,
831 buf_leading_break: String,
832 buf_trailing_breaks: String,
833 buf_whitespaces: String,
834}
835
836impl<'input, T: BorrowedInput<'input>> Iterator for Scanner<'input, T> {
837 type Item = Result<Token<'input>, ScanError>;
838
839 fn next(&mut self) -> Option<Self::Item> {
840 if self.failed {
841 return None;
842 }
843 match self.next_token() {
844 Ok(Some(tok)) => {
845 debug_print!(
846 " \x1B[;32m\u{21B3} {:?} \x1B[;36m{:?}\x1B[;m",
847 tok.1,
848 tok.0
849 );
850 Some(Ok(tok))
851 }
852 Ok(None) => None,
853 Err(error) => {
854 self.failed = true;
855 Some(Err(error))
856 }
857 }
858 }
859}
860
861impl<'input, T: BorrowedInput<'input>> core::iter::FusedIterator for Scanner<'input, T> {}
862
863type ScanResult = Result<(), ScanError>;
865
866#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
871struct SkipToNextTokenOutcome {
872 saw_comment: bool,
873 queued_comment: bool,
874}
875
876#[derive(Debug)]
877enum FlowScalarBuf {
878 Borrowed {
884 start: usize,
885 end: usize,
886 pending_ws_start: Option<usize>,
887 pending_ws_end: usize,
888 },
889 Owned(String),
890}
891
892impl FlowScalarBuf {
893 #[inline]
894 fn new_borrowed(start: usize) -> Self {
895 Self::Borrowed {
896 start,
897 end: start,
898 pending_ws_start: None,
899 pending_ws_end: start,
900 }
901 }
902
903 #[inline]
904 fn new_owned() -> Self {
905 Self::Owned(String::new())
906 }
907
908 #[inline]
909 fn as_owned_mut(&mut self) -> Option<&mut String> {
910 match self {
911 Self::Owned(s) => Some(s),
912 Self::Borrowed { .. } => None,
913 }
914 }
915
916 #[inline]
917 fn commit_pending_ws(&mut self) {
918 if let Self::Borrowed {
919 end,
920 pending_ws_start,
921 pending_ws_end,
922 ..
923 } = self
924 {
925 if pending_ws_start.is_some() {
926 *end = *pending_ws_end;
927 *pending_ws_start = None;
928 }
929 }
930 }
931
932 #[inline]
933 fn note_pending_ws(&mut self, ws_start: usize, ws_end: usize) {
934 if let Self::Borrowed {
935 pending_ws_start,
936 pending_ws_end,
937 ..
938 } = self
939 {
940 if pending_ws_start.is_none() {
941 *pending_ws_start = Some(ws_start);
942 }
943 *pending_ws_end = ws_end;
944 }
945 }
946
947 #[inline]
948 fn discard_pending_ws(&mut self) {
949 if let Self::Borrowed {
950 pending_ws_start,
951 pending_ws_end,
952 end,
953 ..
954 } = self
955 {
956 *pending_ws_start = None;
957 *pending_ws_end = *end;
958 }
959 }
960}
961
962impl<'input, T: BorrowedInput<'input>> Scanner<'input, T> {
963 #[inline]
964 fn promote_flow_scalar_buf_to_owned(
965 &self,
966 start_mark: &Marker,
967 buf: &mut FlowScalarBuf,
968 ) -> Result<(), ScanError> {
969 let FlowScalarBuf::Borrowed {
970 start,
971 end,
972 pending_ws_start: _,
973 pending_ws_end: _,
974 } = *buf
975 else {
976 return Ok(());
977 };
978
979 let slice = self.input.slice_bytes(start, end).ok_or_else(|| {
980 ScanError::from_kind(*start_mark, ErrorKind::InputOffsetsWithoutSlice)
981 })?;
982 *buf = FlowScalarBuf::Owned(slice.to_owned());
983 Ok(())
984 }
985 #[inline]
991 fn try_borrow_slice(&self, start: usize, end: usize) -> Option<&'input str> {
992 self.input.slice_borrowed(start, end)
993 }
994
995 fn scan_tag_handle_directive_cow(
1000 &mut self,
1001 mark: &Marker,
1002 budget: &mut usize,
1003 ) -> Result<Cow<'input, str>, ScanError> {
1004 let Some(start) = self.input.byte_offset() else {
1005 return Ok(Cow::Owned(
1006 self.scan_tag_handle_directive_owned(mark, budget)?,
1007 ));
1008 };
1009
1010 if self.input.look_ch() != '!' {
1011 return Err(ScanError::from_kind(*mark, ErrorKind::ExpectedTagBang));
1012 }
1013
1014 self.spend_directive_bytes(budget, 1, *mark)?;
1016 self.skip_non_blank();
1017
1018 self.input.lookahead(1);
1021 while self.input.next_is_alpha() {
1022 self.spend_directive_bytes(budget, 1, *mark)?;
1023 self.skip_non_blank();
1024 self.input.lookahead(1);
1025 }
1026
1027 if self.input.peek() == '!' {
1029 self.spend_directive_bytes(budget, 1, *mark)?;
1030 self.skip_non_blank();
1031 }
1032
1033 let Some(end) = self.input.byte_offset() else {
1034 return Err(ScanError::from_kind(
1035 *mark,
1036 ErrorKind::InputSlicingUnavailable,
1037 ));
1038 };
1039
1040 let Some(slice) = self.try_borrow_slice(start, end) else {
1041 let slice = self
1043 .input
1044 .slice_bytes(start, end)
1045 .ok_or_else(|| ScanError::from_kind(*mark, ErrorKind::InputSlicingUnavailable))?;
1046 if !slice.ends_with('!') && slice != "!" {
1047 return Err(ScanError::from_kind(
1048 *mark,
1049 ErrorKind::ExpectedTagDirectiveBang,
1050 ));
1051 }
1052 return Ok(Cow::Owned(slice.to_owned()));
1053 };
1054
1055 if !slice.ends_with('!') && slice != "!" {
1056 return Err(ScanError::from_kind(
1057 *mark,
1058 ErrorKind::ExpectedTagDirectiveBang,
1059 ));
1060 }
1061
1062 Ok(Cow::Borrowed(slice))
1063 }
1064
1065 fn scan_tag_handle_directive_owned(
1067 &mut self,
1068 mark: &Marker,
1069 budget: &mut usize,
1070 ) -> Result<String, ScanError> {
1071 let mut string = String::new();
1072 if self.input.look_ch() != '!' {
1073 return Err(ScanError::from_kind(*mark, ErrorKind::ExpectedTagBang));
1074 }
1075
1076 self.spend_directive_bytes(budget, 1, *mark)?;
1077 string.push('!');
1078 self.skip_non_blank();
1079
1080 self.input.lookahead(1);
1081 while self.input.next_is_alpha() {
1082 self.spend_directive_bytes(budget, 1, *mark)?;
1083 string.push(self.input.peek());
1084 self.skip_non_blank();
1085 self.input.lookahead(1);
1086 }
1087
1088 if self.input.peek() == '!' {
1089 self.spend_directive_bytes(budget, 1, *mark)?;
1090 string.push('!');
1091 self.skip_non_blank();
1092 } else if string != "!" {
1093 return Err(ScanError::from_kind(
1094 *mark,
1095 ErrorKind::ExpectedTagDirectiveBang,
1096 ));
1097 }
1098
1099 Ok(string)
1100 }
1101
1102 fn scan_tag_prefix_directive_cow(
1107 &mut self,
1108 start_mark: &Marker,
1109 budget: &mut usize,
1110 ) -> Result<Cow<'input, str>, ScanError> {
1111 let Some(start) = self.input.byte_offset() else {
1112 return Ok(Cow::Owned(
1113 self.scan_tag_prefix_directive_owned(start_mark, budget)?,
1114 ));
1115 };
1116
1117 if self.input.look_ch() == '!' {
1119 self.spend_directive_bytes(budget, 1, *start_mark)?;
1120 self.skip_non_blank();
1121 } else if !is_tag_char(self.input.peek()) {
1122 return Err(ScanError::from_kind(
1123 *start_mark,
1124 ErrorKind::InvalidGlobalTagCharacter,
1125 ));
1126 } else if self.input.peek() == '%' {
1127 } else {
1129 self.spend_directive_bytes(budget, 1, *start_mark)?;
1130 self.skip_non_blank();
1131 }
1132
1133 while is_uri_char(self.input.look_ch()) {
1135 if self.input.peek() == '%' {
1136 break;
1137 }
1138 self.spend_directive_bytes(budget, 1, *start_mark)?;
1139 self.skip_non_blank();
1140 }
1141
1142 if self.input.peek() == '%' {
1144 let current = self
1145 .input
1146 .byte_offset()
1147 .expect("byte_offset() must remain available once enabled");
1148 let mut out = if let Some(slice) = self.input.slice_bytes(start, current) {
1149 slice.to_owned()
1150 } else {
1151 String::new()
1152 };
1153
1154 while is_uri_char(self.input.look_ch()) {
1155 if self.input.peek() == '%' {
1156 out.push(self.scan_uri_escapes_with_budget(start_mark, Some(budget))?);
1157 } else {
1158 self.spend_directive_bytes(budget, 1, *start_mark)?;
1159 out.push(self.input.peek());
1160 self.skip_non_blank();
1161 }
1162 }
1163 return Ok(Cow::Owned(out));
1164 }
1165
1166 let Some(end) = self.input.byte_offset() else {
1167 return Err(ScanError::from_kind(
1168 *start_mark,
1169 ErrorKind::InputSlicingUnavailable,
1170 ));
1171 };
1172
1173 let Some(slice) = self.try_borrow_slice(start, end) else {
1174 let slice = self.input.slice_bytes(start, end).ok_or_else(|| {
1176 ScanError::from_kind(*start_mark, ErrorKind::InputSlicingUnavailable)
1177 })?;
1178 return Ok(Cow::Owned(slice.to_owned()));
1179 };
1180
1181 Ok(Cow::Borrowed(slice))
1182 }
1183
1184 fn scan_tag_prefix_directive_owned(
1186 &mut self,
1187 start_mark: &Marker,
1188 budget: &mut usize,
1189 ) -> Result<String, ScanError> {
1190 let mut string = String::new();
1191
1192 if self.input.look_ch() == '!' {
1193 self.spend_directive_bytes(budget, 1, *start_mark)?;
1194 string.push('!');
1195 self.skip_non_blank();
1196 } else if !is_tag_char(self.input.peek()) {
1197 return Err(ScanError::from_kind(
1198 *start_mark,
1199 ErrorKind::InvalidGlobalTagCharacter,
1200 ));
1201 } else if self.input.peek() == '%' {
1202 string.push(self.scan_uri_escapes_with_budget(start_mark, Some(budget))?);
1203 } else {
1204 self.spend_directive_bytes(budget, 1, *start_mark)?;
1205 string.push(self.input.peek());
1206 self.skip_non_blank();
1207 }
1208
1209 while is_uri_char(self.input.look_ch()) {
1210 if self.input.peek() == '%' {
1211 string.push(self.scan_uri_escapes_with_budget(start_mark, Some(budget))?);
1212 } else {
1213 self.spend_directive_bytes(budget, 1, *start_mark)?;
1214 string.push(self.input.peek());
1215 self.skip_non_blank();
1216 }
1217 }
1218
1219 Ok(string)
1220 }
1221 #[must_use]
1223 pub fn new(input: T) -> Self {
1224 Self::with_options(input, Options::default())
1225 }
1226
1227 #[must_use]
1233 pub fn with_options(input: T, options: Options) -> Self {
1234 let initial_byte_offset = input.byte_offset();
1235 let comments_possible = !options.emit_comments || input.may_contain_comments();
1238 Scanner {
1239 input,
1240 options,
1241 mark: Marker::new(0, 1, 0).with_byte_offset(initial_byte_offset),
1242 tokens: VecDeque::with_capacity(64),
1243 failed: false,
1244 deferred_error: None,
1245 comments_possible,
1246
1247 stream_start_produced: false,
1248 stream_end_produced: false,
1249 document_prefix_allowed: true,
1250 adjacent_value_allowed_at: 0,
1251 simple_key_allowed: true,
1252 simple_keys: smallvec::SmallVec::new(),
1253 first_simple_key: None,
1254 indent: -1,
1255 indents: smallvec::SmallVec::new(),
1256 flow_level: 0,
1257 block_level: 0,
1258 tokens_parsed: 0,
1259 token_available: false,
1260 leading_whitespace: true,
1261 flow_mapping_started: smallvec::SmallVec::new(),
1262 implicit_flow_mapping_states: smallvec::SmallVec::new(),
1263 flow_markers: smallvec::SmallVec::new(),
1264 interrupted_plain_by_comment: None,
1265 explicit_key_tab_check_pending: false,
1266
1267 buf_leading_break: String::with_capacity(128),
1268 buf_trailing_breaks: String::with_capacity(128),
1269 buf_whitespaces: String::with_capacity(128),
1270 }
1271 }
1272
1273 #[cold]
1274 fn scan_error(&self, kind: ErrorKind) -> ScanError {
1275 ScanError::from_kind(self.mark, kind)
1276 }
1277
1278 #[inline]
1279 fn ensure_current_char_is_printable(&self) -> ScanResult {
1280 let character = self.input.peek();
1281 if self.input.next_is_z() || is_printable(character) {
1282 Ok(())
1283 } else {
1284 Err(self.scan_error(ErrorKind::UnexpectedCharacter { character }))
1285 }
1286 }
1287
1288 #[cold]
1289 fn simple_key_expected(mark: Marker) -> ScanError {
1290 ScanError::from_kind(mark, ErrorKind::SimpleKeyExpected)
1291 }
1292
1293 #[cold]
1294 fn unclosed_bracket(mark: Marker, bracket: char) -> ScanError {
1295 ScanError::from_kind(mark, ErrorKind::UnclosedFlowCollection { open: bracket })
1296 }
1297
1298 #[inline]
1300 fn skip_blank(&mut self) {
1301 self.input.skip();
1302
1303 self.mark.offsets.chars += 1;
1304 self.mark.col += 1;
1305 self.mark.offsets.bytes = self.input.byte_offset();
1306 }
1307
1308 #[inline]
1310 fn skip_non_blank(&mut self) {
1311 self.input.skip();
1312
1313 self.mark.offsets.chars += 1;
1314 self.mark.col += 1;
1315 self.mark.offsets.bytes = self.input.byte_offset();
1316 self.leading_whitespace = false;
1317 }
1318
1319 #[inline]
1324 fn skip_bom(&mut self) {
1325 self.input.skip();
1326
1327 self.mark.offsets.chars += 1;
1328 self.mark.offsets.bytes = self.input.byte_offset();
1329 }
1330
1331 #[inline]
1337 fn skip_comment_char(&mut self) {
1338 self.input.skip();
1339
1340 self.mark.offsets.chars += 1;
1341 self.mark.col += 1;
1342 self.mark.offsets.bytes = self.input.byte_offset();
1343 }
1344
1345 #[inline]
1347 fn skip_n_non_blank(&mut self, count: usize) {
1348 for _ in 0..count {
1349 self.input.skip();
1350 self.mark.offsets.chars += 1;
1351 self.mark.col += 1;
1352 }
1353 self.mark.offsets.bytes = self.input.byte_offset();
1354 self.leading_whitespace = false;
1355 }
1356
1357 #[inline]
1359 fn skip_nl(&mut self) {
1360 self.input.skip();
1361
1362 self.mark.offsets.chars += 1;
1363 self.mark.col = 0;
1364 self.mark.line += 1;
1365 self.mark.offsets.bytes = self.input.byte_offset();
1366 self.leading_whitespace = true;
1367 }
1368
1369 #[inline]
1371 fn skip_linebreak(&mut self) {
1372 if self.input.next_2_are('\r', '\n') {
1373 self.skip_blank();
1376 self.skip_nl();
1377 } else if self.input.next_is_break() {
1378 self.skip_nl();
1379 }
1380 }
1381
1382 #[cfg(test)]
1383 fn scan_comment_token(&mut self) -> Result<Token<'input>, ScanError> {
1384 Ok(self.scan_comment_queued_token()?.into_public())
1385 }
1386
1387 fn scan_comment_queued_token(&mut self) -> Result<QueuedToken<'input>, ScanError> {
1388 let start_mark = self.mark;
1389 debug_assert_eq!(self.input.peek(), '#');
1390 let placement = if self.leading_whitespace {
1391 Placement::Free
1392 } else {
1393 Placement::Right
1394 };
1395
1396 self.skip_comment_char();
1397
1398 let text = if let Some(start) = self.input.byte_offset() {
1399 let n = self.input.skip_while_non_breakz();
1401 self.mark.offsets.chars += n;
1402 self.mark.col += n;
1403 let byte_offset = self.input.byte_offset();
1404 self.mark.offsets.bytes = byte_offset;
1405 let end = byte_offset.expect("byte_offset must remain available once enabled");
1406
1407 if let Some(slice) = self.try_borrow_slice(start, end) {
1408 Cow::Borrowed(slice)
1409 } else if let Some(slice) = self.input.slice_bytes(start, end) {
1410 Cow::Owned(slice.to_owned())
1412 } else {
1413 return Err(ScanError::from_kind(
1414 start_mark,
1415 ErrorKind::InputOffsetsWithoutSlice,
1416 ));
1417 }
1418 } else {
1419 let mut owned = String::new();
1421 while {
1422 let character = self.input.look_ch();
1423 !is_breakz(character) && is_printable(character)
1424 } {
1425 owned.push(self.input.peek());
1426 self.skip_comment_char();
1427 }
1428 Cow::Owned(owned)
1429 };
1430
1431 self.ensure_current_char_is_printable()?;
1432
1433 let end_mark = self.mark;
1434 let span = Span::new(start_mark, end_mark);
1435 Ok(QueuedToken(
1436 span,
1437 QueuedTokenType::Comment(Comment { text, placement }),
1438 ))
1439 }
1440
1441 fn push_comment_token(&mut self) -> ScanResult {
1442 let token = self.scan_comment_queued_token()?;
1443 self.tokens.push_back(token);
1444 Ok(())
1445 }
1446
1447 fn consume_comment(&mut self) -> Result<bool, ScanError> {
1449 if self.options.emit_comments {
1450 self.push_comment_token()?;
1451 Ok(true)
1452 } else {
1453 self.skip_comment()?;
1454 Ok(false)
1455 }
1456 }
1457
1458 fn skip_comment(&mut self) -> ScanResult {
1459 debug_assert_eq!(self.input.peek(), '#');
1460
1461 self.skip_comment_char();
1462 let n = self.input.skip_while_non_breakz();
1463 self.mark.offsets.chars += n;
1464 self.mark.col += n;
1465 self.mark.offsets.bytes = self.input.byte_offset();
1466 self.ensure_current_char_is_printable()
1467 }
1468
1469 #[inline]
1471 #[must_use]
1472 pub fn stream_started(&self) -> bool {
1473 self.stream_start_produced
1474 }
1475
1476 #[inline]
1478 #[must_use]
1479 pub fn stream_ended(&self) -> bool {
1480 self.stream_end_produced
1481 }
1482
1483 #[inline]
1485 #[must_use]
1486 pub fn mark(&self) -> Marker {
1487 self.mark
1488 }
1489
1490 #[inline]
1492 pub(crate) fn comments_possible(&self) -> bool {
1493 self.options.emit_comments && self.comments_possible
1494 }
1495
1496 #[track_caller]
1503 #[inline]
1504 fn read_break(&mut self, s: &mut String) {
1505 self.skip_break();
1506 s.push('\n');
1507 }
1508
1509 #[track_caller]
1514 #[inline]
1515 fn skip_break(&mut self) {
1516 let c = self.input.peek();
1517 let nc = self.input.peek_nth(1);
1518 debug_assert!(is_break(c));
1519 if c == '\r' && nc == '\n' {
1520 self.skip_blank();
1521 }
1522 self.skip_nl();
1523 }
1524
1525 #[track_caller]
1530 fn insert_token(&mut self, pos: usize, tok: Token<'input>) {
1531 let old_len = self.tokens.len();
1532 assert!(pos <= old_len);
1533 self.tokens.insert(pos, tok.into());
1534 }
1535
1536 fn simple_key_token_index(&self, sk: &SimpleKey, mark: Marker) -> Result<usize, ScanError> {
1537 let Some(index) = sk.token_number.checked_sub(self.tokens_parsed) else {
1538 return Err(ScanError::from_kind(mark, ErrorKind::InvalidSimpleKey));
1539 };
1540 if index > self.tokens.len() {
1541 return Err(ScanError::from_kind(mark, ErrorKind::InvalidSimpleKey));
1542 }
1543 Ok(index)
1544 }
1545
1546 #[inline]
1547 fn allow_simple_key(&mut self) {
1548 self.simple_key_allowed = true;
1549 }
1550
1551 #[inline]
1552 fn disallow_simple_key(&mut self) {
1553 self.simple_key_allowed = false;
1554 }
1555
1556 fn fetch_next_token(&mut self) -> ScanResult {
1561 let result = self.fetch_next_token_impl();
1562 if let Some(kind) = self.input.take_source_error() {
1563 return Err(ScanError::from_kind(self.mark, kind));
1564 }
1565 result
1566 }
1567
1568 fn fetch_next_token_impl(&mut self) -> ScanResult {
1569 self.input.lookahead(1);
1570
1571 if !self.stream_start_produced {
1572 self.fetch_stream_start();
1573 return Ok(());
1574 }
1575 loop {
1576 let outcome = self.skip_to_next_token()?;
1577 if outcome.queued_comment || (outcome.saw_comment && !self.tokens.is_empty()) {
1578 return Ok(());
1579 }
1580 if !outcome.saw_comment {
1581 break;
1582 }
1583 }
1586
1587 debug_print!(
1588 " \x1B[38;5;244m\u{2192} fetch_next_token after whitespace {:?} {:?}\x1B[m",
1589 self.mark,
1590 self.input.peek()
1591 );
1592
1593 self.stale_simple_keys()?;
1594
1595 let mark = self.mark;
1596 self.unroll_indent(mark.col as isize);
1597
1598 self.input.lookahead(4);
1599
1600 if self.input.next_is_z() {
1601 self.fetch_stream_end()?;
1602 return Ok(());
1603 }
1604
1605 self.ensure_current_char_is_printable()?;
1606
1607 if self.mark.col == 0 {
1608 if self.input.next_char_is('%') {
1609 return self.fetch_directive();
1610 } else if self.input.next_is_document_start() {
1611 return self.fetch_document_indicator(TokenType::DocumentStart);
1612 } else if self.input.next_is_document_end() {
1613 self.fetch_document_indicator(TokenType::DocumentEnd)?;
1614 self.skip_ws_to_eol(SkipTabs::Yes)?;
1615 if !self.input.next_is_breakz() {
1616 return Err(self.scan_error(ErrorKind::InvalidDocumentEnd));
1617 }
1618 return Ok(());
1619 }
1620 }
1621
1622 if self.document_prefix_allowed {
1623 self.document_prefix_allowed = false;
1624 }
1625
1626 let c = self.input.peek();
1629 let nc = self.input.peek_nth(1);
1630 match c {
1631 '[' => self.fetch_flow_collection_start(TokenType::FlowSequenceStart),
1632 '{' => self.fetch_flow_collection_start(TokenType::FlowMappingStart),
1633 ']' => self.fetch_flow_collection_end(TokenType::FlowSequenceEnd, '[', ']'),
1634 '}' => self.fetch_flow_collection_end(TokenType::FlowMappingEnd, '{', '}'),
1635 ',' => self.fetch_flow_entry(),
1636 '-' if is_blank_or_breakz(nc) => self.fetch_block_entry(),
1637 '?' if is_blank_or_breakz(nc) => self.fetch_key(),
1638 ':' if is_blank_or_breakz(nc) => self.fetch_value(),
1639 ':' if self.flow_level > 0
1640 && (is_flow(nc) || self.mark.index() == self.adjacent_value_allowed_at) =>
1641 {
1642 self.fetch_flow_value()
1643 }
1644 '*' => self.fetch_anchor(true),
1646 '&' => self.fetch_anchor(false),
1648 '!' => self.fetch_tag(),
1649 '|' if self.flow_level == 0 => self.fetch_block_scalar(true),
1651 '>' if self.flow_level == 0 => self.fetch_block_scalar(false),
1653 '\'' => self.fetch_flow_scalar(true),
1654 '"' => self.fetch_flow_scalar(false),
1655 '-' if !is_blank_or_breakz(nc) => self.fetch_plain_scalar(),
1657 ':' | '?' if !is_blank_or_breakz(nc) && self.flow_level == 0 => {
1658 self.fetch_plain_scalar()
1659 }
1660 c if is_bom(c) => Err(self.scan_error(ErrorKind::BomInsideDocument)),
1661 '|' | '>' | '%' | '@' | '`' => {
1662 Err(self.scan_error(ErrorKind::UnexpectedCharacter { character: c }))
1663 }
1664 _ => self.fetch_plain_scalar(),
1665 }
1666 }
1667
1668 pub(crate) fn next_queued_token(&mut self) -> Result<Option<QueuedToken<'input>>, ScanError> {
1673 if self.deferred_error.is_some() {
1674 if !matches!(
1675 self.tokens.front().map(|token| &token.1),
1676 Some(QueuedTokenType::Comment(_))
1677 ) {
1678 if let Some(error) = self.deferred_error.take() {
1679 return error.into_result();
1680 }
1681 }
1682 self.token_available = true;
1683 }
1684
1685 if self.stream_end_produced {
1686 return Ok(None);
1687 }
1688
1689 if !self.token_available {
1690 if let Err(error) = self.fetch_more_tokens() {
1691 if matches!(
1692 self.tokens.front().map(|token| &token.1),
1693 Some(QueuedTokenType::Comment(_))
1694 ) {
1695 self.deferred_error = Some(error);
1696 } else {
1697 return Err(error);
1698 }
1699 }
1700 }
1701 let Some(t) = self.tokens.pop_front() else {
1702 unreachable!("fetch_more_tokens succeeded without producing a token")
1703 };
1704 self.token_available = false;
1705 self.tokens_parsed += 1;
1706
1707 let is_stream_end = matches!(t.1, QueuedTokenType::StreamEnd);
1708 if is_stream_end {
1709 self.stream_end_produced = true;
1710 }
1711 Ok(Some(t))
1712 }
1713
1714 fn next_token(&mut self) -> Result<Option<Token<'input>>, ScanError> {
1719 Ok(self.next_queued_token()?.map(QueuedToken::into_public))
1720 }
1721
1722 fn fetch_more_tokens(&mut self) -> ScanResult {
1727 let mut need_more;
1728 loop {
1729 if self.tokens.is_empty() {
1730 need_more = true;
1731 } else {
1732 need_more = false;
1733 self.stale_simple_keys()?;
1735 if !matches!(
1736 self.tokens.front().map(|token| &token.1),
1737 Some(QueuedTokenType::Comment(_))
1738 ) {
1739 need_more = self.first_simple_key.is_some_and(|index| {
1741 self.simple_keys[index].token_number == self.tokens_parsed
1742 });
1743 }
1744 }
1745
1746 if let Some(token) = self.tokens.back() {
1749 if matches!(
1750 token.1,
1751 QueuedTokenType::DocumentEnd | QueuedTokenType::DocumentStart
1752 ) {
1753 break;
1754 }
1755 }
1756
1757 if !need_more {
1758 break;
1759 }
1760 self.fetch_next_token()?;
1761 }
1762 self.token_available = true;
1763
1764 Ok(())
1765 }
1766
1767 fn stale_simple_keys(&mut self) -> ScanResult {
1776 while let Some(index) = self.first_simple_key {
1777 let sk = &mut self.simple_keys[index];
1778 debug_assert!(sk.possible);
1779 let is_line_stale = self.flow_level == 0 && sk.mark.line < self.mark.line;
1780 let is_length_stale = self.mark.index().saturating_sub(sk.mark.index())
1783 > self.options.simple_key_max_lookahead;
1784
1785 if !is_line_stale && !is_length_stale {
1786 break;
1788 }
1789 if sk.required {
1790 return Err(Self::simple_key_expected(sk.mark));
1791 }
1792 sk.possible = false;
1793 self.first_simple_key = self.simple_keys[index + 1..]
1796 .iter()
1797 .position(|key| key.possible)
1798 .map(|offset| index + 1 + offset);
1799 }
1800 Ok(())
1801 }
1802
1803 fn skip_to_next_token(&mut self) -> Result<SkipToNextTokenOutcome, ScanError> {
1814 let consume_linebreak = |this: &mut Self| {
1817 this.input.lookahead(2);
1818 this.skip_linebreak();
1819 if this.flow_level == 0 {
1820 this.allow_simple_key();
1821 }
1822 };
1823
1824 let mut outcome = SkipToNextTokenOutcome::default();
1825 loop {
1826 let ch = self.input.look_ch();
1827 if self.explicit_key_tab_check_pending {
1828 match ch {
1829 '\t' => {
1830 return Err(self.scan_error(ErrorKind::TabNotAllowed));
1831 }
1832 ' ' | '\n' | '\r' | '#' => {}
1833 _ => self.explicit_key_tab_check_pending = false,
1834 }
1835 }
1836
1837 match ch {
1838 '\t' => {
1840 if self.is_within_block()
1841 && self.leading_whitespace
1842 && (self.mark.col as isize) < self.indent
1843 {
1844 self.skip_ws_to_eol(SkipTabs::Yes)?;
1845
1846 if !self.input.next_is_breakz() {
1848 return Err(self.scan_error(ErrorKind::TabInBlockIndentation));
1849 }
1850
1851 if matches!(self.input.look_ch(), '\n' | '\r') {
1853 consume_linebreak(self);
1854 }
1855 } else {
1856 self.skip_blank();
1858 }
1859 }
1860
1861 ' ' => self.skip_blank(),
1862
1863 '\n' | '\r' => consume_linebreak(self),
1864
1865 c if is_bom(c)
1866 && self.document_prefix_allowed
1867 && self.flow_level == 0
1868 && self.mark.col == 0 =>
1869 {
1870 self.skip_bom();
1871 }
1872
1873 '#' => {
1874 outcome.saw_comment = true;
1875 let queued = self.consume_comment()?;
1876 outcome.queued_comment |= queued;
1877
1878 if matches!(self.input.look_ch(), '\n' | '\r') {
1880 consume_linebreak(self);
1881 }
1882 return Ok(outcome);
1883 }
1884
1885 _ => break,
1886 }
1887 }
1888
1889 if let Some(err_mark) = self.interrupted_plain_by_comment.take() {
1892 let is_immediate_next_line = self.mark.line == err_mark.line + 1;
1896
1897 if self.flow_level == 0
1899 && is_immediate_next_line
1900 && (self.mark.col as isize) > self.indent
1901 {
1902 self.input.lookahead(4);
1906
1907 if !self.input.next_is_z()
1908 && !self.input.next_is_document_indicator()
1909 && self.input.next_can_be_plain_scalar(false)
1910 {
1911 return Err(ScanError::from_kind(
1912 err_mark,
1913 ErrorKind::CommentInterceptedScalar,
1914 ));
1915 }
1916 }
1917 }
1918
1919 Ok(outcome)
1920 }
1921
1922 fn skip_yaml_whitespace(&mut self) -> Result<bool, ScanError> {
1930 let mut need_whitespace = true;
1931 loop {
1932 match self.input.look_ch() {
1933 ' ' => {
1934 self.skip_blank();
1935
1936 need_whitespace = false;
1937 }
1938 '\n' | '\r' => {
1939 self.input.lookahead(2);
1940 self.skip_linebreak();
1941 if self.flow_level == 0 {
1942 self.allow_simple_key();
1943 }
1944 need_whitespace = false;
1945 }
1946 '#' => {
1947 if need_whitespace {
1948 self.skip_comment()?;
1949 } else {
1950 self.consume_comment()?;
1951 return Ok(true);
1952 }
1953 }
1954 _ => break,
1955 }
1956 }
1957
1958 if need_whitespace {
1959 Err(self.scan_error(ErrorKind::ExpectedWhitespace))
1960 } else {
1961 Ok(false)
1962 }
1963 }
1964
1965 #[track_caller]
1970 fn skip_ws_to_eol(&mut self, skip_tabs: SkipTabs) -> Result<SkipTabs, ScanError> {
1971 debug_assert!(!matches!(skip_tabs, SkipTabs::Result(..)));
1972
1973 if !self.comments_possible {
1974 let (chars_consumed, result) = self.input.skip_ws_to_eol(skip_tabs);
1975 self.mark.col += chars_consumed;
1976 self.mark.offsets.chars += chars_consumed;
1977 self.mark.offsets.bytes = self.input.byte_offset();
1978 return result.map_err(|kind| self.scan_error(kind));
1979 }
1980
1981 let (chars_consumed, whitespace) = self.input.skip_ws_to_eol_blanks(skip_tabs);
1982 self.mark.col += chars_consumed;
1983 self.mark.offsets.chars += chars_consumed;
1984 self.mark.offsets.bytes = self.input.byte_offset();
1985
1986 if self.input.look_ch() != '#' {
1987 return Ok(whitespace);
1988 }
1989
1990 if !whitespace.found_tabs() && !whitespace.has_valid_yaml_ws() {
1991 return Err(self.scan_error(ErrorKind::CommentNotSeparated));
1992 }
1993
1994 self.consume_comment()?;
1995 Ok(whitespace)
1996 }
1997
1998 fn fetch_stream_start(&mut self) {
1999 let mark = self.mark;
2000 self.indent = -1;
2001 self.stream_start_produced = true;
2002 self.allow_simple_key();
2003 self.tokens
2004 .push_back(Token(Span::empty(mark), TokenType::StreamStart).into());
2005 self.simple_keys.push(SimpleKey::new(Marker::new(0, 0, 0)));
2006 }
2007
2008 fn fetch_stream_end(&mut self) -> ScanResult {
2009 if self.mark.col != 0 {
2011 self.mark.col = 0;
2012 self.mark.line += 1;
2013 }
2014
2015 if let Some((mark, bracket)) = self.flow_markers.pop() {
2016 return Err(Self::unclosed_bracket(mark, bracket));
2017 }
2018
2019 for sk in &mut self.simple_keys {
2022 if sk.required && sk.possible {
2023 return Err(Self::simple_key_expected(sk.mark));
2024 }
2025 sk.possible = false;
2026 }
2027 self.first_simple_key = None;
2028
2029 self.unroll_indent(-1);
2030 self.remove_simple_key()?;
2031 self.disallow_simple_key();
2032
2033 self.tokens
2034 .push_back(Token(Span::empty(self.mark), TokenType::StreamEnd).into());
2035 Ok(())
2036 }
2037
2038 fn fetch_directive(&mut self) -> ScanResult {
2039 self.unroll_indent(-1);
2040 self.remove_simple_key()?;
2041
2042 self.disallow_simple_key();
2043
2044 let token_index = self.tokens.len();
2045 let tok = self.scan_directive()?;
2046 self.insert_token(token_index, tok);
2047
2048 Ok(())
2049 }
2050
2051 fn scan_directive(&mut self) -> Result<Token<'input>, ScanError> {
2052 let start_mark = self.mark;
2053 self.skip_non_blank();
2054
2055 let mut budget = self.options.max_directive_bytes;
2058
2059 let name = self.scan_directive_name(&mut budget)?;
2060 let mut separated_comment = false;
2061 let tok = match name.as_ref() {
2062 "YAML" => self.scan_version_directive_value(&start_mark)?,
2063 "TAG" => self.scan_tag_directive_value(&start_mark, &mut budget)?,
2064 _ => {
2065 let mut params = Vec::new();
2066 while self.input.next_is_blank() {
2067 let n_blanks = self.input.skip_while_blank();
2068 self.mark.offsets.chars += n_blanks;
2069 self.mark.col += n_blanks;
2070 self.mark.offsets.bytes = self.input.byte_offset();
2071
2072 if self.input.peek() == '#' {
2073 separated_comment = true;
2074 break;
2075 }
2076
2077 if !is_blank_or_breakz(self.input.peek()) {
2078 self.spend_directive_bytes(&mut budget, n_blanks, start_mark)?;
2079 if params.len() >= self.options.max_reserved_directive_params {
2080 return Err(ScanError::from_kind(
2081 start_mark,
2082 ErrorKind::TooManyReservedDirectiveParams {
2083 limit: self.options.max_reserved_directive_params,
2084 },
2085 ));
2086 }
2087
2088 let mut param = String::new();
2089 let Some(n_chars) = self.fetch_directive_word(&mut param, &mut budget)
2090 else {
2091 return Err(self.directive_byte_limit(start_mark));
2092 };
2093 self.mark.offsets.chars += n_chars;
2094 self.mark.col += n_chars;
2095 self.mark.offsets.bytes = self.input.byte_offset();
2096 params.push(param);
2097 }
2098 }
2099
2100 Token(
2101 Span::new(start_mark, self.mark),
2102 TokenType::ReservedDirective(name, params),
2103 )
2104 }
2105 };
2106
2107 if separated_comment {
2108 self.consume_comment()?;
2110 } else {
2111 self.skip_ws_to_eol(SkipTabs::Yes)?;
2112 }
2113
2114 if self.input.next_is_breakz() {
2115 self.input.lookahead(2);
2116 self.skip_linebreak();
2117 Ok(tok)
2118 } else {
2119 Err(ScanError::from_kind(
2120 start_mark,
2121 ErrorKind::InvalidDirectiveTerminator,
2122 ))
2123 }
2124 }
2125
2126 fn scan_version_directive_value(&mut self, mark: &Marker) -> Result<Token<'input>, ScanError> {
2127 let n_blanks = self.input.skip_while_blank();
2128 self.mark.offsets.chars += n_blanks;
2129 self.mark.col += n_blanks;
2130 self.mark.offsets.bytes = self.input.byte_offset();
2131
2132 let major = self.scan_version_directive_number(mark)?;
2133
2134 if self.input.peek() != '.' {
2135 return Err(ScanError::from_kind(
2136 *mark,
2137 ErrorKind::MissingYamlVersionSeparator,
2138 ));
2139 }
2140 self.skip_non_blank();
2141
2142 let minor = self.scan_version_directive_number(mark)?;
2143
2144 Ok(Token(
2145 Span::new(*mark, self.mark),
2146 TokenType::VersionDirective(major, minor),
2147 ))
2148 }
2149
2150 fn directive_byte_limit(&self, start_mark: Marker) -> ScanError {
2152 ScanError::from_kind(
2153 start_mark,
2154 ErrorKind::DirectiveByteLimitExceeded {
2155 limit: self.options.max_directive_bytes,
2156 },
2157 )
2158 }
2159
2160 fn spend_directive_bytes(
2162 &self,
2163 budget: &mut usize,
2164 bytes: usize,
2165 start_mark: Marker,
2166 ) -> ScanResult {
2167 let Some(remaining) = budget.checked_sub(bytes) else {
2168 return Err(self.directive_byte_limit(start_mark));
2169 };
2170 *budget = remaining;
2171 Ok(())
2172 }
2173
2174 fn fetch_directive_word(&mut self, out: &mut String, budget: &mut usize) -> Option<usize> {
2181 let mut n_chars = 0;
2182
2183 loop {
2184 let c = self.input.look_ch();
2185 if !is_yaml_non_space(c) || is_z(c) {
2186 break;
2187 }
2188
2189 let len = c.len_utf8();
2190 if len > *budget {
2191 return None;
2192 }
2193 *budget -= len;
2194
2195 out.push(c);
2196 self.input.skip();
2197 n_chars += 1;
2198 }
2199
2200 Some(n_chars)
2201 }
2202
2203 fn scan_directive_name(&mut self, budget: &mut usize) -> Result<String, ScanError> {
2204 let start_mark = self.mark;
2205 let mut string = String::new();
2206
2207 let Some(n_chars) = self.fetch_directive_word(&mut string, budget) else {
2208 return Err(self.directive_byte_limit(start_mark));
2209 };
2210 self.mark.offsets.chars += n_chars;
2211 self.mark.col += n_chars;
2212 self.mark.offsets.bytes = self.input.byte_offset();
2213
2214 if string.is_empty() {
2215 return Err(ScanError::from_kind(
2216 start_mark,
2217 ErrorKind::MissingDirectiveName,
2218 ));
2219 }
2220
2221 if !is_blank_or_breakz(self.input.peek()) {
2222 return Err(ScanError::from_kind(
2223 start_mark,
2224 ErrorKind::InvalidDirectiveName,
2225 ));
2226 }
2227
2228 Ok(string)
2229 }
2230
2231 fn scan_version_directive_number(&mut self, mark: &Marker) -> Result<u32, ScanError> {
2232 let mut val = 0u32;
2233 let mut length = 0usize;
2234 while let Some(digit) = self.input.look_ch().to_digit(10) {
2235 if length + 1 > 9 {
2236 return Err(ScanError::from_kind(*mark, ErrorKind::YamlVersionTooLong));
2237 }
2238 length += 1;
2239 val = val * 10 + digit;
2240 self.skip_non_blank();
2241 }
2242
2243 if length == 0 {
2244 return Err(ScanError::from_kind(*mark, ErrorKind::MissingYamlVersion));
2245 }
2246
2247 Ok(val)
2248 }
2249
2250 fn scan_tag_directive_value(
2251 &mut self,
2252 mark: &Marker,
2253 budget: &mut usize,
2254 ) -> Result<Token<'input>, ScanError> {
2255 let n_blanks = self.input.skip_while_blank();
2256 self.mark.offsets.chars += n_blanks;
2257 self.mark.col += n_blanks;
2258 self.mark.offsets.bytes = self.input.byte_offset();
2259 self.spend_directive_bytes(budget, n_blanks, *mark)?;
2260
2261 let handle = self.scan_tag_handle_directive_cow(mark, budget)?;
2262
2263 let n_blanks = self.input.skip_while_blank();
2264 self.mark.offsets.chars += n_blanks;
2265 self.mark.col += n_blanks;
2266 self.mark.offsets.bytes = self.input.byte_offset();
2267 self.spend_directive_bytes(budget, n_blanks, *mark)?;
2268
2269 let prefix = self.scan_tag_prefix_directive_cow(mark, budget)?;
2270
2271 self.input.lookahead(1);
2272
2273 if self.input.next_is_blank_or_breakz() {
2274 Ok(Token(
2275 Span::new(*mark, self.mark),
2276 TokenType::TagDirective(handle, prefix),
2277 ))
2278 } else {
2279 Err(ScanError::from_kind(
2280 *mark,
2281 ErrorKind::InvalidTagDirectiveTerminator,
2282 ))
2283 }
2284 }
2285
2286 fn fetch_tag(&mut self) -> ScanResult {
2287 self.save_simple_key();
2288 self.disallow_simple_key();
2289
2290 let tok = self.scan_tag()?;
2291 self.tokens.push_back(tok.into());
2292 Ok(())
2293 }
2294
2295 fn scan_tag(&mut self) -> Result<Token<'input>, ScanError> {
2296 let start_mark = self.mark;
2297
2298 self.input.lookahead(2);
2300
2301 if self.input.byte_offset().is_none() {
2303 return self.scan_tag_owned(&start_mark);
2304 }
2305
2306 let (handle, suffix): (Cow<'input, str>, Cow<'input, str>) = if self
2307 .input
2308 .nth_char_is(1, '<')
2309 {
2310 let suffix = self.scan_verbatim_tag(&start_mark)?;
2312 (Cow::Owned(String::new()), Cow::Owned(suffix))
2313 } else {
2314 let handle = self.scan_tag_handle_cow(&start_mark)?;
2316 if handle.len() >= 2 && handle.starts_with('!') && handle.ends_with('!') {
2318 let suffix = self.scan_tag_shorthand_suffix_cow(&start_mark, true)?;
2320 (handle, suffix)
2321 } else {
2322 let remaining_suffix = self.scan_tag_shorthand_suffix_cow(&start_mark, false)?;
2327
2328 let suffix = self.combine_local_tag_suffix(&start_mark, handle, remaining_suffix);
2329
2330 if suffix.is_empty() {
2333 (Cow::Borrowed(""), Cow::Borrowed("!"))
2334 } else {
2335 (Cow::Borrowed("!"), suffix)
2336 }
2337 }
2338 };
2339
2340 if is_blank_or_breakz(self.input.look_ch())
2341 || (self.flow_level > 0 && matches!(self.input.peek(), ',' | ']' | '}'))
2342 {
2343 Ok(Token(
2346 Span::new(start_mark, self.mark),
2347 TokenType::Tag(handle, suffix),
2348 ))
2349 } else {
2350 Err(ScanError::from_kind(
2351 start_mark,
2352 ErrorKind::InvalidTagTerminator,
2353 ))
2354 }
2355 }
2356
2357 fn combine_local_tag_suffix(
2358 &self,
2359 start_mark: &Marker,
2360 handle: Cow<'input, str>,
2361 remaining: Cow<'input, str>,
2362 ) -> Cow<'input, str> {
2363 if handle.len() == 1 {
2364 return remaining;
2365 }
2366
2367 match (handle, remaining) {
2368 (Cow::Borrowed(handle), Cow::Borrowed(remaining)) => {
2369 if remaining.is_empty() {
2370 return Cow::Borrowed(&handle[1..]);
2371 }
2372
2373 let borrowed = start_mark
2374 .byte_offset()
2375 .and_then(|start| start.checked_add(1))
2376 .zip(self.input.byte_offset())
2377 .and_then(|(start, end)| self.try_borrow_slice(start, end));
2378 borrowed.map_or_else(
2379 || {
2380 let mut combined =
2381 String::with_capacity(handle.len() - 1 + remaining.len());
2382 combined.push_str(&handle[1..]);
2383 combined.push_str(remaining);
2384 Cow::Owned(combined)
2385 },
2386 Cow::Borrowed,
2387 )
2388 }
2389 (Cow::Borrowed(handle), Cow::Owned(mut remaining)) => {
2390 remaining.reserve(handle.len() - 1);
2391 remaining.insert_str(0, &handle[1..]);
2392 Cow::Owned(remaining)
2393 }
2394 (Cow::Owned(mut handle), remaining) => {
2395 handle.remove(0);
2396 handle.push_str(&remaining);
2397 Cow::Owned(handle)
2398 }
2399 }
2400 }
2401
2402 fn scan_tag_owned(&mut self, start_mark: &Marker) -> Result<Token<'input>, ScanError> {
2404 let mut handle = String::new();
2405 let mut suffix;
2406
2407 if self.input.nth_char_is(1, '<') {
2408 suffix = self.scan_verbatim_tag(start_mark)?;
2409 } else {
2410 handle = self.scan_tag_handle(false, start_mark)?;
2412 if handle.len() >= 2 && handle.starts_with('!') && handle.ends_with('!') {
2414 let is_secondary_handle = handle == "!!";
2416 suffix =
2417 self.scan_tag_shorthand_suffix(false, is_secondary_handle, "", start_mark)?;
2418 } else {
2419 suffix = self.scan_tag_shorthand_suffix(false, false, &handle, start_mark)?;
2420 "!".clone_into(&mut handle);
2421 if suffix.is_empty() {
2424 handle.clear();
2425 "!".clone_into(&mut suffix);
2426 }
2427 }
2428 }
2429
2430 if is_blank_or_breakz(self.input.look_ch())
2431 || (self.flow_level > 0 && matches!(self.input.peek(), ',' | ']' | '}'))
2432 {
2433 Ok(Token(
2436 Span::new(*start_mark, self.mark),
2437 TokenType::Tag(handle.into(), suffix.into()),
2438 ))
2439 } else {
2440 Err(ScanError::from_kind(
2441 *start_mark,
2442 ErrorKind::InvalidTagTerminator,
2443 ))
2444 }
2445 }
2446
2447 fn scan_tag_handle_cow(&mut self, mark: &Marker) -> Result<Cow<'input, str>, ScanError> {
2452 let Some(start) = self.input.byte_offset() else {
2453 return Ok(Cow::Owned(self.scan_tag_handle(false, mark)?));
2454 };
2455
2456 if self.input.look_ch() != '!' {
2457 return Err(ScanError::from_kind(*mark, ErrorKind::ExpectedTagBang));
2458 }
2459
2460 self.skip_non_blank();
2462
2463 self.input.lookahead(1);
2465 while self.input.next_is_alpha() {
2466 self.skip_non_blank();
2467 self.input.lookahead(1);
2468 }
2469
2470 if self.input.peek() == '!' {
2472 self.skip_non_blank();
2473 }
2474
2475 let Some(end) = self.input.byte_offset() else {
2476 return Ok(Cow::Owned(self.scan_tag_handle(false, mark)?));
2477 };
2478
2479 if let Some(slice) = self.try_borrow_slice(start, end) {
2480 Ok(Cow::Borrowed(slice))
2481 } else {
2482 let slice = self
2483 .input
2484 .slice_bytes(start, end)
2485 .ok_or_else(|| ScanError::from_kind(*mark, ErrorKind::InputSlicingUnavailable))?;
2486 Ok(Cow::Owned(slice.to_owned()))
2487 }
2488 }
2489
2490 fn scan_tag_shorthand_suffix_cow(
2494 &mut self,
2495 mark: &Marker,
2496 require_non_empty: bool,
2497 ) -> Result<Cow<'input, str>, ScanError> {
2498 let Some(start) = self.input.byte_offset() else {
2499 return Ok(Cow::Owned(
2500 self.scan_tag_shorthand_suffix(false, false, "", mark)?,
2501 ));
2502 };
2503
2504 while is_tag_char(self.input.look_ch()) {
2506 if self.input.peek() == '%' {
2507 let current = self
2509 .input
2510 .byte_offset()
2511 .expect("byte_offset() must remain available once enabled");
2512 let mut out = if let Some(slice) = self.input.slice_bytes(start, current) {
2513 slice.to_owned()
2514 } else {
2515 String::new()
2516 };
2517
2518 while is_tag_char(self.input.look_ch()) {
2520 if self.input.peek() == '%' {
2521 out.push(self.scan_uri_escapes(mark)?);
2522 } else {
2523 out.push(self.input.peek());
2524 self.skip_non_blank();
2525 }
2526 }
2527 return Ok(Cow::Owned(out));
2528 }
2529 self.skip_non_blank();
2530 }
2531
2532 let Some(end) = self.input.byte_offset() else {
2533 return Ok(Cow::Owned(
2534 self.scan_tag_shorthand_suffix(false, false, "", mark)?,
2535 ));
2536 };
2537
2538 if require_non_empty && start == end {
2539 return Err(ScanError::from_kind(*mark, ErrorKind::MissingTagUri));
2540 }
2541
2542 if let Some(slice) = self.try_borrow_slice(start, end) {
2543 Ok(Cow::Borrowed(slice))
2544 } else {
2545 let slice = self
2546 .input
2547 .slice_bytes(start, end)
2548 .ok_or_else(|| ScanError::from_kind(*mark, ErrorKind::InputSlicingUnavailable))?;
2549 Ok(Cow::Owned(slice.to_owned()))
2550 }
2551 }
2552
2553 fn scan_tag_handle(&mut self, directive: bool, mark: &Marker) -> Result<String, ScanError> {
2554 let mut string = String::new();
2555 if self.input.look_ch() != '!' {
2556 return Err(ScanError::from_kind(*mark, ErrorKind::ExpectedTagBang));
2557 }
2558
2559 string.push(self.input.peek());
2560 self.skip_non_blank();
2561
2562 let n_chars = self.input.fetch_while_is_alpha(&mut string);
2563 self.mark.offsets.chars += n_chars;
2564 self.mark.col += n_chars;
2565 self.mark.offsets.bytes = self.input.byte_offset();
2566
2567 if self.input.peek() == '!' {
2569 string.push(self.input.peek());
2570 self.skip_non_blank();
2571 } else if directive && string != "!" {
2572 return Err(ScanError::from_kind(
2576 *mark,
2577 ErrorKind::ExpectedTagDirectiveBang,
2578 ));
2579 }
2580 Ok(string)
2581 }
2582
2583 fn scan_verbatim_tag(&mut self, start_mark: &Marker) -> Result<String, ScanError> {
2587 self.skip_non_blank();
2589 self.skip_non_blank();
2590
2591 let mut string = String::new();
2592 while is_uri_char(self.input.look_ch()) {
2593 if self.input.peek() == '%' {
2594 string.push(self.scan_uri_escapes(start_mark)?);
2595 } else {
2596 string.push(self.input.peek());
2597 self.skip_non_blank();
2598 }
2599 }
2600
2601 if string.is_empty() {
2602 return Err(ScanError::from_kind(*start_mark, ErrorKind::MissingTagUri));
2603 }
2604
2605 if self.input.peek() != '>' {
2606 return Err(ScanError::from_kind(
2607 *start_mark,
2608 ErrorKind::UnclosedVerbatimTag,
2609 ));
2610 }
2611 self.skip_non_blank();
2612
2613 Ok(string)
2614 }
2615
2616 fn scan_tag_shorthand_suffix(
2617 &mut self,
2618 _directive: bool,
2619 _is_secondary: bool,
2620 head: &str,
2621 mark: &Marker,
2622 ) -> Result<String, ScanError> {
2623 let mut length = head.len();
2624 let mut string = String::new();
2625
2626 if length > 1 {
2629 string.extend(head.chars().skip(1));
2630 }
2631
2632 while is_tag_char(self.input.look_ch()) {
2633 if self.input.peek() == '%' {
2635 string.push(self.scan_uri_escapes(mark)?);
2636 } else {
2637 string.push(self.input.peek());
2638 self.skip_non_blank();
2639 }
2640
2641 length += 1;
2642 }
2643
2644 if length == 0 {
2645 return Err(ScanError::from_kind(*mark, ErrorKind::MissingTagUri));
2646 }
2647
2648 Ok(string)
2649 }
2650
2651 fn scan_uri_escapes(&mut self, mark: &Marker) -> Result<char, ScanError> {
2652 self.scan_uri_escapes_with_budget(mark, None)
2653 }
2654
2655 fn scan_uri_escapes_with_budget(
2656 &mut self,
2657 mark: &Marker,
2658 mut directive_budget: Option<&mut usize>,
2659 ) -> Result<char, ScanError> {
2660 let mut width = 0usize;
2661 let mut bytes = [0u8; 4];
2662 let mut bytes_len = 0usize;
2663 loop {
2664 self.input.lookahead(3);
2665
2666 let c = self.input.peek_nth(1);
2667 let nc = self.input.peek_nth(2);
2668
2669 if !(self.input.peek() == '%' && is_hex(c) && is_hex(nc)) {
2670 return Err(ScanError::from_kind(*mark, ErrorKind::InvalidTagEscape));
2671 }
2672
2673 if let Some(budget) = directive_budget.as_deref_mut() {
2674 self.spend_directive_bytes(budget, 3, *mark)?;
2675 }
2676
2677 let byte = u8::try_from((as_hex(c) << 4) + as_hex(nc))
2678 .expect("two hex nibbles always fit in a byte");
2679 if width == 0 {
2680 width = match byte {
2681 _ if byte & 0x80 == 0x00 => 1,
2682 _ if byte & 0xE0 == 0xC0 => 2,
2683 _ if byte & 0xF0 == 0xE0 => 3,
2684 _ if byte & 0xF8 == 0xF0 => 4,
2685 _ => {
2686 return Err(ScanError::from_kind(
2687 *mark,
2688 ErrorKind::InvalidTagUtf8LeadingByte,
2689 ));
2690 }
2691 };
2692 } else if byte & 0xc0 != 0x80 {
2693 return Err(ScanError::from_kind(
2694 *mark,
2695 ErrorKind::InvalidTagUtf8TrailingByte,
2696 ));
2697 }
2698
2699 bytes[bytes_len] = byte;
2700 bytes_len += 1;
2701
2702 self.skip_n_non_blank(3);
2703
2704 width -= 1;
2705 if width == 0 {
2706 break;
2707 }
2708 }
2709
2710 let s = core::str::from_utf8(&bytes[..bytes_len])
2711 .map_err(|_| ScanError::from_kind(*mark, ErrorKind::InvalidTagUtf8))?;
2712
2713 let Some(ch) = s.chars().next() else {
2714 unreachable!("a validated URI escape cannot decode to an empty string")
2715 };
2716 Ok(ch)
2717 }
2718
2719 fn fetch_anchor(&mut self, alias: bool) -> ScanResult {
2720 self.save_simple_key();
2721 self.disallow_simple_key();
2722
2723 let tok = self.scan_anchor(alias)?;
2724
2725 self.tokens.push_back(tok.into());
2726
2727 Ok(())
2728 }
2729
2730 fn scan_anchor(&mut self, alias: bool) -> Result<Token<'input>, ScanError> {
2731 let start_mark = self.mark;
2732
2733 self.skip_non_blank();
2735
2736 if let Some(start) = self.input.byte_offset() {
2738 while is_anchor_char(self.input.look_ch()) {
2739 self.skip_non_blank();
2740 }
2741
2742 let end = self
2743 .input
2744 .byte_offset()
2745 .expect("byte_offset() must remain available once enabled");
2746
2747 if start == end {
2748 return Err(ScanError::from_kind(
2749 start_mark,
2750 ErrorKind::MissingAnchorOrAliasName,
2751 ));
2752 }
2753
2754 let cow = if let Some(slice) = self.try_borrow_slice(start, end) {
2755 Cow::Borrowed(slice)
2756 } else if let Some(slice) = self.input.slice_bytes(start, end) {
2757 Cow::Owned(slice.to_owned())
2758 } else {
2759 return Err(ScanError::from_kind(
2760 start_mark,
2761 ErrorKind::InputSlicingUnavailable,
2762 ));
2763 };
2764
2765 let tok = if alias {
2766 TokenType::Alias(cow)
2767 } else {
2768 TokenType::Anchor(cow)
2769 };
2770 return Ok(Token(Span::new(start_mark, self.mark), tok));
2771 }
2772
2773 let mut string = String::new();
2774 while is_anchor_char(self.input.look_ch()) {
2775 string.push(self.input.peek());
2776 self.skip_non_blank();
2777 }
2778
2779 if string.is_empty() {
2780 return Err(ScanError::from_kind(
2781 start_mark,
2782 ErrorKind::MissingAnchorOrAliasName,
2783 ));
2784 }
2785
2786 let tok = if alias {
2787 TokenType::Alias(string.into())
2788 } else {
2789 TokenType::Anchor(string.into())
2790 };
2791 Ok(Token(Span::new(start_mark, self.mark), tok))
2792 }
2793
2794 fn fetch_flow_collection_start(&mut self, tok: TokenType<'input>) -> ScanResult {
2795 self.save_simple_key();
2797
2798 let start_mark = self.mark;
2799 let indicator = self.input.peek();
2800 self.flow_markers.push((start_mark, indicator));
2801
2802 self.roll_one_col_indent();
2803 self.increase_flow_level()?;
2804
2805 self.allow_simple_key();
2806
2807 self.skip_non_blank();
2808 let end_mark = self.mark;
2809
2810 if tok == TokenType::FlowMappingStart {
2811 self.flow_mapping_started.push(true);
2812 } else {
2813 self.flow_mapping_started.push(false);
2814 self.implicit_flow_mapping_states
2815 .push(ImplicitMappingState::Possible);
2816 }
2817
2818 let token_index = self.tokens.len();
2819 self.skip_ws_to_eol(SkipTabs::Yes)?;
2820
2821 self.insert_token(token_index, Token(Span::new(start_mark, end_mark), tok));
2822 Ok(())
2823 }
2824
2825 fn fetch_flow_collection_end(
2826 &mut self,
2827 tok: TokenType<'input>,
2828 expected_open: char,
2829 actual_close: char,
2830 ) -> ScanResult {
2831 if self.flow_level == 0 {
2833 return Err(self.scan_error(ErrorKind::MisplacedFlowCollectionEnd));
2834 }
2835
2836 let Some((open_mark, open_ch)) = self.flow_markers.pop() else {
2837 return Err(self.scan_error(ErrorKind::MisplacedFlowCollectionEnd));
2838 };
2839
2840 if open_ch != expected_open {
2841 return Err(ScanError::from_kind(
2842 open_mark,
2843 ErrorKind::MismatchedFlowCollectionEnd {
2844 open: open_ch,
2845 close: actual_close,
2846 },
2847 ));
2848 }
2849
2850 let flow_level = self.flow_level;
2851
2852 self.remove_simple_key()?;
2853
2854 if matches!(tok, TokenType::FlowSequenceEnd) {
2855 self.end_implicit_mapping(self.mark, flow_level);
2856 self.implicit_flow_mapping_states.pop();
2858 }
2859 self.flow_mapping_started.pop();
2860
2861 self.decrease_flow_level();
2862
2863 self.disallow_simple_key();
2864
2865 let start_mark = self.mark;
2866 self.skip_non_blank();
2867 let end_mark = self.mark;
2868 let token_index = self.tokens.len();
2869 self.skip_ws_to_eol(SkipTabs::Yes)?;
2870
2871 if self.flow_level > 0 {
2877 self.adjacent_value_allowed_at = self.mark.index();
2878 }
2879
2880 self.insert_token(token_index, Token(Span::new(start_mark, end_mark), tok));
2881 Ok(())
2882 }
2883
2884 fn fetch_flow_entry(&mut self) -> ScanResult {
2886 self.remove_simple_key()?;
2887 self.allow_simple_key();
2888
2889 self.end_implicit_mapping(self.mark, self.flow_level);
2890 if self.current_flow_collection_is_sequence() {
2891 self.set_current_flow_mapping_started(false);
2892 }
2893
2894 let start_mark = self.mark;
2895 self.skip_non_blank();
2896 let end_mark = self.mark;
2897 let token_index = self.tokens.len();
2898 self.skip_ws_to_eol(SkipTabs::Yes)?;
2899
2900 self.insert_token(
2901 token_index,
2902 Token(Span::new(start_mark, end_mark), TokenType::FlowEntry),
2903 );
2904 Ok(())
2905 }
2906
2907 fn increase_flow_level(&mut self) -> ScanResult {
2908 if self.flow_level >= self.options.flow_nesting_limit {
2909 return Err(self.scan_error(ErrorKind::RecursionLimitExceeded));
2910 }
2911
2912 self.simple_keys.push(SimpleKey::new(Marker::new(0, 0, 0)));
2913 self.flow_level += 1;
2914 Ok(())
2915 }
2916
2917 fn decrease_flow_level(&mut self) {
2918 if self.flow_level > 0 {
2919 debug_assert!(!self.simple_keys.last().unwrap().possible);
2921 debug_assert_ne!(self.first_simple_key, Some(self.flow_level));
2922 self.flow_level -= 1;
2923 self.simple_keys.pop().unwrap();
2924 }
2925 }
2926
2927 fn fetch_block_entry(&mut self) -> ScanResult {
2933 if self.flow_level > 0 {
2934 return Err(self.scan_error(ErrorKind::BlockEntryInFlowCollection));
2936 }
2937 if !self.simple_key_allowed {
2939 return Err(self.scan_error(ErrorKind::BlockSequenceEntryNotAllowed));
2940 }
2941
2942 let mark = self.mark;
2944 self.skip_non_blank();
2945
2946 self.roll_indent(mark.col, None, TokenType::BlockSequenceStart, mark)?;
2948 let token_index = self.tokens.len();
2949 let found_tabs = self.skip_ws_to_eol(SkipTabs::Yes)?.found_tabs();
2950 self.input.lookahead(2);
2951 if found_tabs && self.input.next_char_is('-') && is_blank_or_breakz(self.input.peek_nth(1))
2952 {
2953 return Err(self.scan_error(ErrorKind::InvalidBlockEntryWhitespace));
2954 }
2955
2956 self.skip_ws_to_eol(SkipTabs::No)?;
2957 self.input.lookahead(1);
2958 if self.input.next_is_break() || self.input.next_is_flow() {
2959 self.roll_one_col_indent();
2960 }
2961
2962 self.remove_simple_key()?;
2963 self.allow_simple_key();
2964
2965 self.insert_token(
2966 token_index,
2967 Token(Span::empty(self.mark), TokenType::BlockEntry),
2968 );
2969
2970 Ok(())
2971 }
2972
2973 fn fetch_document_indicator(&mut self, t: TokenType<'input>) -> ScanResult {
2974 if let Some((mark, bracket)) = self.flow_markers.pop() {
2975 return Err(ScanError::from_kind(
2976 mark,
2977 ErrorKind::UnclosedFlowCollection { open: bracket },
2978 ));
2979 }
2980
2981 self.unroll_indent(-1);
2982 self.remove_simple_key()?;
2983 self.disallow_simple_key();
2984
2985 let mark = self.mark;
2986
2987 self.skip_n_non_blank(3);
2988
2989 self.document_prefix_allowed = matches!(t, TokenType::DocumentEnd);
2990 self.tokens
2991 .push_back(Token(Span::new(mark, self.mark), t).into());
2992 Ok(())
2993 }
2994
2995 fn fetch_block_scalar(&mut self, literal: bool) -> ScanResult {
2996 self.save_simple_key();
2997 self.allow_simple_key();
2998 let token_index = self.tokens.len();
2999 let tok = self.scan_block_scalar(literal)?;
3000
3001 self.insert_token(token_index, tok);
3002 Ok(())
3003 }
3004
3005 #[allow(clippy::too_many_lines)]
3006 fn scan_block_scalar(&mut self, literal: bool) -> Result<Token<'input>, ScanError> {
3007 let start_mark = self.mark;
3008 let mut chomping = Chomping::Clip;
3009 let mut increment: usize = 0;
3010 let mut indent: usize = 0;
3011 let mut trailing_blank: bool;
3012 let mut leading_blank: bool = false;
3013 let style = if literal {
3014 ScalarStyle::Literal
3015 } else {
3016 ScalarStyle::Folded
3017 };
3018
3019 let mut string = String::new();
3020 let mut leading_break = String::new();
3021 let mut trailing_breaks = String::new();
3022 let mut chomping_break = String::new();
3023
3024 self.skip_non_blank();
3026 self.unroll_non_block_indents();
3027
3028 if self.input.look_ch() == '+' || self.input.peek() == '-' {
3029 if self.input.peek() == '+' {
3030 chomping = Chomping::Keep;
3031 } else {
3032 chomping = Chomping::Strip;
3033 }
3034 self.skip_non_blank();
3035 self.input.lookahead(1);
3036 if self.input.next_is_digit() {
3037 if self.input.peek() == '0' {
3038 return Err(ScanError::from_kind(
3039 start_mark,
3040 ErrorKind::ZeroBlockScalarIndent,
3041 ));
3042 }
3043 increment = (self.input.peek() as usize) - ('0' as usize);
3044 self.skip_non_blank();
3045 }
3046 } else if self.input.next_is_digit() {
3047 if self.input.peek() == '0' {
3048 return Err(ScanError::from_kind(
3049 start_mark,
3050 ErrorKind::ZeroBlockScalarIndent,
3051 ));
3052 }
3053
3054 increment = (self.input.peek() as usize) - ('0' as usize);
3055 self.skip_non_blank();
3056 self.input.lookahead(1);
3057 if self.input.peek() == '+' || self.input.peek() == '-' {
3058 if self.input.peek() == '+' {
3059 chomping = Chomping::Keep;
3060 } else {
3061 chomping = Chomping::Strip;
3062 }
3063 self.skip_non_blank();
3064 }
3065 }
3066
3067 self.skip_ws_to_eol(SkipTabs::Yes)?;
3068
3069 self.input.lookahead(1);
3071 self.ensure_current_char_is_printable()?;
3072 if !self.input.next_is_breakz() {
3073 return Err(ScanError::from_kind(
3074 start_mark,
3075 ErrorKind::InvalidBlockScalarHeader,
3076 ));
3077 }
3078
3079 if self.input.next_is_break() {
3080 self.input.lookahead(2);
3081 self.read_break(&mut chomping_break);
3082 }
3083
3084 self.ensure_current_char_is_printable()?;
3085
3086 if self.input.look_ch() == '\t' {
3087 return Err(ScanError::from_kind(
3088 start_mark,
3089 ErrorKind::TabAtBlockScalarStart,
3090 ));
3091 }
3092
3093 if increment > 0 {
3094 indent = if self.indent >= 0 {
3095 (self.indent + increment as isize) as usize
3096 } else {
3097 increment
3098 }
3099 }
3100
3101 if indent == 0 {
3103 self.skip_block_scalar_first_line_indent(&mut indent, &mut trailing_breaks);
3104 } else {
3105 self.skip_block_scalar_indent(indent, &mut trailing_breaks);
3106 }
3107
3108 self.ensure_current_char_is_printable()?;
3109
3110 if self.input.next_is_z() {
3115 let contents = match chomping {
3116 Chomping::Strip => String::new(),
3118 _ if self.mark.line == start_mark.line() => String::new(),
3120 Chomping::Clip => String::new(),
3122 Chomping::Keep if trailing_breaks.is_empty() && self.mark.col > 0 => chomping_break,
3124 Chomping::Keep => trailing_breaks,
3126 };
3127
3128 return Ok(Token(
3129 Span::new(start_mark, self.mark),
3130 TokenType::Scalar(style, contents.into()),
3131 ));
3132 }
3133
3134 if self.mark.col < indent && (self.mark.col as isize) > self.indent {
3135 if self.indent < 0 && self.mark.col == 0 {
3136 self.input.lookahead(4);
3137 if self.input.next_is_document_start()
3138 || self.input.next_is_document_end()
3139 || self.input.peek() == '#'
3140 {
3141 } else {
3145 return Err(self.scan_error(ErrorKind::InvalidBlockScalarIndent));
3146 }
3147 } else {
3148 return Err(self.scan_error(ErrorKind::InvalidBlockScalarIndent));
3149 }
3150 }
3151
3152 let start_mark = self.mark;
3153 while self.mark.col == indent && !self.input.next_is_z() {
3154 self.ensure_current_char_is_printable()?;
3155
3156 if indent == 0 {
3157 self.input.lookahead(4);
3158 if self.input.next_is_document_indicator() {
3159 break;
3160 }
3161 }
3162
3163 trailing_blank = self.input.next_is_blank();
3165 if !literal && !leading_break.is_empty() && !leading_blank && !trailing_blank {
3166 string.push_str(&trailing_breaks);
3167 if trailing_breaks.is_empty() {
3168 string.push(' ');
3169 }
3170 } else {
3171 string.push_str(&leading_break);
3172 string.push_str(&trailing_breaks);
3173 }
3174
3175 leading_break.clear();
3176 trailing_breaks.clear();
3177
3178 leading_blank = self.input.next_is_blank();
3179
3180 self.scan_block_scalar_content_line(&mut string);
3181
3182 self.input.lookahead(2);
3184 if self.input.next_is_z() {
3185 break;
3186 }
3187
3188 self.ensure_current_char_is_printable()?;
3189
3190 self.read_break(&mut leading_break);
3191
3192 self.skip_block_scalar_indent(indent, &mut trailing_breaks);
3194 }
3195
3196 self.ensure_current_char_is_printable()?;
3197
3198 if chomping != Chomping::Strip {
3200 string.push_str(&leading_break);
3201 if self.input.next_is_z() && self.mark.col >= indent.max(1) {
3205 string.push('\n');
3206 }
3207 }
3208
3209 if chomping == Chomping::Keep {
3210 string.push_str(&trailing_breaks);
3211 }
3212
3213 if let Some(character) = find_non_printable(&string) {
3214 return Err(ScanError::from_kind(
3215 start_mark,
3216 ErrorKind::UnexpectedCharacter { character },
3217 ));
3218 }
3219
3220 let span = if string.trim().is_empty() {
3221 Span::new(start_mark, self.mark)
3222 } else {
3223 Span::new(start_mark, self.mark).with_indent(Some(indent))
3224 };
3225
3226 Ok(Token(span, TokenType::Scalar(style, string.into())))
3227 }
3228
3229 fn scan_block_scalar_content_line(&mut self, string: &mut String) {
3234 let chars_consumed = self.input.fetch_block_scalar_line(string);
3235 self.mark.col += chars_consumed;
3236 self.mark.offsets.chars += chars_consumed;
3237 self.mark.offsets.bytes = self.input.byte_offset();
3238 }
3241
3242 fn skip_block_scalar_indent(&mut self, indent: usize, breaks: &mut String) {
3244 loop {
3245 if indent < self.input.bufmaxlen().saturating_sub(2) {
3247 self.input.lookahead(self.input.bufmaxlen());
3248 while self.mark.col < indent && self.input.peek() == ' ' {
3249 self.skip_blank();
3250 }
3251 } else {
3252 loop {
3253 self.input.lookahead(self.input.bufmaxlen());
3254 while !self.input.buf_is_empty()
3255 && self.mark.col < indent
3256 && self.input.peek() == ' '
3257 {
3258 self.skip_blank();
3259 }
3260 if self.mark.col == indent
3264 || (!self.input.buf_is_empty() && self.input.peek() != ' ')
3265 {
3266 break;
3267 }
3268 }
3269 self.input.lookahead(2);
3270 }
3271
3272 if self.input.next_is_break() {
3274 self.read_break(breaks);
3275 } else {
3276 break;
3278 }
3279 }
3280 }
3281
3282 fn skip_block_scalar_first_line_indent(&mut self, indent: &mut usize, breaks: &mut String) {
3287 let mut max_indent = 0;
3288 loop {
3289 while self.input.look_ch() == ' ' {
3291 self.skip_blank();
3292 }
3293
3294 if self.mark.col > max_indent {
3295 max_indent = self.mark.col;
3296 }
3297
3298 if self.input.next_is_break() {
3299 self.input.lookahead(2);
3301 self.read_break(breaks);
3302 } else {
3303 break;
3305 }
3306 }
3307
3308 *indent = max_indent.max((self.indent + 1) as usize);
3317 }
3318
3319 fn fetch_flow_scalar(&mut self, single: bool) -> ScanResult {
3320 self.save_simple_key();
3321 self.disallow_simple_key();
3322
3323 let token_index = self.tokens.len();
3324 let tok = self.scan_flow_scalar(single)?;
3325
3326 if self.skip_to_next_token()?.saw_comment {
3329 self.adjacent_value_allowed_at = usize::MAX;
3330 } else {
3331 self.adjacent_value_allowed_at = self.mark.index();
3332 }
3333
3334 self.insert_token(token_index, tok);
3335 Ok(())
3336 }
3337
3338 #[allow(clippy::too_many_lines)]
3339 fn scan_flow_scalar(&mut self, single: bool) -> Result<Token<'input>, ScanError> {
3340 let start_mark = self.mark;
3341
3342 let mut buf = match self.input.byte_offset() {
3344 Some(off) => FlowScalarBuf::new_borrowed(off + self.input.peek().len_utf8()),
3345 None => FlowScalarBuf::new_owned(),
3346 };
3347
3348 let mut break_scratch = String::new();
3351
3352 self.skip_non_blank();
3354
3355 loop {
3356 self.input.lookahead(4);
3358
3359 if self.mark.col == 0 && self.input.next_is_document_indicator() {
3360 return Err(ScanError::from_kind(
3361 start_mark,
3362 ErrorKind::DocumentIndicatorInQuotedScalar,
3363 ));
3364 }
3365
3366 if self.input.next_is_z() {
3367 return Err(ScanError::from_kind(
3368 start_mark,
3369 ErrorKind::UnclosedQuotedScalar,
3370 ));
3371 }
3372
3373 self.ensure_current_char_is_printable()?;
3374
3375 let mut leading_blanks = false;
3378 self.consume_flow_scalar_non_whitespace_chars(
3379 single,
3380 &mut buf,
3381 &mut leading_blanks,
3382 &start_mark,
3383 )?;
3384
3385 match self.input.look_ch() {
3386 '\'' if single => break,
3387 '"' if !single => break,
3388 _ => {}
3389 }
3390
3391 let mut trailing_ws_start: Option<usize> = None;
3407 let mut has_leading_break = false;
3408 let mut has_trailing_breaks = false;
3409
3410 let mut pending_ws_start: Option<usize> = None;
3412
3413 while self.input.next_is_blank() || self.input.next_is_break() {
3415 if self.input.next_is_blank() {
3416 if leading_blanks {
3418 if self.input.peek() == '\t' && (self.mark.col as isize) < self.indent {
3419 return Err(self.scan_error(ErrorKind::TabInIndentation));
3420 }
3421 self.skip_blank();
3422 } else {
3423 match buf {
3425 FlowScalarBuf::Owned(ref mut string) => {
3426 if trailing_ws_start.is_none() {
3427 trailing_ws_start = Some(string.len());
3428 }
3429 string.push(self.input.peek());
3430 }
3431 FlowScalarBuf::Borrowed { .. } => {
3432 if pending_ws_start.is_none() {
3433 pending_ws_start = self.input.byte_offset();
3434 }
3435 }
3436 }
3437 self.skip_blank();
3438
3439 if let (FlowScalarBuf::Borrowed { .. }, Some(ws_start), Some(ws_end)) =
3440 (&mut buf, pending_ws_start, self.input.byte_offset())
3441 {
3442 buf.note_pending_ws(ws_start, ws_end);
3443 }
3444 }
3445 } else {
3446 self.input.lookahead(2);
3447
3448 if leading_blanks {
3450 match buf {
3452 FlowScalarBuf::Owned(ref mut string) => self.read_break(string),
3453 FlowScalarBuf::Borrowed { .. } => {
3454 self.promote_flow_scalar_buf_to_owned(&start_mark, &mut buf)?;
3455 let Some(string) = buf.as_owned_mut() else {
3456 unreachable!()
3457 };
3458 self.read_break(string);
3459 }
3460 }
3461 has_trailing_breaks = true;
3462 } else {
3463 if let Some(pos) = trailing_ws_start.take() {
3465 if let FlowScalarBuf::Owned(ref mut string) = buf {
3466 string.truncate(pos);
3467 }
3468 }
3469
3470 if pending_ws_start.take().is_some() {
3471 if matches!(buf, FlowScalarBuf::Borrowed { .. }) {
3473 self.promote_flow_scalar_buf_to_owned(&start_mark, &mut buf)?;
3474 }
3475 buf.discard_pending_ws();
3476 } else {
3477 buf.commit_pending_ws();
3478 }
3479
3480 break_scratch.clear();
3481 self.read_break(&mut break_scratch);
3482 has_leading_break = true;
3485 leading_blanks = true;
3486 }
3487 }
3488
3489 self.input.lookahead(1);
3490 }
3491
3492 if leading_blanks && has_leading_break && self.flow_level == 0 {
3495 let next_ch = self.input.peek();
3496 let is_closing_quote = (single && next_ch == '\'') || (!single && next_ch == '"');
3497 if !is_closing_quote && (self.mark.col as isize) <= self.indent {
3498 return Err(self.scan_error(ErrorKind::InvalidQuotedScalarIndent));
3499 }
3500 }
3501
3502 if leading_blanks {
3504 if has_leading_break && !has_trailing_breaks {
3509 match buf {
3510 FlowScalarBuf::Owned(ref mut string) => string.push(' '),
3511 FlowScalarBuf::Borrowed { .. } => {
3512 self.promote_flow_scalar_buf_to_owned(&start_mark, &mut buf)?;
3513 let Some(string) = buf.as_owned_mut() else {
3514 unreachable!()
3515 };
3516 string.push(' ');
3517 }
3518 }
3519 }
3520 }
3521 } self.skip_non_blank();
3526 let end_mark = self.mark;
3527
3528 self.skip_ws_to_eol(SkipTabs::Yes)?;
3530 self.ensure_current_char_is_printable()?;
3531 match self.input.peek() {
3532 ',' | '}' | ']' if self.flow_level > 0 => {}
3534 c if is_breakz(c) => {}
3536 ':' if self.flow_level == 0 && start_mark.line == self.mark.line => {}
3539 ':' if self.flow_level > 0 => {}
3541 _ => {
3542 let kind = if single {
3543 ErrorKind::InvalidTrailingSingleQuotedScalar
3544 } else {
3545 ErrorKind::InvalidTrailingDoubleQuotedScalar
3546 };
3547 return Err(self.scan_error(kind));
3548 }
3549 }
3550
3551 let style = if single {
3552 ScalarStyle::SingleQuoted
3553 } else {
3554 ScalarStyle::DoubleQuoted
3555 };
3556
3557 let contents = match buf {
3558 FlowScalarBuf::Owned(string) => Cow::Owned(string),
3559 FlowScalarBuf::Borrowed {
3560 start,
3561 mut end,
3562 pending_ws_start,
3563 pending_ws_end,
3564 } => {
3565 if pending_ws_start.is_some() {
3567 end = pending_ws_end;
3568 }
3569 if let Some(slice) = self.try_borrow_slice(start, end) {
3570 Cow::Borrowed(slice)
3571 } else {
3572 let slice = self.input.slice_bytes(start, end).ok_or_else(|| {
3573 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
3574 })?;
3575 Cow::Owned(slice.to_owned())
3576 }
3577 }
3578 };
3579
3580 Ok(Token(
3581 Span::new(start_mark, end_mark),
3582 TokenType::Scalar(style, contents),
3583 ))
3584 }
3585
3586 fn consume_flow_scalar_ascii_chunk(&mut self, single: bool, buf: &mut FlowScalarBuf) -> bool {
3588 let chunk = self.input.take_quoted_scalar_ascii_chunk(single);
3589 let consumed = chunk.len();
3590 if consumed == 0 {
3591 return false;
3592 }
3593
3594 match buf {
3595 FlowScalarBuf::Owned(string) => string.push_str(chunk),
3596 FlowScalarBuf::Borrowed { .. } => buf.commit_pending_ws(),
3597 }
3598
3599 self.mark.offsets.chars += consumed;
3601 self.mark.col += consumed;
3602 self.mark.offsets.bytes = self.input.byte_offset();
3603 self.leading_whitespace = false;
3604 if let (Some(new_end), FlowScalarBuf::Borrowed { end, .. }) = (self.mark.offsets.bytes, buf)
3605 {
3606 *end = new_end;
3607 }
3608 true
3609 }
3610
3611 fn consume_flow_scalar_non_whitespace_chars(
3620 &mut self,
3621 single: bool,
3622 buf: &mut FlowScalarBuf,
3623 leading_blanks: &mut bool,
3624 start_mark: &Marker,
3625 ) -> Result<(), ScanError> {
3626 self.input.lookahead(2);
3627 loop {
3628 let character = self.input.peek();
3629 if is_blank_or_breakz(character) || !is_printable(character) {
3630 break;
3631 }
3632 match character {
3633 '\'' if self.input.peek_nth(1) == '\'' && single => {
3635 if matches!(buf, FlowScalarBuf::Borrowed { .. }) {
3636 buf.commit_pending_ws();
3637 self.promote_flow_scalar_buf_to_owned(start_mark, buf)?;
3638 }
3639 let Some(string) = buf.as_owned_mut() else {
3640 unreachable!()
3641 };
3642 string.push('\'');
3643 self.skip_n_non_blank(2);
3644 }
3645 '\'' if single => break,
3647 '"' if !single => break,
3648 '\\' if !single && is_break(self.input.peek_nth(1)) => {
3650 self.input.lookahead(3);
3651 if matches!(buf, FlowScalarBuf::Borrowed { .. }) {
3652 buf.commit_pending_ws();
3653 self.promote_flow_scalar_buf_to_owned(start_mark, buf)?;
3654 }
3655 self.skip_non_blank();
3656 self.skip_linebreak();
3657 *leading_blanks = true;
3658 break;
3659 }
3660 '\\' if !single => {
3662 if matches!(buf, FlowScalarBuf::Borrowed { .. }) {
3663 buf.commit_pending_ws();
3664 self.promote_flow_scalar_buf_to_owned(start_mark, buf)?;
3665 }
3666 let Some(string) = buf.as_owned_mut() else {
3667 unreachable!()
3668 };
3669 string.push(self.resolve_flow_scalar_escape_sequence(start_mark)?);
3670 }
3671 c => {
3672 if c.is_ascii() && self.consume_flow_scalar_ascii_chunk(single, buf) {
3673 self.input.lookahead(2);
3674 continue;
3675 }
3676 match buf {
3677 FlowScalarBuf::Owned(ref mut string) => {
3678 string.push(c);
3679 }
3680 FlowScalarBuf::Borrowed { .. } => {
3681 buf.commit_pending_ws();
3682 }
3683 }
3684 self.skip_non_blank();
3685
3686 if let Some(new_end) = self.input.byte_offset() {
3687 if let FlowScalarBuf::Borrowed { end, .. } = buf {
3688 *end = new_end;
3689 }
3690 }
3691 }
3692 }
3693 self.input.lookahead(2);
3694 }
3695 Ok(())
3696 }
3697
3698 fn resolve_flow_scalar_escape_sequence(
3705 &mut self,
3706 start_mark: &Marker,
3707 ) -> Result<char, ScanError> {
3708 let mut code_length = 0usize;
3709 let mut ret = '\0';
3710
3711 match self.input.peek_nth(1) {
3712 '0' => ret = '\0',
3713 'a' => ret = '\x07',
3714 'b' => ret = '\x08',
3715 't' | '\t' => ret = '\t',
3716 'n' => ret = '\n',
3717 'v' => ret = '\x0b',
3718 'f' => ret = '\x0c',
3719 'r' => ret = '\x0d',
3720 'e' => ret = '\x1b',
3721 ' ' => ret = '\x20',
3722 '"' => ret = '"',
3723 '/' => ret = '/',
3724 '\\' => ret = '\\',
3725 'N' => ret = char::from_u32(0x85).unwrap(),
3727 '_' => ret = char::from_u32(0xA0).unwrap(),
3729 'L' => ret = char::from_u32(0x2028).unwrap(),
3731 'P' => ret = char::from_u32(0x2029).unwrap(),
3733 'x' => code_length = 2,
3734 'u' => code_length = 4,
3735 'U' => code_length = 8,
3736 _ => {
3737 return Err(ScanError::from_kind(
3738 *start_mark,
3739 ErrorKind::UnknownQuotedScalarEscape,
3740 ))
3741 }
3742 }
3743 self.skip_n_non_blank(2);
3744
3745 if code_length > 0 {
3747 self.input.lookahead(code_length);
3748 let mut value = 0u32;
3749 for i in 0..code_length {
3750 let c = self.input.peek_nth(i);
3751 if !is_hex(c) {
3752 return Err(ScanError::from_kind(
3753 *start_mark,
3754 ErrorKind::InvalidQuotedScalarHexEscape,
3755 ));
3756 }
3757 value = (value << 4) + as_hex(c);
3758 }
3759
3760 self.skip_n_non_blank(code_length);
3761
3762 if code_length == 4 && (0xD800..=0xDBFF).contains(&value) {
3764 self.input.lookahead(2);
3765 if self.input.peek() == '\\' && self.input.peek_nth(1) == 'u' {
3766 self.skip_n_non_blank(2);
3767 self.input.lookahead(4);
3768 let mut low_value = 0u32;
3769 for i in 0..4 {
3770 let c = self.input.peek_nth(i);
3771 if !is_hex(c) {
3772 return Err(ScanError::from_kind(
3773 *start_mark,
3774 ErrorKind::InvalidLowSurrogateHexEscape,
3775 ));
3776 }
3777 low_value = (low_value << 4) + as_hex(c);
3778 }
3779 if (0xDC00..=0xDFFF).contains(&low_value) {
3780 value = 0x10000 + (((value - 0xD800) << 10) | (low_value - 0xDC00));
3781 self.skip_n_non_blank(4);
3782 } else {
3783 return Err(ScanError::from_kind(
3784 *start_mark,
3785 ErrorKind::InvalidLowSurrogate,
3786 ));
3787 }
3788 } else {
3789 return Err(ScanError::from_kind(
3790 *start_mark,
3791 ErrorKind::MissingLowSurrogate,
3792 ));
3793 }
3794 } else if code_length == 4 && (0xDC00..=0xDFFF).contains(&value) {
3795 return Err(ScanError::from_kind(
3796 *start_mark,
3797 ErrorKind::UnpairedLowSurrogate,
3798 ));
3799 }
3800
3801 let Some(ch) = char::from_u32(value) else {
3802 return Err(ScanError::from_kind(
3803 *start_mark,
3804 ErrorKind::InvalidUnicodeEscape,
3805 ));
3806 };
3807 ret = ch;
3808 }
3809 Ok(ret)
3810 }
3811
3812 fn fetch_plain_scalar(&mut self) -> ScanResult {
3813 self.save_simple_key();
3814 self.disallow_simple_key();
3815
3816 let token_index = self.tokens.len();
3817 let tok = self.scan_plain_scalar()?;
3818
3819 self.insert_token(token_index, tok);
3820 Ok(())
3821 }
3822
3823 #[allow(clippy::too_many_lines)]
3828 fn scan_plain_scalar(&mut self) -> Result<Token<'input>, ScanError> {
3829 self.unroll_non_block_indents();
3830 let indent = self.indent + 1;
3831 let start_mark = self.mark;
3832
3833 let borrow_start = start_mark
3834 .byte_offset()
3835 .filter(|start| self.try_borrow_slice(*start, *start).is_some());
3836 let mut string = borrow_start.is_none().then(|| String::with_capacity(32));
3837 let mut has_content = false;
3838 self.buf_whitespaces.clear();
3839 self.buf_leading_break.clear();
3840 self.buf_trailing_breaks.clear();
3841 let mut end_mark = self.mark;
3842
3843 loop {
3844 self.input.lookahead(4);
3845 if (self.mark.col == 0 && self.input.next_is_document_indicator())
3846 || self.input.peek() == '#'
3847 {
3848 if self.input.peek() == '#'
3853 && has_content
3854 && !self.buf_whitespaces.is_empty()
3855 && self.flow_level == 0
3856 {
3857 self.interrupted_plain_by_comment = Some(self.mark);
3858 }
3859 break;
3860 }
3861
3862 if !has_content
3864 && self.flow_level > 0
3865 && self.input.peek() == '-'
3866 && is_flow(self.input.peek_nth(1))
3867 {
3868 return Err(self.scan_error(ErrorKind::PlainScalarStartsWithDashFlowIndicator));
3869 }
3870
3871 if !self.input.next_is_blank_or_breakz()
3872 && self.input.next_can_be_plain_scalar(self.flow_level > 0)
3873 {
3874 if self.leading_whitespace {
3875 if has_content && string.is_none() {
3876 let start = borrow_start.expect("borrowed scalar has a start offset");
3877 let end = end_mark.byte_offset().ok_or_else(|| {
3878 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
3879 })?;
3880 let prefix = self.try_borrow_slice(start, end).ok_or_else(|| {
3881 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
3882 })?;
3883 string = Some(prefix.to_owned());
3884 }
3885 if self.buf_leading_break.is_empty() {
3886 if let Some(output) = string.as_mut() {
3887 output.push_str(&self.buf_leading_break);
3888 output.push_str(&self.buf_trailing_breaks);
3889 }
3890 self.buf_trailing_breaks.clear();
3891 self.buf_leading_break.clear();
3892 } else {
3893 if self.buf_trailing_breaks.is_empty() {
3894 if let Some(output) = string.as_mut() {
3895 output.push(' ');
3896 }
3897 } else {
3898 if let Some(output) = string.as_mut() {
3899 output.push_str(&self.buf_trailing_breaks);
3900 }
3901 self.buf_trailing_breaks.clear();
3902 }
3903 self.buf_leading_break.clear();
3904 }
3905 self.leading_whitespace = false;
3906 } else if !self.buf_whitespaces.is_empty() {
3907 if let Some(output) = string.as_mut() {
3908 output.push_str(&self.buf_whitespaces);
3909 }
3910 self.buf_whitespaces.clear();
3911 }
3912
3913 has_content = true;
3915 if let Some(output) = string.as_mut() {
3916 output.push(self.input.peek());
3917 }
3918 self.skip_non_blank();
3919 if let Some(output) = string.as_mut() {
3920 output.reserve(self.input.bufmaxlen());
3921 }
3922
3923 let mut end = false;
3925 while !end {
3926 self.input.lookahead(self.input.bufmaxlen());
3931 let chunk_len = self.input.bufmaxlen().saturating_sub(1).max(1);
3932 let (stop, chars_consumed) = if let Some(output) = string.as_mut() {
3933 self.input
3934 .fetch_plain_scalar_chunk(output, chunk_len, self.flow_level > 0)
3935 } else {
3936 self.input
3937 .skip_plain_scalar_chunk(chunk_len, self.flow_level > 0)
3938 };
3939 end = stop;
3940 self.mark.offsets.chars += chars_consumed;
3941 self.mark.col += chars_consumed;
3942 self.mark.offsets.bytes = self.input.byte_offset();
3943 }
3944 end_mark = self.mark;
3945 }
3946
3947 if !(self.input.next_is_blank() || self.input.next_is_break()) {
3952 break;
3953 }
3954
3955 self.input.lookahead(2);
3957 while self.input.next_is_blank_or_break() {
3958 if self.input.next_is_blank() {
3959 if !self.leading_whitespace {
3960 self.buf_whitespaces.push(self.input.peek());
3961 self.skip_blank();
3962 } else if (self.mark.col as isize) < indent && self.input.peek() == '\t' {
3963 self.skip_ws_to_eol(SkipTabs::Yes)?;
3966 if !self.input.next_is_breakz() {
3967 return Err(ScanError::from_kind(
3968 start_mark,
3969 ErrorKind::TabInPlainScalar,
3970 ));
3971 }
3972 } else {
3973 self.skip_blank();
3974 }
3975 } else {
3976 if self.leading_whitespace {
3978 self.skip_break();
3979 self.buf_trailing_breaks.push('\n');
3980 } else {
3981 self.buf_whitespaces.clear();
3982 self.skip_break();
3983 self.buf_leading_break.push('\n');
3984 self.leading_whitespace = true;
3985 }
3986 }
3987 self.input.lookahead(2);
3988 }
3989
3990 if self.flow_level == 0 && (self.mark.col as isize) < indent {
3992 break;
3993 }
3994 }
3995
3996 if self.leading_whitespace {
3997 self.allow_simple_key();
3998 }
3999
4000 let borrowed_contents = if string.is_none() {
4001 let start = borrow_start.expect("borrowed scalar has a start offset");
4002 let end = end_mark.byte_offset().ok_or_else(|| {
4003 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
4004 })?;
4005 Some(self.try_borrow_slice(start, end).ok_or_else(|| {
4006 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
4007 })?)
4008 } else {
4009 None
4010 };
4011 let scalar_text = borrowed_contents.unwrap_or_else(|| {
4012 string
4013 .as_deref()
4014 .expect("owned plain scalar has an output buffer")
4015 });
4016 if let Some(character) = find_non_printable(scalar_text) {
4017 return Err(ScanError::from_kind(
4018 start_mark,
4019 ErrorKind::UnexpectedCharacter { character },
4020 ));
4021 }
4022 self.ensure_current_char_is_printable()?;
4023
4024 if has_content {
4025 let contents = if let Some(slice) = borrowed_contents {
4026 Cow::Borrowed(slice)
4027 } else {
4028 Cow::Owned(string.expect("owned plain scalar has an output buffer"))
4029 };
4030
4031 Ok(Token(
4032 Span::new(start_mark, end_mark),
4033 TokenType::Scalar(ScalarStyle::Plain, contents),
4034 ))
4035 } else {
4036 Err(ScanError::from_kind(
4040 start_mark,
4041 ErrorKind::UnexpectedEndOfPlainScalar,
4042 ))
4043 }
4044 }
4045
4046 fn fetch_key(&mut self) -> ScanResult {
4047 let start_mark = self.mark;
4048 if self.flow_level == 0 {
4049 if !self.simple_key_allowed {
4051 return Err(self.scan_error(ErrorKind::MappingKeyNotAllowed));
4052 }
4053 self.roll_indent(
4054 start_mark.col,
4055 None,
4056 TokenType::BlockMappingStart,
4057 start_mark,
4058 )?;
4059 } else {
4060 self.set_current_flow_mapping_started(true);
4062 }
4063
4064 self.remove_simple_key()?;
4065
4066 if self.flow_level == 0 {
4067 self.allow_simple_key();
4068 } else {
4069 self.disallow_simple_key();
4070 }
4071
4072 self.skip_non_blank();
4073 let end_mark = self.mark;
4074 let token_index = self.tokens.len();
4075 self.explicit_key_tab_check_pending = false;
4076 let stopped_after_comment = self.skip_yaml_whitespace()?;
4077 if self.input.peek() == '\t' {
4078 return Err(self.scan_error(ErrorKind::TabNotAllowed));
4079 }
4080 self.explicit_key_tab_check_pending = stopped_after_comment;
4081 self.insert_token(
4082 token_index,
4083 Token(Span::new(start_mark, end_mark), TokenType::Key),
4084 );
4085 Ok(())
4086 }
4087
4088 fn fetch_flow_value(&mut self) -> ScanResult {
4096 let nc = self.input.peek_nth(1);
4097
4098 if self.mark.index() != self.adjacent_value_allowed_at && (nc == '[' || nc == '{') {
4110 return Err(self.scan_error(ErrorKind::FlowMappingValueAdjacentCollection));
4111 }
4112
4113 self.fetch_value()
4114 }
4115
4116 fn fetch_value(&mut self) -> ScanResult {
4118 let sk = *self.simple_keys.last().unwrap();
4119 let start_mark = self.mark;
4120 let is_implicit_flow_mapping = self.current_flow_collection_is_sequence()
4121 && !self.current_flow_mapping_started()
4122 && !self.implicit_flow_mapping_states.is_empty();
4123 if is_implicit_flow_mapping {
4124 *self.implicit_flow_mapping_states.last_mut().unwrap() =
4125 ImplicitMappingState::Inside(self.flow_level);
4126 }
4127
4128 self.skip_non_blank();
4130 let mut trailing_tokens = VecDeque::new();
4133 let mut found_tabs = false;
4134 if matches!(self.input.look_ch(), ' ' | '\t') {
4135 let trailing_token_index = self.tokens.len();
4136 found_tabs = self.skip_ws_to_eol(SkipTabs::Yes)?.found_tabs();
4137 trailing_tokens = self.tokens.split_off(trailing_token_index);
4138 }
4139
4140 if sk.possible {
4141 let block_indent = self
4145 .indents
4146 .last()
4147 .filter(|indent| !indent.needs_block_end)
4148 .map_or(self.indent, |indent| indent.indent);
4149 if self.flow_level > 0
4150 && sk.mark.line < start_mark.line
4151 && (sk.mark.col as isize) <= block_indent
4152 {
4153 return Err(ScanError::from_kind(
4154 start_mark,
4155 ErrorKind::InvalidColonPlacement,
4156 ));
4157 }
4158 let token_index = self.simple_key_token_index(&sk, start_mark)?;
4159 self.validate_flow_key_indentation(&sk, token_index, start_mark)?;
4160 let tok = Token(Span::empty(sk.mark), TokenType::Key);
4162 self.insert_token(token_index, tok);
4163 if is_implicit_flow_mapping {
4164 if sk.mark.line < start_mark.line {
4165 return Err(ScanError::from_kind(
4166 start_mark,
4167 ErrorKind::InvalidColonPlacement,
4168 ));
4169 }
4170 self.insert_token(
4171 token_index,
4172 Token(Span::empty(sk.mark), TokenType::FlowMappingStart),
4173 );
4174 }
4175
4176 self.roll_indent(
4178 sk.mark.col,
4179 Some(sk.token_number),
4180 TokenType::BlockMappingStart,
4181 sk.mark,
4182 )?;
4183 self.roll_one_col_indent();
4184
4185 self.clear_simple_key();
4186 self.disallow_simple_key();
4187 } else {
4188 if is_implicit_flow_mapping {
4189 self.tokens
4190 .push_back(Token(Span::empty(start_mark), TokenType::FlowMappingStart).into());
4191 }
4192 if self.flow_level == 0 {
4194 if !self.simple_key_allowed {
4195 return Err(ScanError::from_kind(
4196 start_mark,
4197 ErrorKind::MappingValueNotAllowed,
4198 ));
4199 }
4200
4201 self.roll_indent(
4202 start_mark.col,
4203 None,
4204 TokenType::BlockMappingStart,
4205 start_mark,
4206 )?;
4207 }
4208 self.roll_one_col_indent();
4209
4210 if self.flow_level == 0 && !found_tabs {
4213 self.allow_simple_key();
4214 } else {
4215 self.disallow_simple_key();
4216 }
4217 }
4218 self.tokens
4219 .push_back(Token(Span::empty(start_mark), TokenType::Value).into());
4220 self.tokens.append(&mut trailing_tokens);
4221
4222 Ok(())
4223 }
4224
4225 fn validate_flow_key_indentation(
4227 &self,
4228 key: &SimpleKey,
4229 token_index: usize,
4230 colon: Marker,
4231 ) -> ScanResult {
4232 if self.flow_level == 0 || key.mark.line == colon.line {
4233 return Ok(());
4234 }
4235
4236 let block_indent = self
4237 .indents
4238 .last()
4239 .filter(|indent| !indent.needs_block_end)
4240 .map_or(self.indent, |indent| indent.indent);
4241 let mut under_indented = [key.mark.col, colon.col]
4245 .iter()
4246 .any(|&col| (col as isize) <= block_indent);
4247 for token in self.tokens.iter().skip(token_index) {
4248 if matches!(token.1, QueuedTokenType::Comment(_)) {
4249 continue;
4250 }
4251 under_indented |= (token.0.start.col as isize) <= block_indent;
4252 if !matches!(
4253 token.1,
4254 QueuedTokenType::Anchor(_) | QueuedTokenType::Tag(..)
4255 ) {
4256 break;
4257 }
4258 }
4259 if under_indented {
4260 return Err(ScanError::from_kind(
4261 colon,
4262 ErrorKind::InvalidColonPlacement,
4263 ));
4264 }
4265 Ok(())
4266 }
4267
4268 fn roll_indent(
4274 &mut self,
4275 col: usize,
4276 number: Option<usize>,
4277 tok: TokenType<'input>,
4278 mark: Marker,
4279 ) -> ScanResult {
4280 if self.flow_level > 0 {
4281 return Ok(());
4282 }
4283
4284 if self.indent <= col as isize {
4288 if let Some(indent) = self.indents.last() {
4289 if !indent.needs_block_end {
4290 self.indent = indent.indent;
4291 self.indents.pop();
4292 }
4293 }
4294 }
4295
4296 if self.indent < col as isize {
4297 if self.block_level >= self.options.block_nesting_limit {
4298 return Err(ScanError::from_kind(
4299 mark,
4300 ErrorKind::RecursionLimitExceeded,
4301 ));
4302 }
4303 self.indents.push(Indent {
4304 indent: self.indent,
4305 needs_block_end: true,
4306 });
4307 self.block_level += 1;
4308 self.indent = col as isize;
4309 let tokens_parsed = self.tokens_parsed;
4310 match number {
4311 Some(n) => self.insert_token(n - tokens_parsed, Token(Span::empty(mark), tok)),
4312 None => self.tokens.push_back(Token(Span::empty(mark), tok).into()),
4313 }
4314 }
4315 Ok(())
4316 }
4317
4318 fn unroll_indent(&mut self, col: isize) {
4324 if self.flow_level > 0 {
4325 return;
4326 }
4327 while self.indent > col {
4328 let indent = self.indents.pop().unwrap();
4329 self.indent = indent.indent;
4330 if indent.needs_block_end {
4331 debug_assert!(self.block_level > 0);
4332 self.block_level -= 1;
4333 self.tokens
4334 .push_back(Token(Span::empty(self.mark), TokenType::BlockEnd).into());
4335 }
4336 }
4337 }
4338
4339 fn roll_one_col_indent(&mut self) {
4345 if self.flow_level == 0 && self.indents.last().is_some_and(|x| x.needs_block_end) {
4346 self.indents.push(Indent {
4347 indent: self.indent,
4348 needs_block_end: false,
4349 });
4350 self.indent += 1;
4351 }
4352 }
4353
4354 fn unroll_non_block_indents(&mut self) {
4356 while let Some(indent) = self.indents.last() {
4357 if indent.needs_block_end {
4358 break;
4359 }
4360 self.indent = indent.indent;
4361 self.indents.pop();
4362 }
4363 }
4364
4365 fn save_simple_key(&mut self) {
4367 if self.simple_key_allowed {
4368 let required = self.flow_level == 0
4369 && self.indent == (self.mark.col as isize)
4370 && self.indents.last().unwrap().needs_block_end;
4371
4372 if let Some(last) = self.simple_keys.last_mut() {
4373 *last = SimpleKey {
4374 mark: self.mark,
4375 possible: true,
4376 required,
4377 token_number: self.tokens_parsed + self.tokens.len(),
4378 };
4379 self.first_simple_key.get_or_insert(self.flow_level);
4380 }
4381 }
4382 }
4383
4384 fn remove_simple_key(&mut self) -> ScanResult {
4385 let last = self.simple_keys.last().unwrap();
4386 if last.possible && last.required {
4387 return Err(Self::simple_key_expected(last.mark));
4388 }
4389
4390 self.clear_simple_key();
4391 Ok(())
4392 }
4393
4394 fn clear_simple_key(&mut self) {
4396 self.simple_keys.last_mut().unwrap().possible = false;
4397 if self.first_simple_key == Some(self.flow_level) {
4398 self.first_simple_key = None;
4400 }
4401 }
4402
4403 fn is_within_block(&self) -> bool {
4405 !self.indents.is_empty()
4406 }
4407
4408 fn end_implicit_mapping(&mut self, mark: Marker, flow_level: usize) {
4414 if self
4415 .implicit_flow_mapping_states
4416 .last()
4417 .is_some_and(|state| *state == ImplicitMappingState::Inside(flow_level))
4418 {
4419 *self.implicit_flow_mapping_states.last_mut().unwrap() = ImplicitMappingState::Possible;
4420 self.set_current_flow_mapping_started(false);
4421 self.tokens
4422 .push_back(Token(Span::empty(mark), TokenType::FlowMappingEnd).into());
4423 }
4424 }
4425
4426 fn current_flow_collection_is_sequence(&self) -> bool {
4427 self.flow_markers
4428 .last()
4429 .is_some_and(|(_, bracket)| *bracket == '[')
4430 }
4431
4432 fn current_flow_mapping_started(&self) -> bool {
4433 self.flow_mapping_started.last().copied().unwrap_or(false)
4434 }
4435
4436 fn set_current_flow_mapping_started(&mut self, started: bool) {
4437 if let Some(current) = self.flow_mapping_started.last_mut() {
4438 *current = started;
4439 }
4440 }
4441}
4442
4443#[derive(PartialEq, Eq)]
4447enum Chomping {
4448 Strip,
4450 Clip,
4452 Keep,
4454}
4455
4456#[cfg(test)]
4457mod test {
4458 use alloc::{
4459 borrow::{Cow, ToOwned},
4460 rc::Rc,
4461 string::String,
4462 vec,
4463 vec::Vec,
4464 };
4465 use core::cell::Cell;
4466
4467 use crate::error::{ErrorKind, ScanError};
4468 use crate::{
4469 input::{str::StrInput, BorrowedInput, BufferedInput, Input},
4470 scanner::{
4471 Comment, Marker, Placement, QueuedToken, QueuedTokenType, ScalarStyle, Scanner, Span,
4472 Token, TokenType,
4473 },
4474 };
4475
4476 struct CountingChars {
4477 chars: alloc::vec::IntoIter<char>,
4478 read: Rc<Cell<usize>>,
4479 }
4480
4481 impl Iterator for CountingChars {
4482 type Item = char;
4483
4484 fn next(&mut self) -> Option<Self::Item> {
4485 let next = self.chars.next();
4486 if next.is_some() {
4487 self.read.set(self.read.get() + 1);
4488 }
4489 next
4490 }
4491 }
4492
4493 struct SlicingOnlyInput<'input> {
4494 inner: StrInput<'input>,
4495 expose_slice: bool,
4496 }
4497
4498 impl<'input> SlicingOnlyInput<'input> {
4499 fn new(source: &'input str, expose_slice: bool) -> Self {
4500 Self {
4501 inner: StrInput::new(source),
4502 expose_slice,
4503 }
4504 }
4505 }
4506
4507 impl Input for SlicingOnlyInput<'_> {
4508 fn lookahead(&mut self, count: usize) {
4509 self.inner.lookahead(count);
4510 }
4511
4512 fn buflen(&self) -> usize {
4513 self.inner.buflen()
4514 }
4515
4516 fn bufmaxlen(&self) -> usize {
4517 self.inner.bufmaxlen()
4518 }
4519
4520 fn raw_read_ch(&mut self) -> char {
4521 self.inner.raw_read_ch()
4522 }
4523
4524 fn raw_read_non_breakz_ch(&mut self) -> Option<char> {
4525 self.inner.raw_read_non_breakz_ch()
4526 }
4527
4528 fn skip(&mut self) {
4529 self.inner.skip();
4530 }
4531
4532 fn skip_n(&mut self, count: usize) {
4533 self.inner.skip_n(count);
4534 }
4535
4536 fn peek(&self) -> char {
4537 self.inner.peek()
4538 }
4539
4540 fn peek_nth(&self, n: usize) -> char {
4541 self.inner.peek_nth(n)
4542 }
4543
4544 fn byte_offset(&self) -> Option<usize> {
4545 self.inner.byte_offset()
4546 }
4547
4548 fn slice_bytes(&self, start: usize, end: usize) -> Option<&str> {
4549 if self.expose_slice {
4550 self.inner.slice_bytes(start, end)
4551 } else {
4552 None
4553 }
4554 }
4555 }
4556
4557 impl<'input> BorrowedInput<'input> for SlicingOnlyInput<'input> {
4558 fn slice_borrowed(&self, _start: usize, _end: usize) -> Option<&'input str> {
4559 None
4560 }
4561 }
4562
4563 struct SmallReportedBufferInput<'input> {
4564 inner: StrInput<'input>,
4565 reported_bufmaxlen: usize,
4566 }
4567
4568 impl<'input> SmallReportedBufferInput<'input> {
4569 fn new(source: &'input str, reported_bufmaxlen: usize) -> Self {
4570 Self {
4571 inner: StrInput::new(source),
4572 reported_bufmaxlen,
4573 }
4574 }
4575 }
4576
4577 impl Input for SmallReportedBufferInput<'_> {
4578 fn lookahead(&mut self, count: usize) {
4579 self.inner.lookahead(count);
4580 }
4581
4582 fn buflen(&self) -> usize {
4583 self.inner.buflen()
4584 }
4585
4586 fn bufmaxlen(&self) -> usize {
4587 self.reported_bufmaxlen
4588 }
4589
4590 fn raw_read_ch(&mut self) -> char {
4591 self.inner.raw_read_ch()
4592 }
4593
4594 fn raw_read_non_breakz_ch(&mut self) -> Option<char> {
4595 self.inner.raw_read_non_breakz_ch()
4596 }
4597
4598 fn skip(&mut self) {
4599 self.inner.skip();
4600 }
4601
4602 fn skip_n(&mut self, count: usize) {
4603 self.inner.skip_n(count);
4604 }
4605
4606 fn peek(&self) -> char {
4607 self.inner.peek()
4608 }
4609
4610 fn peek_nth(&self, n: usize) -> char {
4611 self.inner.peek_nth(n)
4612 }
4613 }
4614
4615 impl<'input> BorrowedInput<'input> for SmallReportedBufferInput<'input> {
4616 fn slice_borrowed(&self, start: usize, end: usize) -> Option<&'input str> {
4617 self.inner.slice_borrowed(start, end)
4618 }
4619 }
4620
4621 #[test]
4622 fn anchor_character_set_allows_colon_and_rejects_flow_indicators() {
4623 use super::is_anchor_char;
4624
4625 assert!(is_anchor_char('x'));
4626 assert!(is_anchor_char('-'));
4627 assert!(is_anchor_char('_'));
4628 assert!(is_anchor_char(':'));
4629 assert!(is_anchor_char('#'));
4630 assert!(is_anchor_char('/'));
4631 assert!(is_anchor_char('?'));
4632
4633 for c in [',', '[', ']', '{', '}', ' ', '\t', '\n', '\r', '\0'] {
4634 assert!(
4635 !is_anchor_char(c),
4636 "character {c:?} must not be accepted in anchor/alias names"
4637 );
4638 }
4639 }
4640
4641 #[test]
4642 fn flow_simple_key_length_limit_bounds_buffering() {
4643 let mut yaml = String::from("[\n\"start\"\n");
4644 for _ in 0..600 {
4645 yaml.push_str("\"x\"\n");
4646 }
4647 let total_chars = yaml.chars().count();
4648 let read = Rc::new(Cell::new(0));
4649 let chars = yaml.chars().collect::<Vec<_>>().into_iter();
4650 let mut scanner = Scanner::new(BufferedInput::new(CountingChars {
4651 chars,
4652 read: Rc::clone(&read),
4653 }));
4654
4655 assert!(matches!(
4656 scanner.next_token().unwrap().unwrap().1,
4657 TokenType::StreamStart
4658 ));
4659
4660 let token = scanner.next_token().unwrap().unwrap();
4661 assert!(matches!(token.1, TokenType::FlowSequenceStart));
4662
4663 let token = scanner.next_token().unwrap().unwrap();
4664 assert!(matches!(
4665 token.1,
4666 TokenType::Scalar(_, ref value) if value == "start"
4667 ));
4668 assert!(
4669 read.get() < total_chars,
4670 "scanner consumed all {total_chars} chars before yielding the first flow scalar"
4671 );
4672 assert!(
4673 read.get() <= scanner.options.simple_key_max_lookahead + 128,
4674 "scanner read {} chars before yielding the first flow scalar",
4675 read.get()
4676 );
4677 }
4678
4679 #[test]
4680 fn block_scalar_indent_tolerates_small_reported_bufmaxlen() {
4681 let mut scanner = Scanner::new(SmallReportedBufferInput::new("|\n value\n", 0));
4682
4683 let scalar = scanner
4684 .find_map(
4685 |token| match token.expect("valid YAML should scan without errors") {
4686 Token(_, TokenType::Scalar(ScalarStyle::Literal, value)) => {
4687 Some(value.into_owned())
4688 }
4689 _ => None,
4690 },
4691 )
4692 .expect("expected block scalar token");
4693
4694 assert_eq!(scalar, "value\n");
4695 }
4696
4697 #[test]
4698 fn plain_scalar_chunk_tolerates_small_reported_bufmaxlen() {
4699 let mut scanner = Scanner::new(SmallReportedBufferInput::new("plain\n", 0));
4700
4701 let scalar = scanner
4702 .find_map(
4703 |token| match token.expect("valid YAML should scan without errors") {
4704 Token(_, TokenType::Scalar(ScalarStyle::Plain, value)) => {
4705 Some(value.into_owned())
4706 }
4707 _ => None,
4708 },
4709 )
4710 .expect("expected plain scalar token");
4711
4712 assert_eq!(scalar, "plain");
4713 }
4714
4715 fn first_token_slice(
4716 yaml: &str,
4717 matches_token: impl Fn(&TokenType<'_>) -> bool,
4718 ) -> Option<String> {
4719 let mut scanner = Scanner::new(StrInput::new(yaml));
4720
4721 loop {
4722 let token = scanner
4723 .next_token()
4724 .expect("scanner should accept the test YAML")?;
4725 if matches_token(&token.1) {
4726 return token.0.slice(yaml).map(ToOwned::to_owned);
4727 }
4728 }
4729 }
4730
4731 #[test]
4732 fn flow_indicator_token_spans_cover_only_the_indicator() {
4733 assert_eq!(
4734 first_token_slice("[ # c\n a]\n", |token| matches!(
4735 token,
4736 TokenType::FlowSequenceStart
4737 ))
4738 .as_deref(),
4739 Some("[")
4740 );
4741 assert_eq!(
4742 first_token_slice("{ # c\n a: b}\n", |token| matches!(
4743 token,
4744 TokenType::FlowMappingStart
4745 ))
4746 .as_deref(),
4747 Some("{")
4748 );
4749 assert_eq!(
4750 first_token_slice("[a] # c\n", |token| matches!(
4751 token,
4752 TokenType::FlowSequenceEnd
4753 ))
4754 .as_deref(),
4755 Some("]")
4756 );
4757 assert_eq!(
4758 first_token_slice("{a: b} # c\n", |token| matches!(
4759 token,
4760 TokenType::FlowMappingEnd
4761 ))
4762 .as_deref(),
4763 Some("}")
4764 );
4765 assert_eq!(
4766 first_token_slice("[a, # c\nb]\n", |token| matches!(
4767 token,
4768 TokenType::FlowEntry
4769 ))
4770 .as_deref(),
4771 Some(",")
4772 );
4773 }
4774
4775 #[test]
4776 fn explicit_key_token_span_covers_only_the_indicator() {
4777 assert_eq!(
4778 first_token_slice("? # c\n: value\n", |token| matches!(token, TokenType::Key))
4779 .as_deref(),
4780 Some("?")
4781 );
4782 }
4783
4784 #[test]
4785 fn comment_capture_does_not_change_leading_whitespace() {
4786 let mut scanner = Scanner::new(StrInput::new("# comment\n"));
4787
4788 let token = scanner.scan_comment_token().unwrap();
4789
4790 assert!(scanner.leading_whitespace);
4791 assert!(matches!(token.1, TokenType::Comment(ref comment) if comment.text == " comment"));
4792
4793 let mut scanner = Scanner::new(BufferedInput::new("# streaming\n".chars()));
4794 scanner.input.lookahead(1);
4795
4796 let token = scanner.scan_comment_token().unwrap();
4797
4798 assert!(scanner.leading_whitespace);
4799 assert!(matches!(token.1, TokenType::Comment(ref comment) if comment.text == " streaming"));
4800 }
4801
4802 #[test]
4803 fn comment_capture_falls_back_to_owned_slice_when_borrow_unavailable() {
4804 let mut scanner = Scanner::new(SlicingOnlyInput::new("# sliced\n", true));
4805 scanner.input.lookahead(2);
4806 assert_eq!(scanner.input.peek_nth(1), ' ');
4807
4808 let token = scanner.scan_comment_token().unwrap();
4809
4810 assert!(matches!(token.1, TokenType::Comment(ref comment)
4811 if matches!(comment.text, Cow::Owned(ref text) if text == " sliced")));
4812 }
4813
4814 #[test]
4815 fn comment_capture_errors_when_offsets_have_no_slice() {
4816 let mut scanner = Scanner::new(SlicingOnlyInput::new("# broken\n", false));
4817
4818 let error = scanner.scan_comment_token().unwrap_err();
4819
4820 assert_eq!(error.kind(), &ErrorKind::InputOffsetsWithoutSlice);
4821 }
4822
4823 #[test]
4824 fn disabled_comments_do_not_require_input_slicing() {
4825 let options = crate::options! { emit_comments: false };
4826 let scanner = Scanner::with_options(
4827 SlicingOnlyInput::new("# ignored\nkey: value\n", false),
4828 options,
4829 );
4830
4831 let tokens = scanner
4832 .collect::<Result<Vec<_>, _>>()
4833 .expect("ignored comments should not capture their payload");
4834
4835 assert!(tokens
4836 .iter()
4837 .all(|token| !matches!(token.1, TokenType::Comment(_))));
4838 }
4839
4840 #[test]
4841 fn queued_token_roundtrips_public_token_variants() {
4842 let span = Span::new(Marker::new(0, 1, 0), Marker::new(7, 1, 7));
4843 let tokens = [
4844 Token(span, TokenType::StreamStart),
4845 Token(span, TokenType::StreamEnd),
4846 Token(span, TokenType::VersionDirective(1, 2)),
4847 Token(
4848 span,
4849 TokenType::TagDirective(Cow::Borrowed("!app!"), Cow::Borrowed("tag:app.example,")),
4850 ),
4851 Token(span, TokenType::DocumentStart),
4852 Token(span, TokenType::DocumentEnd),
4853 Token(span, TokenType::BlockSequenceStart),
4854 Token(span, TokenType::BlockMappingStart),
4855 Token(span, TokenType::BlockEnd),
4856 Token(span, TokenType::FlowSequenceStart),
4857 Token(span, TokenType::FlowSequenceEnd),
4858 Token(span, TokenType::FlowMappingStart),
4859 Token(span, TokenType::FlowMappingEnd),
4860 Token(span, TokenType::BlockEntry),
4861 Token(span, TokenType::FlowEntry),
4862 Token(span, TokenType::Key),
4863 Token(span, TokenType::Value),
4864 Token(span, TokenType::Alias(Cow::Borrowed("alias"))),
4865 Token(span, TokenType::Anchor(Cow::Borrowed("anchor"))),
4866 Token(
4867 span,
4868 TokenType::Tag(Cow::Borrowed("!"), Cow::Borrowed("tag")),
4869 ),
4870 Token(
4871 span,
4872 TokenType::Scalar(ScalarStyle::Literal, Cow::Borrowed("scalar")),
4873 ),
4874 Token(
4875 span,
4876 TokenType::Comment(
4877 Comment::new(Cow::Borrowed(" comment")).with_placement(Placement::Right),
4878 ),
4879 ),
4880 Token(
4881 span,
4882 TokenType::ReservedDirective(
4883 "reserved".to_owned(),
4884 vec!["one".to_owned(), "two".to_owned()],
4885 ),
4886 ),
4887 ];
4888
4889 for token in tokens {
4890 let queued: QueuedToken = token.clone().into();
4891
4892 assert_eq!(queued.into_public(), token);
4893 }
4894 }
4895
4896 #[test]
4897 fn comment_skipping_path_consumes_comment_without_tokenizing_it() {
4898 let mut scanner = Scanner::new(StrInput::new("# skipped\nnext: value\n"));
4899
4900 scanner.skip_yaml_whitespace().unwrap();
4901
4902 assert!(scanner.tokens.is_empty());
4903 assert_eq!(scanner.mark.line(), 2);
4904 assert_eq!(scanner.mark.col(), 0);
4905 }
4906
4907 #[test]
4908 fn yaml_whitespace_can_stop_after_queued_comment() {
4909 let mut scanner = Scanner::new(StrInput::new(" # queued\n# later\n"));
4910
4911 assert!(scanner.skip_yaml_whitespace().unwrap());
4912
4913 assert_eq!(scanner.tokens.len(), 1);
4914 assert!(matches!(
4915 scanner.tokens.front().unwrap().1,
4916 QueuedTokenType::Comment(ref comment) if comment.text == " queued"
4917 ));
4918 assert_eq!(scanner.mark.line(), 1);
4919 assert_eq!(scanner.mark.col(), 9);
4920 }
4921
4922 #[test]
4923 fn token_skip_can_stop_after_queued_comment() {
4924 let mut scanner = Scanner::new(StrInput::new("# first\n# second\n"));
4925
4926 assert!(scanner.skip_to_next_token().unwrap().queued_comment);
4927
4928 assert_eq!(scanner.tokens.len(), 1);
4929 assert!(matches!(
4930 scanner.tokens.front().unwrap().1,
4931 QueuedTokenType::Comment(ref comment) if comment.text == " first"
4932 ));
4933 assert_eq!(scanner.mark.line(), 2);
4934 assert_eq!(scanner.mark.col(), 0);
4935 }
4936
4937 #[test]
4938 fn token_skip_stops_after_one_ignored_comment_without_queuing_it() {
4939 let options = crate::options! { emit_comments: false };
4940 let mut scanner =
4941 Scanner::with_options(StrInput::new("# first\n# second\nvalue\n"), options);
4942
4943 let outcome = scanner.skip_to_next_token().unwrap();
4944
4945 assert!(outcome.saw_comment);
4946 assert!(!outcome.queued_comment);
4947 assert!(scanner.tokens.is_empty());
4948 assert_eq!((scanner.mark.line(), scanner.mark.col()), (2, 0));
4949 assert_eq!(scanner.input.look_ch(), '#');
4950 }
4951
4952 #[test]
4953 fn yaml_whitespace_stops_after_one_separated_ignored_comment() {
4954 let options = crate::options! { emit_comments: false };
4955 let mut scanner =
4956 Scanner::with_options(StrInput::new(" # first\n# second\nvalue\n"), options);
4957
4958 assert!(scanner.skip_yaml_whitespace().unwrap());
4959
4960 assert!(scanner.tokens.is_empty());
4961 assert_eq!((scanner.mark.line(), scanner.mark.col()), (1, 8));
4962 assert_eq!(scanner.input.look_ch(), '\n');
4963 }
4964
4965 #[test]
4966 fn scanner_skips_consecutive_leading_ignored_comments_before_next_token() {
4967 let options = crate::options! { emit_comments: false };
4968 let mut scanner =
4969 Scanner::with_options(StrInput::new("# first\n# second\nvalue\n"), options);
4970
4971 assert!(matches!(
4972 scanner.next_token().unwrap().unwrap().1,
4973 TokenType::StreamStart
4974 ));
4975 assert!(matches!(
4976 scanner.next_token().unwrap().unwrap().1,
4977 TokenType::Scalar(ScalarStyle::Plain, ref value) if value == "value"
4978 ));
4979 assert!(scanner
4980 .tokens
4981 .iter()
4982 .all(|QueuedToken(_, token)| !matches!(token, QueuedTokenType::Comment(_))));
4983 }
4984
4985 #[test]
4986 fn scanner_emits_first_leading_comment_before_scanning_next_comment() {
4987 let mut scanner = Scanner::new(StrInput::new("# first\n# second\nkey: value\n"));
4988
4989 assert!(matches!(
4990 scanner.next_token().unwrap().unwrap().1,
4991 TokenType::StreamStart
4992 ));
4993 assert!(matches!(
4994 scanner.next_token().unwrap().unwrap().1,
4995 TokenType::Comment(ref comment) if comment.text == " first"
4996 ));
4997 assert!(scanner.tokens.is_empty());
4998 assert!(matches!(
4999 scanner.next_token().unwrap().unwrap().1,
5000 TokenType::Comment(ref comment) if comment.text == " second"
5001 ));
5002 }
5003
5004 #[test]
5005 fn scanner_emits_quoted_scalar_comment_before_scanning_following_value() {
5006 let mut scanner = Scanner::new(StrInput::new("\"key\" # quoted\n: value\n"));
5007
5008 assert!(matches!(
5009 scanner.next_token().unwrap().unwrap().1,
5010 TokenType::StreamStart
5011 ));
5012 assert!(matches!(
5013 scanner.next_token().unwrap().unwrap().1,
5014 TokenType::Scalar(ScalarStyle::DoubleQuoted, ref value) if value == "key"
5015 ));
5016 assert!(matches!(
5017 scanner.next_token().unwrap().unwrap().1,
5018 TokenType::Comment(ref comment) if comment.text == " quoted"
5019 ));
5020 }
5021
5022 #[test]
5023 fn flow_scalar_comment_disables_adjacent_value_lookahead() {
5024 let mut scanner = Scanner::new(StrInput::new("\"key\"\n# quoted\n: value\n"));
5025
5026 scanner.fetch_flow_scalar(false).unwrap();
5027
5028 assert_eq!(scanner.adjacent_value_allowed_at, usize::MAX);
5029 assert!(matches!(
5030 scanner.tokens.front().unwrap().1,
5031 QueuedTokenType::Scalar(ScalarStyle::DoubleQuoted, ref value) if value == "key"
5032 ));
5033 assert!(scanner.tokens.iter().any(|QueuedToken(_, token)| matches!(
5034 token,
5035 QueuedTokenType::Comment(comment) if comment.text == " quoted"
5036 )));
5037 }
5038
5039 #[test]
5040 fn ignored_flow_scalar_comment_still_disables_adjacent_value_lookahead() {
5041 let options = crate::options! { emit_comments: false };
5042 let mut scanner =
5043 Scanner::with_options(StrInput::new("\"key\"\n# ignored\n: value\n"), options);
5044
5045 scanner.fetch_flow_scalar(false).unwrap();
5046
5047 assert_eq!(scanner.adjacent_value_allowed_at, usize::MAX);
5048 assert!(scanner
5049 .tokens
5050 .iter()
5051 .all(|QueuedToken(_, token)| { !matches!(token, QueuedTokenType::Comment(_)) }));
5052 }
5053
5054 #[test]
5055 fn deferred_error_waits_for_all_comment_tokens() {
5056 let mut scanner = Scanner::new(StrInput::new("# first\n# second\n@\n"));
5057
5058 assert!(matches!(
5059 scanner.next_token().unwrap().unwrap().1,
5060 TokenType::StreamStart
5061 ));
5062 assert!(matches!(
5063 scanner.next_token().unwrap().unwrap().1,
5064 TokenType::Comment(ref comment) if comment.text == " first"
5065 ));
5066 assert!(matches!(
5067 scanner.next_token().unwrap().unwrap().1,
5068 TokenType::Comment(ref comment) if comment.text == " second"
5069 ));
5070
5071 let error = scanner.next_token().unwrap_err();
5072
5073 assert_eq!(
5074 error.kind(),
5075 &ErrorKind::UnexpectedCharacter { character: '@' }
5076 );
5077 }
5078
5079 #[test]
5081 fn anchor_name_is_borrowed_for_str_input() {
5082 let mut scanner = Scanner::new(StrInput::new("&anch\n"));
5083
5084 loop {
5085 let tok = scanner
5086 .next_token()
5087 .expect("valid YAML must scan without errors")
5088 .expect("scanner must eventually produce a token");
5089 if let TokenType::Anchor(name) = tok.1 {
5090 assert!(matches!(name, Cow::Borrowed("anch")));
5091 break;
5092 }
5093 }
5094 }
5095
5096 #[test]
5097 fn anchor_name_rejects_non_printable_control_chars() {
5098 let mut scanner = Scanner::new(StrInput::new("&foo\u{0001}\n"));
5099
5100 scanner.next_token().unwrap();
5101 assert_eq!(
5102 scanner.next_token().unwrap_err().kind(),
5103 &ErrorKind::UnexpectedCharacter {
5104 character: '\u{0001}'
5105 }
5106 );
5107 }
5108
5109 #[test]
5110 fn alias_name_rejects_non_printable_control_chars() {
5111 let mut scanner = Scanner::new(StrInput::new("*foo\u{0001}\n"));
5112
5113 scanner.next_token().unwrap();
5114 assert_eq!(
5115 scanner.next_token().unwrap_err().kind(),
5116 &ErrorKind::UnexpectedCharacter {
5117 character: '\u{0001}'
5118 }
5119 );
5120 }
5121
5122 #[test]
5123 fn alias_name_is_borrowed_for_str_input() {
5124 let mut scanner = Scanner::new(StrInput::new("*anch\n"));
5125
5126 loop {
5127 let tok = scanner
5128 .next_token()
5129 .expect("valid YAML must scan without errors")
5130 .expect("scanner must eventually produce a token");
5131 if let TokenType::Alias(name) = tok.1 {
5132 assert!(matches!(name, Cow::Borrowed("anch")));
5133 break;
5134 }
5135 }
5136 }
5137
5138 #[test]
5139 fn alias_name_scans_colon_as_part_of_name() {
5140 let mut scanner = Scanner::new(StrInput::new("*foo: bar\n"));
5141
5142 loop {
5143 let tok = scanner
5144 .next_token()
5145 .expect("scanner must not fail before alias token")
5146 .expect("scanner must eventually emit an alias token");
5147
5148 if let TokenType::Alias(name) = tok.1 {
5149 assert_eq!(name.as_ref(), "foo:");
5150 break;
5151 }
5152 }
5153 }
5154
5155 #[test]
5156 fn anchor_name_scans_colon_as_part_of_name() {
5157 let mut scanner = Scanner::new(StrInput::new("&foo: bar\n"));
5158
5159 loop {
5160 let tok = scanner
5161 .next_token()
5162 .expect("scanner must not fail before anchor token")
5163 .expect("scanner must eventually emit an anchor token");
5164
5165 if let TokenType::Anchor(name) = tok.1 {
5166 assert_eq!(name.as_ref(), "foo:");
5167 break;
5168 }
5169 }
5170 }
5171
5172 #[test]
5174 fn tag_directive_parts_are_borrowed_for_str_input() {
5175 let mut scanner = Scanner::new(StrInput::new("%TAG !e! tag:example.com,2000:app/\n"));
5176
5177 loop {
5178 let tok = scanner
5179 .next_token()
5180 .expect("valid YAML must scan without errors")
5181 .expect("scanner must eventually produce a token");
5182 if let TokenType::TagDirective(handle, prefix) = tok.1 {
5183 assert!(matches!(handle, Cow::Borrowed("!e!")));
5184 assert!(matches!(prefix, Cow::Borrowed("tag:example.com,2000:app/")));
5185 break;
5186 }
5187 }
5188 }
5189
5190 #[test]
5191 fn tag_directive_parts_are_owned_for_buffered_input() {
5192 let mut scanner = Scanner::new(BufferedInput::new(
5193 "%TAG !e! tag:example.com,2000:app/\n".chars(),
5194 ));
5195
5196 loop {
5197 let tok = scanner
5198 .next_token()
5199 .expect("valid YAML must scan without errors")
5200 .expect("scanner must eventually produce a token");
5201 if let TokenType::TagDirective(handle, prefix) = tok.1 {
5202 assert!(matches!(handle, Cow::Owned(_)));
5203 assert_eq!(&*handle, "!e!");
5204 assert!(matches!(prefix, Cow::Owned(_)));
5205 assert_eq!(&*prefix, "tag:example.com,2000:app/");
5206 break;
5207 }
5208 }
5209 }
5210
5211 #[test]
5212 fn buffered_tag_directive_decodes_prefix_escape() {
5213 let mut scanner = Scanner::new(BufferedInput::new(
5214 "%TAG !e! %74ag:example.com,2000:app/\n".chars(),
5215 ));
5216
5217 loop {
5218 let tok = scanner
5219 .next_token()
5220 .expect("valid YAML must scan without errors")
5221 .expect("scanner must eventually produce a token");
5222 if let TokenType::TagDirective(handle, prefix) = tok.1 {
5223 assert_eq!(&*handle, "!e!");
5224 assert!(matches!(prefix, Cow::Owned(_)));
5225 assert_eq!(&*prefix, "tag:example.com,2000:app/");
5226 break;
5227 }
5228 }
5229 }
5230
5231 #[test]
5232 fn local_tag_combines_handle_text_with_escaped_suffix() {
5233 let mut scanner = Scanner::new(StrInput::new("!foo%20bar value\n"));
5234
5235 loop {
5236 let tok = scanner
5237 .next_token()
5238 .expect("valid YAML must scan without errors")
5239 .expect("scanner must eventually produce a token");
5240 if let TokenType::Tag(handle, suffix) = tok.1 {
5241 assert!(matches!(handle, Cow::Borrowed("!")));
5242 assert!(matches!(suffix, Cow::Owned(_)));
5243 assert_eq!(&*suffix, "foo bar");
5244 break;
5245 }
5246 }
5247 }
5248
5249 #[test]
5250 fn secondary_tag_requires_suffix_in_borrowed_and_buffered_paths() {
5251 assert_eq!(
5252 first_scanner_error_kind("!! value\n"),
5253 ErrorKind::MissingTagUri
5254 );
5255 assert_eq!(
5256 first_buffered_scanner_error_kind("!! value\n"),
5257 ErrorKind::MissingTagUri
5258 );
5259 }
5260
5261 #[test]
5262 fn plain_scalar_is_borrowed_when_whitespace_free_for_str_input() {
5263 let mut scanner = Scanner::new(StrInput::new("foo\n"));
5264
5265 loop {
5266 let tok = scanner
5267 .next_token()
5268 .expect("valid YAML must scan without errors")
5269 .expect("scanner must eventually produce a token");
5270 if let TokenType::Scalar(_, value) = tok.1 {
5271 assert!(matches!(value, Cow::Borrowed("foo")));
5272 break;
5273 }
5274 }
5275 }
5276
5277 #[test]
5278 fn plain_scalar_is_borrowed_when_whitespace_present_for_str_input() {
5279 let mut scanner = Scanner::new(StrInput::new("foo bar\n"));
5280
5281 loop {
5282 let tok = scanner
5283 .next_token()
5284 .expect("valid YAML must scan without errors")
5285 .expect("scanner must eventually produce a token");
5286 if let TokenType::Scalar(_, value) = tok.1 {
5287 assert!(matches!(value, Cow::Borrowed("foo bar")));
5288 break;
5289 }
5290 }
5291 }
5292
5293 #[test]
5294 fn single_quoted_scalar_is_borrowed_when_verbatim_for_str_input() {
5295 let mut scanner = Scanner::new(StrInput::new("'foo bar'\n"));
5296
5297 loop {
5298 let tok = scanner
5299 .next_token()
5300 .expect("valid YAML must scan without errors")
5301 .expect("scanner must eventually produce a token");
5302 if let TokenType::Scalar(_, value) = tok.1 {
5303 assert!(matches!(value, Cow::Borrowed("foo bar")));
5304 break;
5305 }
5306 }
5307 }
5308
5309 #[test]
5310 fn single_quoted_scalar_is_owned_when_quote_is_escaped_for_str_input() {
5311 let mut scanner = Scanner::new(StrInput::new("'foo''bar'\n"));
5312
5313 loop {
5314 let tok = scanner
5315 .next_token()
5316 .expect("valid YAML must scan without errors")
5317 .expect("scanner must eventually produce a token");
5318 if let TokenType::Scalar(_, value) = tok.1 {
5319 assert!(matches!(value, Cow::Owned(_)));
5320 assert_eq!(&*value, "foo'bar");
5321 break;
5322 }
5323 }
5324 }
5325
5326 #[test]
5327 fn double_quoted_scalar_is_borrowed_when_verbatim_for_str_input() {
5328 let mut scanner = Scanner::new(StrInput::new("\"foo bar\"\n"));
5329
5330 loop {
5331 let tok = scanner
5332 .next_token()
5333 .expect("valid YAML must scan without errors")
5334 .expect("scanner must eventually produce a token");
5335 if let TokenType::Scalar(_, value) = tok.1 {
5336 assert!(matches!(value, Cow::Borrowed("foo bar")));
5337 break;
5338 }
5339 }
5340 }
5341
5342 #[test]
5343 fn double_quoted_scalar_is_owned_when_escape_sequence_present_for_str_input() {
5344 let mut scanner = Scanner::new(StrInput::new("\"foo\\nbar\"\n"));
5345
5346 loop {
5347 let tok = scanner
5348 .next_token()
5349 .expect("valid YAML must scan without errors")
5350 .expect("scanner must eventually produce a token");
5351 if let TokenType::Scalar(_, value) = tok.1 {
5352 assert!(matches!(value, Cow::Owned(_)));
5353 assert_eq!(&*value, "foo\nbar");
5354 break;
5355 }
5356 }
5357 }
5358
5359 #[test]
5360 fn plain_key_is_borrowed_for_str_input() {
5361 let mut scanner = Scanner::new(StrInput::new("mykey: value\n"));
5363
5364 let mut found_key = false;
5365 let mut key_value: Option<Cow<'_, str>> = None;
5366
5367 loop {
5368 let tok = scanner
5369 .next_token()
5370 .expect("valid YAML must scan without errors");
5371 let Some(tok) = tok else { break };
5372
5373 if matches!(tok.1, TokenType::Key) {
5374 found_key = true;
5375 } else if found_key {
5376 if let TokenType::Scalar(_, value) = tok.1 {
5377 key_value = Some(value);
5378 break;
5379 }
5380 }
5381 }
5382
5383 assert!(found_key, "expected to find a Key token");
5384 let key_value = key_value.expect("expected to find a scalar after Key token");
5385 assert!(
5386 matches!(key_value, Cow::Borrowed("mykey")),
5387 "key should be borrowed, got: {key_value:?}"
5388 );
5389 }
5390
5391 #[test]
5392 fn quoted_key_is_borrowed_when_verbatim_for_str_input() {
5393 let mut scanner = Scanner::new(StrInput::new("\"mykey\": value\n"));
5394
5395 let mut found_key = false;
5396 let mut key_value: Option<Cow<'_, str>> = None;
5397
5398 loop {
5399 let tok = scanner
5400 .next_token()
5401 .expect("valid YAML must scan without errors");
5402 let Some(tok) = tok else { break };
5403
5404 if matches!(tok.1, TokenType::Key) {
5405 found_key = true;
5406 } else if found_key {
5407 if let TokenType::Scalar(_, value) = tok.1 {
5408 key_value = Some(value);
5409 break;
5410 }
5411 }
5412 }
5413
5414 assert!(found_key, "expected to find a Key token");
5415 let key_value = key_value.expect("expected to find a scalar after Key token");
5416 assert!(
5417 matches!(key_value, Cow::Borrowed("mykey")),
5418 "quoted key should be borrowed when verbatim, got: {key_value:?}"
5419 );
5420 }
5421
5422 #[test]
5423 fn tag_handle_and_suffix_are_borrowed_for_str_input() {
5424 let mut scanner = Scanner::new(StrInput::new("!!str foo\n"));
5426
5427 loop {
5428 let tok = scanner
5429 .next_token()
5430 .expect("valid YAML must scan without errors")
5431 .expect("scanner must eventually produce a token");
5432 if let TokenType::Tag(handle, suffix) = tok.1 {
5433 assert!(
5434 matches!(handle, Cow::Borrowed("!!")),
5435 "tag handle should be borrowed, got: {handle:?}"
5436 );
5437 assert!(
5438 matches!(suffix, Cow::Borrowed("str")),
5439 "tag suffix should be borrowed, got: {suffix:?}"
5440 );
5441 break;
5442 }
5443 }
5444 }
5445
5446 #[test]
5447 fn local_tag_suffix_is_borrowed_for_str_input() {
5448 let mut scanner = Scanner::new(StrInput::new("!mytag foo\n"));
5450
5451 loop {
5452 let tok = scanner
5453 .next_token()
5454 .expect("valid YAML must scan without errors")
5455 .expect("scanner must eventually produce a token");
5456 if let TokenType::Tag(handle, suffix) = tok.1 {
5457 assert!(
5458 matches!(handle, Cow::Borrowed("!")),
5459 "local tag handle should be '!', got: {handle:?}"
5460 );
5461 assert!(
5462 matches!(suffix, Cow::Borrowed("mytag")),
5463 "local tag suffix should be borrowed, got: {suffix:?}"
5464 );
5465 break;
5466 }
5467 }
5468 }
5469
5470 #[test]
5471 fn local_tag_suffix_with_punctuation_is_borrowed_for_str_input() {
5472 let mut scanner = Scanner::new(StrInput::new("!mytag/part foo\n"));
5473
5474 loop {
5475 let tok = scanner
5476 .next_token()
5477 .expect("valid YAML must scan without errors")
5478 .expect("scanner must eventually produce a token");
5479 if let TokenType::Tag(handle, suffix) = tok.1 {
5480 assert!(matches!(handle, Cow::Borrowed("!")));
5481 assert!(matches!(suffix, Cow::Borrowed("mytag/part")));
5482 break;
5483 }
5484 }
5485 }
5486
5487 #[test]
5488 fn tag_with_uri_escape_is_owned_for_str_input() {
5489 let mut scanner = Scanner::new(StrInput::new("!!my%20tag foo\n"));
5491
5492 loop {
5493 let tok = scanner
5494 .next_token()
5495 .expect("valid YAML must scan without errors")
5496 .expect("scanner must eventually produce a token");
5497 if let TokenType::Tag(handle, suffix) = tok.1 {
5498 assert!(
5499 matches!(handle, Cow::Borrowed("!!")),
5500 "tag handle should still be borrowed, got: {handle:?}"
5501 );
5502 assert!(
5503 matches!(suffix, Cow::Owned(_)),
5504 "tag suffix with URI escape should be owned, got: {suffix:?}"
5505 );
5506 assert_eq!(&*suffix, "my tag");
5507 break;
5508 }
5509 }
5510 }
5511
5512 #[test]
5513 fn flow_scalar_buffer_tracks_pending_whitespace() {
5514 let mut borrowed = super::FlowScalarBuf::new_borrowed(2);
5515
5516 borrowed.note_pending_ws(5, 8);
5517 borrowed.commit_pending_ws();
5518 assert!(matches!(
5519 borrowed,
5520 super::FlowScalarBuf::Borrowed {
5521 end: 8,
5522 pending_ws_start: None,
5523 pending_ws_end: 8,
5524 ..
5525 }
5526 ));
5527
5528 borrowed.note_pending_ws(9, 11);
5529 borrowed.discard_pending_ws();
5530 assert!(matches!(
5531 borrowed,
5532 super::FlowScalarBuf::Borrowed {
5533 end: 8,
5534 pending_ws_start: None,
5535 pending_ws_end: 8,
5536 ..
5537 }
5538 ));
5539 assert!(borrowed.as_owned_mut().is_none());
5540
5541 let mut owned = super::FlowScalarBuf::new_owned();
5542 owned.as_owned_mut().unwrap().push_str("owned");
5543 assert!(matches!(owned, super::FlowScalarBuf::Owned(ref s) if s == "owned"));
5544 }
5545
5546 fn first_scanner_error_kind(input: &str) -> ErrorKind {
5547 first_scanner_error(input).kind().clone()
5548 }
5549
5550 fn first_buffered_scanner_error_kind(input: &str) -> ErrorKind {
5551 let mut scanner = Scanner::new(BufferedInput::new(input.chars()));
5552 loop {
5553 match scanner.next_token() {
5554 Ok(Some(_)) => {}
5555 Ok(None) => panic!("expected scanner error"),
5556 Err(error) => return error.kind().clone(),
5557 }
5558 }
5559 }
5560
5561 fn first_scanner_error(input: &str) -> ScanError {
5562 let mut scanner = Scanner::new(StrInput::new(input));
5563 loop {
5564 match scanner.next_token() {
5565 Ok(Some(_)) => {}
5566 Ok(None) => panic!("expected scanner error"),
5567 Err(error) => return error,
5568 }
5569 }
5570 }
5571
5572 fn first_scalar_value(input: &str) -> String {
5573 let mut scanner = Scanner::new(StrInput::new(input));
5574 loop {
5575 match scanner.next_token().expect("scanner should not error") {
5576 Some(Token(_, TokenType::Scalar(_, value))) => return value.into_owned(),
5577 Some(_) => {}
5578 None => panic!("expected scalar token"),
5579 }
5580 }
5581 }
5582
5583 fn first_buffered_scalar_value(input: &str) -> String {
5584 let mut scanner = Scanner::new(BufferedInput::new(input.chars()));
5585 loop {
5586 match scanner.next_token().expect("scanner should not error") {
5587 Some(Token(_, TokenType::Scalar(_, value))) => return value.into_owned(),
5588 Some(_) => {}
5589 None => panic!("expected scalar token"),
5590 }
5591 }
5592 }
5593
5594 #[test]
5595 fn iterator_next_emits_error_and_then_stays_empty() {
5596 let mut scanner = Scanner::new(StrInput::new("\"unterminated"));
5597
5598 let error = scanner
5599 .by_ref()
5600 .find_map(Result::err)
5601 .expect("scanner should emit the error");
5602 assert_eq!(error.kind(), &ErrorKind::UnclosedQuotedScalar);
5603 assert!(scanner.next().is_none());
5604 }
5605
5606 #[test]
5607 fn next_token_returns_none_after_stream_end() {
5608 let mut scanner = Scanner::new(StrInput::new(""));
5609
5610 while let Some(token) = scanner.next_token().unwrap() {
5611 if matches!(token.1, TokenType::StreamEnd) {
5612 break;
5613 }
5614 }
5615
5616 assert!(scanner.stream_started());
5617 assert!(scanner.stream_ended());
5618 assert!(scanner.next_token().unwrap().is_none());
5619 }
5620
5621 #[test]
5622 fn directive_name_must_be_present() {
5623 assert_eq!(
5624 first_scanner_error_kind("%\n"),
5625 ErrorKind::MissingDirectiveName
5626 );
5627 }
5628
5629 #[test]
5630 fn yaml_directive_requires_dot_between_version_numbers() {
5631 assert_eq!(
5632 first_scanner_error_kind("%YAML 1\n"),
5633 ErrorKind::MissingYamlVersionSeparator
5634 );
5635 }
5636
5637 #[test]
5638 fn yaml_directive_requires_major_version_number() {
5639 assert_eq!(
5640 first_scanner_error_kind("%YAML .2\n"),
5641 ErrorKind::MissingYamlVersion
5642 );
5643 }
5644
5645 #[test]
5646 fn yaml_directive_rejects_extremely_long_version_number() {
5647 assert_eq!(
5648 first_scanner_error_kind("%YAML 1234567890.2\n"),
5649 ErrorKind::YamlVersionTooLong
5650 );
5651 }
5652
5653 #[test]
5654 fn tag_directive_handle_must_end_with_bang() {
5655 assert_eq!(
5656 first_scanner_error_kind("%TAG !bad tag:example.com,2024:\n"),
5657 ErrorKind::ExpectedTagDirectiveBang
5658 );
5659 }
5660
5661 #[test]
5662 fn tag_directive_handle_must_start_with_bang() {
5663 assert_eq!(
5664 first_scanner_error_kind("%TAG bad! tag:example.com,2024:\n"),
5665 ErrorKind::ExpectedTagBang
5666 );
5667 assert_eq!(
5668 first_buffered_scanner_error_kind("%TAG bad! tag:example.com,2024:\n"),
5669 ErrorKind::ExpectedTagBang
5670 );
5671 }
5672
5673 #[test]
5674 fn tag_directive_prefix_must_start_with_tag_character() {
5675 assert_eq!(
5676 first_scanner_error_kind("%TAG !e! `bad\n"),
5677 ErrorKind::InvalidGlobalTagCharacter
5678 );
5679 }
5680
5681 #[test]
5682 fn tag_directive_prefix_must_end_before_invalid_content() {
5683 assert_eq!(
5684 first_scanner_error_kind("%TAG !e! tag:example.com^suffix\n"),
5685 ErrorKind::InvalidTagDirectiveTerminator
5686 );
5687 }
5688
5689 #[test]
5690 fn tag_directive_prefix_with_uri_escape_is_owned_and_decoded() {
5691 let mut scanner =
5692 Scanner::new(StrInput::new("%TAG !e! tag:example.com,2024:some%20app/\n"));
5693
5694 loop {
5695 let token = scanner
5696 .next_token()
5697 .expect("valid directive should scan")
5698 .expect("scanner must produce a directive token");
5699 if let TokenType::TagDirective(handle, prefix) = token.1 {
5700 assert!(matches!(handle, Cow::Borrowed("!e!")));
5701 assert!(matches!(prefix, Cow::Owned(_)));
5702 assert_eq!(&*prefix, "tag:example.com,2024:some app/");
5703 break;
5704 }
5705 }
5706 }
5707
5708 #[test]
5709 fn bare_bang_tag_scans_as_non_specific_tag() {
5710 let mut scanner = Scanner::new(StrInput::new("! foo\n"));
5711
5712 loop {
5713 let token = scanner
5714 .next_token()
5715 .expect("valid tag should scan")
5716 .expect("scanner must produce a tag token");
5717 if let TokenType::Tag(handle, suffix) = token.1 {
5718 assert_eq!(&*handle, "");
5719 assert_eq!(&*suffix, "!");
5720 break;
5721 }
5722 }
5723 }
5724
5725 #[test]
5726 fn tag_requires_separation_after_suffix() {
5727 assert_eq!(
5728 first_scanner_error_kind("!foo,bar\n"),
5729 ErrorKind::InvalidTagTerminator
5730 );
5731 }
5732
5733 #[test]
5734 fn verbatim_tag_requires_uri() {
5735 assert_eq!(
5736 first_scanner_error_kind("!<> foo\n"),
5737 ErrorKind::MissingTagUri
5738 );
5739 }
5740
5741 #[test]
5742 fn verbatim_tag_requires_closing_angle_bracket() {
5743 assert_eq!(
5744 first_scanner_error_kind("!<tag:yaml.org,2002:str foo\n"),
5745 ErrorKind::UnclosedVerbatimTag
5746 );
5747 }
5748
5749 #[test]
5750 fn tag_uri_escape_requires_hex_digits() {
5751 assert_eq!(
5752 first_scanner_error_kind("!!bad%zz foo\n"),
5753 ErrorKind::InvalidTagEscape
5754 );
5755 }
5756
5757 #[test]
5758 fn tag_uri_escape_rejects_bad_leading_utf8_byte() {
5759 assert_eq!(
5760 first_scanner_error_kind("!!bad%80 foo\n"),
5761 ErrorKind::InvalidTagUtf8LeadingByte
5762 );
5763 }
5764
5765 #[test]
5766 fn tag_uri_escape_rejects_bad_trailing_utf8_byte() {
5767 assert_eq!(
5768 first_scanner_error_kind("!!bad%C2%41 foo\n"),
5769 ErrorKind::InvalidTagUtf8TrailingByte
5770 );
5771 }
5772
5773 #[test]
5774 fn tag_uri_escape_rejects_invalid_utf8_codepoint() {
5775 assert_eq!(
5776 first_scanner_error_kind("!!bad%F4%90%80%80 foo\n"),
5777 ErrorKind::InvalidTagUtf8
5778 );
5779 }
5780
5781 #[test]
5782 fn anchors_and_aliases_require_names() {
5783 assert_eq!(
5784 first_scanner_error_kind("& \n"),
5785 ErrorKind::MissingAnchorOrAliasName
5786 );
5787 assert_eq!(
5788 first_scanner_error_kind("* \n"),
5789 ErrorKind::MissingAnchorOrAliasName
5790 );
5791 }
5792
5793 #[test]
5794 fn document_end_marker_rejects_trailing_content() {
5795 assert_eq!(
5796 first_scanner_error_kind("... trailing\n"),
5797 ErrorKind::InvalidDocumentEnd
5798 );
5799 }
5800
5801 #[test]
5802 fn reserved_indicators_are_rejected_outside_directives() {
5803 let error = first_scanner_error(" @\n");
5804
5805 assert_eq!(
5806 error.kind(),
5807 &ErrorKind::UnexpectedCharacter { character: '@' }
5808 );
5809 }
5810
5811 #[test]
5812 fn flow_block_entry_indicator_is_rejected() {
5813 assert_eq!(
5814 first_scanner_error_kind("[- ]\n"),
5815 ErrorKind::BlockEntryInFlowCollection
5816 );
5817 }
5818
5819 #[test]
5820 fn block_entry_after_tabbed_separator_reports_specific_error() {
5821 assert_eq!(
5822 first_scanner_error_kind("-\t- value\n"),
5823 ErrorKind::InvalidBlockEntryWhitespace
5824 );
5825 }
5826
5827 #[test]
5828 fn document_indicator_reports_unclosed_flow_collection() {
5829 let error = first_scanner_error("[\n---\n");
5830
5831 assert_eq!(
5832 error.kind(),
5833 &ErrorKind::UnclosedFlowCollection { open: '[' }
5834 );
5835 }
5836
5837 #[test]
5838 fn block_scalar_header_rejects_trailing_content() {
5839 assert_eq!(
5840 first_scanner_error_kind("|+ trailing\n"),
5841 ErrorKind::InvalidBlockScalarHeader
5842 );
5843 }
5844
5845 #[test]
5846 fn block_scalar_rejects_zero_indent_indicator() {
5847 assert_eq!(
5848 first_scanner_error_kind("|0\n"),
5849 ErrorKind::ZeroBlockScalarIndent
5850 );
5851 assert_eq!(
5852 first_scanner_error_kind("|+0\n"),
5853 ErrorKind::ZeroBlockScalarIndent
5854 );
5855 }
5856
5857 #[test]
5858 fn empty_block_scalar_at_eof_honors_chomping() {
5859 assert_eq!(first_scalar_value("|\n"), "");
5860 assert_eq!(first_scalar_value("|-\n"), "");
5861 assert_eq!(first_scalar_value("|+\n"), "");
5862 assert_eq!(first_scalar_value("|+\n\n"), "\n");
5863 assert_eq!(first_scalar_value("|+\n "), "\n");
5864 }
5865
5866 #[test]
5867 fn buffered_block_scalar_reads_content_past_lookahead_window() {
5868 assert_eq!(
5869 first_buffered_scalar_value("|\n abcdefghijklmnopqrstuvwxyz\n"),
5870 "abcdefghijklmnopqrstuvwxyz\n"
5871 );
5872 }
5873
5874 #[test]
5875 fn explicit_indent_block_scalar_can_end_at_document_marker() {
5876 assert_eq!(first_scalar_value("|1\n...\n"), "");
5877 }
5878
5879 #[test]
5880 fn root_explicit_indent_block_scalar_rejects_underindented_content() {
5881 assert_eq!(
5882 first_scanner_error_kind("|2\nx\n"),
5883 ErrorKind::InvalidBlockScalarIndent
5884 );
5885 }
5886
5887 #[test]
5888 fn quoted_scalar_rejects_document_indicator_at_line_start() {
5889 assert_eq!(
5890 first_scanner_error_kind("\"one\n---\ntwo\"\n"),
5891 ErrorKind::DocumentIndicatorInQuotedScalar
5892 );
5893 }
5894
5895 #[test]
5896 fn quoted_scalar_rejects_tab_indentation_after_line_break() {
5897 assert_eq!(
5898 first_scanner_error_kind("a: \"one\n\tbad\"\n"),
5899 ErrorKind::TabInIndentation
5900 );
5901 }
5902
5903 #[test]
5904 fn quoted_scalar_rejects_underindented_continuation() {
5905 assert_eq!(
5906 first_scanner_error_kind("a: \"one\nbad\"\n"),
5907 ErrorKind::InvalidQuotedScalarIndent
5908 );
5909 }
5910
5911 #[test]
5912 fn quoted_scalar_trailing_content_error_names_quote_style() {
5913 assert_eq!(
5914 first_scanner_error_kind("'foo' trailing\n"),
5915 ErrorKind::InvalidTrailingSingleQuotedScalar
5916 );
5917 assert_eq!(
5918 first_scanner_error_kind("\"foo\" trailing\n"),
5919 ErrorKind::InvalidTrailingDoubleQuotedScalar
5920 );
5921 }
5922
5923 #[test]
5924 fn quoted_scalar_escape_errors_cover_hex_and_surrogate_edges() {
5925 assert_eq!(
5926 first_scanner_error_kind("\"\\xG0\"\n"),
5927 ErrorKind::InvalidQuotedScalarHexEscape
5928 );
5929 assert_eq!(
5930 first_scanner_error_kind("\"\\uD800\\uGGGG\"\n"),
5931 ErrorKind::InvalidLowSurrogateHexEscape
5932 );
5933 assert_eq!(
5934 first_scanner_error_kind("\"\\uD800\\u0041\"\n"),
5935 ErrorKind::InvalidLowSurrogate
5936 );
5937 assert_eq!(
5938 first_scanner_error_kind("\"\\U00110000\"\n"),
5939 ErrorKind::InvalidUnicodeEscape
5940 );
5941 }
5942
5943 #[test]
5944 fn under_indented_flow_scalar_is_accepted() {
5945 let tokens: Vec<_> = Scanner::new(StrInput::new("a:\n [\nfoo]\n"))
5946 .map(|result| result.expect("under-indented flow entry should scan").1)
5947 .collect();
5948 assert_eq!(
5949 tokens,
5950 [
5951 TokenType::StreamStart,
5952 TokenType::BlockMappingStart,
5953 TokenType::Key,
5954 TokenType::Scalar(ScalarStyle::Plain, "a".into()),
5955 TokenType::Value,
5956 TokenType::FlowSequenceStart,
5957 TokenType::Scalar(ScalarStyle::Plain, "foo".into()),
5958 TokenType::FlowSequenceEnd,
5959 TokenType::BlockEnd,
5960 TokenType::StreamEnd,
5961 ]
5962 );
5963 }
5964
5965 #[test]
5966 fn required_simple_key_requires_value_at_stream_end() {
5967 let error = first_scanner_error("a:\n&b\n- c\n");
5968
5969 assert_eq!(error.kind(), &ErrorKind::SimpleKeyExpected);
5970 assert_eq!(error.marker().index(), 3);
5971 assert_eq!(error.marker().line(), 2);
5972 assert_eq!(error.marker().col(), 0);
5973 }
5974
5975 #[test]
5976 fn first_simple_key_tracks_ordered_possible_candidates() {
5977 let deep = alloc::format!(
5978 "{}{}{{[é, z]: v}}{}\n",
5979 "[".repeat(240),
5980 "a, ".repeat(400),
5981 "]".repeat(240)
5982 );
5983 for source in [
5984 "",
5985 "a: b\nmissing",
5986 "a: b\nmissing\n# after\nnext: value\n",
5987 "{[a, {b: c}]: [d, e], []: f}\n",
5988 "[&a !tag [x, y], *a, {z: []}]\n",
5989 "key: {\n&anchor\nname: value}\n",
5990 "---\n{[a, b]: c} # end\n...\n# next\n---\nx: y\n",
5991 "a: b\n[unterminated",
5992 &deep,
5993 ] {
5994 for limit in [0, 4, 1024, usize::MAX] {
5995 for emit_comments in [false, true] {
5996 let mut scanner = Scanner::with_options(
5997 StrInput::new(source),
5998 crate::options! {
5999 simple_key_max_lookahead: limit,
6000 emit_comments: emit_comments,
6001 },
6002 );
6003 loop {
6004 let result = scanner.next();
6005 assert_eq!(
6006 scanner.first_simple_key,
6007 scanner.simple_keys.iter().position(|key| key.possible),
6008 "source: {source:?}, limit: {limit}, comments: {emit_comments}"
6009 );
6010 let mut previous = None;
6011 for key in scanner.simple_keys.iter().filter(|key| key.possible) {
6012 assert!(key.token_number >= scanner.tokens_parsed);
6013 if let Some((index, token_number)) = previous {
6014 assert!(key.mark.index() >= index);
6015 assert!(key.token_number >= token_number);
6016 }
6017 previous = Some((key.mark.index(), key.token_number));
6018 }
6019 if result.is_none() {
6020 break;
6021 }
6022 }
6023 }
6024 }
6025 }
6026 }
6027
6028 #[test]
6029 fn plain_scalar_rejects_dash_before_flow_indicator() {
6030 assert_eq!(
6031 first_scanner_error_kind("[-]\n"),
6032 ErrorKind::PlainScalarStartsWithDashFlowIndicator
6033 );
6034 }
6035
6036 #[test]
6037 fn explicit_key_rejects_tab_after_indicator() {
6038 assert_eq!(
6039 first_scanner_error_kind("? \tfoo\n"),
6040 ErrorKind::TabNotAllowed
6041 );
6042 }
6043
6044 #[test]
6045 fn flow_mapping_rejects_adjacent_collection_value_after_plain_key() {
6046 assert_eq!(
6047 first_scanner_error_kind("[a:[]]\n"),
6048 ErrorKind::FlowMappingValueAdjacentCollection
6049 );
6050 }
6051
6052 #[test]
6053 fn implicit_flow_mapping_colon_cannot_move_to_next_line() {
6054 assert_eq!(
6055 first_scanner_error_kind("[foo\n: bar]\n"),
6056 ErrorKind::InvalidColonPlacement
6057 );
6058 }
6059
6060 #[test]
6061 fn invalid_simple_key_token_positions_are_scan_errors() {
6062 for (tokens_parsed, token_number) in [(1, 0), (0, 1)] {
6063 let mut scanner = Scanner::new(StrInput::new(": value\n"));
6064 scanner.fetch_stream_start();
6065 scanner.tokens.clear();
6066 scanner.tokens_parsed = tokens_parsed;
6067
6068 let simple_key = scanner
6069 .simple_keys
6070 .last_mut()
6071 .expect("stream start should create a simple key slot");
6072 simple_key.possible = true;
6073 simple_key.token_number = token_number;
6074
6075 let error = scanner
6076 .fetch_value()
6077 .expect_err("invalid simple key position should be reported as a scan error");
6078 assert_eq!(error.kind(), &ErrorKind::InvalidSimpleKey);
6079 assert_eq!(error.marker(), &Marker::new(0, 1, 0));
6080 }
6081 }
6082
6083 #[test]
6084 fn issue14_alias_scanner_consumes_colon_as_name_character() {
6085 let mut scanner = Scanner::new(StrInput::new("*foo: bar\n"));
6086
6087 assert!(matches!(
6088 scanner.next_token().unwrap().unwrap().1,
6089 TokenType::StreamStart
6090 ));
6091
6092 let token = scanner.next_token().unwrap().unwrap();
6093
6094 assert!(
6095 matches!(token.1, TokenType::Alias(ref name) if name.as_ref() == "foo:"),
6096 "expected `*foo: bar` to start with Alias(\"foo:\"), got {token:?}"
6097 );
6098 }
6099
6100 #[test]
6101 fn issue14_anchor_scanner_consumes_colon_as_name_character() {
6102 let mut scanner = Scanner::new(StrInput::new("&foo: bar\n"));
6103
6104 assert!(matches!(
6105 scanner.next_token().unwrap().unwrap().1,
6106 TokenType::StreamStart
6107 ));
6108
6109 let token = scanner.next_token().unwrap().unwrap();
6110
6111 assert!(
6112 matches!(token.1, TokenType::Anchor(ref name) if name.as_ref() == "foo:"),
6113 "expected `&foo: bar` to start with Anchor(\"foo:\"), got {token:?}"
6114 );
6115 }
6116}