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 indent: isize,
781 indents: smallvec::SmallVec<[Indent; 8]>,
783 flow_level: usize,
785 block_level: usize,
787 tokens_parsed: usize,
791 token_available: bool,
793 leading_whitespace: bool,
795 flow_mapping_started: smallvec::SmallVec<[bool; 8]>,
802 implicit_flow_mapping_states: smallvec::SmallVec<[ImplicitMappingState; 8]>,
815 interrupted_plain_by_comment: Option<Marker>,
818 explicit_key_tab_check_pending: bool,
823 flow_markers: smallvec::SmallVec<[(Marker, char); 8]>,
825 buf_leading_break: String,
826 buf_trailing_breaks: String,
827 buf_whitespaces: String,
828}
829
830impl<'input, T: BorrowedInput<'input>> Iterator for Scanner<'input, T> {
831 type Item = Result<Token<'input>, ScanError>;
832
833 fn next(&mut self) -> Option<Self::Item> {
834 if self.failed {
835 return None;
836 }
837 match self.next_token() {
838 Ok(Some(tok)) => {
839 debug_print!(
840 " \x1B[;32m\u{21B3} {:?} \x1B[;36m{:?}\x1B[;m",
841 tok.1,
842 tok.0
843 );
844 Some(Ok(tok))
845 }
846 Ok(None) => None,
847 Err(error) => {
848 self.failed = true;
849 Some(Err(error))
850 }
851 }
852 }
853}
854
855impl<'input, T: BorrowedInput<'input>> core::iter::FusedIterator for Scanner<'input, T> {}
856
857type ScanResult = Result<(), ScanError>;
859
860#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
865struct SkipToNextTokenOutcome {
866 saw_comment: bool,
867 queued_comment: bool,
868}
869
870#[derive(Debug)]
871enum FlowScalarBuf {
872 Borrowed {
878 start: usize,
879 end: usize,
880 pending_ws_start: Option<usize>,
881 pending_ws_end: usize,
882 },
883 Owned(String),
884}
885
886impl FlowScalarBuf {
887 #[inline]
888 fn new_borrowed(start: usize) -> Self {
889 Self::Borrowed {
890 start,
891 end: start,
892 pending_ws_start: None,
893 pending_ws_end: start,
894 }
895 }
896
897 #[inline]
898 fn new_owned() -> Self {
899 Self::Owned(String::new())
900 }
901
902 #[inline]
903 fn as_owned_mut(&mut self) -> Option<&mut String> {
904 match self {
905 Self::Owned(s) => Some(s),
906 Self::Borrowed { .. } => None,
907 }
908 }
909
910 #[inline]
911 fn commit_pending_ws(&mut self) {
912 if let Self::Borrowed {
913 end,
914 pending_ws_start,
915 pending_ws_end,
916 ..
917 } = self
918 {
919 if pending_ws_start.is_some() {
920 *end = *pending_ws_end;
921 *pending_ws_start = None;
922 }
923 }
924 }
925
926 #[inline]
927 fn note_pending_ws(&mut self, ws_start: usize, ws_end: usize) {
928 if let Self::Borrowed {
929 pending_ws_start,
930 pending_ws_end,
931 ..
932 } = self
933 {
934 if pending_ws_start.is_none() {
935 *pending_ws_start = Some(ws_start);
936 }
937 *pending_ws_end = ws_end;
938 }
939 }
940
941 #[inline]
942 fn discard_pending_ws(&mut self) {
943 if let Self::Borrowed {
944 pending_ws_start,
945 pending_ws_end,
946 end,
947 ..
948 } = self
949 {
950 *pending_ws_start = None;
951 *pending_ws_end = *end;
952 }
953 }
954}
955
956impl<'input, T: BorrowedInput<'input>> Scanner<'input, T> {
957 #[inline]
958 fn promote_flow_scalar_buf_to_owned(
959 &self,
960 start_mark: &Marker,
961 buf: &mut FlowScalarBuf,
962 ) -> Result<(), ScanError> {
963 let FlowScalarBuf::Borrowed {
964 start,
965 end,
966 pending_ws_start: _,
967 pending_ws_end: _,
968 } = *buf
969 else {
970 return Ok(());
971 };
972
973 let slice = self.input.slice_bytes(start, end).ok_or_else(|| {
974 ScanError::from_kind(*start_mark, ErrorKind::InputOffsetsWithoutSlice)
975 })?;
976 *buf = FlowScalarBuf::Owned(slice.to_owned());
977 Ok(())
978 }
979 #[inline]
985 fn try_borrow_slice(&self, start: usize, end: usize) -> Option<&'input str> {
986 self.input.slice_borrowed(start, end)
987 }
988
989 fn scan_tag_handle_directive_cow(
994 &mut self,
995 mark: &Marker,
996 budget: &mut usize,
997 ) -> Result<Cow<'input, str>, ScanError> {
998 let Some(start) = self.input.byte_offset() else {
999 return Ok(Cow::Owned(
1000 self.scan_tag_handle_directive_owned(mark, budget)?,
1001 ));
1002 };
1003
1004 if self.input.look_ch() != '!' {
1005 return Err(ScanError::from_kind(*mark, ErrorKind::ExpectedTagBang));
1006 }
1007
1008 self.spend_directive_bytes(budget, 1, *mark)?;
1010 self.skip_non_blank();
1011
1012 self.input.lookahead(1);
1015 while self.input.next_is_alpha() {
1016 self.spend_directive_bytes(budget, 1, *mark)?;
1017 self.skip_non_blank();
1018 self.input.lookahead(1);
1019 }
1020
1021 if self.input.peek() == '!' {
1023 self.spend_directive_bytes(budget, 1, *mark)?;
1024 self.skip_non_blank();
1025 }
1026
1027 let Some(end) = self.input.byte_offset() else {
1028 return Err(ScanError::from_kind(
1029 *mark,
1030 ErrorKind::InputSlicingUnavailable,
1031 ));
1032 };
1033
1034 let Some(slice) = self.try_borrow_slice(start, end) else {
1035 let slice = self
1037 .input
1038 .slice_bytes(start, end)
1039 .ok_or_else(|| ScanError::from_kind(*mark, ErrorKind::InputSlicingUnavailable))?;
1040 if !slice.ends_with('!') && slice != "!" {
1041 return Err(ScanError::from_kind(
1042 *mark,
1043 ErrorKind::ExpectedTagDirectiveBang,
1044 ));
1045 }
1046 return Ok(Cow::Owned(slice.to_owned()));
1047 };
1048
1049 if !slice.ends_with('!') && slice != "!" {
1050 return Err(ScanError::from_kind(
1051 *mark,
1052 ErrorKind::ExpectedTagDirectiveBang,
1053 ));
1054 }
1055
1056 Ok(Cow::Borrowed(slice))
1057 }
1058
1059 fn scan_tag_handle_directive_owned(
1061 &mut self,
1062 mark: &Marker,
1063 budget: &mut usize,
1064 ) -> Result<String, ScanError> {
1065 let mut string = String::new();
1066 if self.input.look_ch() != '!' {
1067 return Err(ScanError::from_kind(*mark, ErrorKind::ExpectedTagBang));
1068 }
1069
1070 self.spend_directive_bytes(budget, 1, *mark)?;
1071 string.push('!');
1072 self.skip_non_blank();
1073
1074 self.input.lookahead(1);
1075 while self.input.next_is_alpha() {
1076 self.spend_directive_bytes(budget, 1, *mark)?;
1077 string.push(self.input.peek());
1078 self.skip_non_blank();
1079 self.input.lookahead(1);
1080 }
1081
1082 if self.input.peek() == '!' {
1083 self.spend_directive_bytes(budget, 1, *mark)?;
1084 string.push('!');
1085 self.skip_non_blank();
1086 } else if string != "!" {
1087 return Err(ScanError::from_kind(
1088 *mark,
1089 ErrorKind::ExpectedTagDirectiveBang,
1090 ));
1091 }
1092
1093 Ok(string)
1094 }
1095
1096 fn scan_tag_prefix_directive_cow(
1101 &mut self,
1102 start_mark: &Marker,
1103 budget: &mut usize,
1104 ) -> Result<Cow<'input, str>, ScanError> {
1105 let Some(start) = self.input.byte_offset() else {
1106 return Ok(Cow::Owned(
1107 self.scan_tag_prefix_directive_owned(start_mark, budget)?,
1108 ));
1109 };
1110
1111 if self.input.look_ch() == '!' {
1113 self.spend_directive_bytes(budget, 1, *start_mark)?;
1114 self.skip_non_blank();
1115 } else if !is_tag_char(self.input.peek()) {
1116 return Err(ScanError::from_kind(
1117 *start_mark,
1118 ErrorKind::InvalidGlobalTagCharacter,
1119 ));
1120 } else if self.input.peek() == '%' {
1121 } else {
1123 self.spend_directive_bytes(budget, 1, *start_mark)?;
1124 self.skip_non_blank();
1125 }
1126
1127 while is_uri_char(self.input.look_ch()) {
1129 if self.input.peek() == '%' {
1130 break;
1131 }
1132 self.spend_directive_bytes(budget, 1, *start_mark)?;
1133 self.skip_non_blank();
1134 }
1135
1136 if self.input.peek() == '%' {
1138 let current = self
1139 .input
1140 .byte_offset()
1141 .expect("byte_offset() must remain available once enabled");
1142 let mut out = if let Some(slice) = self.input.slice_bytes(start, current) {
1143 slice.to_owned()
1144 } else {
1145 String::new()
1146 };
1147
1148 while is_uri_char(self.input.look_ch()) {
1149 if self.input.peek() == '%' {
1150 out.push(self.scan_uri_escapes_with_budget(start_mark, Some(budget))?);
1151 } else {
1152 self.spend_directive_bytes(budget, 1, *start_mark)?;
1153 out.push(self.input.peek());
1154 self.skip_non_blank();
1155 }
1156 }
1157 return Ok(Cow::Owned(out));
1158 }
1159
1160 let Some(end) = self.input.byte_offset() else {
1161 return Err(ScanError::from_kind(
1162 *start_mark,
1163 ErrorKind::InputSlicingUnavailable,
1164 ));
1165 };
1166
1167 let Some(slice) = self.try_borrow_slice(start, end) else {
1168 let slice = self.input.slice_bytes(start, end).ok_or_else(|| {
1170 ScanError::from_kind(*start_mark, ErrorKind::InputSlicingUnavailable)
1171 })?;
1172 return Ok(Cow::Owned(slice.to_owned()));
1173 };
1174
1175 Ok(Cow::Borrowed(slice))
1176 }
1177
1178 fn scan_tag_prefix_directive_owned(
1180 &mut self,
1181 start_mark: &Marker,
1182 budget: &mut usize,
1183 ) -> Result<String, ScanError> {
1184 let mut string = String::new();
1185
1186 if self.input.look_ch() == '!' {
1187 self.spend_directive_bytes(budget, 1, *start_mark)?;
1188 string.push('!');
1189 self.skip_non_blank();
1190 } else if !is_tag_char(self.input.peek()) {
1191 return Err(ScanError::from_kind(
1192 *start_mark,
1193 ErrorKind::InvalidGlobalTagCharacter,
1194 ));
1195 } else if self.input.peek() == '%' {
1196 string.push(self.scan_uri_escapes_with_budget(start_mark, Some(budget))?);
1197 } else {
1198 self.spend_directive_bytes(budget, 1, *start_mark)?;
1199 string.push(self.input.peek());
1200 self.skip_non_blank();
1201 }
1202
1203 while is_uri_char(self.input.look_ch()) {
1204 if self.input.peek() == '%' {
1205 string.push(self.scan_uri_escapes_with_budget(start_mark, Some(budget))?);
1206 } else {
1207 self.spend_directive_bytes(budget, 1, *start_mark)?;
1208 string.push(self.input.peek());
1209 self.skip_non_blank();
1210 }
1211 }
1212
1213 Ok(string)
1214 }
1215 #[must_use]
1217 pub fn new(input: T) -> Self {
1218 Self::with_options(input, Options::default())
1219 }
1220
1221 #[must_use]
1227 pub fn with_options(input: T, options: Options) -> Self {
1228 let initial_byte_offset = input.byte_offset();
1229 let comments_possible = !options.emit_comments || input.may_contain_comments();
1232 Scanner {
1233 input,
1234 options,
1235 mark: Marker::new(0, 1, 0).with_byte_offset(initial_byte_offset),
1236 tokens: VecDeque::with_capacity(64),
1237 failed: false,
1238 deferred_error: None,
1239 comments_possible,
1240
1241 stream_start_produced: false,
1242 stream_end_produced: false,
1243 document_prefix_allowed: true,
1244 adjacent_value_allowed_at: 0,
1245 simple_key_allowed: true,
1246 simple_keys: smallvec::SmallVec::new(),
1247 indent: -1,
1248 indents: smallvec::SmallVec::new(),
1249 flow_level: 0,
1250 block_level: 0,
1251 tokens_parsed: 0,
1252 token_available: false,
1253 leading_whitespace: true,
1254 flow_mapping_started: smallvec::SmallVec::new(),
1255 implicit_flow_mapping_states: smallvec::SmallVec::new(),
1256 flow_markers: smallvec::SmallVec::new(),
1257 interrupted_plain_by_comment: None,
1258 explicit_key_tab_check_pending: false,
1259
1260 buf_leading_break: String::with_capacity(128),
1261 buf_trailing_breaks: String::with_capacity(128),
1262 buf_whitespaces: String::with_capacity(128),
1263 }
1264 }
1265
1266 #[cold]
1267 fn scan_error(&self, kind: ErrorKind) -> ScanError {
1268 ScanError::from_kind(self.mark, kind)
1269 }
1270
1271 #[inline]
1272 fn ensure_current_char_is_printable(&self) -> ScanResult {
1273 let character = self.input.peek();
1274 if self.input.next_is_z() || is_printable(character) {
1275 Ok(())
1276 } else {
1277 Err(self.scan_error(ErrorKind::UnexpectedCharacter { character }))
1278 }
1279 }
1280
1281 #[cold]
1282 fn simple_key_expected(mark: Marker) -> ScanError {
1283 ScanError::from_kind(mark, ErrorKind::SimpleKeyExpected)
1284 }
1285
1286 #[cold]
1287 fn unclosed_bracket(mark: Marker, bracket: char) -> ScanError {
1288 ScanError::from_kind(mark, ErrorKind::UnclosedFlowCollection { open: bracket })
1289 }
1290
1291 #[inline]
1293 fn skip_blank(&mut self) {
1294 self.input.skip();
1295
1296 self.mark.offsets.chars += 1;
1297 self.mark.col += 1;
1298 self.mark.offsets.bytes = self.input.byte_offset();
1299 }
1300
1301 #[inline]
1303 fn skip_non_blank(&mut self) {
1304 self.input.skip();
1305
1306 self.mark.offsets.chars += 1;
1307 self.mark.col += 1;
1308 self.mark.offsets.bytes = self.input.byte_offset();
1309 self.leading_whitespace = false;
1310 }
1311
1312 #[inline]
1317 fn skip_bom(&mut self) {
1318 self.input.skip();
1319
1320 self.mark.offsets.chars += 1;
1321 self.mark.offsets.bytes = self.input.byte_offset();
1322 }
1323
1324 #[inline]
1330 fn skip_comment_char(&mut self) {
1331 self.input.skip();
1332
1333 self.mark.offsets.chars += 1;
1334 self.mark.col += 1;
1335 self.mark.offsets.bytes = self.input.byte_offset();
1336 }
1337
1338 #[inline]
1340 fn skip_n_non_blank(&mut self, count: usize) {
1341 for _ in 0..count {
1342 self.input.skip();
1343 self.mark.offsets.chars += 1;
1344 self.mark.col += 1;
1345 }
1346 self.mark.offsets.bytes = self.input.byte_offset();
1347 self.leading_whitespace = false;
1348 }
1349
1350 #[inline]
1352 fn skip_nl(&mut self) {
1353 self.input.skip();
1354
1355 self.mark.offsets.chars += 1;
1356 self.mark.col = 0;
1357 self.mark.line += 1;
1358 self.mark.offsets.bytes = self.input.byte_offset();
1359 self.leading_whitespace = true;
1360 }
1361
1362 #[inline]
1364 fn skip_linebreak(&mut self) {
1365 if self.input.next_2_are('\r', '\n') {
1366 self.skip_blank();
1369 self.skip_nl();
1370 } else if self.input.next_is_break() {
1371 self.skip_nl();
1372 }
1373 }
1374
1375 #[cfg(test)]
1376 fn scan_comment_token(&mut self) -> Result<Token<'input>, ScanError> {
1377 Ok(self.scan_comment_queued_token()?.into_public())
1378 }
1379
1380 fn scan_comment_queued_token(&mut self) -> Result<QueuedToken<'input>, ScanError> {
1381 let start_mark = self.mark;
1382 debug_assert_eq!(self.input.peek(), '#');
1383 let placement = if self.leading_whitespace {
1384 Placement::Free
1385 } else {
1386 Placement::Right
1387 };
1388
1389 self.skip_comment_char();
1390
1391 let text = if let Some(start) = self.input.byte_offset() {
1392 let n = self.input.skip_while_non_breakz();
1394 self.mark.offsets.chars += n;
1395 self.mark.col += n;
1396 let byte_offset = self.input.byte_offset();
1397 self.mark.offsets.bytes = byte_offset;
1398 let end = byte_offset.expect("byte_offset must remain available once enabled");
1399
1400 if let Some(slice) = self.try_borrow_slice(start, end) {
1401 Cow::Borrowed(slice)
1402 } else if let Some(slice) = self.input.slice_bytes(start, end) {
1403 Cow::Owned(slice.to_owned())
1405 } else {
1406 return Err(ScanError::from_kind(
1407 start_mark,
1408 ErrorKind::InputOffsetsWithoutSlice,
1409 ));
1410 }
1411 } else {
1412 let mut owned = String::new();
1414 while {
1415 let character = self.input.look_ch();
1416 !is_breakz(character) && is_printable(character)
1417 } {
1418 owned.push(self.input.peek());
1419 self.skip_comment_char();
1420 }
1421 Cow::Owned(owned)
1422 };
1423
1424 self.ensure_current_char_is_printable()?;
1425
1426 let end_mark = self.mark;
1427 let span = Span::new(start_mark, end_mark);
1428 Ok(QueuedToken(
1429 span,
1430 QueuedTokenType::Comment(Comment { text, placement }),
1431 ))
1432 }
1433
1434 fn push_comment_token(&mut self) -> ScanResult {
1435 let token = self.scan_comment_queued_token()?;
1436 self.tokens.push_back(token);
1437 Ok(())
1438 }
1439
1440 fn consume_comment(&mut self) -> Result<bool, ScanError> {
1442 if self.options.emit_comments {
1443 self.push_comment_token()?;
1444 Ok(true)
1445 } else {
1446 self.skip_comment()?;
1447 Ok(false)
1448 }
1449 }
1450
1451 fn skip_comment(&mut self) -> ScanResult {
1452 debug_assert_eq!(self.input.peek(), '#');
1453
1454 self.skip_comment_char();
1455 let n = self.input.skip_while_non_breakz();
1456 self.mark.offsets.chars += n;
1457 self.mark.col += n;
1458 self.mark.offsets.bytes = self.input.byte_offset();
1459 self.ensure_current_char_is_printable()
1460 }
1461
1462 #[inline]
1464 #[must_use]
1465 pub fn stream_started(&self) -> bool {
1466 self.stream_start_produced
1467 }
1468
1469 #[inline]
1471 #[must_use]
1472 pub fn stream_ended(&self) -> bool {
1473 self.stream_end_produced
1474 }
1475
1476 #[inline]
1478 #[must_use]
1479 pub fn mark(&self) -> Marker {
1480 self.mark
1481 }
1482
1483 #[inline]
1485 pub(crate) fn comments_possible(&self) -> bool {
1486 self.options.emit_comments && self.comments_possible
1487 }
1488
1489 #[track_caller]
1496 #[inline]
1497 fn read_break(&mut self, s: &mut String) {
1498 self.skip_break();
1499 s.push('\n');
1500 }
1501
1502 #[track_caller]
1507 #[inline]
1508 fn skip_break(&mut self) {
1509 let c = self.input.peek();
1510 let nc = self.input.peek_nth(1);
1511 debug_assert!(is_break(c));
1512 if c == '\r' && nc == '\n' {
1513 self.skip_blank();
1514 }
1515 self.skip_nl();
1516 }
1517
1518 #[track_caller]
1523 fn insert_token(&mut self, pos: usize, tok: Token<'input>) {
1524 let old_len = self.tokens.len();
1525 assert!(pos <= old_len);
1526 self.tokens.insert(pos, tok.into());
1527 }
1528
1529 fn simple_key_token_index(&self, sk: &SimpleKey, mark: Marker) -> Result<usize, ScanError> {
1530 let Some(index) = sk.token_number.checked_sub(self.tokens_parsed) else {
1531 return Err(ScanError::from_kind(mark, ErrorKind::InvalidSimpleKey));
1532 };
1533 if index > self.tokens.len() {
1534 return Err(ScanError::from_kind(mark, ErrorKind::InvalidSimpleKey));
1535 }
1536 Ok(index)
1537 }
1538
1539 #[inline]
1540 fn allow_simple_key(&mut self) {
1541 self.simple_key_allowed = true;
1542 }
1543
1544 #[inline]
1545 fn disallow_simple_key(&mut self) {
1546 self.simple_key_allowed = false;
1547 }
1548
1549 fn fetch_next_token(&mut self) -> ScanResult {
1554 let result = self.fetch_next_token_impl();
1555 if let Some(kind) = self.input.take_source_error() {
1556 return Err(ScanError::from_kind(self.mark, kind));
1557 }
1558 result
1559 }
1560
1561 fn fetch_next_token_impl(&mut self) -> ScanResult {
1562 self.input.lookahead(1);
1563
1564 if !self.stream_start_produced {
1565 self.fetch_stream_start();
1566 return Ok(());
1567 }
1568 loop {
1569 let outcome = self.skip_to_next_token()?;
1570 if outcome.queued_comment || (outcome.saw_comment && !self.tokens.is_empty()) {
1571 return Ok(());
1572 }
1573 if !outcome.saw_comment {
1574 break;
1575 }
1576 }
1579
1580 debug_print!(
1581 " \x1B[38;5;244m\u{2192} fetch_next_token after whitespace {:?} {:?}\x1B[m",
1582 self.mark,
1583 self.input.peek()
1584 );
1585
1586 self.stale_simple_keys()?;
1587
1588 let mark = self.mark;
1589 self.unroll_indent(mark.col as isize);
1590
1591 self.input.lookahead(4);
1592
1593 if self.input.next_is_z() {
1594 self.fetch_stream_end()?;
1595 return Ok(());
1596 }
1597
1598 self.ensure_current_char_is_printable()?;
1599
1600 if self.mark.col == 0 {
1601 if self.input.next_char_is('%') {
1602 return self.fetch_directive();
1603 } else if self.input.next_is_document_start() {
1604 return self.fetch_document_indicator(TokenType::DocumentStart);
1605 } else if self.input.next_is_document_end() {
1606 self.fetch_document_indicator(TokenType::DocumentEnd)?;
1607 self.skip_ws_to_eol(SkipTabs::Yes)?;
1608 if !self.input.next_is_breakz() {
1609 return Err(self.scan_error(ErrorKind::InvalidDocumentEnd));
1610 }
1611 return Ok(());
1612 }
1613 }
1614
1615 if self.document_prefix_allowed {
1616 self.document_prefix_allowed = false;
1617 }
1618
1619 if (self.mark.col as isize) < self.indent {
1620 self.input.lookahead(1);
1621 let c = self.input.peek();
1622 if self.flow_level == 0 || !matches!(c, ']' | '}' | ',') {
1623 return Err(self.scan_error(ErrorKind::InvalidIndentation));
1624 }
1625 }
1626
1627 let c = self.input.peek();
1628 let nc = self.input.peek_nth(1);
1629 match c {
1630 '[' => self.fetch_flow_collection_start(TokenType::FlowSequenceStart),
1631 '{' => self.fetch_flow_collection_start(TokenType::FlowMappingStart),
1632 ']' => self.fetch_flow_collection_end(TokenType::FlowSequenceEnd, '[', ']'),
1633 '}' => self.fetch_flow_collection_end(TokenType::FlowMappingEnd, '{', '}'),
1634 ',' => self.fetch_flow_entry(),
1635 '-' if is_blank_or_breakz(nc) => self.fetch_block_entry(),
1636 '?' if is_blank_or_breakz(nc) => self.fetch_key(),
1637 ':' if is_blank_or_breakz(nc) => self.fetch_value(),
1638 ':' if self.flow_level > 0
1639 && (is_flow(nc) || self.mark.index() == self.adjacent_value_allowed_at) =>
1640 {
1641 self.fetch_flow_value()
1642 }
1643 '*' => self.fetch_anchor(true),
1645 '&' => self.fetch_anchor(false),
1647 '!' => self.fetch_tag(),
1648 '|' if self.flow_level == 0 => self.fetch_block_scalar(true),
1650 '>' if self.flow_level == 0 => self.fetch_block_scalar(false),
1652 '\'' => self.fetch_flow_scalar(true),
1653 '"' => self.fetch_flow_scalar(false),
1654 '-' if !is_blank_or_breakz(nc) => self.fetch_plain_scalar(),
1656 ':' | '?' if !is_blank_or_breakz(nc) && self.flow_level == 0 => {
1657 self.fetch_plain_scalar()
1658 }
1659 c if is_bom(c) => Err(self.scan_error(ErrorKind::BomInsideDocument)),
1660 '%' | '@' | '`' => {
1661 Err(self.scan_error(ErrorKind::UnexpectedCharacter { character: c }))
1662 }
1663 _ => self.fetch_plain_scalar(),
1664 }
1665 }
1666
1667 pub(crate) fn next_queued_token(&mut self) -> Result<Option<QueuedToken<'input>>, ScanError> {
1672 if self.deferred_error.is_some() {
1673 if !matches!(
1674 self.tokens.front().map(|token| &token.1),
1675 Some(QueuedTokenType::Comment(_))
1676 ) {
1677 if let Some(error) = self.deferred_error.take() {
1678 return error.into_result();
1679 }
1680 }
1681 self.token_available = true;
1682 }
1683
1684 if self.stream_end_produced {
1685 return Ok(None);
1686 }
1687
1688 if !self.token_available {
1689 if let Err(error) = self.fetch_more_tokens() {
1690 if matches!(
1691 self.tokens.front().map(|token| &token.1),
1692 Some(QueuedTokenType::Comment(_))
1693 ) {
1694 self.deferred_error = Some(error);
1695 } else {
1696 return Err(error);
1697 }
1698 }
1699 }
1700 let Some(t) = self.tokens.pop_front() else {
1701 unreachable!("fetch_more_tokens succeeded without producing a token")
1702 };
1703 self.token_available = false;
1704 self.tokens_parsed += 1;
1705
1706 let is_stream_end = matches!(t.1, QueuedTokenType::StreamEnd);
1707 if is_stream_end {
1708 self.stream_end_produced = true;
1709 }
1710 Ok(Some(t))
1711 }
1712
1713 fn next_token(&mut self) -> Result<Option<Token<'input>>, ScanError> {
1718 Ok(self.next_queued_token()?.map(QueuedToken::into_public))
1719 }
1720
1721 fn fetch_more_tokens(&mut self) -> ScanResult {
1726 let mut need_more;
1727 loop {
1728 if self.tokens.is_empty() {
1729 need_more = true;
1730 } else {
1731 need_more = false;
1732 self.stale_simple_keys()?;
1734 if !matches!(
1735 self.tokens.front().map(|token| &token.1),
1736 Some(QueuedTokenType::Comment(_))
1737 ) {
1738 for sk in &self.simple_keys {
1740 if sk.possible && sk.token_number == self.tokens_parsed {
1741 need_more = true;
1742 break;
1743 }
1744 }
1745 }
1746 }
1747
1748 if let Some(token) = self.tokens.back() {
1751 if matches!(
1752 token.1,
1753 QueuedTokenType::DocumentEnd | QueuedTokenType::DocumentStart
1754 ) {
1755 break;
1756 }
1757 }
1758
1759 if !need_more {
1760 break;
1761 }
1762 self.fetch_next_token()?;
1763 }
1764 self.token_available = true;
1765
1766 Ok(())
1767 }
1768
1769 fn stale_simple_keys(&mut self) -> ScanResult {
1778 for sk in &mut self.simple_keys {
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 sk.possible && (is_line_stale || is_length_stale) {
1786 if sk.required {
1787 return Err(Self::simple_key_expected(sk.mark));
1788 }
1789 sk.possible = false;
1790 }
1791 }
1792 Ok(())
1793 }
1794
1795 fn skip_to_next_token(&mut self) -> Result<SkipToNextTokenOutcome, ScanError> {
1806 let consume_linebreak = |this: &mut Self| {
1809 this.input.lookahead(2);
1810 this.skip_linebreak();
1811 if this.flow_level == 0 {
1812 this.allow_simple_key();
1813 }
1814 };
1815
1816 let mut outcome = SkipToNextTokenOutcome::default();
1817 loop {
1818 let ch = self.input.look_ch();
1819 if self.explicit_key_tab_check_pending {
1820 match ch {
1821 '\t' => {
1822 return Err(self.scan_error(ErrorKind::TabNotAllowed));
1823 }
1824 ' ' | '\n' | '\r' | '#' => {}
1825 _ => self.explicit_key_tab_check_pending = false,
1826 }
1827 }
1828
1829 match ch {
1830 '\t' => {
1832 if self.is_within_block()
1833 && self.leading_whitespace
1834 && (self.mark.col as isize) < self.indent
1835 {
1836 self.skip_ws_to_eol(SkipTabs::Yes)?;
1837
1838 if !self.input.next_is_breakz() {
1840 return Err(self.scan_error(ErrorKind::TabInBlockIndentation));
1841 }
1842
1843 if matches!(self.input.look_ch(), '\n' | '\r') {
1845 consume_linebreak(self);
1846 }
1847 } else {
1848 self.skip_blank();
1850 }
1851 }
1852
1853 ' ' => self.skip_blank(),
1854
1855 '\n' | '\r' => consume_linebreak(self),
1856
1857 c if is_bom(c)
1858 && self.document_prefix_allowed
1859 && self.flow_level == 0
1860 && self.mark.col == 0 =>
1861 {
1862 self.skip_bom();
1863 }
1864
1865 '#' => {
1866 outcome.saw_comment = true;
1867 let queued = self.consume_comment()?;
1868 outcome.queued_comment |= queued;
1869
1870 if matches!(self.input.look_ch(), '\n' | '\r') {
1872 consume_linebreak(self);
1873 }
1874 return Ok(outcome);
1875 }
1876
1877 _ => break,
1878 }
1879 }
1880
1881 if let Some(err_mark) = self.interrupted_plain_by_comment.take() {
1884 let is_immediate_next_line = self.mark.line == err_mark.line + 1;
1888
1889 if self.flow_level == 0
1891 && is_immediate_next_line
1892 && (self.mark.col as isize) > self.indent
1893 {
1894 self.input.lookahead(4);
1898
1899 if !self.input.next_is_z()
1900 && !self.input.next_is_document_indicator()
1901 && self.input.next_can_be_plain_scalar(false)
1902 {
1903 return Err(ScanError::from_kind(
1904 err_mark,
1905 ErrorKind::CommentInterceptedScalar,
1906 ));
1907 }
1908 }
1909 }
1910
1911 Ok(outcome)
1912 }
1913
1914 fn skip_yaml_whitespace(&mut self) -> Result<bool, ScanError> {
1922 let mut need_whitespace = true;
1923 loop {
1924 match self.input.look_ch() {
1925 ' ' => {
1926 self.skip_blank();
1927
1928 need_whitespace = false;
1929 }
1930 '\n' | '\r' => {
1931 self.input.lookahead(2);
1932 self.skip_linebreak();
1933 if self.flow_level == 0 {
1934 self.allow_simple_key();
1935 }
1936 need_whitespace = false;
1937 }
1938 '#' => {
1939 if need_whitespace {
1940 self.skip_comment()?;
1941 } else {
1942 self.consume_comment()?;
1943 return Ok(true);
1944 }
1945 }
1946 _ => break,
1947 }
1948 }
1949
1950 if need_whitespace {
1951 Err(self.scan_error(ErrorKind::ExpectedWhitespace))
1952 } else {
1953 Ok(false)
1954 }
1955 }
1956
1957 #[track_caller]
1962 fn skip_ws_to_eol(&mut self, skip_tabs: SkipTabs) -> Result<SkipTabs, ScanError> {
1963 debug_assert!(!matches!(skip_tabs, SkipTabs::Result(..)));
1964
1965 if !self.comments_possible {
1966 let (chars_consumed, result) = self.input.skip_ws_to_eol(skip_tabs);
1967 self.mark.col += chars_consumed;
1968 self.mark.offsets.chars += chars_consumed;
1969 self.mark.offsets.bytes = self.input.byte_offset();
1970 return result.map_err(|kind| self.scan_error(kind));
1971 }
1972
1973 let (chars_consumed, whitespace) = self.input.skip_ws_to_eol_blanks(skip_tabs);
1974 self.mark.col += chars_consumed;
1975 self.mark.offsets.chars += chars_consumed;
1976 self.mark.offsets.bytes = self.input.byte_offset();
1977
1978 if self.input.look_ch() != '#' {
1979 return Ok(whitespace);
1980 }
1981
1982 if !whitespace.found_tabs() && !whitespace.has_valid_yaml_ws() {
1983 return Err(self.scan_error(ErrorKind::CommentNotSeparated));
1984 }
1985
1986 self.consume_comment()?;
1987 Ok(whitespace)
1988 }
1989
1990 fn fetch_stream_start(&mut self) {
1991 let mark = self.mark;
1992 self.indent = -1;
1993 self.stream_start_produced = true;
1994 self.allow_simple_key();
1995 self.tokens
1996 .push_back(Token(Span::empty(mark), TokenType::StreamStart).into());
1997 self.simple_keys.push(SimpleKey::new(Marker::new(0, 0, 0)));
1998 }
1999
2000 fn fetch_stream_end(&mut self) -> ScanResult {
2001 if self.mark.col != 0 {
2003 self.mark.col = 0;
2004 self.mark.line += 1;
2005 }
2006
2007 if let Some((mark, bracket)) = self.flow_markers.pop() {
2008 return Err(Self::unclosed_bracket(mark, bracket));
2009 }
2010
2011 for sk in &mut self.simple_keys {
2014 if sk.required && sk.possible {
2015 return Err(Self::simple_key_expected(sk.mark));
2016 }
2017 sk.possible = false;
2018 }
2019
2020 self.unroll_indent(-1);
2021 self.remove_simple_key()?;
2022 self.disallow_simple_key();
2023
2024 self.tokens
2025 .push_back(Token(Span::empty(self.mark), TokenType::StreamEnd).into());
2026 Ok(())
2027 }
2028
2029 fn fetch_directive(&mut self) -> ScanResult {
2030 self.unroll_indent(-1);
2031 self.remove_simple_key()?;
2032
2033 self.disallow_simple_key();
2034
2035 let token_index = self.tokens.len();
2036 let tok = self.scan_directive()?;
2037 self.insert_token(token_index, tok);
2038
2039 Ok(())
2040 }
2041
2042 fn scan_directive(&mut self) -> Result<Token<'input>, ScanError> {
2043 let start_mark = self.mark;
2044 self.skip_non_blank();
2045
2046 let mut budget = self.options.max_directive_bytes;
2049
2050 let name = self.scan_directive_name(&mut budget)?;
2051 let tok = match name.as_ref() {
2052 "YAML" => self.scan_version_directive_value(&start_mark)?,
2053 "TAG" => self.scan_tag_directive_value(&start_mark, &mut budget)?,
2054 _ => {
2055 let mut params = Vec::new();
2056 while self.input.next_is_blank() {
2057 let n_blanks = self.input.skip_while_blank();
2058 self.mark.offsets.chars += n_blanks;
2059 self.mark.col += n_blanks;
2060 self.mark.offsets.bytes = self.input.byte_offset();
2061
2062 if !is_blank_or_breakz(self.input.peek()) {
2063 self.spend_directive_bytes(&mut budget, n_blanks, start_mark)?;
2064 if params.len() >= self.options.max_reserved_directive_params {
2065 return Err(ScanError::from_kind(
2066 start_mark,
2067 ErrorKind::TooManyReservedDirectiveParams {
2068 limit: self.options.max_reserved_directive_params,
2069 },
2070 ));
2071 }
2072
2073 let mut param = String::new();
2074 let Some(n_chars) = self.fetch_directive_word(&mut param, &mut budget)
2075 else {
2076 return Err(self.directive_byte_limit(start_mark));
2077 };
2078 self.mark.offsets.chars += n_chars;
2079 self.mark.col += n_chars;
2080 self.mark.offsets.bytes = self.input.byte_offset();
2081 params.push(param);
2082 }
2083 }
2084
2085 Token(
2086 Span::new(start_mark, self.mark),
2087 TokenType::ReservedDirective(name, params),
2088 )
2089 }
2090 };
2091
2092 self.skip_ws_to_eol(SkipTabs::Yes)?;
2093
2094 if self.input.next_is_breakz() {
2095 self.input.lookahead(2);
2096 self.skip_linebreak();
2097 Ok(tok)
2098 } else {
2099 Err(ScanError::from_kind(
2100 start_mark,
2101 ErrorKind::InvalidDirectiveTerminator,
2102 ))
2103 }
2104 }
2105
2106 fn scan_version_directive_value(&mut self, mark: &Marker) -> Result<Token<'input>, ScanError> {
2107 let n_blanks = self.input.skip_while_blank();
2108 self.mark.offsets.chars += n_blanks;
2109 self.mark.col += n_blanks;
2110 self.mark.offsets.bytes = self.input.byte_offset();
2111
2112 let major = self.scan_version_directive_number(mark)?;
2113
2114 if self.input.peek() != '.' {
2115 return Err(ScanError::from_kind(
2116 *mark,
2117 ErrorKind::MissingYamlVersionSeparator,
2118 ));
2119 }
2120 self.skip_non_blank();
2121
2122 let minor = self.scan_version_directive_number(mark)?;
2123
2124 Ok(Token(
2125 Span::new(*mark, self.mark),
2126 TokenType::VersionDirective(major, minor),
2127 ))
2128 }
2129
2130 fn directive_byte_limit(&self, start_mark: Marker) -> ScanError {
2132 ScanError::from_kind(
2133 start_mark,
2134 ErrorKind::DirectiveByteLimitExceeded {
2135 limit: self.options.max_directive_bytes,
2136 },
2137 )
2138 }
2139
2140 fn spend_directive_bytes(
2142 &self,
2143 budget: &mut usize,
2144 bytes: usize,
2145 start_mark: Marker,
2146 ) -> ScanResult {
2147 let Some(remaining) = budget.checked_sub(bytes) else {
2148 return Err(self.directive_byte_limit(start_mark));
2149 };
2150 *budget = remaining;
2151 Ok(())
2152 }
2153
2154 fn fetch_directive_word(&mut self, out: &mut String, budget: &mut usize) -> Option<usize> {
2161 let mut n_chars = 0;
2162
2163 loop {
2164 let c = self.input.look_ch();
2165 if !is_yaml_non_space(c) || is_z(c) {
2166 break;
2167 }
2168
2169 let len = c.len_utf8();
2170 if len > *budget {
2171 return None;
2172 }
2173 *budget -= len;
2174
2175 out.push(c);
2176 self.input.skip();
2177 n_chars += 1;
2178 }
2179
2180 Some(n_chars)
2181 }
2182
2183 fn scan_directive_name(&mut self, budget: &mut usize) -> Result<String, ScanError> {
2184 let start_mark = self.mark;
2185 let mut string = String::new();
2186
2187 let Some(n_chars) = self.fetch_directive_word(&mut string, budget) else {
2188 return Err(self.directive_byte_limit(start_mark));
2189 };
2190 self.mark.offsets.chars += n_chars;
2191 self.mark.col += n_chars;
2192 self.mark.offsets.bytes = self.input.byte_offset();
2193
2194 if string.is_empty() {
2195 return Err(ScanError::from_kind(
2196 start_mark,
2197 ErrorKind::MissingDirectiveName,
2198 ));
2199 }
2200
2201 if !is_blank_or_breakz(self.input.peek()) {
2202 return Err(ScanError::from_kind(
2203 start_mark,
2204 ErrorKind::InvalidDirectiveName,
2205 ));
2206 }
2207
2208 Ok(string)
2209 }
2210
2211 fn scan_version_directive_number(&mut self, mark: &Marker) -> Result<u32, ScanError> {
2212 let mut val = 0u32;
2213 let mut length = 0usize;
2214 while let Some(digit) = self.input.look_ch().to_digit(10) {
2215 if length + 1 > 9 {
2216 return Err(ScanError::from_kind(*mark, ErrorKind::YamlVersionTooLong));
2217 }
2218 length += 1;
2219 val = val * 10 + digit;
2220 self.skip_non_blank();
2221 }
2222
2223 if length == 0 {
2224 return Err(ScanError::from_kind(*mark, ErrorKind::MissingYamlVersion));
2225 }
2226
2227 Ok(val)
2228 }
2229
2230 fn scan_tag_directive_value(
2231 &mut self,
2232 mark: &Marker,
2233 budget: &mut usize,
2234 ) -> Result<Token<'input>, ScanError> {
2235 let n_blanks = self.input.skip_while_blank();
2236 self.mark.offsets.chars += n_blanks;
2237 self.mark.col += n_blanks;
2238 self.mark.offsets.bytes = self.input.byte_offset();
2239 self.spend_directive_bytes(budget, n_blanks, *mark)?;
2240
2241 let handle = self.scan_tag_handle_directive_cow(mark, budget)?;
2242
2243 let n_blanks = self.input.skip_while_blank();
2244 self.mark.offsets.chars += n_blanks;
2245 self.mark.col += n_blanks;
2246 self.mark.offsets.bytes = self.input.byte_offset();
2247 self.spend_directive_bytes(budget, n_blanks, *mark)?;
2248
2249 let prefix = self.scan_tag_prefix_directive_cow(mark, budget)?;
2250
2251 self.input.lookahead(1);
2252
2253 if self.input.next_is_blank_or_breakz() {
2254 Ok(Token(
2255 Span::new(*mark, self.mark),
2256 TokenType::TagDirective(handle, prefix),
2257 ))
2258 } else {
2259 Err(ScanError::from_kind(
2260 *mark,
2261 ErrorKind::InvalidTagDirectiveTerminator,
2262 ))
2263 }
2264 }
2265
2266 fn fetch_tag(&mut self) -> ScanResult {
2267 self.save_simple_key();
2268 self.disallow_simple_key();
2269
2270 let tok = self.scan_tag()?;
2271 self.tokens.push_back(tok.into());
2272 Ok(())
2273 }
2274
2275 fn scan_tag(&mut self) -> Result<Token<'input>, ScanError> {
2276 let start_mark = self.mark;
2277
2278 self.input.lookahead(2);
2280
2281 if self.input.byte_offset().is_none() {
2283 return self.scan_tag_owned(&start_mark);
2284 }
2285
2286 let (handle, suffix): (Cow<'input, str>, Cow<'input, str>) = if self
2287 .input
2288 .nth_char_is(1, '<')
2289 {
2290 let suffix = self.scan_verbatim_tag(&start_mark)?;
2292 (Cow::Owned(String::new()), Cow::Owned(suffix))
2293 } else {
2294 let handle = self.scan_tag_handle_cow(&start_mark)?;
2296 if handle.len() >= 2 && handle.starts_with('!') && handle.ends_with('!') {
2298 let suffix = self.scan_tag_shorthand_suffix_cow(&start_mark, true)?;
2300 (handle, suffix)
2301 } else {
2302 let remaining_suffix = self.scan_tag_shorthand_suffix_cow(&start_mark, false)?;
2307
2308 let suffix = self.combine_local_tag_suffix(&start_mark, handle, remaining_suffix);
2309
2310 if suffix.is_empty() {
2313 (Cow::Borrowed(""), Cow::Borrowed("!"))
2314 } else {
2315 (Cow::Borrowed("!"), suffix)
2316 }
2317 }
2318 };
2319
2320 if is_blank_or_breakz(self.input.look_ch())
2321 || (self.flow_level > 0 && matches!(self.input.peek(), ',' | ']' | '}'))
2322 {
2323 Ok(Token(
2326 Span::new(start_mark, self.mark),
2327 TokenType::Tag(handle, suffix),
2328 ))
2329 } else {
2330 Err(ScanError::from_kind(
2331 start_mark,
2332 ErrorKind::InvalidTagTerminator,
2333 ))
2334 }
2335 }
2336
2337 fn combine_local_tag_suffix(
2338 &self,
2339 start_mark: &Marker,
2340 handle: Cow<'input, str>,
2341 remaining: Cow<'input, str>,
2342 ) -> Cow<'input, str> {
2343 if handle.len() == 1 {
2344 return remaining;
2345 }
2346
2347 match (handle, remaining) {
2348 (Cow::Borrowed(handle), Cow::Borrowed(remaining)) => {
2349 if remaining.is_empty() {
2350 return Cow::Borrowed(&handle[1..]);
2351 }
2352
2353 let borrowed = start_mark
2354 .byte_offset()
2355 .and_then(|start| start.checked_add(1))
2356 .zip(self.input.byte_offset())
2357 .and_then(|(start, end)| self.try_borrow_slice(start, end));
2358 borrowed.map_or_else(
2359 || {
2360 let mut combined =
2361 String::with_capacity(handle.len() - 1 + remaining.len());
2362 combined.push_str(&handle[1..]);
2363 combined.push_str(remaining);
2364 Cow::Owned(combined)
2365 },
2366 Cow::Borrowed,
2367 )
2368 }
2369 (Cow::Borrowed(handle), Cow::Owned(mut remaining)) => {
2370 remaining.reserve(handle.len() - 1);
2371 remaining.insert_str(0, &handle[1..]);
2372 Cow::Owned(remaining)
2373 }
2374 (Cow::Owned(mut handle), remaining) => {
2375 handle.remove(0);
2376 handle.push_str(&remaining);
2377 Cow::Owned(handle)
2378 }
2379 }
2380 }
2381
2382 fn scan_tag_owned(&mut self, start_mark: &Marker) -> Result<Token<'input>, ScanError> {
2384 let mut handle = String::new();
2385 let mut suffix;
2386
2387 if self.input.nth_char_is(1, '<') {
2388 suffix = self.scan_verbatim_tag(start_mark)?;
2389 } else {
2390 handle = self.scan_tag_handle(false, start_mark)?;
2392 if handle.len() >= 2 && handle.starts_with('!') && handle.ends_with('!') {
2394 let is_secondary_handle = handle == "!!";
2396 suffix =
2397 self.scan_tag_shorthand_suffix(false, is_secondary_handle, "", start_mark)?;
2398 } else {
2399 suffix = self.scan_tag_shorthand_suffix(false, false, &handle, start_mark)?;
2400 "!".clone_into(&mut handle);
2401 if suffix.is_empty() {
2404 handle.clear();
2405 "!".clone_into(&mut suffix);
2406 }
2407 }
2408 }
2409
2410 if is_blank_or_breakz(self.input.look_ch())
2411 || (self.flow_level > 0 && matches!(self.input.peek(), ',' | ']' | '}'))
2412 {
2413 Ok(Token(
2416 Span::new(*start_mark, self.mark),
2417 TokenType::Tag(handle.into(), suffix.into()),
2418 ))
2419 } else {
2420 Err(ScanError::from_kind(
2421 *start_mark,
2422 ErrorKind::InvalidTagTerminator,
2423 ))
2424 }
2425 }
2426
2427 fn scan_tag_handle_cow(&mut self, mark: &Marker) -> Result<Cow<'input, str>, ScanError> {
2432 let Some(start) = self.input.byte_offset() else {
2433 return Ok(Cow::Owned(self.scan_tag_handle(false, mark)?));
2434 };
2435
2436 if self.input.look_ch() != '!' {
2437 return Err(ScanError::from_kind(*mark, ErrorKind::ExpectedTagBang));
2438 }
2439
2440 self.skip_non_blank();
2442
2443 self.input.lookahead(1);
2445 while self.input.next_is_alpha() {
2446 self.skip_non_blank();
2447 self.input.lookahead(1);
2448 }
2449
2450 if self.input.peek() == '!' {
2452 self.skip_non_blank();
2453 }
2454
2455 let Some(end) = self.input.byte_offset() else {
2456 return Ok(Cow::Owned(self.scan_tag_handle(false, mark)?));
2457 };
2458
2459 if let Some(slice) = self.try_borrow_slice(start, end) {
2460 Ok(Cow::Borrowed(slice))
2461 } else {
2462 let slice = self
2463 .input
2464 .slice_bytes(start, end)
2465 .ok_or_else(|| ScanError::from_kind(*mark, ErrorKind::InputSlicingUnavailable))?;
2466 Ok(Cow::Owned(slice.to_owned()))
2467 }
2468 }
2469
2470 fn scan_tag_shorthand_suffix_cow(
2474 &mut self,
2475 mark: &Marker,
2476 require_non_empty: bool,
2477 ) -> Result<Cow<'input, str>, ScanError> {
2478 let Some(start) = self.input.byte_offset() else {
2479 return Ok(Cow::Owned(
2480 self.scan_tag_shorthand_suffix(false, false, "", mark)?,
2481 ));
2482 };
2483
2484 while is_tag_char(self.input.look_ch()) {
2486 if self.input.peek() == '%' {
2487 let current = self
2489 .input
2490 .byte_offset()
2491 .expect("byte_offset() must remain available once enabled");
2492 let mut out = if let Some(slice) = self.input.slice_bytes(start, current) {
2493 slice.to_owned()
2494 } else {
2495 String::new()
2496 };
2497
2498 while is_tag_char(self.input.look_ch()) {
2500 if self.input.peek() == '%' {
2501 out.push(self.scan_uri_escapes(mark)?);
2502 } else {
2503 out.push(self.input.peek());
2504 self.skip_non_blank();
2505 }
2506 }
2507 return Ok(Cow::Owned(out));
2508 }
2509 self.skip_non_blank();
2510 }
2511
2512 let Some(end) = self.input.byte_offset() else {
2513 return Ok(Cow::Owned(
2514 self.scan_tag_shorthand_suffix(false, false, "", mark)?,
2515 ));
2516 };
2517
2518 if require_non_empty && start == end {
2519 return Err(ScanError::from_kind(*mark, ErrorKind::MissingTagUri));
2520 }
2521
2522 if let Some(slice) = self.try_borrow_slice(start, end) {
2523 Ok(Cow::Borrowed(slice))
2524 } else {
2525 let slice = self
2526 .input
2527 .slice_bytes(start, end)
2528 .ok_or_else(|| ScanError::from_kind(*mark, ErrorKind::InputSlicingUnavailable))?;
2529 Ok(Cow::Owned(slice.to_owned()))
2530 }
2531 }
2532
2533 fn scan_tag_handle(&mut self, directive: bool, mark: &Marker) -> Result<String, ScanError> {
2534 let mut string = String::new();
2535 if self.input.look_ch() != '!' {
2536 return Err(ScanError::from_kind(*mark, ErrorKind::ExpectedTagBang));
2537 }
2538
2539 string.push(self.input.peek());
2540 self.skip_non_blank();
2541
2542 let n_chars = self.input.fetch_while_is_alpha(&mut string);
2543 self.mark.offsets.chars += n_chars;
2544 self.mark.col += n_chars;
2545 self.mark.offsets.bytes = self.input.byte_offset();
2546
2547 if self.input.peek() == '!' {
2549 string.push(self.input.peek());
2550 self.skip_non_blank();
2551 } else if directive && string != "!" {
2552 return Err(ScanError::from_kind(
2556 *mark,
2557 ErrorKind::ExpectedTagDirectiveBang,
2558 ));
2559 }
2560 Ok(string)
2561 }
2562
2563 fn scan_verbatim_tag(&mut self, start_mark: &Marker) -> Result<String, ScanError> {
2567 self.skip_non_blank();
2569 self.skip_non_blank();
2570
2571 let mut string = String::new();
2572 while is_uri_char(self.input.look_ch()) {
2573 if self.input.peek() == '%' {
2574 string.push(self.scan_uri_escapes(start_mark)?);
2575 } else {
2576 string.push(self.input.peek());
2577 self.skip_non_blank();
2578 }
2579 }
2580
2581 if string.is_empty() {
2582 return Err(ScanError::from_kind(*start_mark, ErrorKind::MissingTagUri));
2583 }
2584
2585 if self.input.peek() != '>' {
2586 return Err(ScanError::from_kind(
2587 *start_mark,
2588 ErrorKind::UnclosedVerbatimTag,
2589 ));
2590 }
2591 self.skip_non_blank();
2592
2593 Ok(string)
2594 }
2595
2596 fn scan_tag_shorthand_suffix(
2597 &mut self,
2598 _directive: bool,
2599 _is_secondary: bool,
2600 head: &str,
2601 mark: &Marker,
2602 ) -> Result<String, ScanError> {
2603 let mut length = head.len();
2604 let mut string = String::new();
2605
2606 if length > 1 {
2609 string.extend(head.chars().skip(1));
2610 }
2611
2612 while is_tag_char(self.input.look_ch()) {
2613 if self.input.peek() == '%' {
2615 string.push(self.scan_uri_escapes(mark)?);
2616 } else {
2617 string.push(self.input.peek());
2618 self.skip_non_blank();
2619 }
2620
2621 length += 1;
2622 }
2623
2624 if length == 0 {
2625 return Err(ScanError::from_kind(*mark, ErrorKind::MissingTagUri));
2626 }
2627
2628 Ok(string)
2629 }
2630
2631 fn scan_uri_escapes(&mut self, mark: &Marker) -> Result<char, ScanError> {
2632 self.scan_uri_escapes_with_budget(mark, None)
2633 }
2634
2635 fn scan_uri_escapes_with_budget(
2636 &mut self,
2637 mark: &Marker,
2638 mut directive_budget: Option<&mut usize>,
2639 ) -> Result<char, ScanError> {
2640 let mut width = 0usize;
2641 let mut bytes = [0u8; 4];
2642 let mut bytes_len = 0usize;
2643 loop {
2644 self.input.lookahead(3);
2645
2646 let c = self.input.peek_nth(1);
2647 let nc = self.input.peek_nth(2);
2648
2649 if !(self.input.peek() == '%' && is_hex(c) && is_hex(nc)) {
2650 return Err(ScanError::from_kind(*mark, ErrorKind::InvalidTagEscape));
2651 }
2652
2653 if let Some(budget) = directive_budget.as_deref_mut() {
2654 self.spend_directive_bytes(budget, 3, *mark)?;
2655 }
2656
2657 let byte = u8::try_from((as_hex(c) << 4) + as_hex(nc))
2658 .expect("two hex nibbles always fit in a byte");
2659 if width == 0 {
2660 width = match byte {
2661 _ if byte & 0x80 == 0x00 => 1,
2662 _ if byte & 0xE0 == 0xC0 => 2,
2663 _ if byte & 0xF0 == 0xE0 => 3,
2664 _ if byte & 0xF8 == 0xF0 => 4,
2665 _ => {
2666 return Err(ScanError::from_kind(
2667 *mark,
2668 ErrorKind::InvalidTagUtf8LeadingByte,
2669 ));
2670 }
2671 };
2672 } else if byte & 0xc0 != 0x80 {
2673 return Err(ScanError::from_kind(
2674 *mark,
2675 ErrorKind::InvalidTagUtf8TrailingByte,
2676 ));
2677 }
2678
2679 bytes[bytes_len] = byte;
2680 bytes_len += 1;
2681
2682 self.skip_n_non_blank(3);
2683
2684 width -= 1;
2685 if width == 0 {
2686 break;
2687 }
2688 }
2689
2690 let s = core::str::from_utf8(&bytes[..bytes_len])
2691 .map_err(|_| ScanError::from_kind(*mark, ErrorKind::InvalidTagUtf8))?;
2692
2693 let Some(ch) = s.chars().next() else {
2694 unreachable!("a validated URI escape cannot decode to an empty string")
2695 };
2696 Ok(ch)
2697 }
2698
2699 fn fetch_anchor(&mut self, alias: bool) -> ScanResult {
2700 self.save_simple_key();
2701 self.disallow_simple_key();
2702
2703 let tok = self.scan_anchor(alias)?;
2704
2705 self.tokens.push_back(tok.into());
2706
2707 Ok(())
2708 }
2709
2710 fn scan_anchor(&mut self, alias: bool) -> Result<Token<'input>, ScanError> {
2711 let start_mark = self.mark;
2712
2713 self.skip_non_blank();
2715
2716 if let Some(start) = self.input.byte_offset() {
2718 while is_anchor_char(self.input.look_ch()) {
2719 self.skip_non_blank();
2720 }
2721
2722 let end = self
2723 .input
2724 .byte_offset()
2725 .expect("byte_offset() must remain available once enabled");
2726
2727 if start == end {
2728 return Err(ScanError::from_kind(
2729 start_mark,
2730 ErrorKind::MissingAnchorOrAliasName,
2731 ));
2732 }
2733
2734 let cow = if let Some(slice) = self.try_borrow_slice(start, end) {
2735 Cow::Borrowed(slice)
2736 } else if let Some(slice) = self.input.slice_bytes(start, end) {
2737 Cow::Owned(slice.to_owned())
2738 } else {
2739 return Err(ScanError::from_kind(
2740 start_mark,
2741 ErrorKind::InputSlicingUnavailable,
2742 ));
2743 };
2744
2745 let tok = if alias {
2746 TokenType::Alias(cow)
2747 } else {
2748 TokenType::Anchor(cow)
2749 };
2750 return Ok(Token(Span::new(start_mark, self.mark), tok));
2751 }
2752
2753 let mut string = String::new();
2754 while is_anchor_char(self.input.look_ch()) {
2755 string.push(self.input.peek());
2756 self.skip_non_blank();
2757 }
2758
2759 if string.is_empty() {
2760 return Err(ScanError::from_kind(
2761 start_mark,
2762 ErrorKind::MissingAnchorOrAliasName,
2763 ));
2764 }
2765
2766 let tok = if alias {
2767 TokenType::Alias(string.into())
2768 } else {
2769 TokenType::Anchor(string.into())
2770 };
2771 Ok(Token(Span::new(start_mark, self.mark), tok))
2772 }
2773
2774 fn fetch_flow_collection_start(&mut self, tok: TokenType<'input>) -> ScanResult {
2775 self.save_simple_key();
2777
2778 let start_mark = self.mark;
2779 let indicator = self.input.peek();
2780 self.flow_markers.push((start_mark, indicator));
2781
2782 self.roll_one_col_indent();
2783 self.increase_flow_level()?;
2784
2785 self.allow_simple_key();
2786
2787 self.skip_non_blank();
2788 let end_mark = self.mark;
2789
2790 if tok == TokenType::FlowMappingStart {
2791 self.flow_mapping_started.push(true);
2792 } else {
2793 self.flow_mapping_started.push(false);
2794 self.implicit_flow_mapping_states
2795 .push(ImplicitMappingState::Possible);
2796 }
2797
2798 let token_index = self.tokens.len();
2799 self.skip_ws_to_eol(SkipTabs::Yes)?;
2800
2801 self.insert_token(token_index, Token(Span::new(start_mark, end_mark), tok));
2802 Ok(())
2803 }
2804
2805 fn fetch_flow_collection_end(
2806 &mut self,
2807 tok: TokenType<'input>,
2808 expected_open: char,
2809 actual_close: char,
2810 ) -> ScanResult {
2811 if self.flow_level == 0 {
2813 return Err(self.scan_error(ErrorKind::MisplacedFlowCollectionEnd));
2814 }
2815
2816 let Some((open_mark, open_ch)) = self.flow_markers.pop() else {
2817 return Err(self.scan_error(ErrorKind::MisplacedFlowCollectionEnd));
2818 };
2819
2820 if open_ch != expected_open {
2821 return Err(ScanError::from_kind(
2822 open_mark,
2823 ErrorKind::MismatchedFlowCollectionEnd {
2824 open: open_ch,
2825 close: actual_close,
2826 },
2827 ));
2828 }
2829
2830 let flow_level = self.flow_level;
2831
2832 self.remove_simple_key()?;
2833
2834 if matches!(tok, TokenType::FlowSequenceEnd) {
2835 self.end_implicit_mapping(self.mark, flow_level);
2836 self.implicit_flow_mapping_states.pop();
2838 }
2839 self.flow_mapping_started.pop();
2840
2841 self.decrease_flow_level();
2842
2843 self.disallow_simple_key();
2844
2845 let start_mark = self.mark;
2846 self.skip_non_blank();
2847 let end_mark = self.mark;
2848 let token_index = self.tokens.len();
2849 self.skip_ws_to_eol(SkipTabs::Yes)?;
2850
2851 if self.flow_level > 0 {
2857 self.adjacent_value_allowed_at = self.mark.index();
2858 }
2859
2860 self.insert_token(token_index, Token(Span::new(start_mark, end_mark), tok));
2861 Ok(())
2862 }
2863
2864 fn fetch_flow_entry(&mut self) -> ScanResult {
2866 self.remove_simple_key()?;
2867 self.allow_simple_key();
2868
2869 self.end_implicit_mapping(self.mark, self.flow_level);
2870 if self.current_flow_collection_is_sequence() {
2871 self.set_current_flow_mapping_started(false);
2872 }
2873
2874 let start_mark = self.mark;
2875 self.skip_non_blank();
2876 let end_mark = self.mark;
2877 let token_index = self.tokens.len();
2878 self.skip_ws_to_eol(SkipTabs::Yes)?;
2879
2880 self.insert_token(
2881 token_index,
2882 Token(Span::new(start_mark, end_mark), TokenType::FlowEntry),
2883 );
2884 Ok(())
2885 }
2886
2887 fn increase_flow_level(&mut self) -> ScanResult {
2888 if self.flow_level >= self.options.flow_nesting_limit {
2889 return Err(self.scan_error(ErrorKind::RecursionLimitExceeded));
2890 }
2891
2892 self.simple_keys.push(SimpleKey::new(Marker::new(0, 0, 0)));
2893 self.flow_level += 1;
2894 Ok(())
2895 }
2896
2897 fn decrease_flow_level(&mut self) {
2898 if self.flow_level > 0 {
2899 self.flow_level -= 1;
2900 self.simple_keys.pop().unwrap();
2901 }
2902 }
2903
2904 fn fetch_block_entry(&mut self) -> ScanResult {
2910 if self.flow_level > 0 {
2911 return Err(self.scan_error(ErrorKind::BlockEntryInFlowCollection));
2913 }
2914 if !self.simple_key_allowed {
2916 return Err(self.scan_error(ErrorKind::BlockSequenceEntryNotAllowed));
2917 }
2918
2919 let mark = self.mark;
2921 self.skip_non_blank();
2922
2923 self.roll_indent(mark.col, None, TokenType::BlockSequenceStart, mark)?;
2925 let token_index = self.tokens.len();
2926 let found_tabs = self.skip_ws_to_eol(SkipTabs::Yes)?.found_tabs();
2927 self.input.lookahead(2);
2928 if found_tabs && self.input.next_char_is('-') && is_blank_or_breakz(self.input.peek_nth(1))
2929 {
2930 return Err(self.scan_error(ErrorKind::InvalidBlockEntryWhitespace));
2931 }
2932
2933 self.skip_ws_to_eol(SkipTabs::No)?;
2934 self.input.lookahead(1);
2935 if self.input.next_is_break() || self.input.next_is_flow() {
2936 self.roll_one_col_indent();
2937 }
2938
2939 self.remove_simple_key()?;
2940 self.allow_simple_key();
2941
2942 self.insert_token(
2943 token_index,
2944 Token(Span::empty(self.mark), TokenType::BlockEntry),
2945 );
2946
2947 Ok(())
2948 }
2949
2950 fn fetch_document_indicator(&mut self, t: TokenType<'input>) -> ScanResult {
2951 if let Some((mark, bracket)) = self.flow_markers.pop() {
2952 return Err(ScanError::from_kind(
2953 mark,
2954 ErrorKind::UnclosedFlowCollection { open: bracket },
2955 ));
2956 }
2957
2958 self.unroll_indent(-1);
2959 self.remove_simple_key()?;
2960 self.disallow_simple_key();
2961
2962 let mark = self.mark;
2963
2964 self.skip_n_non_blank(3);
2965
2966 self.document_prefix_allowed = matches!(t, TokenType::DocumentEnd);
2967 self.tokens
2968 .push_back(Token(Span::new(mark, self.mark), t).into());
2969 Ok(())
2970 }
2971
2972 fn fetch_block_scalar(&mut self, literal: bool) -> ScanResult {
2973 self.save_simple_key();
2974 self.allow_simple_key();
2975 let token_index = self.tokens.len();
2976 let tok = self.scan_block_scalar(literal)?;
2977
2978 self.insert_token(token_index, tok);
2979 Ok(())
2980 }
2981
2982 #[allow(clippy::too_many_lines)]
2983 fn scan_block_scalar(&mut self, literal: bool) -> Result<Token<'input>, ScanError> {
2984 let start_mark = self.mark;
2985 let mut chomping = Chomping::Clip;
2986 let mut increment: usize = 0;
2987 let mut indent: usize = 0;
2988 let mut trailing_blank: bool;
2989 let mut leading_blank: bool = false;
2990 let style = if literal {
2991 ScalarStyle::Literal
2992 } else {
2993 ScalarStyle::Folded
2994 };
2995
2996 let mut string = String::new();
2997 let mut leading_break = String::new();
2998 let mut trailing_breaks = String::new();
2999 let mut chomping_break = String::new();
3000
3001 self.skip_non_blank();
3003 self.unroll_non_block_indents();
3004
3005 if self.input.look_ch() == '+' || self.input.peek() == '-' {
3006 if self.input.peek() == '+' {
3007 chomping = Chomping::Keep;
3008 } else {
3009 chomping = Chomping::Strip;
3010 }
3011 self.skip_non_blank();
3012 self.input.lookahead(1);
3013 if self.input.next_is_digit() {
3014 if self.input.peek() == '0' {
3015 return Err(ScanError::from_kind(
3016 start_mark,
3017 ErrorKind::ZeroBlockScalarIndent,
3018 ));
3019 }
3020 increment = (self.input.peek() as usize) - ('0' as usize);
3021 self.skip_non_blank();
3022 }
3023 } else if self.input.next_is_digit() {
3024 if self.input.peek() == '0' {
3025 return Err(ScanError::from_kind(
3026 start_mark,
3027 ErrorKind::ZeroBlockScalarIndent,
3028 ));
3029 }
3030
3031 increment = (self.input.peek() as usize) - ('0' as usize);
3032 self.skip_non_blank();
3033 self.input.lookahead(1);
3034 if self.input.peek() == '+' || self.input.peek() == '-' {
3035 if self.input.peek() == '+' {
3036 chomping = Chomping::Keep;
3037 } else {
3038 chomping = Chomping::Strip;
3039 }
3040 self.skip_non_blank();
3041 }
3042 }
3043
3044 self.skip_ws_to_eol(SkipTabs::Yes)?;
3045
3046 self.input.lookahead(1);
3048 self.ensure_current_char_is_printable()?;
3049 if !self.input.next_is_breakz() {
3050 return Err(ScanError::from_kind(
3051 start_mark,
3052 ErrorKind::InvalidBlockScalarHeader,
3053 ));
3054 }
3055
3056 if self.input.next_is_break() {
3057 self.input.lookahead(2);
3058 self.read_break(&mut chomping_break);
3059 }
3060
3061 self.ensure_current_char_is_printable()?;
3062
3063 if self.input.look_ch() == '\t' {
3064 return Err(ScanError::from_kind(
3065 start_mark,
3066 ErrorKind::TabAtBlockScalarStart,
3067 ));
3068 }
3069
3070 if increment > 0 {
3071 indent = if self.indent >= 0 {
3072 (self.indent + increment as isize) as usize
3073 } else {
3074 increment
3075 }
3076 }
3077
3078 if indent == 0 {
3080 self.skip_block_scalar_first_line_indent(&mut indent, &mut trailing_breaks);
3081 } else {
3082 self.skip_block_scalar_indent(indent, &mut trailing_breaks);
3083 }
3084
3085 self.ensure_current_char_is_printable()?;
3086
3087 if self.input.next_is_z() {
3092 let contents = match chomping {
3093 Chomping::Strip => String::new(),
3095 _ if self.mark.line == start_mark.line() => String::new(),
3097 Chomping::Clip => String::new(),
3099 Chomping::Keep if trailing_breaks.is_empty() && self.mark.col > 0 => chomping_break,
3101 Chomping::Keep => trailing_breaks,
3103 };
3104
3105 return Ok(Token(
3106 Span::new(start_mark, self.mark),
3107 TokenType::Scalar(style, contents.into()),
3108 ));
3109 }
3110
3111 if self.mark.col < indent && (self.mark.col as isize) > self.indent {
3112 if self.indent < 0 && self.mark.col == 0 {
3113 self.input.lookahead(4);
3114 if self.input.next_is_document_start()
3115 || self.input.next_is_document_end()
3116 || self.input.peek() == '#'
3117 {
3118 } else {
3122 return Err(self.scan_error(ErrorKind::InvalidBlockScalarIndent));
3123 }
3124 } else {
3125 return Err(self.scan_error(ErrorKind::InvalidBlockScalarIndent));
3126 }
3127 }
3128
3129 let mut line_buffer = String::with_capacity(100);
3130 let start_mark = self.mark;
3131 while self.mark.col == indent && !self.input.next_is_z() {
3132 self.ensure_current_char_is_printable()?;
3133
3134 if indent == 0 {
3135 self.input.lookahead(4);
3136 if self.input.next_is_document_end() {
3137 break;
3138 }
3139 }
3140
3141 trailing_blank = self.input.next_is_blank();
3143 if !literal && !leading_break.is_empty() && !leading_blank && !trailing_blank {
3144 string.push_str(&trailing_breaks);
3145 if trailing_breaks.is_empty() {
3146 string.push(' ');
3147 }
3148 } else {
3149 string.push_str(&leading_break);
3150 string.push_str(&trailing_breaks);
3151 }
3152
3153 leading_break.clear();
3154 trailing_breaks.clear();
3155
3156 leading_blank = self.input.next_is_blank();
3157
3158 self.scan_block_scalar_content_line(&mut string, &mut line_buffer);
3159
3160 self.input.lookahead(2);
3162 if self.input.next_is_z() {
3163 break;
3164 }
3165
3166 self.ensure_current_char_is_printable()?;
3167
3168 self.read_break(&mut leading_break);
3169
3170 self.skip_block_scalar_indent(indent, &mut trailing_breaks);
3172 }
3173
3174 self.ensure_current_char_is_printable()?;
3175
3176 if chomping != Chomping::Strip {
3178 string.push_str(&leading_break);
3179 if self.input.next_is_z() && self.mark.col >= indent.max(1) {
3183 string.push('\n');
3184 }
3185 }
3186
3187 if chomping == Chomping::Keep {
3188 string.push_str(&trailing_breaks);
3189 }
3190
3191 if let Some(character) = find_non_printable(&string) {
3192 return Err(ScanError::from_kind(
3193 start_mark,
3194 ErrorKind::UnexpectedCharacter { character },
3195 ));
3196 }
3197
3198 let span = if string.trim().is_empty() {
3199 Span::new(start_mark, self.mark)
3200 } else {
3201 Span::new(start_mark, self.mark).with_indent(Some(indent))
3202 };
3203
3204 Ok(Token(span, TokenType::Scalar(style, string.into())))
3205 }
3206
3207 fn scan_block_scalar_content_line(&mut self, string: &mut String, line_buffer: &mut String) {
3217 while !self.input.buf_is_empty() && !self.input.next_is_breakz() {
3219 string.push(self.input.peek());
3220 self.skip_blank();
3226 }
3227
3228 if self.input.buf_is_empty() {
3231 let mut n_chars = 0;
3239 debug_assert!(line_buffer.is_empty());
3240 while let Some(c) = self.input.raw_read_non_breakz_ch() {
3241 line_buffer.push(c);
3242 n_chars += 1;
3243 }
3244
3245 self.mark.col += n_chars;
3247 self.mark.offsets.chars += n_chars;
3248 self.mark.offsets.bytes = self.input.byte_offset();
3249
3250 string.reserve(line_buffer.len());
3252 string.push_str(line_buffer);
3253 line_buffer.clear();
3255 }
3256 }
3257
3258 fn skip_block_scalar_indent(&mut self, indent: usize, breaks: &mut String) {
3260 loop {
3261 if indent < self.input.bufmaxlen().saturating_sub(2) {
3263 self.input.lookahead(self.input.bufmaxlen());
3264 while self.mark.col < indent && self.input.peek() == ' ' {
3265 self.skip_blank();
3266 }
3267 } else {
3268 loop {
3269 self.input.lookahead(self.input.bufmaxlen());
3270 while !self.input.buf_is_empty()
3271 && self.mark.col < indent
3272 && self.input.peek() == ' '
3273 {
3274 self.skip_blank();
3275 }
3276 if self.mark.col == indent
3280 || (!self.input.buf_is_empty() && self.input.peek() != ' ')
3281 {
3282 break;
3283 }
3284 }
3285 self.input.lookahead(2);
3286 }
3287
3288 if self.input.next_is_break() {
3290 self.read_break(breaks);
3291 } else {
3292 break;
3294 }
3295 }
3296 }
3297
3298 fn skip_block_scalar_first_line_indent(&mut self, indent: &mut usize, breaks: &mut String) {
3303 let mut max_indent = 0;
3304 loop {
3305 while self.input.look_ch() == ' ' {
3307 self.skip_blank();
3308 }
3309
3310 if self.mark.col > max_indent {
3311 max_indent = self.mark.col;
3312 }
3313
3314 if self.input.next_is_break() {
3315 self.input.lookahead(2);
3317 self.read_break(breaks);
3318 } else {
3319 break;
3321 }
3322 }
3323
3324 *indent = max_indent.max((self.indent + 1) as usize);
3333 }
3334
3335 fn fetch_flow_scalar(&mut self, single: bool) -> ScanResult {
3336 self.save_simple_key();
3337 self.disallow_simple_key();
3338
3339 let token_index = self.tokens.len();
3340 let tok = self.scan_flow_scalar(single)?;
3341
3342 if self.skip_to_next_token()?.saw_comment {
3345 self.adjacent_value_allowed_at = usize::MAX;
3346 } else {
3347 self.adjacent_value_allowed_at = self.mark.index();
3348 }
3349
3350 self.insert_token(token_index, tok);
3351 Ok(())
3352 }
3353
3354 #[allow(clippy::too_many_lines)]
3355 fn scan_flow_scalar(&mut self, single: bool) -> Result<Token<'input>, ScanError> {
3356 let start_mark = self.mark;
3357
3358 let mut buf = match self.input.byte_offset() {
3360 Some(off) => FlowScalarBuf::new_borrowed(off + self.input.peek().len_utf8()),
3361 None => FlowScalarBuf::new_owned(),
3362 };
3363
3364 let mut break_scratch = String::new();
3367
3368 self.skip_non_blank();
3370
3371 loop {
3372 self.input.lookahead(4);
3374
3375 if self.mark.col == 0 && self.input.next_is_document_indicator() {
3376 return Err(ScanError::from_kind(
3377 start_mark,
3378 ErrorKind::DocumentIndicatorInQuotedScalar,
3379 ));
3380 }
3381
3382 if self.input.next_is_z() {
3383 return Err(ScanError::from_kind(
3384 start_mark,
3385 ErrorKind::UnclosedQuotedScalar,
3386 ));
3387 }
3388
3389 self.ensure_current_char_is_printable()?;
3390
3391 let mut leading_blanks = false;
3394 self.consume_flow_scalar_non_whitespace_chars(
3395 single,
3396 &mut buf,
3397 &mut leading_blanks,
3398 &start_mark,
3399 )?;
3400
3401 match self.input.look_ch() {
3402 '\'' if single => break,
3403 '"' if !single => break,
3404 _ => {}
3405 }
3406
3407 let mut trailing_ws_start: Option<usize> = None;
3423 let mut has_leading_break = false;
3424 let mut has_trailing_breaks = false;
3425
3426 let mut pending_ws_start: Option<usize> = None;
3428
3429 while self.input.next_is_blank() || self.input.next_is_break() {
3431 if self.input.next_is_blank() {
3432 if leading_blanks {
3434 if self.input.peek() == '\t' && (self.mark.col as isize) < self.indent {
3435 return Err(self.scan_error(ErrorKind::TabInIndentation));
3436 }
3437 self.skip_blank();
3438 } else {
3439 match buf {
3441 FlowScalarBuf::Owned(ref mut string) => {
3442 if trailing_ws_start.is_none() {
3443 trailing_ws_start = Some(string.len());
3444 }
3445 string.push(self.input.peek());
3446 }
3447 FlowScalarBuf::Borrowed { .. } => {
3448 if pending_ws_start.is_none() {
3449 pending_ws_start = self.input.byte_offset();
3450 }
3451 }
3452 }
3453 self.skip_blank();
3454
3455 if let (FlowScalarBuf::Borrowed { .. }, Some(ws_start), Some(ws_end)) =
3456 (&mut buf, pending_ws_start, self.input.byte_offset())
3457 {
3458 buf.note_pending_ws(ws_start, ws_end);
3459 }
3460 }
3461 } else {
3462 self.input.lookahead(2);
3463
3464 if leading_blanks {
3466 match buf {
3468 FlowScalarBuf::Owned(ref mut string) => self.read_break(string),
3469 FlowScalarBuf::Borrowed { .. } => {
3470 self.promote_flow_scalar_buf_to_owned(&start_mark, &mut buf)?;
3471 let Some(string) = buf.as_owned_mut() else {
3472 unreachable!()
3473 };
3474 self.read_break(string);
3475 }
3476 }
3477 has_trailing_breaks = true;
3478 } else {
3479 if let Some(pos) = trailing_ws_start.take() {
3481 if let FlowScalarBuf::Owned(ref mut string) = buf {
3482 string.truncate(pos);
3483 }
3484 }
3485
3486 if pending_ws_start.take().is_some() {
3487 if matches!(buf, FlowScalarBuf::Borrowed { .. }) {
3489 self.promote_flow_scalar_buf_to_owned(&start_mark, &mut buf)?;
3490 }
3491 buf.discard_pending_ws();
3492 } else {
3493 buf.commit_pending_ws();
3494 }
3495
3496 break_scratch.clear();
3497 self.read_break(&mut break_scratch);
3498 has_leading_break = true;
3501 leading_blanks = true;
3502 }
3503 }
3504
3505 self.input.lookahead(1);
3506 }
3507
3508 if leading_blanks && has_leading_break && self.flow_level == 0 {
3511 let next_ch = self.input.peek();
3512 let is_closing_quote = (single && next_ch == '\'') || (!single && next_ch == '"');
3513 if !is_closing_quote && (self.mark.col as isize) <= self.indent {
3514 return Err(self.scan_error(ErrorKind::InvalidQuotedScalarIndent));
3515 }
3516 }
3517
3518 if leading_blanks {
3520 if has_leading_break && !has_trailing_breaks {
3525 match buf {
3526 FlowScalarBuf::Owned(ref mut string) => string.push(' '),
3527 FlowScalarBuf::Borrowed { .. } => {
3528 self.promote_flow_scalar_buf_to_owned(&start_mark, &mut buf)?;
3529 let Some(string) = buf.as_owned_mut() else {
3530 unreachable!()
3531 };
3532 string.push(' ');
3533 }
3534 }
3535 }
3536 }
3537 } self.skip_non_blank();
3542 let end_mark = self.mark;
3543
3544 self.skip_ws_to_eol(SkipTabs::Yes)?;
3546 self.ensure_current_char_is_printable()?;
3547 match self.input.peek() {
3548 ',' | '}' | ']' if self.flow_level > 0 => {}
3550 c if is_breakz(c) => {}
3552 ':' if self.flow_level == 0 && start_mark.line == self.mark.line => {}
3555 ':' if self.flow_level > 0 => {}
3557 _ => {
3558 let kind = if single {
3559 ErrorKind::InvalidTrailingSingleQuotedScalar
3560 } else {
3561 ErrorKind::InvalidTrailingDoubleQuotedScalar
3562 };
3563 return Err(self.scan_error(kind));
3564 }
3565 }
3566
3567 let style = if single {
3568 ScalarStyle::SingleQuoted
3569 } else {
3570 ScalarStyle::DoubleQuoted
3571 };
3572
3573 let contents = match buf {
3574 FlowScalarBuf::Owned(string) => Cow::Owned(string),
3575 FlowScalarBuf::Borrowed {
3576 start,
3577 mut end,
3578 pending_ws_start,
3579 pending_ws_end,
3580 } => {
3581 if pending_ws_start.is_some() {
3583 end = pending_ws_end;
3584 }
3585 if let Some(slice) = self.try_borrow_slice(start, end) {
3586 Cow::Borrowed(slice)
3587 } else {
3588 let slice = self.input.slice_bytes(start, end).ok_or_else(|| {
3589 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
3590 })?;
3591 Cow::Owned(slice.to_owned())
3592 }
3593 }
3594 };
3595
3596 Ok(Token(
3597 Span::new(start_mark, end_mark),
3598 TokenType::Scalar(style, contents),
3599 ))
3600 }
3601
3602 fn consume_flow_scalar_non_whitespace_chars(
3611 &mut self,
3612 single: bool,
3613 buf: &mut FlowScalarBuf,
3614 leading_blanks: &mut bool,
3615 start_mark: &Marker,
3616 ) -> Result<(), ScanError> {
3617 self.input.lookahead(2);
3618 while !is_blank_or_breakz(self.input.peek()) && is_printable(self.input.peek()) {
3619 match self.input.peek() {
3620 '\'' if self.input.peek_nth(1) == '\'' && single => {
3622 if matches!(buf, FlowScalarBuf::Borrowed { .. }) {
3623 buf.commit_pending_ws();
3624 self.promote_flow_scalar_buf_to_owned(start_mark, buf)?;
3625 }
3626 let Some(string) = buf.as_owned_mut() else {
3627 unreachable!()
3628 };
3629 string.push('\'');
3630 self.skip_n_non_blank(2);
3631 }
3632 '\'' if single => break,
3634 '"' if !single => break,
3635 '\\' if !single && is_break(self.input.peek_nth(1)) => {
3637 self.input.lookahead(3);
3638 if matches!(buf, FlowScalarBuf::Borrowed { .. }) {
3639 buf.commit_pending_ws();
3640 self.promote_flow_scalar_buf_to_owned(start_mark, buf)?;
3641 }
3642 self.skip_non_blank();
3643 self.skip_linebreak();
3644 *leading_blanks = true;
3645 break;
3646 }
3647 '\\' if !single => {
3649 if matches!(buf, FlowScalarBuf::Borrowed { .. }) {
3650 buf.commit_pending_ws();
3651 self.promote_flow_scalar_buf_to_owned(start_mark, buf)?;
3652 }
3653 let Some(string) = buf.as_owned_mut() else {
3654 unreachable!()
3655 };
3656 string.push(self.resolve_flow_scalar_escape_sequence(start_mark)?);
3657 }
3658 c => {
3659 match buf {
3660 FlowScalarBuf::Owned(ref mut string) => {
3661 string.push(c);
3662 }
3663 FlowScalarBuf::Borrowed { .. } => {
3664 buf.commit_pending_ws();
3665 }
3666 }
3667 self.skip_non_blank();
3668
3669 if let Some(new_end) = self.input.byte_offset() {
3670 if let FlowScalarBuf::Borrowed { end, .. } = buf {
3671 *end = new_end;
3672 }
3673 }
3674 }
3675 }
3676 self.input.lookahead(2);
3677 }
3678 Ok(())
3679 }
3680
3681 fn resolve_flow_scalar_escape_sequence(
3688 &mut self,
3689 start_mark: &Marker,
3690 ) -> Result<char, ScanError> {
3691 let mut code_length = 0usize;
3692 let mut ret = '\0';
3693
3694 match self.input.peek_nth(1) {
3695 '0' => ret = '\0',
3696 'a' => ret = '\x07',
3697 'b' => ret = '\x08',
3698 't' | '\t' => ret = '\t',
3699 'n' => ret = '\n',
3700 'v' => ret = '\x0b',
3701 'f' => ret = '\x0c',
3702 'r' => ret = '\x0d',
3703 'e' => ret = '\x1b',
3704 ' ' => ret = '\x20',
3705 '"' => ret = '"',
3706 '/' => ret = '/',
3707 '\\' => ret = '\\',
3708 'N' => ret = char::from_u32(0x85).unwrap(),
3710 '_' => ret = char::from_u32(0xA0).unwrap(),
3712 'L' => ret = char::from_u32(0x2028).unwrap(),
3714 'P' => ret = char::from_u32(0x2029).unwrap(),
3716 'x' => code_length = 2,
3717 'u' => code_length = 4,
3718 'U' => code_length = 8,
3719 _ => {
3720 return Err(ScanError::from_kind(
3721 *start_mark,
3722 ErrorKind::UnknownQuotedScalarEscape,
3723 ))
3724 }
3725 }
3726 self.skip_n_non_blank(2);
3727
3728 if code_length > 0 {
3730 self.input.lookahead(code_length);
3731 let mut value = 0u32;
3732 for i in 0..code_length {
3733 let c = self.input.peek_nth(i);
3734 if !is_hex(c) {
3735 return Err(ScanError::from_kind(
3736 *start_mark,
3737 ErrorKind::InvalidQuotedScalarHexEscape,
3738 ));
3739 }
3740 value = (value << 4) + as_hex(c);
3741 }
3742
3743 self.skip_n_non_blank(code_length);
3744
3745 if code_length == 4 && (0xD800..=0xDBFF).contains(&value) {
3747 self.input.lookahead(2);
3748 if self.input.peek() == '\\' && self.input.peek_nth(1) == 'u' {
3749 self.skip_n_non_blank(2);
3750 self.input.lookahead(4);
3751 let mut low_value = 0u32;
3752 for i in 0..4 {
3753 let c = self.input.peek_nth(i);
3754 if !is_hex(c) {
3755 return Err(ScanError::from_kind(
3756 *start_mark,
3757 ErrorKind::InvalidLowSurrogateHexEscape,
3758 ));
3759 }
3760 low_value = (low_value << 4) + as_hex(c);
3761 }
3762 if (0xDC00..=0xDFFF).contains(&low_value) {
3763 value = 0x10000 + (((value - 0xD800) << 10) | (low_value - 0xDC00));
3764 self.skip_n_non_blank(4);
3765 } else {
3766 return Err(ScanError::from_kind(
3767 *start_mark,
3768 ErrorKind::InvalidLowSurrogate,
3769 ));
3770 }
3771 } else {
3772 return Err(ScanError::from_kind(
3773 *start_mark,
3774 ErrorKind::MissingLowSurrogate,
3775 ));
3776 }
3777 } else if code_length == 4 && (0xDC00..=0xDFFF).contains(&value) {
3778 return Err(ScanError::from_kind(
3779 *start_mark,
3780 ErrorKind::UnpairedLowSurrogate,
3781 ));
3782 }
3783
3784 let Some(ch) = char::from_u32(value) else {
3785 return Err(ScanError::from_kind(
3786 *start_mark,
3787 ErrorKind::InvalidUnicodeEscape,
3788 ));
3789 };
3790 ret = ch;
3791 }
3792 Ok(ret)
3793 }
3794
3795 fn fetch_plain_scalar(&mut self) -> ScanResult {
3796 self.save_simple_key();
3797 self.disallow_simple_key();
3798
3799 let token_index = self.tokens.len();
3800 let tok = self.scan_plain_scalar()?;
3801
3802 self.insert_token(token_index, tok);
3803 Ok(())
3804 }
3805
3806 #[allow(clippy::too_many_lines)]
3811 fn scan_plain_scalar(&mut self) -> Result<Token<'input>, ScanError> {
3812 self.unroll_non_block_indents();
3813 let indent = self.indent + 1;
3814 let start_mark = self.mark;
3815
3816 if self.flow_level > 0 && (start_mark.col as isize) < indent {
3817 return Err(ScanError::from_kind(
3818 start_mark,
3819 ErrorKind::InvalidFlowScalarIndent,
3820 ));
3821 }
3822
3823 let borrow_start = start_mark
3824 .byte_offset()
3825 .filter(|start| self.try_borrow_slice(*start, *start).is_some());
3826 let mut string = borrow_start.is_none().then(|| String::with_capacity(32));
3827 let mut has_content = false;
3828 self.buf_whitespaces.clear();
3829 self.buf_leading_break.clear();
3830 self.buf_trailing_breaks.clear();
3831 let mut end_mark = self.mark;
3832
3833 loop {
3834 self.input.lookahead(4);
3835 if (self.mark.col == 0 && self.input.next_is_document_indicator())
3836 || self.input.peek() == '#'
3837 {
3838 if self.input.peek() == '#'
3843 && has_content
3844 && !self.buf_whitespaces.is_empty()
3845 && self.flow_level == 0
3846 {
3847 self.interrupted_plain_by_comment = Some(self.mark);
3848 }
3849 break;
3850 }
3851
3852 if self.flow_level > 0 && self.input.peek() == '-' && is_flow(self.input.peek_nth(1)) {
3853 return Err(self.scan_error(ErrorKind::PlainScalarStartsWithDashFlowIndicator));
3854 }
3855
3856 if !self.input.next_is_blank_or_breakz()
3857 && self.input.next_can_be_plain_scalar(self.flow_level > 0)
3858 {
3859 if self.leading_whitespace {
3860 if has_content && string.is_none() {
3861 let start = borrow_start.expect("borrowed scalar has a start offset");
3862 let end = end_mark.byte_offset().ok_or_else(|| {
3863 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
3864 })?;
3865 let prefix = self.try_borrow_slice(start, end).ok_or_else(|| {
3866 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
3867 })?;
3868 string = Some(prefix.to_owned());
3869 }
3870 if self.buf_leading_break.is_empty() {
3871 if let Some(output) = string.as_mut() {
3872 output.push_str(&self.buf_leading_break);
3873 output.push_str(&self.buf_trailing_breaks);
3874 }
3875 self.buf_trailing_breaks.clear();
3876 self.buf_leading_break.clear();
3877 } else {
3878 if self.buf_trailing_breaks.is_empty() {
3879 if let Some(output) = string.as_mut() {
3880 output.push(' ');
3881 }
3882 } else {
3883 if let Some(output) = string.as_mut() {
3884 output.push_str(&self.buf_trailing_breaks);
3885 }
3886 self.buf_trailing_breaks.clear();
3887 }
3888 self.buf_leading_break.clear();
3889 }
3890 self.leading_whitespace = false;
3891 } else if !self.buf_whitespaces.is_empty() {
3892 if let Some(output) = string.as_mut() {
3893 output.push_str(&self.buf_whitespaces);
3894 }
3895 self.buf_whitespaces.clear();
3896 }
3897
3898 has_content = true;
3900 if let Some(output) = string.as_mut() {
3901 output.push(self.input.peek());
3902 }
3903 self.skip_non_blank();
3904 if let Some(output) = string.as_mut() {
3905 output.reserve(self.input.bufmaxlen());
3906 }
3907
3908 let mut end = false;
3910 while !end {
3911 self.input.lookahead(self.input.bufmaxlen());
3916 let chunk_len = self.input.bufmaxlen().saturating_sub(1).max(1);
3917 let (stop, chars_consumed) = if let Some(output) = string.as_mut() {
3918 self.input
3919 .fetch_plain_scalar_chunk(output, chunk_len, self.flow_level > 0)
3920 } else {
3921 self.input
3922 .skip_plain_scalar_chunk(chunk_len, self.flow_level > 0)
3923 };
3924 end = stop;
3925 self.mark.offsets.chars += chars_consumed;
3926 self.mark.col += chars_consumed;
3927 self.mark.offsets.bytes = self.input.byte_offset();
3928 }
3929 end_mark = self.mark;
3930 }
3931
3932 if !(self.input.next_is_blank() || self.input.next_is_break()) {
3937 break;
3938 }
3939
3940 self.input.lookahead(2);
3942 while self.input.next_is_blank_or_break() {
3943 if self.input.next_is_blank() {
3944 if !self.leading_whitespace {
3945 self.buf_whitespaces.push(self.input.peek());
3946 self.skip_blank();
3947 } else if (self.mark.col as isize) < indent && self.input.peek() == '\t' {
3948 self.skip_ws_to_eol(SkipTabs::Yes)?;
3951 if !self.input.next_is_breakz() {
3952 return Err(ScanError::from_kind(
3953 start_mark,
3954 ErrorKind::TabInPlainScalar,
3955 ));
3956 }
3957 } else {
3958 self.skip_blank();
3959 }
3960 } else {
3961 if self.leading_whitespace {
3963 self.skip_break();
3964 self.buf_trailing_breaks.push('\n');
3965 } else {
3966 self.buf_whitespaces.clear();
3967 self.skip_break();
3968 self.buf_leading_break.push('\n');
3969 self.leading_whitespace = true;
3970 }
3971 }
3972 self.input.lookahead(2);
3973 }
3974
3975 if self.flow_level == 0 && (self.mark.col as isize) < indent {
3977 break;
3978 }
3979 }
3980
3981 if self.leading_whitespace {
3982 self.allow_simple_key();
3983 }
3984
3985 let borrowed_contents = if string.is_none() {
3986 let start = borrow_start.expect("borrowed scalar has a start offset");
3987 let end = end_mark.byte_offset().ok_or_else(|| {
3988 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
3989 })?;
3990 Some(self.try_borrow_slice(start, end).ok_or_else(|| {
3991 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
3992 })?)
3993 } else {
3994 None
3995 };
3996 let scalar_text = borrowed_contents.unwrap_or_else(|| {
3997 string
3998 .as_deref()
3999 .expect("owned plain scalar has an output buffer")
4000 });
4001 if let Some(character) = find_non_printable(scalar_text) {
4002 return Err(ScanError::from_kind(
4003 start_mark,
4004 ErrorKind::UnexpectedCharacter { character },
4005 ));
4006 }
4007 self.ensure_current_char_is_printable()?;
4008
4009 if has_content {
4010 let contents = if let Some(slice) = borrowed_contents {
4011 Cow::Borrowed(slice)
4012 } else {
4013 Cow::Owned(string.expect("owned plain scalar has an output buffer"))
4014 };
4015
4016 Ok(Token(
4017 Span::new(start_mark, end_mark),
4018 TokenType::Scalar(ScalarStyle::Plain, contents),
4019 ))
4020 } else {
4021 Err(ScanError::from_kind(
4025 start_mark,
4026 ErrorKind::UnexpectedEndOfPlainScalar,
4027 ))
4028 }
4029 }
4030
4031 fn fetch_key(&mut self) -> ScanResult {
4032 let start_mark = self.mark;
4033 if self.flow_level == 0 {
4034 if !self.simple_key_allowed {
4036 return Err(self.scan_error(ErrorKind::MappingKeyNotAllowed));
4037 }
4038 self.roll_indent(
4039 start_mark.col,
4040 None,
4041 TokenType::BlockMappingStart,
4042 start_mark,
4043 )?;
4044 } else {
4045 self.set_current_flow_mapping_started(true);
4047 }
4048
4049 self.remove_simple_key()?;
4050
4051 if self.flow_level == 0 {
4052 self.allow_simple_key();
4053 } else {
4054 self.disallow_simple_key();
4055 }
4056
4057 self.skip_non_blank();
4058 let end_mark = self.mark;
4059 let token_index = self.tokens.len();
4060 self.explicit_key_tab_check_pending = false;
4061 let stopped_after_comment = self.skip_yaml_whitespace()?;
4062 if self.input.peek() == '\t' {
4063 return Err(self.scan_error(ErrorKind::TabNotAllowed));
4064 }
4065 self.explicit_key_tab_check_pending = stopped_after_comment;
4066 self.insert_token(
4067 token_index,
4068 Token(Span::new(start_mark, end_mark), TokenType::Key),
4069 );
4070 Ok(())
4071 }
4072
4073 fn fetch_flow_value(&mut self) -> ScanResult {
4081 let nc = self.input.peek_nth(1);
4082
4083 if self.mark.index() != self.adjacent_value_allowed_at && (nc == '[' || nc == '{') {
4095 return Err(self.scan_error(ErrorKind::FlowMappingValueAdjacentCollection));
4096 }
4097
4098 self.fetch_value()
4099 }
4100
4101 fn fetch_value(&mut self) -> ScanResult {
4103 let sk = *self.simple_keys.last().unwrap();
4104 let start_mark = self.mark;
4105 let is_implicit_flow_mapping = self.current_flow_collection_is_sequence()
4106 && !self.current_flow_mapping_started()
4107 && !self.implicit_flow_mapping_states.is_empty();
4108 if is_implicit_flow_mapping {
4109 *self.implicit_flow_mapping_states.last_mut().unwrap() =
4110 ImplicitMappingState::Inside(self.flow_level);
4111 }
4112
4113 self.skip_non_blank();
4115 let mut trailing_tokens = VecDeque::new();
4122 if self.input.look_ch() == '\t' {
4123 let trailing_token_index = self.tokens.len();
4124 let whitespace = self.skip_ws_to_eol(SkipTabs::Yes)?;
4125 trailing_tokens = self.tokens.split_off(trailing_token_index);
4126
4127 if !whitespace.has_valid_yaml_ws()
4128 && (self.input.peek() == '-' || self.input.next_is_alpha())
4129 {
4130 return Err(self.scan_error(ErrorKind::InvalidMappingValueWhitespace));
4131 }
4132 }
4133
4134 if sk.possible {
4135 let token_index = self.simple_key_token_index(&sk, start_mark)?;
4136 let tok = Token(Span::empty(sk.mark), TokenType::Key);
4138 self.insert_token(token_index, tok);
4139 if is_implicit_flow_mapping {
4140 if sk.mark.line < start_mark.line {
4141 return Err(ScanError::from_kind(
4142 start_mark,
4143 ErrorKind::InvalidColonPlacement,
4144 ));
4145 }
4146 self.insert_token(
4147 token_index,
4148 Token(Span::empty(sk.mark), TokenType::FlowMappingStart),
4149 );
4150 }
4151
4152 self.roll_indent(
4154 sk.mark.col,
4155 Some(sk.token_number),
4156 TokenType::BlockMappingStart,
4157 sk.mark,
4158 )?;
4159 self.roll_one_col_indent();
4160
4161 self.simple_keys.last_mut().unwrap().possible = false;
4162 self.disallow_simple_key();
4163 } else {
4164 if is_implicit_flow_mapping {
4165 self.tokens
4166 .push_back(Token(Span::empty(start_mark), TokenType::FlowMappingStart).into());
4167 }
4168 if self.flow_level == 0 {
4170 if !self.simple_key_allowed {
4171 return Err(ScanError::from_kind(
4172 start_mark,
4173 ErrorKind::MappingValueNotAllowed,
4174 ));
4175 }
4176
4177 self.roll_indent(
4178 start_mark.col,
4179 None,
4180 TokenType::BlockMappingStart,
4181 start_mark,
4182 )?;
4183 }
4184 self.roll_one_col_indent();
4185
4186 if self.flow_level == 0 {
4187 self.allow_simple_key();
4188 } else {
4189 self.disallow_simple_key();
4190 }
4191 }
4192 self.tokens
4193 .push_back(Token(Span::empty(start_mark), TokenType::Value).into());
4194 self.tokens.append(&mut trailing_tokens);
4195
4196 Ok(())
4197 }
4198
4199 fn roll_indent(
4205 &mut self,
4206 col: usize,
4207 number: Option<usize>,
4208 tok: TokenType<'input>,
4209 mark: Marker,
4210 ) -> ScanResult {
4211 if self.flow_level > 0 {
4212 return Ok(());
4213 }
4214
4215 if self.indent <= col as isize {
4219 if let Some(indent) = self.indents.last() {
4220 if !indent.needs_block_end {
4221 self.indent = indent.indent;
4222 self.indents.pop();
4223 }
4224 }
4225 }
4226
4227 if self.indent < col as isize {
4228 if self.block_level >= self.options.block_nesting_limit {
4229 return Err(ScanError::from_kind(
4230 mark,
4231 ErrorKind::RecursionLimitExceeded,
4232 ));
4233 }
4234 self.indents.push(Indent {
4235 indent: self.indent,
4236 needs_block_end: true,
4237 });
4238 self.block_level += 1;
4239 self.indent = col as isize;
4240 let tokens_parsed = self.tokens_parsed;
4241 match number {
4242 Some(n) => self.insert_token(n - tokens_parsed, Token(Span::empty(mark), tok)),
4243 None => self.tokens.push_back(Token(Span::empty(mark), tok).into()),
4244 }
4245 }
4246 Ok(())
4247 }
4248
4249 fn unroll_indent(&mut self, col: isize) {
4255 if self.flow_level > 0 {
4256 return;
4257 }
4258 while self.indent > col {
4259 let indent = self.indents.pop().unwrap();
4260 self.indent = indent.indent;
4261 if indent.needs_block_end {
4262 debug_assert!(self.block_level > 0);
4263 self.block_level -= 1;
4264 self.tokens
4265 .push_back(Token(Span::empty(self.mark), TokenType::BlockEnd).into());
4266 }
4267 }
4268 }
4269
4270 fn roll_one_col_indent(&mut self) {
4276 if self.flow_level == 0 && self.indents.last().is_some_and(|x| x.needs_block_end) {
4277 self.indents.push(Indent {
4278 indent: self.indent,
4279 needs_block_end: false,
4280 });
4281 self.indent += 1;
4282 }
4283 }
4284
4285 fn unroll_non_block_indents(&mut self) {
4287 while let Some(indent) = self.indents.last() {
4288 if indent.needs_block_end {
4289 break;
4290 }
4291 self.indent = indent.indent;
4292 self.indents.pop();
4293 }
4294 }
4295
4296 fn save_simple_key(&mut self) {
4298 if self.simple_key_allowed {
4299 let required = self.flow_level == 0
4300 && self.indent == (self.mark.col as isize)
4301 && self.indents.last().unwrap().needs_block_end;
4302
4303 if let Some(last) = self.simple_keys.last_mut() {
4304 *last = SimpleKey {
4305 mark: self.mark,
4306 possible: true,
4307 required,
4308 token_number: self.tokens_parsed + self.tokens.len(),
4309 };
4310 }
4311 }
4312 }
4313
4314 fn remove_simple_key(&mut self) -> ScanResult {
4315 let last = self.simple_keys.last_mut().unwrap();
4316 if last.possible && last.required {
4317 return Err(Self::simple_key_expected(last.mark));
4318 }
4319
4320 last.possible = false;
4321 Ok(())
4322 }
4323
4324 fn is_within_block(&self) -> bool {
4326 !self.indents.is_empty()
4327 }
4328
4329 fn end_implicit_mapping(&mut self, mark: Marker, flow_level: usize) {
4335 if self
4336 .implicit_flow_mapping_states
4337 .last()
4338 .is_some_and(|state| *state == ImplicitMappingState::Inside(flow_level))
4339 {
4340 *self.implicit_flow_mapping_states.last_mut().unwrap() = ImplicitMappingState::Possible;
4341 self.set_current_flow_mapping_started(false);
4342 self.tokens
4343 .push_back(Token(Span::empty(mark), TokenType::FlowMappingEnd).into());
4344 }
4345 }
4346
4347 fn current_flow_collection_is_sequence(&self) -> bool {
4348 self.flow_markers
4349 .last()
4350 .is_some_and(|(_, bracket)| *bracket == '[')
4351 }
4352
4353 fn current_flow_mapping_started(&self) -> bool {
4354 self.flow_mapping_started.last().copied().unwrap_or(false)
4355 }
4356
4357 fn set_current_flow_mapping_started(&mut self, started: bool) {
4358 if let Some(current) = self.flow_mapping_started.last_mut() {
4359 *current = started;
4360 }
4361 }
4362}
4363
4364#[derive(PartialEq, Eq)]
4368enum Chomping {
4369 Strip,
4371 Clip,
4373 Keep,
4375}
4376
4377#[cfg(test)]
4378mod test {
4379 use alloc::{
4380 borrow::{Cow, ToOwned},
4381 rc::Rc,
4382 string::String,
4383 vec,
4384 vec::Vec,
4385 };
4386 use core::cell::Cell;
4387
4388 use crate::error::{ErrorKind, ScanError};
4389 use crate::{
4390 input::{str::StrInput, BorrowedInput, BufferedInput, Input},
4391 scanner::{
4392 Comment, Marker, Placement, QueuedToken, QueuedTokenType, ScalarStyle, Scanner, Span,
4393 Token, TokenType,
4394 },
4395 };
4396
4397 struct CountingChars {
4398 chars: alloc::vec::IntoIter<char>,
4399 read: Rc<Cell<usize>>,
4400 }
4401
4402 impl Iterator for CountingChars {
4403 type Item = char;
4404
4405 fn next(&mut self) -> Option<Self::Item> {
4406 let next = self.chars.next();
4407 if next.is_some() {
4408 self.read.set(self.read.get() + 1);
4409 }
4410 next
4411 }
4412 }
4413
4414 struct SlicingOnlyInput<'input> {
4415 inner: StrInput<'input>,
4416 expose_slice: bool,
4417 }
4418
4419 impl<'input> SlicingOnlyInput<'input> {
4420 fn new(source: &'input str, expose_slice: bool) -> Self {
4421 Self {
4422 inner: StrInput::new(source),
4423 expose_slice,
4424 }
4425 }
4426 }
4427
4428 impl Input for SlicingOnlyInput<'_> {
4429 fn lookahead(&mut self, count: usize) {
4430 self.inner.lookahead(count);
4431 }
4432
4433 fn buflen(&self) -> usize {
4434 self.inner.buflen()
4435 }
4436
4437 fn bufmaxlen(&self) -> usize {
4438 self.inner.bufmaxlen()
4439 }
4440
4441 fn raw_read_ch(&mut self) -> char {
4442 self.inner.raw_read_ch()
4443 }
4444
4445 fn raw_read_non_breakz_ch(&mut self) -> Option<char> {
4446 self.inner.raw_read_non_breakz_ch()
4447 }
4448
4449 fn skip(&mut self) {
4450 self.inner.skip();
4451 }
4452
4453 fn skip_n(&mut self, count: usize) {
4454 self.inner.skip_n(count);
4455 }
4456
4457 fn peek(&self) -> char {
4458 self.inner.peek()
4459 }
4460
4461 fn peek_nth(&self, n: usize) -> char {
4462 self.inner.peek_nth(n)
4463 }
4464
4465 fn byte_offset(&self) -> Option<usize> {
4466 self.inner.byte_offset()
4467 }
4468
4469 fn slice_bytes(&self, start: usize, end: usize) -> Option<&str> {
4470 if self.expose_slice {
4471 self.inner.slice_bytes(start, end)
4472 } else {
4473 None
4474 }
4475 }
4476 }
4477
4478 impl<'input> BorrowedInput<'input> for SlicingOnlyInput<'input> {
4479 fn slice_borrowed(&self, _start: usize, _end: usize) -> Option<&'input str> {
4480 None
4481 }
4482 }
4483
4484 struct SmallReportedBufferInput<'input> {
4485 inner: StrInput<'input>,
4486 reported_bufmaxlen: usize,
4487 }
4488
4489 impl<'input> SmallReportedBufferInput<'input> {
4490 fn new(source: &'input str, reported_bufmaxlen: usize) -> Self {
4491 Self {
4492 inner: StrInput::new(source),
4493 reported_bufmaxlen,
4494 }
4495 }
4496 }
4497
4498 impl Input for SmallReportedBufferInput<'_> {
4499 fn lookahead(&mut self, count: usize) {
4500 self.inner.lookahead(count);
4501 }
4502
4503 fn buflen(&self) -> usize {
4504 self.inner.buflen()
4505 }
4506
4507 fn bufmaxlen(&self) -> usize {
4508 self.reported_bufmaxlen
4509 }
4510
4511 fn raw_read_ch(&mut self) -> char {
4512 self.inner.raw_read_ch()
4513 }
4514
4515 fn raw_read_non_breakz_ch(&mut self) -> Option<char> {
4516 self.inner.raw_read_non_breakz_ch()
4517 }
4518
4519 fn skip(&mut self) {
4520 self.inner.skip();
4521 }
4522
4523 fn skip_n(&mut self, count: usize) {
4524 self.inner.skip_n(count);
4525 }
4526
4527 fn peek(&self) -> char {
4528 self.inner.peek()
4529 }
4530
4531 fn peek_nth(&self, n: usize) -> char {
4532 self.inner.peek_nth(n)
4533 }
4534 }
4535
4536 impl<'input> BorrowedInput<'input> for SmallReportedBufferInput<'input> {
4537 fn slice_borrowed(&self, start: usize, end: usize) -> Option<&'input str> {
4538 self.inner.slice_borrowed(start, end)
4539 }
4540 }
4541
4542 #[test]
4543 fn anchor_character_set_allows_colon_and_rejects_flow_indicators() {
4544 use super::is_anchor_char;
4545
4546 assert!(is_anchor_char('x'));
4547 assert!(is_anchor_char('-'));
4548 assert!(is_anchor_char('_'));
4549 assert!(is_anchor_char(':'));
4550 assert!(is_anchor_char('#'));
4551 assert!(is_anchor_char('/'));
4552 assert!(is_anchor_char('?'));
4553
4554 for c in [',', '[', ']', '{', '}', ' ', '\t', '\n', '\r', '\0'] {
4555 assert!(
4556 !is_anchor_char(c),
4557 "character {c:?} must not be accepted in anchor/alias names"
4558 );
4559 }
4560 }
4561
4562 #[test]
4563 fn flow_simple_key_length_limit_bounds_buffering() {
4564 let mut yaml = String::from("[\n\"start\"\n");
4565 for _ in 0..600 {
4566 yaml.push_str("\"x\"\n");
4567 }
4568 let total_chars = yaml.chars().count();
4569 let read = Rc::new(Cell::new(0));
4570 let chars = yaml.chars().collect::<Vec<_>>().into_iter();
4571 let mut scanner = Scanner::new(BufferedInput::new(CountingChars {
4572 chars,
4573 read: Rc::clone(&read),
4574 }));
4575
4576 assert!(matches!(
4577 scanner.next_token().unwrap().unwrap().1,
4578 TokenType::StreamStart
4579 ));
4580
4581 let token = scanner.next_token().unwrap().unwrap();
4582 assert!(matches!(token.1, TokenType::FlowSequenceStart));
4583
4584 let token = scanner.next_token().unwrap().unwrap();
4585 assert!(matches!(
4586 token.1,
4587 TokenType::Scalar(_, ref value) if value == "start"
4588 ));
4589 assert!(
4590 read.get() < total_chars,
4591 "scanner consumed all {total_chars} chars before yielding the first flow scalar"
4592 );
4593 assert!(
4594 read.get() <= scanner.options.simple_key_max_lookahead + 128,
4595 "scanner read {} chars before yielding the first flow scalar",
4596 read.get()
4597 );
4598 }
4599
4600 #[test]
4601 fn block_scalar_indent_tolerates_small_reported_bufmaxlen() {
4602 let mut scanner = Scanner::new(SmallReportedBufferInput::new("|\n value\n", 0));
4603
4604 let scalar = scanner
4605 .find_map(
4606 |token| match token.expect("valid YAML should scan without errors") {
4607 Token(_, TokenType::Scalar(ScalarStyle::Literal, value)) => {
4608 Some(value.into_owned())
4609 }
4610 _ => None,
4611 },
4612 )
4613 .expect("expected block scalar token");
4614
4615 assert_eq!(scalar, "value\n");
4616 }
4617
4618 #[test]
4619 fn plain_scalar_chunk_tolerates_small_reported_bufmaxlen() {
4620 let mut scanner = Scanner::new(SmallReportedBufferInput::new("plain\n", 0));
4621
4622 let scalar = scanner
4623 .find_map(
4624 |token| match token.expect("valid YAML should scan without errors") {
4625 Token(_, TokenType::Scalar(ScalarStyle::Plain, value)) => {
4626 Some(value.into_owned())
4627 }
4628 _ => None,
4629 },
4630 )
4631 .expect("expected plain scalar token");
4632
4633 assert_eq!(scalar, "plain");
4634 }
4635
4636 fn first_token_slice(
4637 yaml: &str,
4638 matches_token: impl Fn(&TokenType<'_>) -> bool,
4639 ) -> Option<String> {
4640 let mut scanner = Scanner::new(StrInput::new(yaml));
4641
4642 loop {
4643 let token = scanner
4644 .next_token()
4645 .expect("scanner should accept the test YAML")?;
4646 if matches_token(&token.1) {
4647 return token.0.slice(yaml).map(ToOwned::to_owned);
4648 }
4649 }
4650 }
4651
4652 #[test]
4653 fn flow_indicator_token_spans_cover_only_the_indicator() {
4654 assert_eq!(
4655 first_token_slice("[ # c\n a]\n", |token| matches!(
4656 token,
4657 TokenType::FlowSequenceStart
4658 ))
4659 .as_deref(),
4660 Some("[")
4661 );
4662 assert_eq!(
4663 first_token_slice("{ # c\n a: b}\n", |token| matches!(
4664 token,
4665 TokenType::FlowMappingStart
4666 ))
4667 .as_deref(),
4668 Some("{")
4669 );
4670 assert_eq!(
4671 first_token_slice("[a] # c\n", |token| matches!(
4672 token,
4673 TokenType::FlowSequenceEnd
4674 ))
4675 .as_deref(),
4676 Some("]")
4677 );
4678 assert_eq!(
4679 first_token_slice("{a: b} # c\n", |token| matches!(
4680 token,
4681 TokenType::FlowMappingEnd
4682 ))
4683 .as_deref(),
4684 Some("}")
4685 );
4686 assert_eq!(
4687 first_token_slice("[a, # c\nb]\n", |token| matches!(
4688 token,
4689 TokenType::FlowEntry
4690 ))
4691 .as_deref(),
4692 Some(",")
4693 );
4694 }
4695
4696 #[test]
4697 fn explicit_key_token_span_covers_only_the_indicator() {
4698 assert_eq!(
4699 first_token_slice("? # c\n: value\n", |token| matches!(token, TokenType::Key))
4700 .as_deref(),
4701 Some("?")
4702 );
4703 }
4704
4705 #[test]
4706 fn comment_capture_does_not_change_leading_whitespace() {
4707 let mut scanner = Scanner::new(StrInput::new("# comment\n"));
4708
4709 let token = scanner.scan_comment_token().unwrap();
4710
4711 assert!(scanner.leading_whitespace);
4712 assert!(matches!(token.1, TokenType::Comment(ref comment) if comment.text == " comment"));
4713
4714 let mut scanner = Scanner::new(BufferedInput::new("# streaming\n".chars()));
4715 scanner.input.lookahead(1);
4716
4717 let token = scanner.scan_comment_token().unwrap();
4718
4719 assert!(scanner.leading_whitespace);
4720 assert!(matches!(token.1, TokenType::Comment(ref comment) if comment.text == " streaming"));
4721 }
4722
4723 #[test]
4724 fn comment_capture_falls_back_to_owned_slice_when_borrow_unavailable() {
4725 let mut scanner = Scanner::new(SlicingOnlyInput::new("# sliced\n", true));
4726 scanner.input.lookahead(2);
4727 assert_eq!(scanner.input.peek_nth(1), ' ');
4728
4729 let token = scanner.scan_comment_token().unwrap();
4730
4731 assert!(matches!(token.1, TokenType::Comment(ref comment)
4732 if matches!(comment.text, Cow::Owned(ref text) if text == " sliced")));
4733 }
4734
4735 #[test]
4736 fn comment_capture_errors_when_offsets_have_no_slice() {
4737 let mut scanner = Scanner::new(SlicingOnlyInput::new("# broken\n", false));
4738
4739 let error = scanner.scan_comment_token().unwrap_err();
4740
4741 assert_eq!(error.kind(), &ErrorKind::InputOffsetsWithoutSlice);
4742 }
4743
4744 #[test]
4745 fn disabled_comments_do_not_require_input_slicing() {
4746 let options = crate::options! { emit_comments: false };
4747 let scanner = Scanner::with_options(
4748 SlicingOnlyInput::new("# ignored\nkey: value\n", false),
4749 options,
4750 );
4751
4752 let tokens = scanner
4753 .collect::<Result<Vec<_>, _>>()
4754 .expect("ignored comments should not capture their payload");
4755
4756 assert!(tokens
4757 .iter()
4758 .all(|token| !matches!(token.1, TokenType::Comment(_))));
4759 }
4760
4761 #[test]
4762 fn queued_token_roundtrips_public_token_variants() {
4763 let span = Span::new(Marker::new(0, 1, 0), Marker::new(7, 1, 7));
4764 let tokens = [
4765 Token(span, TokenType::StreamStart),
4766 Token(span, TokenType::StreamEnd),
4767 Token(span, TokenType::VersionDirective(1, 2)),
4768 Token(
4769 span,
4770 TokenType::TagDirective(Cow::Borrowed("!app!"), Cow::Borrowed("tag:app.example,")),
4771 ),
4772 Token(span, TokenType::DocumentStart),
4773 Token(span, TokenType::DocumentEnd),
4774 Token(span, TokenType::BlockSequenceStart),
4775 Token(span, TokenType::BlockMappingStart),
4776 Token(span, TokenType::BlockEnd),
4777 Token(span, TokenType::FlowSequenceStart),
4778 Token(span, TokenType::FlowSequenceEnd),
4779 Token(span, TokenType::FlowMappingStart),
4780 Token(span, TokenType::FlowMappingEnd),
4781 Token(span, TokenType::BlockEntry),
4782 Token(span, TokenType::FlowEntry),
4783 Token(span, TokenType::Key),
4784 Token(span, TokenType::Value),
4785 Token(span, TokenType::Alias(Cow::Borrowed("alias"))),
4786 Token(span, TokenType::Anchor(Cow::Borrowed("anchor"))),
4787 Token(
4788 span,
4789 TokenType::Tag(Cow::Borrowed("!"), Cow::Borrowed("tag")),
4790 ),
4791 Token(
4792 span,
4793 TokenType::Scalar(ScalarStyle::Literal, Cow::Borrowed("scalar")),
4794 ),
4795 Token(
4796 span,
4797 TokenType::Comment(
4798 Comment::new(Cow::Borrowed(" comment")).with_placement(Placement::Right),
4799 ),
4800 ),
4801 Token(
4802 span,
4803 TokenType::ReservedDirective(
4804 "reserved".to_owned(),
4805 vec!["one".to_owned(), "two".to_owned()],
4806 ),
4807 ),
4808 ];
4809
4810 for token in tokens {
4811 let queued: QueuedToken = token.clone().into();
4812
4813 assert_eq!(queued.into_public(), token);
4814 }
4815 }
4816
4817 #[test]
4818 fn comment_skipping_path_consumes_comment_without_tokenizing_it() {
4819 let mut scanner = Scanner::new(StrInput::new("# skipped\nnext: value\n"));
4820
4821 scanner.skip_yaml_whitespace().unwrap();
4822
4823 assert!(scanner.tokens.is_empty());
4824 assert_eq!(scanner.mark.line(), 2);
4825 assert_eq!(scanner.mark.col(), 0);
4826 }
4827
4828 #[test]
4829 fn yaml_whitespace_can_stop_after_queued_comment() {
4830 let mut scanner = Scanner::new(StrInput::new(" # queued\n# later\n"));
4831
4832 assert!(scanner.skip_yaml_whitespace().unwrap());
4833
4834 assert_eq!(scanner.tokens.len(), 1);
4835 assert!(matches!(
4836 scanner.tokens.front().unwrap().1,
4837 QueuedTokenType::Comment(ref comment) if comment.text == " queued"
4838 ));
4839 assert_eq!(scanner.mark.line(), 1);
4840 assert_eq!(scanner.mark.col(), 9);
4841 }
4842
4843 #[test]
4844 fn token_skip_can_stop_after_queued_comment() {
4845 let mut scanner = Scanner::new(StrInput::new("# first\n# second\n"));
4846
4847 assert!(scanner.skip_to_next_token().unwrap().queued_comment);
4848
4849 assert_eq!(scanner.tokens.len(), 1);
4850 assert!(matches!(
4851 scanner.tokens.front().unwrap().1,
4852 QueuedTokenType::Comment(ref comment) if comment.text == " first"
4853 ));
4854 assert_eq!(scanner.mark.line(), 2);
4855 assert_eq!(scanner.mark.col(), 0);
4856 }
4857
4858 #[test]
4859 fn token_skip_stops_after_one_ignored_comment_without_queuing_it() {
4860 let options = crate::options! { emit_comments: false };
4861 let mut scanner =
4862 Scanner::with_options(StrInput::new("# first\n# second\nvalue\n"), options);
4863
4864 let outcome = scanner.skip_to_next_token().unwrap();
4865
4866 assert!(outcome.saw_comment);
4867 assert!(!outcome.queued_comment);
4868 assert!(scanner.tokens.is_empty());
4869 assert_eq!((scanner.mark.line(), scanner.mark.col()), (2, 0));
4870 assert_eq!(scanner.input.look_ch(), '#');
4871 }
4872
4873 #[test]
4874 fn yaml_whitespace_stops_after_one_separated_ignored_comment() {
4875 let options = crate::options! { emit_comments: false };
4876 let mut scanner =
4877 Scanner::with_options(StrInput::new(" # first\n# second\nvalue\n"), options);
4878
4879 assert!(scanner.skip_yaml_whitespace().unwrap());
4880
4881 assert!(scanner.tokens.is_empty());
4882 assert_eq!((scanner.mark.line(), scanner.mark.col()), (1, 8));
4883 assert_eq!(scanner.input.look_ch(), '\n');
4884 }
4885
4886 #[test]
4887 fn scanner_skips_consecutive_leading_ignored_comments_before_next_token() {
4888 let options = crate::options! { emit_comments: false };
4889 let mut scanner =
4890 Scanner::with_options(StrInput::new("# first\n# second\nvalue\n"), options);
4891
4892 assert!(matches!(
4893 scanner.next_token().unwrap().unwrap().1,
4894 TokenType::StreamStart
4895 ));
4896 assert!(matches!(
4897 scanner.next_token().unwrap().unwrap().1,
4898 TokenType::Scalar(ScalarStyle::Plain, ref value) if value == "value"
4899 ));
4900 assert!(scanner
4901 .tokens
4902 .iter()
4903 .all(|QueuedToken(_, token)| !matches!(token, QueuedTokenType::Comment(_))));
4904 }
4905
4906 #[test]
4907 fn scanner_emits_first_leading_comment_before_scanning_next_comment() {
4908 let mut scanner = Scanner::new(StrInput::new("# first\n# second\nkey: value\n"));
4909
4910 assert!(matches!(
4911 scanner.next_token().unwrap().unwrap().1,
4912 TokenType::StreamStart
4913 ));
4914 assert!(matches!(
4915 scanner.next_token().unwrap().unwrap().1,
4916 TokenType::Comment(ref comment) if comment.text == " first"
4917 ));
4918 assert!(scanner.tokens.is_empty());
4919 assert!(matches!(
4920 scanner.next_token().unwrap().unwrap().1,
4921 TokenType::Comment(ref comment) if comment.text == " second"
4922 ));
4923 }
4924
4925 #[test]
4926 fn scanner_emits_quoted_scalar_comment_before_scanning_following_value() {
4927 let mut scanner = Scanner::new(StrInput::new("\"key\" # quoted\n: value\n"));
4928
4929 assert!(matches!(
4930 scanner.next_token().unwrap().unwrap().1,
4931 TokenType::StreamStart
4932 ));
4933 assert!(matches!(
4934 scanner.next_token().unwrap().unwrap().1,
4935 TokenType::Scalar(ScalarStyle::DoubleQuoted, ref value) if value == "key"
4936 ));
4937 assert!(matches!(
4938 scanner.next_token().unwrap().unwrap().1,
4939 TokenType::Comment(ref comment) if comment.text == " quoted"
4940 ));
4941 }
4942
4943 #[test]
4944 fn flow_scalar_comment_disables_adjacent_value_lookahead() {
4945 let mut scanner = Scanner::new(StrInput::new("\"key\"\n# quoted\n: value\n"));
4946
4947 scanner.fetch_flow_scalar(false).unwrap();
4948
4949 assert_eq!(scanner.adjacent_value_allowed_at, usize::MAX);
4950 assert!(matches!(
4951 scanner.tokens.front().unwrap().1,
4952 QueuedTokenType::Scalar(ScalarStyle::DoubleQuoted, ref value) if value == "key"
4953 ));
4954 assert!(scanner.tokens.iter().any(|QueuedToken(_, token)| matches!(
4955 token,
4956 QueuedTokenType::Comment(comment) if comment.text == " quoted"
4957 )));
4958 }
4959
4960 #[test]
4961 fn ignored_flow_scalar_comment_still_disables_adjacent_value_lookahead() {
4962 let options = crate::options! { emit_comments: false };
4963 let mut scanner =
4964 Scanner::with_options(StrInput::new("\"key\"\n# ignored\n: value\n"), options);
4965
4966 scanner.fetch_flow_scalar(false).unwrap();
4967
4968 assert_eq!(scanner.adjacent_value_allowed_at, usize::MAX);
4969 assert!(scanner
4970 .tokens
4971 .iter()
4972 .all(|QueuedToken(_, token)| { !matches!(token, QueuedTokenType::Comment(_)) }));
4973 }
4974
4975 #[test]
4976 fn deferred_error_waits_for_all_comment_tokens() {
4977 let mut scanner = Scanner::new(StrInput::new("# first\n# second\n@\n"));
4978
4979 assert!(matches!(
4980 scanner.next_token().unwrap().unwrap().1,
4981 TokenType::StreamStart
4982 ));
4983 assert!(matches!(
4984 scanner.next_token().unwrap().unwrap().1,
4985 TokenType::Comment(ref comment) if comment.text == " first"
4986 ));
4987 assert!(matches!(
4988 scanner.next_token().unwrap().unwrap().1,
4989 TokenType::Comment(ref comment) if comment.text == " second"
4990 ));
4991
4992 let error = scanner.next_token().unwrap_err();
4993
4994 assert_eq!(
4995 error.kind(),
4996 &ErrorKind::UnexpectedCharacter { character: '@' }
4997 );
4998 }
4999
5000 #[test]
5002 fn anchor_name_is_borrowed_for_str_input() {
5003 let mut scanner = Scanner::new(StrInput::new("&anch\n"));
5004
5005 loop {
5006 let tok = scanner
5007 .next_token()
5008 .expect("valid YAML must scan without errors")
5009 .expect("scanner must eventually produce a token");
5010 if let TokenType::Anchor(name) = tok.1 {
5011 assert!(matches!(name, Cow::Borrowed("anch")));
5012 break;
5013 }
5014 }
5015 }
5016
5017 #[test]
5018 fn anchor_name_rejects_non_printable_control_chars() {
5019 let mut scanner = Scanner::new(StrInput::new("&foo\u{0001}\n"));
5020
5021 scanner.next_token().unwrap();
5022 assert_eq!(
5023 scanner.next_token().unwrap_err().kind(),
5024 &ErrorKind::UnexpectedCharacter {
5025 character: '\u{0001}'
5026 }
5027 );
5028 }
5029
5030 #[test]
5031 fn alias_name_rejects_non_printable_control_chars() {
5032 let mut scanner = Scanner::new(StrInput::new("*foo\u{0001}\n"));
5033
5034 scanner.next_token().unwrap();
5035 assert_eq!(
5036 scanner.next_token().unwrap_err().kind(),
5037 &ErrorKind::UnexpectedCharacter {
5038 character: '\u{0001}'
5039 }
5040 );
5041 }
5042
5043 #[test]
5044 fn alias_name_is_borrowed_for_str_input() {
5045 let mut scanner = Scanner::new(StrInput::new("*anch\n"));
5046
5047 loop {
5048 let tok = scanner
5049 .next_token()
5050 .expect("valid YAML must scan without errors")
5051 .expect("scanner must eventually produce a token");
5052 if let TokenType::Alias(name) = tok.1 {
5053 assert!(matches!(name, Cow::Borrowed("anch")));
5054 break;
5055 }
5056 }
5057 }
5058
5059 #[test]
5060 fn alias_name_scans_colon_as_part_of_name() {
5061 let mut scanner = Scanner::new(StrInput::new("*foo: bar\n"));
5062
5063 loop {
5064 let tok = scanner
5065 .next_token()
5066 .expect("scanner must not fail before alias token")
5067 .expect("scanner must eventually emit an alias token");
5068
5069 if let TokenType::Alias(name) = tok.1 {
5070 assert_eq!(name.as_ref(), "foo:");
5071 break;
5072 }
5073 }
5074 }
5075
5076 #[test]
5077 fn anchor_name_scans_colon_as_part_of_name() {
5078 let mut scanner = Scanner::new(StrInput::new("&foo: bar\n"));
5079
5080 loop {
5081 let tok = scanner
5082 .next_token()
5083 .expect("scanner must not fail before anchor token")
5084 .expect("scanner must eventually emit an anchor token");
5085
5086 if let TokenType::Anchor(name) = tok.1 {
5087 assert_eq!(name.as_ref(), "foo:");
5088 break;
5089 }
5090 }
5091 }
5092
5093 #[test]
5095 fn tag_directive_parts_are_borrowed_for_str_input() {
5096 let mut scanner = Scanner::new(StrInput::new("%TAG !e! tag:example.com,2000:app/\n"));
5097
5098 loop {
5099 let tok = scanner
5100 .next_token()
5101 .expect("valid YAML must scan without errors")
5102 .expect("scanner must eventually produce a token");
5103 if let TokenType::TagDirective(handle, prefix) = tok.1 {
5104 assert!(matches!(handle, Cow::Borrowed("!e!")));
5105 assert!(matches!(prefix, Cow::Borrowed("tag:example.com,2000:app/")));
5106 break;
5107 }
5108 }
5109 }
5110
5111 #[test]
5112 fn tag_directive_parts_are_owned_for_buffered_input() {
5113 let mut scanner = Scanner::new(BufferedInput::new(
5114 "%TAG !e! tag:example.com,2000:app/\n".chars(),
5115 ));
5116
5117 loop {
5118 let tok = scanner
5119 .next_token()
5120 .expect("valid YAML must scan without errors")
5121 .expect("scanner must eventually produce a token");
5122 if let TokenType::TagDirective(handle, prefix) = tok.1 {
5123 assert!(matches!(handle, Cow::Owned(_)));
5124 assert_eq!(&*handle, "!e!");
5125 assert!(matches!(prefix, Cow::Owned(_)));
5126 assert_eq!(&*prefix, "tag:example.com,2000:app/");
5127 break;
5128 }
5129 }
5130 }
5131
5132 #[test]
5133 fn buffered_tag_directive_decodes_prefix_escape() {
5134 let mut scanner = Scanner::new(BufferedInput::new(
5135 "%TAG !e! %74ag:example.com,2000:app/\n".chars(),
5136 ));
5137
5138 loop {
5139 let tok = scanner
5140 .next_token()
5141 .expect("valid YAML must scan without errors")
5142 .expect("scanner must eventually produce a token");
5143 if let TokenType::TagDirective(handle, prefix) = tok.1 {
5144 assert_eq!(&*handle, "!e!");
5145 assert!(matches!(prefix, Cow::Owned(_)));
5146 assert_eq!(&*prefix, "tag:example.com,2000:app/");
5147 break;
5148 }
5149 }
5150 }
5151
5152 #[test]
5153 fn local_tag_combines_handle_text_with_escaped_suffix() {
5154 let mut scanner = Scanner::new(StrInput::new("!foo%20bar value\n"));
5155
5156 loop {
5157 let tok = scanner
5158 .next_token()
5159 .expect("valid YAML must scan without errors")
5160 .expect("scanner must eventually produce a token");
5161 if let TokenType::Tag(handle, suffix) = tok.1 {
5162 assert!(matches!(handle, Cow::Borrowed("!")));
5163 assert!(matches!(suffix, Cow::Owned(_)));
5164 assert_eq!(&*suffix, "foo bar");
5165 break;
5166 }
5167 }
5168 }
5169
5170 #[test]
5171 fn secondary_tag_requires_suffix_in_borrowed_and_buffered_paths() {
5172 assert_eq!(
5173 first_scanner_error_kind("!! value\n"),
5174 ErrorKind::MissingTagUri
5175 );
5176 assert_eq!(
5177 first_buffered_scanner_error_kind("!! value\n"),
5178 ErrorKind::MissingTagUri
5179 );
5180 }
5181
5182 #[test]
5183 fn plain_scalar_is_borrowed_when_whitespace_free_for_str_input() {
5184 let mut scanner = Scanner::new(StrInput::new("foo\n"));
5185
5186 loop {
5187 let tok = scanner
5188 .next_token()
5189 .expect("valid YAML must scan without errors")
5190 .expect("scanner must eventually produce a token");
5191 if let TokenType::Scalar(_, value) = tok.1 {
5192 assert!(matches!(value, Cow::Borrowed("foo")));
5193 break;
5194 }
5195 }
5196 }
5197
5198 #[test]
5199 fn plain_scalar_is_borrowed_when_whitespace_present_for_str_input() {
5200 let mut scanner = Scanner::new(StrInput::new("foo bar\n"));
5201
5202 loop {
5203 let tok = scanner
5204 .next_token()
5205 .expect("valid YAML must scan without errors")
5206 .expect("scanner must eventually produce a token");
5207 if let TokenType::Scalar(_, value) = tok.1 {
5208 assert!(matches!(value, Cow::Borrowed("foo bar")));
5209 break;
5210 }
5211 }
5212 }
5213
5214 #[test]
5215 fn single_quoted_scalar_is_borrowed_when_verbatim_for_str_input() {
5216 let mut scanner = Scanner::new(StrInput::new("'foo bar'\n"));
5217
5218 loop {
5219 let tok = scanner
5220 .next_token()
5221 .expect("valid YAML must scan without errors")
5222 .expect("scanner must eventually produce a token");
5223 if let TokenType::Scalar(_, value) = tok.1 {
5224 assert!(matches!(value, Cow::Borrowed("foo bar")));
5225 break;
5226 }
5227 }
5228 }
5229
5230 #[test]
5231 fn single_quoted_scalar_is_owned_when_quote_is_escaped_for_str_input() {
5232 let mut scanner = Scanner::new(StrInput::new("'foo''bar'\n"));
5233
5234 loop {
5235 let tok = scanner
5236 .next_token()
5237 .expect("valid YAML must scan without errors")
5238 .expect("scanner must eventually produce a token");
5239 if let TokenType::Scalar(_, value) = tok.1 {
5240 assert!(matches!(value, Cow::Owned(_)));
5241 assert_eq!(&*value, "foo'bar");
5242 break;
5243 }
5244 }
5245 }
5246
5247 #[test]
5248 fn double_quoted_scalar_is_borrowed_when_verbatim_for_str_input() {
5249 let mut scanner = Scanner::new(StrInput::new("\"foo bar\"\n"));
5250
5251 loop {
5252 let tok = scanner
5253 .next_token()
5254 .expect("valid YAML must scan without errors")
5255 .expect("scanner must eventually produce a token");
5256 if let TokenType::Scalar(_, value) = tok.1 {
5257 assert!(matches!(value, Cow::Borrowed("foo bar")));
5258 break;
5259 }
5260 }
5261 }
5262
5263 #[test]
5264 fn double_quoted_scalar_is_owned_when_escape_sequence_present_for_str_input() {
5265 let mut scanner = Scanner::new(StrInput::new("\"foo\\nbar\"\n"));
5266
5267 loop {
5268 let tok = scanner
5269 .next_token()
5270 .expect("valid YAML must scan without errors")
5271 .expect("scanner must eventually produce a token");
5272 if let TokenType::Scalar(_, value) = tok.1 {
5273 assert!(matches!(value, Cow::Owned(_)));
5274 assert_eq!(&*value, "foo\nbar");
5275 break;
5276 }
5277 }
5278 }
5279
5280 #[test]
5281 fn plain_key_is_borrowed_for_str_input() {
5282 let mut scanner = Scanner::new(StrInput::new("mykey: value\n"));
5284
5285 let mut found_key = false;
5286 let mut key_value: Option<Cow<'_, str>> = None;
5287
5288 loop {
5289 let tok = scanner
5290 .next_token()
5291 .expect("valid YAML must scan without errors");
5292 let Some(tok) = tok else { break };
5293
5294 if matches!(tok.1, TokenType::Key) {
5295 found_key = true;
5296 } else if found_key {
5297 if let TokenType::Scalar(_, value) = tok.1 {
5298 key_value = Some(value);
5299 break;
5300 }
5301 }
5302 }
5303
5304 assert!(found_key, "expected to find a Key token");
5305 let key_value = key_value.expect("expected to find a scalar after Key token");
5306 assert!(
5307 matches!(key_value, Cow::Borrowed("mykey")),
5308 "key should be borrowed, got: {key_value:?}"
5309 );
5310 }
5311
5312 #[test]
5313 fn quoted_key_is_borrowed_when_verbatim_for_str_input() {
5314 let mut scanner = Scanner::new(StrInput::new("\"mykey\": value\n"));
5315
5316 let mut found_key = false;
5317 let mut key_value: Option<Cow<'_, str>> = None;
5318
5319 loop {
5320 let tok = scanner
5321 .next_token()
5322 .expect("valid YAML must scan without errors");
5323 let Some(tok) = tok else { break };
5324
5325 if matches!(tok.1, TokenType::Key) {
5326 found_key = true;
5327 } else if found_key {
5328 if let TokenType::Scalar(_, value) = tok.1 {
5329 key_value = Some(value);
5330 break;
5331 }
5332 }
5333 }
5334
5335 assert!(found_key, "expected to find a Key token");
5336 let key_value = key_value.expect("expected to find a scalar after Key token");
5337 assert!(
5338 matches!(key_value, Cow::Borrowed("mykey")),
5339 "quoted key should be borrowed when verbatim, got: {key_value:?}"
5340 );
5341 }
5342
5343 #[test]
5344 fn tag_handle_and_suffix_are_borrowed_for_str_input() {
5345 let mut scanner = Scanner::new(StrInput::new("!!str foo\n"));
5347
5348 loop {
5349 let tok = scanner
5350 .next_token()
5351 .expect("valid YAML must scan without errors")
5352 .expect("scanner must eventually produce a token");
5353 if let TokenType::Tag(handle, suffix) = tok.1 {
5354 assert!(
5355 matches!(handle, Cow::Borrowed("!!")),
5356 "tag handle should be borrowed, got: {handle:?}"
5357 );
5358 assert!(
5359 matches!(suffix, Cow::Borrowed("str")),
5360 "tag suffix should be borrowed, got: {suffix:?}"
5361 );
5362 break;
5363 }
5364 }
5365 }
5366
5367 #[test]
5368 fn local_tag_suffix_is_borrowed_for_str_input() {
5369 let mut scanner = Scanner::new(StrInput::new("!mytag foo\n"));
5371
5372 loop {
5373 let tok = scanner
5374 .next_token()
5375 .expect("valid YAML must scan without errors")
5376 .expect("scanner must eventually produce a token");
5377 if let TokenType::Tag(handle, suffix) = tok.1 {
5378 assert!(
5379 matches!(handle, Cow::Borrowed("!")),
5380 "local tag handle should be '!', got: {handle:?}"
5381 );
5382 assert!(
5383 matches!(suffix, Cow::Borrowed("mytag")),
5384 "local tag suffix should be borrowed, got: {suffix:?}"
5385 );
5386 break;
5387 }
5388 }
5389 }
5390
5391 #[test]
5392 fn local_tag_suffix_with_punctuation_is_borrowed_for_str_input() {
5393 let mut scanner = Scanner::new(StrInput::new("!mytag/part foo\n"));
5394
5395 loop {
5396 let tok = scanner
5397 .next_token()
5398 .expect("valid YAML must scan without errors")
5399 .expect("scanner must eventually produce a token");
5400 if let TokenType::Tag(handle, suffix) = tok.1 {
5401 assert!(matches!(handle, Cow::Borrowed("!")));
5402 assert!(matches!(suffix, Cow::Borrowed("mytag/part")));
5403 break;
5404 }
5405 }
5406 }
5407
5408 #[test]
5409 fn tag_with_uri_escape_is_owned_for_str_input() {
5410 let mut scanner = Scanner::new(StrInput::new("!!my%20tag foo\n"));
5412
5413 loop {
5414 let tok = scanner
5415 .next_token()
5416 .expect("valid YAML must scan without errors")
5417 .expect("scanner must eventually produce a token");
5418 if let TokenType::Tag(handle, suffix) = tok.1 {
5419 assert!(
5420 matches!(handle, Cow::Borrowed("!!")),
5421 "tag handle should still be borrowed, got: {handle:?}"
5422 );
5423 assert!(
5424 matches!(suffix, Cow::Owned(_)),
5425 "tag suffix with URI escape should be owned, got: {suffix:?}"
5426 );
5427 assert_eq!(&*suffix, "my tag");
5428 break;
5429 }
5430 }
5431 }
5432
5433 #[test]
5434 fn flow_scalar_buffer_tracks_pending_whitespace() {
5435 let mut borrowed = super::FlowScalarBuf::new_borrowed(2);
5436
5437 borrowed.note_pending_ws(5, 8);
5438 borrowed.commit_pending_ws();
5439 assert!(matches!(
5440 borrowed,
5441 super::FlowScalarBuf::Borrowed {
5442 end: 8,
5443 pending_ws_start: None,
5444 pending_ws_end: 8,
5445 ..
5446 }
5447 ));
5448
5449 borrowed.note_pending_ws(9, 11);
5450 borrowed.discard_pending_ws();
5451 assert!(matches!(
5452 borrowed,
5453 super::FlowScalarBuf::Borrowed {
5454 end: 8,
5455 pending_ws_start: None,
5456 pending_ws_end: 8,
5457 ..
5458 }
5459 ));
5460 assert!(borrowed.as_owned_mut().is_none());
5461
5462 let mut owned = super::FlowScalarBuf::new_owned();
5463 owned.as_owned_mut().unwrap().push_str("owned");
5464 assert!(matches!(owned, super::FlowScalarBuf::Owned(ref s) if s == "owned"));
5465 }
5466
5467 fn first_scanner_error_kind(input: &str) -> ErrorKind {
5468 first_scanner_error(input).kind().clone()
5469 }
5470
5471 fn first_buffered_scanner_error_kind(input: &str) -> ErrorKind {
5472 let mut scanner = Scanner::new(BufferedInput::new(input.chars()));
5473 loop {
5474 match scanner.next_token() {
5475 Ok(Some(_)) => {}
5476 Ok(None) => panic!("expected scanner error"),
5477 Err(error) => return error.kind().clone(),
5478 }
5479 }
5480 }
5481
5482 fn first_scanner_error(input: &str) -> ScanError {
5483 let mut scanner = Scanner::new(StrInput::new(input));
5484 loop {
5485 match scanner.next_token() {
5486 Ok(Some(_)) => {}
5487 Ok(None) => panic!("expected scanner error"),
5488 Err(error) => return error,
5489 }
5490 }
5491 }
5492
5493 fn first_scalar_value(input: &str) -> String {
5494 let mut scanner = Scanner::new(StrInput::new(input));
5495 loop {
5496 match scanner.next_token().expect("scanner should not error") {
5497 Some(Token(_, TokenType::Scalar(_, value))) => return value.into_owned(),
5498 Some(_) => {}
5499 None => panic!("expected scalar token"),
5500 }
5501 }
5502 }
5503
5504 fn first_buffered_scalar_value(input: &str) -> String {
5505 let mut scanner = Scanner::new(BufferedInput::new(input.chars()));
5506 loop {
5507 match scanner.next_token().expect("scanner should not error") {
5508 Some(Token(_, TokenType::Scalar(_, value))) => return value.into_owned(),
5509 Some(_) => {}
5510 None => panic!("expected scalar token"),
5511 }
5512 }
5513 }
5514
5515 #[test]
5516 fn iterator_next_emits_error_and_then_stays_empty() {
5517 let mut scanner = Scanner::new(StrInput::new("\"unterminated"));
5518
5519 let error = scanner
5520 .by_ref()
5521 .find_map(Result::err)
5522 .expect("scanner should emit the error");
5523 assert_eq!(error.kind(), &ErrorKind::UnclosedQuotedScalar);
5524 assert!(scanner.next().is_none());
5525 }
5526
5527 #[test]
5528 fn next_token_returns_none_after_stream_end() {
5529 let mut scanner = Scanner::new(StrInput::new(""));
5530
5531 while let Some(token) = scanner.next_token().unwrap() {
5532 if matches!(token.1, TokenType::StreamEnd) {
5533 break;
5534 }
5535 }
5536
5537 assert!(scanner.stream_started());
5538 assert!(scanner.stream_ended());
5539 assert!(scanner.next_token().unwrap().is_none());
5540 }
5541
5542 #[test]
5543 fn directive_name_must_be_present() {
5544 assert_eq!(
5545 first_scanner_error_kind("%\n"),
5546 ErrorKind::MissingDirectiveName
5547 );
5548 }
5549
5550 #[test]
5551 fn yaml_directive_requires_dot_between_version_numbers() {
5552 assert_eq!(
5553 first_scanner_error_kind("%YAML 1\n"),
5554 ErrorKind::MissingYamlVersionSeparator
5555 );
5556 }
5557
5558 #[test]
5559 fn yaml_directive_requires_major_version_number() {
5560 assert_eq!(
5561 first_scanner_error_kind("%YAML .2\n"),
5562 ErrorKind::MissingYamlVersion
5563 );
5564 }
5565
5566 #[test]
5567 fn yaml_directive_rejects_extremely_long_version_number() {
5568 assert_eq!(
5569 first_scanner_error_kind("%YAML 1234567890.2\n"),
5570 ErrorKind::YamlVersionTooLong
5571 );
5572 }
5573
5574 #[test]
5575 fn tag_directive_handle_must_end_with_bang() {
5576 assert_eq!(
5577 first_scanner_error_kind("%TAG !bad tag:example.com,2024:\n"),
5578 ErrorKind::ExpectedTagDirectiveBang
5579 );
5580 }
5581
5582 #[test]
5583 fn tag_directive_handle_must_start_with_bang() {
5584 assert_eq!(
5585 first_scanner_error_kind("%TAG bad! tag:example.com,2024:\n"),
5586 ErrorKind::ExpectedTagBang
5587 );
5588 assert_eq!(
5589 first_buffered_scanner_error_kind("%TAG bad! tag:example.com,2024:\n"),
5590 ErrorKind::ExpectedTagBang
5591 );
5592 }
5593
5594 #[test]
5595 fn tag_directive_prefix_must_start_with_tag_character() {
5596 assert_eq!(
5597 first_scanner_error_kind("%TAG !e! `bad\n"),
5598 ErrorKind::InvalidGlobalTagCharacter
5599 );
5600 }
5601
5602 #[test]
5603 fn tag_directive_prefix_must_end_before_invalid_content() {
5604 assert_eq!(
5605 first_scanner_error_kind("%TAG !e! tag:example.com^suffix\n"),
5606 ErrorKind::InvalidTagDirectiveTerminator
5607 );
5608 }
5609
5610 #[test]
5611 fn tag_directive_prefix_with_uri_escape_is_owned_and_decoded() {
5612 let mut scanner =
5613 Scanner::new(StrInput::new("%TAG !e! tag:example.com,2024:some%20app/\n"));
5614
5615 loop {
5616 let token = scanner
5617 .next_token()
5618 .expect("valid directive should scan")
5619 .expect("scanner must produce a directive token");
5620 if let TokenType::TagDirective(handle, prefix) = token.1 {
5621 assert!(matches!(handle, Cow::Borrowed("!e!")));
5622 assert!(matches!(prefix, Cow::Owned(_)));
5623 assert_eq!(&*prefix, "tag:example.com,2024:some app/");
5624 break;
5625 }
5626 }
5627 }
5628
5629 #[test]
5630 fn bare_bang_tag_scans_as_non_specific_tag() {
5631 let mut scanner = Scanner::new(StrInput::new("! foo\n"));
5632
5633 loop {
5634 let token = scanner
5635 .next_token()
5636 .expect("valid tag should scan")
5637 .expect("scanner must produce a tag token");
5638 if let TokenType::Tag(handle, suffix) = token.1 {
5639 assert_eq!(&*handle, "");
5640 assert_eq!(&*suffix, "!");
5641 break;
5642 }
5643 }
5644 }
5645
5646 #[test]
5647 fn tag_requires_separation_after_suffix() {
5648 assert_eq!(
5649 first_scanner_error_kind("!foo,bar\n"),
5650 ErrorKind::InvalidTagTerminator
5651 );
5652 }
5653
5654 #[test]
5655 fn verbatim_tag_requires_uri() {
5656 assert_eq!(
5657 first_scanner_error_kind("!<> foo\n"),
5658 ErrorKind::MissingTagUri
5659 );
5660 }
5661
5662 #[test]
5663 fn verbatim_tag_requires_closing_angle_bracket() {
5664 assert_eq!(
5665 first_scanner_error_kind("!<tag:yaml.org,2002:str foo\n"),
5666 ErrorKind::UnclosedVerbatimTag
5667 );
5668 }
5669
5670 #[test]
5671 fn tag_uri_escape_requires_hex_digits() {
5672 assert_eq!(
5673 first_scanner_error_kind("!!bad%zz foo\n"),
5674 ErrorKind::InvalidTagEscape
5675 );
5676 }
5677
5678 #[test]
5679 fn tag_uri_escape_rejects_bad_leading_utf8_byte() {
5680 assert_eq!(
5681 first_scanner_error_kind("!!bad%80 foo\n"),
5682 ErrorKind::InvalidTagUtf8LeadingByte
5683 );
5684 }
5685
5686 #[test]
5687 fn tag_uri_escape_rejects_bad_trailing_utf8_byte() {
5688 assert_eq!(
5689 first_scanner_error_kind("!!bad%C2%41 foo\n"),
5690 ErrorKind::InvalidTagUtf8TrailingByte
5691 );
5692 }
5693
5694 #[test]
5695 fn tag_uri_escape_rejects_invalid_utf8_codepoint() {
5696 assert_eq!(
5697 first_scanner_error_kind("!!bad%F4%90%80%80 foo\n"),
5698 ErrorKind::InvalidTagUtf8
5699 );
5700 }
5701
5702 #[test]
5703 fn anchors_and_aliases_require_names() {
5704 assert_eq!(
5705 first_scanner_error_kind("& \n"),
5706 ErrorKind::MissingAnchorOrAliasName
5707 );
5708 assert_eq!(
5709 first_scanner_error_kind("* \n"),
5710 ErrorKind::MissingAnchorOrAliasName
5711 );
5712 }
5713
5714 #[test]
5715 fn document_end_marker_rejects_trailing_content() {
5716 assert_eq!(
5717 first_scanner_error_kind("... trailing\n"),
5718 ErrorKind::InvalidDocumentEnd
5719 );
5720 }
5721
5722 #[test]
5723 fn reserved_indicators_are_rejected_outside_directives() {
5724 let error = first_scanner_error(" @\n");
5725
5726 assert_eq!(
5727 error.kind(),
5728 &ErrorKind::UnexpectedCharacter { character: '@' }
5729 );
5730 }
5731
5732 #[test]
5733 fn flow_block_entry_indicator_is_rejected() {
5734 assert_eq!(
5735 first_scanner_error_kind("[- ]\n"),
5736 ErrorKind::BlockEntryInFlowCollection
5737 );
5738 }
5739
5740 #[test]
5741 fn block_entry_after_tabbed_separator_reports_specific_error() {
5742 assert_eq!(
5743 first_scanner_error_kind("-\t- value\n"),
5744 ErrorKind::InvalidBlockEntryWhitespace
5745 );
5746 }
5747
5748 #[test]
5749 fn document_indicator_reports_unclosed_flow_collection() {
5750 let error = first_scanner_error("[\n---\n");
5751
5752 assert_eq!(
5753 error.kind(),
5754 &ErrorKind::UnclosedFlowCollection { open: '[' }
5755 );
5756 }
5757
5758 #[test]
5759 fn block_scalar_header_rejects_trailing_content() {
5760 assert_eq!(
5761 first_scanner_error_kind("|+ trailing\n"),
5762 ErrorKind::InvalidBlockScalarHeader
5763 );
5764 }
5765
5766 #[test]
5767 fn block_scalar_rejects_zero_indent_indicator() {
5768 assert_eq!(
5769 first_scanner_error_kind("|0\n"),
5770 ErrorKind::ZeroBlockScalarIndent
5771 );
5772 assert_eq!(
5773 first_scanner_error_kind("|+0\n"),
5774 ErrorKind::ZeroBlockScalarIndent
5775 );
5776 }
5777
5778 #[test]
5779 fn empty_block_scalar_at_eof_honors_chomping() {
5780 assert_eq!(first_scalar_value("|\n"), "");
5781 assert_eq!(first_scalar_value("|-\n"), "");
5782 assert_eq!(first_scalar_value("|+\n"), "");
5783 assert_eq!(first_scalar_value("|+\n\n"), "\n");
5784 assert_eq!(first_scalar_value("|+\n "), "\n");
5785 }
5786
5787 #[test]
5788 fn buffered_block_scalar_reads_content_past_lookahead_window() {
5789 assert_eq!(
5790 first_buffered_scalar_value("|\n abcdefghijklmnopqrstuvwxyz\n"),
5791 "abcdefghijklmnopqrstuvwxyz\n"
5792 );
5793 }
5794
5795 #[test]
5796 fn explicit_indent_block_scalar_can_end_at_document_marker() {
5797 assert_eq!(first_scalar_value("|1\n...\n"), "");
5798 }
5799
5800 #[test]
5801 fn root_explicit_indent_block_scalar_rejects_underindented_content() {
5802 assert_eq!(
5803 first_scanner_error_kind("|2\nx\n"),
5804 ErrorKind::InvalidBlockScalarIndent
5805 );
5806 }
5807
5808 #[test]
5809 fn quoted_scalar_rejects_document_indicator_at_line_start() {
5810 assert_eq!(
5811 first_scanner_error_kind("\"one\n---\ntwo\"\n"),
5812 ErrorKind::DocumentIndicatorInQuotedScalar
5813 );
5814 }
5815
5816 #[test]
5817 fn quoted_scalar_rejects_tab_indentation_after_line_break() {
5818 assert_eq!(
5819 first_scanner_error_kind("a: \"one\n\tbad\"\n"),
5820 ErrorKind::TabInIndentation
5821 );
5822 }
5823
5824 #[test]
5825 fn quoted_scalar_rejects_underindented_continuation() {
5826 assert_eq!(
5827 first_scanner_error_kind("a: \"one\nbad\"\n"),
5828 ErrorKind::InvalidQuotedScalarIndent
5829 );
5830 }
5831
5832 #[test]
5833 fn quoted_scalar_trailing_content_error_names_quote_style() {
5834 assert_eq!(
5835 first_scanner_error_kind("'foo' trailing\n"),
5836 ErrorKind::InvalidTrailingSingleQuotedScalar
5837 );
5838 assert_eq!(
5839 first_scanner_error_kind("\"foo\" trailing\n"),
5840 ErrorKind::InvalidTrailingDoubleQuotedScalar
5841 );
5842 }
5843
5844 #[test]
5845 fn quoted_scalar_escape_errors_cover_hex_and_surrogate_edges() {
5846 assert_eq!(
5847 first_scanner_error_kind("\"\\xG0\"\n"),
5848 ErrorKind::InvalidQuotedScalarHexEscape
5849 );
5850 assert_eq!(
5851 first_scanner_error_kind("\"\\uD800\\uGGGG\"\n"),
5852 ErrorKind::InvalidLowSurrogateHexEscape
5853 );
5854 assert_eq!(
5855 first_scanner_error_kind("\"\\uD800\\u0041\"\n"),
5856 ErrorKind::InvalidLowSurrogate
5857 );
5858 assert_eq!(
5859 first_scanner_error_kind("\"\\U00110000\"\n"),
5860 ErrorKind::InvalidUnicodeEscape
5861 );
5862 }
5863
5864 #[test]
5865 fn indented_flow_scalar_reports_invalid_indentation() {
5866 assert_eq!(
5867 first_scanner_error_kind("a:\n [\nfoo]\n"),
5868 ErrorKind::InvalidIndentation
5869 );
5870 }
5871
5872 #[test]
5873 fn required_simple_key_requires_value_at_stream_end() {
5874 let error = first_scanner_error("a:\n&b\n- c\n");
5875
5876 assert_eq!(error.kind(), &ErrorKind::SimpleKeyExpected);
5877 assert_eq!(error.marker().index(), 3);
5878 assert_eq!(error.marker().line(), 2);
5879 assert_eq!(error.marker().col(), 0);
5880 }
5881
5882 #[test]
5883 fn plain_scalar_rejects_dash_before_flow_indicator() {
5884 assert_eq!(
5885 first_scanner_error_kind("[-]\n"),
5886 ErrorKind::PlainScalarStartsWithDashFlowIndicator
5887 );
5888 }
5889
5890 #[test]
5891 fn explicit_key_rejects_tab_after_indicator() {
5892 assert_eq!(
5893 first_scanner_error_kind("? \tfoo\n"),
5894 ErrorKind::TabNotAllowed
5895 );
5896 }
5897
5898 #[test]
5899 fn flow_mapping_rejects_adjacent_collection_value_after_plain_key() {
5900 assert_eq!(
5901 first_scanner_error_kind("[a:[]]\n"),
5902 ErrorKind::FlowMappingValueAdjacentCollection
5903 );
5904 }
5905
5906 #[test]
5907 fn implicit_flow_mapping_colon_cannot_move_to_next_line() {
5908 assert_eq!(
5909 first_scanner_error_kind("[foo\n: bar]\n"),
5910 ErrorKind::InvalidColonPlacement
5911 );
5912 }
5913
5914 #[test]
5915 fn invalid_simple_key_token_positions_are_scan_errors() {
5916 for (tokens_parsed, token_number) in [(1, 0), (0, 1)] {
5917 let mut scanner = Scanner::new(StrInput::new(": value\n"));
5918 scanner.fetch_stream_start();
5919 scanner.tokens.clear();
5920 scanner.tokens_parsed = tokens_parsed;
5921
5922 let simple_key = scanner
5923 .simple_keys
5924 .last_mut()
5925 .expect("stream start should create a simple key slot");
5926 simple_key.possible = true;
5927 simple_key.token_number = token_number;
5928
5929 let error = scanner
5930 .fetch_value()
5931 .expect_err("invalid simple key position should be reported as a scan error");
5932 assert_eq!(error.kind(), &ErrorKind::InvalidSimpleKey);
5933 assert_eq!(error.marker(), &Marker::new(0, 1, 0));
5934 }
5935 }
5936
5937 #[test]
5938 fn issue14_alias_scanner_consumes_colon_as_name_character() {
5939 let mut scanner = Scanner::new(StrInput::new("*foo: bar\n"));
5940
5941 assert!(matches!(
5942 scanner.next_token().unwrap().unwrap().1,
5943 TokenType::StreamStart
5944 ));
5945
5946 let token = scanner.next_token().unwrap().unwrap();
5947
5948 assert!(
5949 matches!(token.1, TokenType::Alias(ref name) if name.as_ref() == "foo:"),
5950 "expected `*foo: bar` to start with Alias(\"foo:\"), got {token:?}"
5951 );
5952 }
5953
5954 #[test]
5955 fn issue14_anchor_scanner_consumes_colon_as_name_character() {
5956 let mut scanner = Scanner::new(StrInput::new("&foo: bar\n"));
5957
5958 assert!(matches!(
5959 scanner.next_token().unwrap().unwrap().1,
5960 TokenType::StreamStart
5961 ));
5962
5963 let token = scanner.next_token().unwrap().unwrap();
5964
5965 assert!(
5966 matches!(token.1, TokenType::Anchor(ref name) if name.as_ref() == "foo:"),
5967 "expected `&foo: bar` to start with Anchor(\"foo:\"), got {token:?}"
5968 );
5969 }
5970}