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 let c = self.input.peek();
1622 let nc = self.input.peek_nth(1);
1623 match c {
1624 '[' => self.fetch_flow_collection_start(TokenType::FlowSequenceStart),
1625 '{' => self.fetch_flow_collection_start(TokenType::FlowMappingStart),
1626 ']' => self.fetch_flow_collection_end(TokenType::FlowSequenceEnd, '[', ']'),
1627 '}' => self.fetch_flow_collection_end(TokenType::FlowMappingEnd, '{', '}'),
1628 ',' => self.fetch_flow_entry(),
1629 '-' if is_blank_or_breakz(nc) => self.fetch_block_entry(),
1630 '?' if is_blank_or_breakz(nc) => self.fetch_key(),
1631 ':' if is_blank_or_breakz(nc) => self.fetch_value(),
1632 ':' if self.flow_level > 0
1633 && (is_flow(nc) || self.mark.index() == self.adjacent_value_allowed_at) =>
1634 {
1635 self.fetch_flow_value()
1636 }
1637 '*' => self.fetch_anchor(true),
1639 '&' => self.fetch_anchor(false),
1641 '!' => self.fetch_tag(),
1642 '|' if self.flow_level == 0 => self.fetch_block_scalar(true),
1644 '>' if self.flow_level == 0 => self.fetch_block_scalar(false),
1646 '\'' => self.fetch_flow_scalar(true),
1647 '"' => self.fetch_flow_scalar(false),
1648 '-' if !is_blank_or_breakz(nc) => self.fetch_plain_scalar(),
1650 ':' | '?' if !is_blank_or_breakz(nc) && self.flow_level == 0 => {
1651 self.fetch_plain_scalar()
1652 }
1653 c if is_bom(c) => Err(self.scan_error(ErrorKind::BomInsideDocument)),
1654 '|' | '>' | '%' | '@' | '`' => {
1655 Err(self.scan_error(ErrorKind::UnexpectedCharacter { character: c }))
1656 }
1657 _ => self.fetch_plain_scalar(),
1658 }
1659 }
1660
1661 pub(crate) fn next_queued_token(&mut self) -> Result<Option<QueuedToken<'input>>, ScanError> {
1666 if self.deferred_error.is_some() {
1667 if !matches!(
1668 self.tokens.front().map(|token| &token.1),
1669 Some(QueuedTokenType::Comment(_))
1670 ) {
1671 if let Some(error) = self.deferred_error.take() {
1672 return error.into_result();
1673 }
1674 }
1675 self.token_available = true;
1676 }
1677
1678 if self.stream_end_produced {
1679 return Ok(None);
1680 }
1681
1682 if !self.token_available {
1683 if let Err(error) = self.fetch_more_tokens() {
1684 if matches!(
1685 self.tokens.front().map(|token| &token.1),
1686 Some(QueuedTokenType::Comment(_))
1687 ) {
1688 self.deferred_error = Some(error);
1689 } else {
1690 return Err(error);
1691 }
1692 }
1693 }
1694 let Some(t) = self.tokens.pop_front() else {
1695 unreachable!("fetch_more_tokens succeeded without producing a token")
1696 };
1697 self.token_available = false;
1698 self.tokens_parsed += 1;
1699
1700 let is_stream_end = matches!(t.1, QueuedTokenType::StreamEnd);
1701 if is_stream_end {
1702 self.stream_end_produced = true;
1703 }
1704 Ok(Some(t))
1705 }
1706
1707 fn next_token(&mut self) -> Result<Option<Token<'input>>, ScanError> {
1712 Ok(self.next_queued_token()?.map(QueuedToken::into_public))
1713 }
1714
1715 fn fetch_more_tokens(&mut self) -> ScanResult {
1720 let mut need_more;
1721 loop {
1722 if self.tokens.is_empty() {
1723 need_more = true;
1724 } else {
1725 need_more = false;
1726 self.stale_simple_keys()?;
1728 if !matches!(
1729 self.tokens.front().map(|token| &token.1),
1730 Some(QueuedTokenType::Comment(_))
1731 ) {
1732 for sk in &self.simple_keys {
1734 if sk.possible && sk.token_number == self.tokens_parsed {
1735 need_more = true;
1736 break;
1737 }
1738 }
1739 }
1740 }
1741
1742 if let Some(token) = self.tokens.back() {
1745 if matches!(
1746 token.1,
1747 QueuedTokenType::DocumentEnd | QueuedTokenType::DocumentStart
1748 ) {
1749 break;
1750 }
1751 }
1752
1753 if !need_more {
1754 break;
1755 }
1756 self.fetch_next_token()?;
1757 }
1758 self.token_available = true;
1759
1760 Ok(())
1761 }
1762
1763 fn stale_simple_keys(&mut self) -> ScanResult {
1772 for sk in &mut self.simple_keys {
1773 let is_line_stale = self.flow_level == 0 && sk.mark.line < self.mark.line;
1774 let is_length_stale = self.mark.index().saturating_sub(sk.mark.index())
1777 > self.options.simple_key_max_lookahead;
1778
1779 if sk.possible && (is_line_stale || is_length_stale) {
1780 if sk.required {
1781 return Err(Self::simple_key_expected(sk.mark));
1782 }
1783 sk.possible = false;
1784 }
1785 }
1786 Ok(())
1787 }
1788
1789 fn skip_to_next_token(&mut self) -> Result<SkipToNextTokenOutcome, ScanError> {
1800 let consume_linebreak = |this: &mut Self| {
1803 this.input.lookahead(2);
1804 this.skip_linebreak();
1805 if this.flow_level == 0 {
1806 this.allow_simple_key();
1807 }
1808 };
1809
1810 let mut outcome = SkipToNextTokenOutcome::default();
1811 loop {
1812 let ch = self.input.look_ch();
1813 if self.explicit_key_tab_check_pending {
1814 match ch {
1815 '\t' => {
1816 return Err(self.scan_error(ErrorKind::TabNotAllowed));
1817 }
1818 ' ' | '\n' | '\r' | '#' => {}
1819 _ => self.explicit_key_tab_check_pending = false,
1820 }
1821 }
1822
1823 match ch {
1824 '\t' => {
1826 if self.is_within_block()
1827 && self.leading_whitespace
1828 && (self.mark.col as isize) < self.indent
1829 {
1830 self.skip_ws_to_eol(SkipTabs::Yes)?;
1831
1832 if !self.input.next_is_breakz() {
1834 return Err(self.scan_error(ErrorKind::TabInBlockIndentation));
1835 }
1836
1837 if matches!(self.input.look_ch(), '\n' | '\r') {
1839 consume_linebreak(self);
1840 }
1841 } else {
1842 self.skip_blank();
1844 }
1845 }
1846
1847 ' ' => self.skip_blank(),
1848
1849 '\n' | '\r' => consume_linebreak(self),
1850
1851 c if is_bom(c)
1852 && self.document_prefix_allowed
1853 && self.flow_level == 0
1854 && self.mark.col == 0 =>
1855 {
1856 self.skip_bom();
1857 }
1858
1859 '#' => {
1860 outcome.saw_comment = true;
1861 let queued = self.consume_comment()?;
1862 outcome.queued_comment |= queued;
1863
1864 if matches!(self.input.look_ch(), '\n' | '\r') {
1866 consume_linebreak(self);
1867 }
1868 return Ok(outcome);
1869 }
1870
1871 _ => break,
1872 }
1873 }
1874
1875 if let Some(err_mark) = self.interrupted_plain_by_comment.take() {
1878 let is_immediate_next_line = self.mark.line == err_mark.line + 1;
1882
1883 if self.flow_level == 0
1885 && is_immediate_next_line
1886 && (self.mark.col as isize) > self.indent
1887 {
1888 self.input.lookahead(4);
1892
1893 if !self.input.next_is_z()
1894 && !self.input.next_is_document_indicator()
1895 && self.input.next_can_be_plain_scalar(false)
1896 {
1897 return Err(ScanError::from_kind(
1898 err_mark,
1899 ErrorKind::CommentInterceptedScalar,
1900 ));
1901 }
1902 }
1903 }
1904
1905 Ok(outcome)
1906 }
1907
1908 fn skip_yaml_whitespace(&mut self) -> Result<bool, ScanError> {
1916 let mut need_whitespace = true;
1917 loop {
1918 match self.input.look_ch() {
1919 ' ' => {
1920 self.skip_blank();
1921
1922 need_whitespace = false;
1923 }
1924 '\n' | '\r' => {
1925 self.input.lookahead(2);
1926 self.skip_linebreak();
1927 if self.flow_level == 0 {
1928 self.allow_simple_key();
1929 }
1930 need_whitespace = false;
1931 }
1932 '#' => {
1933 if need_whitespace {
1934 self.skip_comment()?;
1935 } else {
1936 self.consume_comment()?;
1937 return Ok(true);
1938 }
1939 }
1940 _ => break,
1941 }
1942 }
1943
1944 if need_whitespace {
1945 Err(self.scan_error(ErrorKind::ExpectedWhitespace))
1946 } else {
1947 Ok(false)
1948 }
1949 }
1950
1951 #[track_caller]
1956 fn skip_ws_to_eol(&mut self, skip_tabs: SkipTabs) -> Result<SkipTabs, ScanError> {
1957 debug_assert!(!matches!(skip_tabs, SkipTabs::Result(..)));
1958
1959 if !self.comments_possible {
1960 let (chars_consumed, result) = self.input.skip_ws_to_eol(skip_tabs);
1961 self.mark.col += chars_consumed;
1962 self.mark.offsets.chars += chars_consumed;
1963 self.mark.offsets.bytes = self.input.byte_offset();
1964 return result.map_err(|kind| self.scan_error(kind));
1965 }
1966
1967 let (chars_consumed, whitespace) = self.input.skip_ws_to_eol_blanks(skip_tabs);
1968 self.mark.col += chars_consumed;
1969 self.mark.offsets.chars += chars_consumed;
1970 self.mark.offsets.bytes = self.input.byte_offset();
1971
1972 if self.input.look_ch() != '#' {
1973 return Ok(whitespace);
1974 }
1975
1976 if !whitespace.found_tabs() && !whitespace.has_valid_yaml_ws() {
1977 return Err(self.scan_error(ErrorKind::CommentNotSeparated));
1978 }
1979
1980 self.consume_comment()?;
1981 Ok(whitespace)
1982 }
1983
1984 fn fetch_stream_start(&mut self) {
1985 let mark = self.mark;
1986 self.indent = -1;
1987 self.stream_start_produced = true;
1988 self.allow_simple_key();
1989 self.tokens
1990 .push_back(Token(Span::empty(mark), TokenType::StreamStart).into());
1991 self.simple_keys.push(SimpleKey::new(Marker::new(0, 0, 0)));
1992 }
1993
1994 fn fetch_stream_end(&mut self) -> ScanResult {
1995 if self.mark.col != 0 {
1997 self.mark.col = 0;
1998 self.mark.line += 1;
1999 }
2000
2001 if let Some((mark, bracket)) = self.flow_markers.pop() {
2002 return Err(Self::unclosed_bracket(mark, bracket));
2003 }
2004
2005 for sk in &mut self.simple_keys {
2008 if sk.required && sk.possible {
2009 return Err(Self::simple_key_expected(sk.mark));
2010 }
2011 sk.possible = false;
2012 }
2013
2014 self.unroll_indent(-1);
2015 self.remove_simple_key()?;
2016 self.disallow_simple_key();
2017
2018 self.tokens
2019 .push_back(Token(Span::empty(self.mark), TokenType::StreamEnd).into());
2020 Ok(())
2021 }
2022
2023 fn fetch_directive(&mut self) -> ScanResult {
2024 self.unroll_indent(-1);
2025 self.remove_simple_key()?;
2026
2027 self.disallow_simple_key();
2028
2029 let token_index = self.tokens.len();
2030 let tok = self.scan_directive()?;
2031 self.insert_token(token_index, tok);
2032
2033 Ok(())
2034 }
2035
2036 fn scan_directive(&mut self) -> Result<Token<'input>, ScanError> {
2037 let start_mark = self.mark;
2038 self.skip_non_blank();
2039
2040 let mut budget = self.options.max_directive_bytes;
2043
2044 let name = self.scan_directive_name(&mut budget)?;
2045 let mut separated_comment = false;
2046 let tok = match name.as_ref() {
2047 "YAML" => self.scan_version_directive_value(&start_mark)?,
2048 "TAG" => self.scan_tag_directive_value(&start_mark, &mut budget)?,
2049 _ => {
2050 let mut params = Vec::new();
2051 while self.input.next_is_blank() {
2052 let n_blanks = self.input.skip_while_blank();
2053 self.mark.offsets.chars += n_blanks;
2054 self.mark.col += n_blanks;
2055 self.mark.offsets.bytes = self.input.byte_offset();
2056
2057 if self.input.peek() == '#' {
2058 separated_comment = true;
2059 break;
2060 }
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 if separated_comment {
2093 self.consume_comment()?;
2095 } else {
2096 self.skip_ws_to_eol(SkipTabs::Yes)?;
2097 }
2098
2099 if self.input.next_is_breakz() {
2100 self.input.lookahead(2);
2101 self.skip_linebreak();
2102 Ok(tok)
2103 } else {
2104 Err(ScanError::from_kind(
2105 start_mark,
2106 ErrorKind::InvalidDirectiveTerminator,
2107 ))
2108 }
2109 }
2110
2111 fn scan_version_directive_value(&mut self, mark: &Marker) -> Result<Token<'input>, ScanError> {
2112 let n_blanks = self.input.skip_while_blank();
2113 self.mark.offsets.chars += n_blanks;
2114 self.mark.col += n_blanks;
2115 self.mark.offsets.bytes = self.input.byte_offset();
2116
2117 let major = self.scan_version_directive_number(mark)?;
2118
2119 if self.input.peek() != '.' {
2120 return Err(ScanError::from_kind(
2121 *mark,
2122 ErrorKind::MissingYamlVersionSeparator,
2123 ));
2124 }
2125 self.skip_non_blank();
2126
2127 let minor = self.scan_version_directive_number(mark)?;
2128
2129 Ok(Token(
2130 Span::new(*mark, self.mark),
2131 TokenType::VersionDirective(major, minor),
2132 ))
2133 }
2134
2135 fn directive_byte_limit(&self, start_mark: Marker) -> ScanError {
2137 ScanError::from_kind(
2138 start_mark,
2139 ErrorKind::DirectiveByteLimitExceeded {
2140 limit: self.options.max_directive_bytes,
2141 },
2142 )
2143 }
2144
2145 fn spend_directive_bytes(
2147 &self,
2148 budget: &mut usize,
2149 bytes: usize,
2150 start_mark: Marker,
2151 ) -> ScanResult {
2152 let Some(remaining) = budget.checked_sub(bytes) else {
2153 return Err(self.directive_byte_limit(start_mark));
2154 };
2155 *budget = remaining;
2156 Ok(())
2157 }
2158
2159 fn fetch_directive_word(&mut self, out: &mut String, budget: &mut usize) -> Option<usize> {
2166 let mut n_chars = 0;
2167
2168 loop {
2169 let c = self.input.look_ch();
2170 if !is_yaml_non_space(c) || is_z(c) {
2171 break;
2172 }
2173
2174 let len = c.len_utf8();
2175 if len > *budget {
2176 return None;
2177 }
2178 *budget -= len;
2179
2180 out.push(c);
2181 self.input.skip();
2182 n_chars += 1;
2183 }
2184
2185 Some(n_chars)
2186 }
2187
2188 fn scan_directive_name(&mut self, budget: &mut usize) -> Result<String, ScanError> {
2189 let start_mark = self.mark;
2190 let mut string = String::new();
2191
2192 let Some(n_chars) = self.fetch_directive_word(&mut string, budget) else {
2193 return Err(self.directive_byte_limit(start_mark));
2194 };
2195 self.mark.offsets.chars += n_chars;
2196 self.mark.col += n_chars;
2197 self.mark.offsets.bytes = self.input.byte_offset();
2198
2199 if string.is_empty() {
2200 return Err(ScanError::from_kind(
2201 start_mark,
2202 ErrorKind::MissingDirectiveName,
2203 ));
2204 }
2205
2206 if !is_blank_or_breakz(self.input.peek()) {
2207 return Err(ScanError::from_kind(
2208 start_mark,
2209 ErrorKind::InvalidDirectiveName,
2210 ));
2211 }
2212
2213 Ok(string)
2214 }
2215
2216 fn scan_version_directive_number(&mut self, mark: &Marker) -> Result<u32, ScanError> {
2217 let mut val = 0u32;
2218 let mut length = 0usize;
2219 while let Some(digit) = self.input.look_ch().to_digit(10) {
2220 if length + 1 > 9 {
2221 return Err(ScanError::from_kind(*mark, ErrorKind::YamlVersionTooLong));
2222 }
2223 length += 1;
2224 val = val * 10 + digit;
2225 self.skip_non_blank();
2226 }
2227
2228 if length == 0 {
2229 return Err(ScanError::from_kind(*mark, ErrorKind::MissingYamlVersion));
2230 }
2231
2232 Ok(val)
2233 }
2234
2235 fn scan_tag_directive_value(
2236 &mut self,
2237 mark: &Marker,
2238 budget: &mut usize,
2239 ) -> Result<Token<'input>, ScanError> {
2240 let n_blanks = self.input.skip_while_blank();
2241 self.mark.offsets.chars += n_blanks;
2242 self.mark.col += n_blanks;
2243 self.mark.offsets.bytes = self.input.byte_offset();
2244 self.spend_directive_bytes(budget, n_blanks, *mark)?;
2245
2246 let handle = self.scan_tag_handle_directive_cow(mark, budget)?;
2247
2248 let n_blanks = self.input.skip_while_blank();
2249 self.mark.offsets.chars += n_blanks;
2250 self.mark.col += n_blanks;
2251 self.mark.offsets.bytes = self.input.byte_offset();
2252 self.spend_directive_bytes(budget, n_blanks, *mark)?;
2253
2254 let prefix = self.scan_tag_prefix_directive_cow(mark, budget)?;
2255
2256 self.input.lookahead(1);
2257
2258 if self.input.next_is_blank_or_breakz() {
2259 Ok(Token(
2260 Span::new(*mark, self.mark),
2261 TokenType::TagDirective(handle, prefix),
2262 ))
2263 } else {
2264 Err(ScanError::from_kind(
2265 *mark,
2266 ErrorKind::InvalidTagDirectiveTerminator,
2267 ))
2268 }
2269 }
2270
2271 fn fetch_tag(&mut self) -> ScanResult {
2272 self.save_simple_key();
2273 self.disallow_simple_key();
2274
2275 let tok = self.scan_tag()?;
2276 self.tokens.push_back(tok.into());
2277 Ok(())
2278 }
2279
2280 fn scan_tag(&mut self) -> Result<Token<'input>, ScanError> {
2281 let start_mark = self.mark;
2282
2283 self.input.lookahead(2);
2285
2286 if self.input.byte_offset().is_none() {
2288 return self.scan_tag_owned(&start_mark);
2289 }
2290
2291 let (handle, suffix): (Cow<'input, str>, Cow<'input, str>) = if self
2292 .input
2293 .nth_char_is(1, '<')
2294 {
2295 let suffix = self.scan_verbatim_tag(&start_mark)?;
2297 (Cow::Owned(String::new()), Cow::Owned(suffix))
2298 } else {
2299 let handle = self.scan_tag_handle_cow(&start_mark)?;
2301 if handle.len() >= 2 && handle.starts_with('!') && handle.ends_with('!') {
2303 let suffix = self.scan_tag_shorthand_suffix_cow(&start_mark, true)?;
2305 (handle, suffix)
2306 } else {
2307 let remaining_suffix = self.scan_tag_shorthand_suffix_cow(&start_mark, false)?;
2312
2313 let suffix = self.combine_local_tag_suffix(&start_mark, handle, remaining_suffix);
2314
2315 if suffix.is_empty() {
2318 (Cow::Borrowed(""), Cow::Borrowed("!"))
2319 } else {
2320 (Cow::Borrowed("!"), suffix)
2321 }
2322 }
2323 };
2324
2325 if is_blank_or_breakz(self.input.look_ch())
2326 || (self.flow_level > 0 && matches!(self.input.peek(), ',' | ']' | '}'))
2327 {
2328 Ok(Token(
2331 Span::new(start_mark, self.mark),
2332 TokenType::Tag(handle, suffix),
2333 ))
2334 } else {
2335 Err(ScanError::from_kind(
2336 start_mark,
2337 ErrorKind::InvalidTagTerminator,
2338 ))
2339 }
2340 }
2341
2342 fn combine_local_tag_suffix(
2343 &self,
2344 start_mark: &Marker,
2345 handle: Cow<'input, str>,
2346 remaining: Cow<'input, str>,
2347 ) -> Cow<'input, str> {
2348 if handle.len() == 1 {
2349 return remaining;
2350 }
2351
2352 match (handle, remaining) {
2353 (Cow::Borrowed(handle), Cow::Borrowed(remaining)) => {
2354 if remaining.is_empty() {
2355 return Cow::Borrowed(&handle[1..]);
2356 }
2357
2358 let borrowed = start_mark
2359 .byte_offset()
2360 .and_then(|start| start.checked_add(1))
2361 .zip(self.input.byte_offset())
2362 .and_then(|(start, end)| self.try_borrow_slice(start, end));
2363 borrowed.map_or_else(
2364 || {
2365 let mut combined =
2366 String::with_capacity(handle.len() - 1 + remaining.len());
2367 combined.push_str(&handle[1..]);
2368 combined.push_str(remaining);
2369 Cow::Owned(combined)
2370 },
2371 Cow::Borrowed,
2372 )
2373 }
2374 (Cow::Borrowed(handle), Cow::Owned(mut remaining)) => {
2375 remaining.reserve(handle.len() - 1);
2376 remaining.insert_str(0, &handle[1..]);
2377 Cow::Owned(remaining)
2378 }
2379 (Cow::Owned(mut handle), remaining) => {
2380 handle.remove(0);
2381 handle.push_str(&remaining);
2382 Cow::Owned(handle)
2383 }
2384 }
2385 }
2386
2387 fn scan_tag_owned(&mut self, start_mark: &Marker) -> Result<Token<'input>, ScanError> {
2389 let mut handle = String::new();
2390 let mut suffix;
2391
2392 if self.input.nth_char_is(1, '<') {
2393 suffix = self.scan_verbatim_tag(start_mark)?;
2394 } else {
2395 handle = self.scan_tag_handle(false, start_mark)?;
2397 if handle.len() >= 2 && handle.starts_with('!') && handle.ends_with('!') {
2399 let is_secondary_handle = handle == "!!";
2401 suffix =
2402 self.scan_tag_shorthand_suffix(false, is_secondary_handle, "", start_mark)?;
2403 } else {
2404 suffix = self.scan_tag_shorthand_suffix(false, false, &handle, start_mark)?;
2405 "!".clone_into(&mut handle);
2406 if suffix.is_empty() {
2409 handle.clear();
2410 "!".clone_into(&mut suffix);
2411 }
2412 }
2413 }
2414
2415 if is_blank_or_breakz(self.input.look_ch())
2416 || (self.flow_level > 0 && matches!(self.input.peek(), ',' | ']' | '}'))
2417 {
2418 Ok(Token(
2421 Span::new(*start_mark, self.mark),
2422 TokenType::Tag(handle.into(), suffix.into()),
2423 ))
2424 } else {
2425 Err(ScanError::from_kind(
2426 *start_mark,
2427 ErrorKind::InvalidTagTerminator,
2428 ))
2429 }
2430 }
2431
2432 fn scan_tag_handle_cow(&mut self, mark: &Marker) -> Result<Cow<'input, str>, ScanError> {
2437 let Some(start) = self.input.byte_offset() else {
2438 return Ok(Cow::Owned(self.scan_tag_handle(false, mark)?));
2439 };
2440
2441 if self.input.look_ch() != '!' {
2442 return Err(ScanError::from_kind(*mark, ErrorKind::ExpectedTagBang));
2443 }
2444
2445 self.skip_non_blank();
2447
2448 self.input.lookahead(1);
2450 while self.input.next_is_alpha() {
2451 self.skip_non_blank();
2452 self.input.lookahead(1);
2453 }
2454
2455 if self.input.peek() == '!' {
2457 self.skip_non_blank();
2458 }
2459
2460 let Some(end) = self.input.byte_offset() else {
2461 return Ok(Cow::Owned(self.scan_tag_handle(false, mark)?));
2462 };
2463
2464 if let Some(slice) = self.try_borrow_slice(start, end) {
2465 Ok(Cow::Borrowed(slice))
2466 } else {
2467 let slice = self
2468 .input
2469 .slice_bytes(start, end)
2470 .ok_or_else(|| ScanError::from_kind(*mark, ErrorKind::InputSlicingUnavailable))?;
2471 Ok(Cow::Owned(slice.to_owned()))
2472 }
2473 }
2474
2475 fn scan_tag_shorthand_suffix_cow(
2479 &mut self,
2480 mark: &Marker,
2481 require_non_empty: bool,
2482 ) -> Result<Cow<'input, str>, ScanError> {
2483 let Some(start) = self.input.byte_offset() else {
2484 return Ok(Cow::Owned(
2485 self.scan_tag_shorthand_suffix(false, false, "", mark)?,
2486 ));
2487 };
2488
2489 while is_tag_char(self.input.look_ch()) {
2491 if self.input.peek() == '%' {
2492 let current = self
2494 .input
2495 .byte_offset()
2496 .expect("byte_offset() must remain available once enabled");
2497 let mut out = if let Some(slice) = self.input.slice_bytes(start, current) {
2498 slice.to_owned()
2499 } else {
2500 String::new()
2501 };
2502
2503 while is_tag_char(self.input.look_ch()) {
2505 if self.input.peek() == '%' {
2506 out.push(self.scan_uri_escapes(mark)?);
2507 } else {
2508 out.push(self.input.peek());
2509 self.skip_non_blank();
2510 }
2511 }
2512 return Ok(Cow::Owned(out));
2513 }
2514 self.skip_non_blank();
2515 }
2516
2517 let Some(end) = self.input.byte_offset() else {
2518 return Ok(Cow::Owned(
2519 self.scan_tag_shorthand_suffix(false, false, "", mark)?,
2520 ));
2521 };
2522
2523 if require_non_empty && start == end {
2524 return Err(ScanError::from_kind(*mark, ErrorKind::MissingTagUri));
2525 }
2526
2527 if let Some(slice) = self.try_borrow_slice(start, end) {
2528 Ok(Cow::Borrowed(slice))
2529 } else {
2530 let slice = self
2531 .input
2532 .slice_bytes(start, end)
2533 .ok_or_else(|| ScanError::from_kind(*mark, ErrorKind::InputSlicingUnavailable))?;
2534 Ok(Cow::Owned(slice.to_owned()))
2535 }
2536 }
2537
2538 fn scan_tag_handle(&mut self, directive: bool, mark: &Marker) -> Result<String, ScanError> {
2539 let mut string = String::new();
2540 if self.input.look_ch() != '!' {
2541 return Err(ScanError::from_kind(*mark, ErrorKind::ExpectedTagBang));
2542 }
2543
2544 string.push(self.input.peek());
2545 self.skip_non_blank();
2546
2547 let n_chars = self.input.fetch_while_is_alpha(&mut string);
2548 self.mark.offsets.chars += n_chars;
2549 self.mark.col += n_chars;
2550 self.mark.offsets.bytes = self.input.byte_offset();
2551
2552 if self.input.peek() == '!' {
2554 string.push(self.input.peek());
2555 self.skip_non_blank();
2556 } else if directive && string != "!" {
2557 return Err(ScanError::from_kind(
2561 *mark,
2562 ErrorKind::ExpectedTagDirectiveBang,
2563 ));
2564 }
2565 Ok(string)
2566 }
2567
2568 fn scan_verbatim_tag(&mut self, start_mark: &Marker) -> Result<String, ScanError> {
2572 self.skip_non_blank();
2574 self.skip_non_blank();
2575
2576 let mut string = String::new();
2577 while is_uri_char(self.input.look_ch()) {
2578 if self.input.peek() == '%' {
2579 string.push(self.scan_uri_escapes(start_mark)?);
2580 } else {
2581 string.push(self.input.peek());
2582 self.skip_non_blank();
2583 }
2584 }
2585
2586 if string.is_empty() {
2587 return Err(ScanError::from_kind(*start_mark, ErrorKind::MissingTagUri));
2588 }
2589
2590 if self.input.peek() != '>' {
2591 return Err(ScanError::from_kind(
2592 *start_mark,
2593 ErrorKind::UnclosedVerbatimTag,
2594 ));
2595 }
2596 self.skip_non_blank();
2597
2598 Ok(string)
2599 }
2600
2601 fn scan_tag_shorthand_suffix(
2602 &mut self,
2603 _directive: bool,
2604 _is_secondary: bool,
2605 head: &str,
2606 mark: &Marker,
2607 ) -> Result<String, ScanError> {
2608 let mut length = head.len();
2609 let mut string = String::new();
2610
2611 if length > 1 {
2614 string.extend(head.chars().skip(1));
2615 }
2616
2617 while is_tag_char(self.input.look_ch()) {
2618 if self.input.peek() == '%' {
2620 string.push(self.scan_uri_escapes(mark)?);
2621 } else {
2622 string.push(self.input.peek());
2623 self.skip_non_blank();
2624 }
2625
2626 length += 1;
2627 }
2628
2629 if length == 0 {
2630 return Err(ScanError::from_kind(*mark, ErrorKind::MissingTagUri));
2631 }
2632
2633 Ok(string)
2634 }
2635
2636 fn scan_uri_escapes(&mut self, mark: &Marker) -> Result<char, ScanError> {
2637 self.scan_uri_escapes_with_budget(mark, None)
2638 }
2639
2640 fn scan_uri_escapes_with_budget(
2641 &mut self,
2642 mark: &Marker,
2643 mut directive_budget: Option<&mut usize>,
2644 ) -> Result<char, ScanError> {
2645 let mut width = 0usize;
2646 let mut bytes = [0u8; 4];
2647 let mut bytes_len = 0usize;
2648 loop {
2649 self.input.lookahead(3);
2650
2651 let c = self.input.peek_nth(1);
2652 let nc = self.input.peek_nth(2);
2653
2654 if !(self.input.peek() == '%' && is_hex(c) && is_hex(nc)) {
2655 return Err(ScanError::from_kind(*mark, ErrorKind::InvalidTagEscape));
2656 }
2657
2658 if let Some(budget) = directive_budget.as_deref_mut() {
2659 self.spend_directive_bytes(budget, 3, *mark)?;
2660 }
2661
2662 let byte = u8::try_from((as_hex(c) << 4) + as_hex(nc))
2663 .expect("two hex nibbles always fit in a byte");
2664 if width == 0 {
2665 width = match byte {
2666 _ if byte & 0x80 == 0x00 => 1,
2667 _ if byte & 0xE0 == 0xC0 => 2,
2668 _ if byte & 0xF0 == 0xE0 => 3,
2669 _ if byte & 0xF8 == 0xF0 => 4,
2670 _ => {
2671 return Err(ScanError::from_kind(
2672 *mark,
2673 ErrorKind::InvalidTagUtf8LeadingByte,
2674 ));
2675 }
2676 };
2677 } else if byte & 0xc0 != 0x80 {
2678 return Err(ScanError::from_kind(
2679 *mark,
2680 ErrorKind::InvalidTagUtf8TrailingByte,
2681 ));
2682 }
2683
2684 bytes[bytes_len] = byte;
2685 bytes_len += 1;
2686
2687 self.skip_n_non_blank(3);
2688
2689 width -= 1;
2690 if width == 0 {
2691 break;
2692 }
2693 }
2694
2695 let s = core::str::from_utf8(&bytes[..bytes_len])
2696 .map_err(|_| ScanError::from_kind(*mark, ErrorKind::InvalidTagUtf8))?;
2697
2698 let Some(ch) = s.chars().next() else {
2699 unreachable!("a validated URI escape cannot decode to an empty string")
2700 };
2701 Ok(ch)
2702 }
2703
2704 fn fetch_anchor(&mut self, alias: bool) -> ScanResult {
2705 self.save_simple_key();
2706 self.disallow_simple_key();
2707
2708 let tok = self.scan_anchor(alias)?;
2709
2710 self.tokens.push_back(tok.into());
2711
2712 Ok(())
2713 }
2714
2715 fn scan_anchor(&mut self, alias: bool) -> Result<Token<'input>, ScanError> {
2716 let start_mark = self.mark;
2717
2718 self.skip_non_blank();
2720
2721 if let Some(start) = self.input.byte_offset() {
2723 while is_anchor_char(self.input.look_ch()) {
2724 self.skip_non_blank();
2725 }
2726
2727 let end = self
2728 .input
2729 .byte_offset()
2730 .expect("byte_offset() must remain available once enabled");
2731
2732 if start == end {
2733 return Err(ScanError::from_kind(
2734 start_mark,
2735 ErrorKind::MissingAnchorOrAliasName,
2736 ));
2737 }
2738
2739 let cow = if let Some(slice) = self.try_borrow_slice(start, end) {
2740 Cow::Borrowed(slice)
2741 } else if let Some(slice) = self.input.slice_bytes(start, end) {
2742 Cow::Owned(slice.to_owned())
2743 } else {
2744 return Err(ScanError::from_kind(
2745 start_mark,
2746 ErrorKind::InputSlicingUnavailable,
2747 ));
2748 };
2749
2750 let tok = if alias {
2751 TokenType::Alias(cow)
2752 } else {
2753 TokenType::Anchor(cow)
2754 };
2755 return Ok(Token(Span::new(start_mark, self.mark), tok));
2756 }
2757
2758 let mut string = String::new();
2759 while is_anchor_char(self.input.look_ch()) {
2760 string.push(self.input.peek());
2761 self.skip_non_blank();
2762 }
2763
2764 if string.is_empty() {
2765 return Err(ScanError::from_kind(
2766 start_mark,
2767 ErrorKind::MissingAnchorOrAliasName,
2768 ));
2769 }
2770
2771 let tok = if alias {
2772 TokenType::Alias(string.into())
2773 } else {
2774 TokenType::Anchor(string.into())
2775 };
2776 Ok(Token(Span::new(start_mark, self.mark), tok))
2777 }
2778
2779 fn fetch_flow_collection_start(&mut self, tok: TokenType<'input>) -> ScanResult {
2780 self.save_simple_key();
2782
2783 let start_mark = self.mark;
2784 let indicator = self.input.peek();
2785 self.flow_markers.push((start_mark, indicator));
2786
2787 self.roll_one_col_indent();
2788 self.increase_flow_level()?;
2789
2790 self.allow_simple_key();
2791
2792 self.skip_non_blank();
2793 let end_mark = self.mark;
2794
2795 if tok == TokenType::FlowMappingStart {
2796 self.flow_mapping_started.push(true);
2797 } else {
2798 self.flow_mapping_started.push(false);
2799 self.implicit_flow_mapping_states
2800 .push(ImplicitMappingState::Possible);
2801 }
2802
2803 let token_index = self.tokens.len();
2804 self.skip_ws_to_eol(SkipTabs::Yes)?;
2805
2806 self.insert_token(token_index, Token(Span::new(start_mark, end_mark), tok));
2807 Ok(())
2808 }
2809
2810 fn fetch_flow_collection_end(
2811 &mut self,
2812 tok: TokenType<'input>,
2813 expected_open: char,
2814 actual_close: char,
2815 ) -> ScanResult {
2816 if self.flow_level == 0 {
2818 return Err(self.scan_error(ErrorKind::MisplacedFlowCollectionEnd));
2819 }
2820
2821 let Some((open_mark, open_ch)) = self.flow_markers.pop() else {
2822 return Err(self.scan_error(ErrorKind::MisplacedFlowCollectionEnd));
2823 };
2824
2825 if open_ch != expected_open {
2826 return Err(ScanError::from_kind(
2827 open_mark,
2828 ErrorKind::MismatchedFlowCollectionEnd {
2829 open: open_ch,
2830 close: actual_close,
2831 },
2832 ));
2833 }
2834
2835 let flow_level = self.flow_level;
2836
2837 self.remove_simple_key()?;
2838
2839 if matches!(tok, TokenType::FlowSequenceEnd) {
2840 self.end_implicit_mapping(self.mark, flow_level);
2841 self.implicit_flow_mapping_states.pop();
2843 }
2844 self.flow_mapping_started.pop();
2845
2846 self.decrease_flow_level();
2847
2848 self.disallow_simple_key();
2849
2850 let start_mark = self.mark;
2851 self.skip_non_blank();
2852 let end_mark = self.mark;
2853 let token_index = self.tokens.len();
2854 self.skip_ws_to_eol(SkipTabs::Yes)?;
2855
2856 if self.flow_level > 0 {
2862 self.adjacent_value_allowed_at = self.mark.index();
2863 }
2864
2865 self.insert_token(token_index, Token(Span::new(start_mark, end_mark), tok));
2866 Ok(())
2867 }
2868
2869 fn fetch_flow_entry(&mut self) -> ScanResult {
2871 self.remove_simple_key()?;
2872 self.allow_simple_key();
2873
2874 self.end_implicit_mapping(self.mark, self.flow_level);
2875 if self.current_flow_collection_is_sequence() {
2876 self.set_current_flow_mapping_started(false);
2877 }
2878
2879 let start_mark = self.mark;
2880 self.skip_non_blank();
2881 let end_mark = self.mark;
2882 let token_index = self.tokens.len();
2883 self.skip_ws_to_eol(SkipTabs::Yes)?;
2884
2885 self.insert_token(
2886 token_index,
2887 Token(Span::new(start_mark, end_mark), TokenType::FlowEntry),
2888 );
2889 Ok(())
2890 }
2891
2892 fn increase_flow_level(&mut self) -> ScanResult {
2893 if self.flow_level >= self.options.flow_nesting_limit {
2894 return Err(self.scan_error(ErrorKind::RecursionLimitExceeded));
2895 }
2896
2897 self.simple_keys.push(SimpleKey::new(Marker::new(0, 0, 0)));
2898 self.flow_level += 1;
2899 Ok(())
2900 }
2901
2902 fn decrease_flow_level(&mut self) {
2903 if self.flow_level > 0 {
2904 self.flow_level -= 1;
2905 self.simple_keys.pop().unwrap();
2906 }
2907 }
2908
2909 fn fetch_block_entry(&mut self) -> ScanResult {
2915 if self.flow_level > 0 {
2916 return Err(self.scan_error(ErrorKind::BlockEntryInFlowCollection));
2918 }
2919 if !self.simple_key_allowed {
2921 return Err(self.scan_error(ErrorKind::BlockSequenceEntryNotAllowed));
2922 }
2923
2924 let mark = self.mark;
2926 self.skip_non_blank();
2927
2928 self.roll_indent(mark.col, None, TokenType::BlockSequenceStart, mark)?;
2930 let token_index = self.tokens.len();
2931 let found_tabs = self.skip_ws_to_eol(SkipTabs::Yes)?.found_tabs();
2932 self.input.lookahead(2);
2933 if found_tabs && self.input.next_char_is('-') && is_blank_or_breakz(self.input.peek_nth(1))
2934 {
2935 return Err(self.scan_error(ErrorKind::InvalidBlockEntryWhitespace));
2936 }
2937
2938 self.skip_ws_to_eol(SkipTabs::No)?;
2939 self.input.lookahead(1);
2940 if self.input.next_is_break() || self.input.next_is_flow() {
2941 self.roll_one_col_indent();
2942 }
2943
2944 self.remove_simple_key()?;
2945 self.allow_simple_key();
2946
2947 self.insert_token(
2948 token_index,
2949 Token(Span::empty(self.mark), TokenType::BlockEntry),
2950 );
2951
2952 Ok(())
2953 }
2954
2955 fn fetch_document_indicator(&mut self, t: TokenType<'input>) -> ScanResult {
2956 if let Some((mark, bracket)) = self.flow_markers.pop() {
2957 return Err(ScanError::from_kind(
2958 mark,
2959 ErrorKind::UnclosedFlowCollection { open: bracket },
2960 ));
2961 }
2962
2963 self.unroll_indent(-1);
2964 self.remove_simple_key()?;
2965 self.disallow_simple_key();
2966
2967 let mark = self.mark;
2968
2969 self.skip_n_non_blank(3);
2970
2971 self.document_prefix_allowed = matches!(t, TokenType::DocumentEnd);
2972 self.tokens
2973 .push_back(Token(Span::new(mark, self.mark), t).into());
2974 Ok(())
2975 }
2976
2977 fn fetch_block_scalar(&mut self, literal: bool) -> ScanResult {
2978 self.save_simple_key();
2979 self.allow_simple_key();
2980 let token_index = self.tokens.len();
2981 let tok = self.scan_block_scalar(literal)?;
2982
2983 self.insert_token(token_index, tok);
2984 Ok(())
2985 }
2986
2987 #[allow(clippy::too_many_lines)]
2988 fn scan_block_scalar(&mut self, literal: bool) -> Result<Token<'input>, ScanError> {
2989 let start_mark = self.mark;
2990 let mut chomping = Chomping::Clip;
2991 let mut increment: usize = 0;
2992 let mut indent: usize = 0;
2993 let mut trailing_blank: bool;
2994 let mut leading_blank: bool = false;
2995 let style = if literal {
2996 ScalarStyle::Literal
2997 } else {
2998 ScalarStyle::Folded
2999 };
3000
3001 let mut string = String::new();
3002 let mut leading_break = String::new();
3003 let mut trailing_breaks = String::new();
3004 let mut chomping_break = String::new();
3005
3006 self.skip_non_blank();
3008 self.unroll_non_block_indents();
3009
3010 if self.input.look_ch() == '+' || self.input.peek() == '-' {
3011 if self.input.peek() == '+' {
3012 chomping = Chomping::Keep;
3013 } else {
3014 chomping = Chomping::Strip;
3015 }
3016 self.skip_non_blank();
3017 self.input.lookahead(1);
3018 if self.input.next_is_digit() {
3019 if self.input.peek() == '0' {
3020 return Err(ScanError::from_kind(
3021 start_mark,
3022 ErrorKind::ZeroBlockScalarIndent,
3023 ));
3024 }
3025 increment = (self.input.peek() as usize) - ('0' as usize);
3026 self.skip_non_blank();
3027 }
3028 } else if self.input.next_is_digit() {
3029 if self.input.peek() == '0' {
3030 return Err(ScanError::from_kind(
3031 start_mark,
3032 ErrorKind::ZeroBlockScalarIndent,
3033 ));
3034 }
3035
3036 increment = (self.input.peek() as usize) - ('0' as usize);
3037 self.skip_non_blank();
3038 self.input.lookahead(1);
3039 if self.input.peek() == '+' || self.input.peek() == '-' {
3040 if self.input.peek() == '+' {
3041 chomping = Chomping::Keep;
3042 } else {
3043 chomping = Chomping::Strip;
3044 }
3045 self.skip_non_blank();
3046 }
3047 }
3048
3049 self.skip_ws_to_eol(SkipTabs::Yes)?;
3050
3051 self.input.lookahead(1);
3053 self.ensure_current_char_is_printable()?;
3054 if !self.input.next_is_breakz() {
3055 return Err(ScanError::from_kind(
3056 start_mark,
3057 ErrorKind::InvalidBlockScalarHeader,
3058 ));
3059 }
3060
3061 if self.input.next_is_break() {
3062 self.input.lookahead(2);
3063 self.read_break(&mut chomping_break);
3064 }
3065
3066 self.ensure_current_char_is_printable()?;
3067
3068 if self.input.look_ch() == '\t' {
3069 return Err(ScanError::from_kind(
3070 start_mark,
3071 ErrorKind::TabAtBlockScalarStart,
3072 ));
3073 }
3074
3075 if increment > 0 {
3076 indent = if self.indent >= 0 {
3077 (self.indent + increment as isize) as usize
3078 } else {
3079 increment
3080 }
3081 }
3082
3083 if indent == 0 {
3085 self.skip_block_scalar_first_line_indent(&mut indent, &mut trailing_breaks);
3086 } else {
3087 self.skip_block_scalar_indent(indent, &mut trailing_breaks);
3088 }
3089
3090 self.ensure_current_char_is_printable()?;
3091
3092 if self.input.next_is_z() {
3097 let contents = match chomping {
3098 Chomping::Strip => String::new(),
3100 _ if self.mark.line == start_mark.line() => String::new(),
3102 Chomping::Clip => String::new(),
3104 Chomping::Keep if trailing_breaks.is_empty() && self.mark.col > 0 => chomping_break,
3106 Chomping::Keep => trailing_breaks,
3108 };
3109
3110 return Ok(Token(
3111 Span::new(start_mark, self.mark),
3112 TokenType::Scalar(style, contents.into()),
3113 ));
3114 }
3115
3116 if self.mark.col < indent && (self.mark.col as isize) > self.indent {
3117 if self.indent < 0 && self.mark.col == 0 {
3118 self.input.lookahead(4);
3119 if self.input.next_is_document_start()
3120 || self.input.next_is_document_end()
3121 || self.input.peek() == '#'
3122 {
3123 } else {
3127 return Err(self.scan_error(ErrorKind::InvalidBlockScalarIndent));
3128 }
3129 } else {
3130 return Err(self.scan_error(ErrorKind::InvalidBlockScalarIndent));
3131 }
3132 }
3133
3134 let mut line_buffer = String::with_capacity(100);
3135 let start_mark = self.mark;
3136 while self.mark.col == indent && !self.input.next_is_z() {
3137 self.ensure_current_char_is_printable()?;
3138
3139 if indent == 0 {
3140 self.input.lookahead(4);
3141 if self.input.next_is_document_indicator() {
3142 break;
3143 }
3144 }
3145
3146 trailing_blank = self.input.next_is_blank();
3148 if !literal && !leading_break.is_empty() && !leading_blank && !trailing_blank {
3149 string.push_str(&trailing_breaks);
3150 if trailing_breaks.is_empty() {
3151 string.push(' ');
3152 }
3153 } else {
3154 string.push_str(&leading_break);
3155 string.push_str(&trailing_breaks);
3156 }
3157
3158 leading_break.clear();
3159 trailing_breaks.clear();
3160
3161 leading_blank = self.input.next_is_blank();
3162
3163 self.scan_block_scalar_content_line(&mut string, &mut line_buffer);
3164
3165 self.input.lookahead(2);
3167 if self.input.next_is_z() {
3168 break;
3169 }
3170
3171 self.ensure_current_char_is_printable()?;
3172
3173 self.read_break(&mut leading_break);
3174
3175 self.skip_block_scalar_indent(indent, &mut trailing_breaks);
3177 }
3178
3179 self.ensure_current_char_is_printable()?;
3180
3181 if chomping != Chomping::Strip {
3183 string.push_str(&leading_break);
3184 if self.input.next_is_z() && self.mark.col >= indent.max(1) {
3188 string.push('\n');
3189 }
3190 }
3191
3192 if chomping == Chomping::Keep {
3193 string.push_str(&trailing_breaks);
3194 }
3195
3196 if let Some(character) = find_non_printable(&string) {
3197 return Err(ScanError::from_kind(
3198 start_mark,
3199 ErrorKind::UnexpectedCharacter { character },
3200 ));
3201 }
3202
3203 let span = if string.trim().is_empty() {
3204 Span::new(start_mark, self.mark)
3205 } else {
3206 Span::new(start_mark, self.mark).with_indent(Some(indent))
3207 };
3208
3209 Ok(Token(span, TokenType::Scalar(style, string.into())))
3210 }
3211
3212 fn scan_block_scalar_content_line(&mut self, string: &mut String, line_buffer: &mut String) {
3222 while !self.input.buf_is_empty() && !self.input.next_is_breakz() {
3224 string.push(self.input.peek());
3225 self.skip_blank();
3231 }
3232
3233 if self.input.buf_is_empty() {
3236 let mut n_chars = 0;
3244 debug_assert!(line_buffer.is_empty());
3245 while let Some(c) = self.input.raw_read_non_breakz_ch() {
3246 line_buffer.push(c);
3247 n_chars += 1;
3248 }
3249
3250 self.mark.col += n_chars;
3252 self.mark.offsets.chars += n_chars;
3253 self.mark.offsets.bytes = self.input.byte_offset();
3254
3255 string.reserve(line_buffer.len());
3257 string.push_str(line_buffer);
3258 line_buffer.clear();
3260 }
3261 }
3262
3263 fn skip_block_scalar_indent(&mut self, indent: usize, breaks: &mut String) {
3265 loop {
3266 if indent < self.input.bufmaxlen().saturating_sub(2) {
3268 self.input.lookahead(self.input.bufmaxlen());
3269 while self.mark.col < indent && self.input.peek() == ' ' {
3270 self.skip_blank();
3271 }
3272 } else {
3273 loop {
3274 self.input.lookahead(self.input.bufmaxlen());
3275 while !self.input.buf_is_empty()
3276 && self.mark.col < indent
3277 && self.input.peek() == ' '
3278 {
3279 self.skip_blank();
3280 }
3281 if self.mark.col == indent
3285 || (!self.input.buf_is_empty() && self.input.peek() != ' ')
3286 {
3287 break;
3288 }
3289 }
3290 self.input.lookahead(2);
3291 }
3292
3293 if self.input.next_is_break() {
3295 self.read_break(breaks);
3296 } else {
3297 break;
3299 }
3300 }
3301 }
3302
3303 fn skip_block_scalar_first_line_indent(&mut self, indent: &mut usize, breaks: &mut String) {
3308 let mut max_indent = 0;
3309 loop {
3310 while self.input.look_ch() == ' ' {
3312 self.skip_blank();
3313 }
3314
3315 if self.mark.col > max_indent {
3316 max_indent = self.mark.col;
3317 }
3318
3319 if self.input.next_is_break() {
3320 self.input.lookahead(2);
3322 self.read_break(breaks);
3323 } else {
3324 break;
3326 }
3327 }
3328
3329 *indent = max_indent.max((self.indent + 1) as usize);
3338 }
3339
3340 fn fetch_flow_scalar(&mut self, single: bool) -> ScanResult {
3341 self.save_simple_key();
3342 self.disallow_simple_key();
3343
3344 let token_index = self.tokens.len();
3345 let tok = self.scan_flow_scalar(single)?;
3346
3347 if self.skip_to_next_token()?.saw_comment {
3350 self.adjacent_value_allowed_at = usize::MAX;
3351 } else {
3352 self.adjacent_value_allowed_at = self.mark.index();
3353 }
3354
3355 self.insert_token(token_index, tok);
3356 Ok(())
3357 }
3358
3359 #[allow(clippy::too_many_lines)]
3360 fn scan_flow_scalar(&mut self, single: bool) -> Result<Token<'input>, ScanError> {
3361 let start_mark = self.mark;
3362
3363 let mut buf = match self.input.byte_offset() {
3365 Some(off) => FlowScalarBuf::new_borrowed(off + self.input.peek().len_utf8()),
3366 None => FlowScalarBuf::new_owned(),
3367 };
3368
3369 let mut break_scratch = String::new();
3372
3373 self.skip_non_blank();
3375
3376 loop {
3377 self.input.lookahead(4);
3379
3380 if self.mark.col == 0 && self.input.next_is_document_indicator() {
3381 return Err(ScanError::from_kind(
3382 start_mark,
3383 ErrorKind::DocumentIndicatorInQuotedScalar,
3384 ));
3385 }
3386
3387 if self.input.next_is_z() {
3388 return Err(ScanError::from_kind(
3389 start_mark,
3390 ErrorKind::UnclosedQuotedScalar,
3391 ));
3392 }
3393
3394 self.ensure_current_char_is_printable()?;
3395
3396 let mut leading_blanks = false;
3399 self.consume_flow_scalar_non_whitespace_chars(
3400 single,
3401 &mut buf,
3402 &mut leading_blanks,
3403 &start_mark,
3404 )?;
3405
3406 match self.input.look_ch() {
3407 '\'' if single => break,
3408 '"' if !single => break,
3409 _ => {}
3410 }
3411
3412 let mut trailing_ws_start: Option<usize> = None;
3428 let mut has_leading_break = false;
3429 let mut has_trailing_breaks = false;
3430
3431 let mut pending_ws_start: Option<usize> = None;
3433
3434 while self.input.next_is_blank() || self.input.next_is_break() {
3436 if self.input.next_is_blank() {
3437 if leading_blanks {
3439 if self.input.peek() == '\t' && (self.mark.col as isize) < self.indent {
3440 return Err(self.scan_error(ErrorKind::TabInIndentation));
3441 }
3442 self.skip_blank();
3443 } else {
3444 match buf {
3446 FlowScalarBuf::Owned(ref mut string) => {
3447 if trailing_ws_start.is_none() {
3448 trailing_ws_start = Some(string.len());
3449 }
3450 string.push(self.input.peek());
3451 }
3452 FlowScalarBuf::Borrowed { .. } => {
3453 if pending_ws_start.is_none() {
3454 pending_ws_start = self.input.byte_offset();
3455 }
3456 }
3457 }
3458 self.skip_blank();
3459
3460 if let (FlowScalarBuf::Borrowed { .. }, Some(ws_start), Some(ws_end)) =
3461 (&mut buf, pending_ws_start, self.input.byte_offset())
3462 {
3463 buf.note_pending_ws(ws_start, ws_end);
3464 }
3465 }
3466 } else {
3467 self.input.lookahead(2);
3468
3469 if leading_blanks {
3471 match buf {
3473 FlowScalarBuf::Owned(ref mut string) => self.read_break(string),
3474 FlowScalarBuf::Borrowed { .. } => {
3475 self.promote_flow_scalar_buf_to_owned(&start_mark, &mut buf)?;
3476 let Some(string) = buf.as_owned_mut() else {
3477 unreachable!()
3478 };
3479 self.read_break(string);
3480 }
3481 }
3482 has_trailing_breaks = true;
3483 } else {
3484 if let Some(pos) = trailing_ws_start.take() {
3486 if let FlowScalarBuf::Owned(ref mut string) = buf {
3487 string.truncate(pos);
3488 }
3489 }
3490
3491 if pending_ws_start.take().is_some() {
3492 if matches!(buf, FlowScalarBuf::Borrowed { .. }) {
3494 self.promote_flow_scalar_buf_to_owned(&start_mark, &mut buf)?;
3495 }
3496 buf.discard_pending_ws();
3497 } else {
3498 buf.commit_pending_ws();
3499 }
3500
3501 break_scratch.clear();
3502 self.read_break(&mut break_scratch);
3503 has_leading_break = true;
3506 leading_blanks = true;
3507 }
3508 }
3509
3510 self.input.lookahead(1);
3511 }
3512
3513 if leading_blanks && has_leading_break && self.flow_level == 0 {
3516 let next_ch = self.input.peek();
3517 let is_closing_quote = (single && next_ch == '\'') || (!single && next_ch == '"');
3518 if !is_closing_quote && (self.mark.col as isize) <= self.indent {
3519 return Err(self.scan_error(ErrorKind::InvalidQuotedScalarIndent));
3520 }
3521 }
3522
3523 if leading_blanks {
3525 if has_leading_break && !has_trailing_breaks {
3530 match buf {
3531 FlowScalarBuf::Owned(ref mut string) => string.push(' '),
3532 FlowScalarBuf::Borrowed { .. } => {
3533 self.promote_flow_scalar_buf_to_owned(&start_mark, &mut buf)?;
3534 let Some(string) = buf.as_owned_mut() else {
3535 unreachable!()
3536 };
3537 string.push(' ');
3538 }
3539 }
3540 }
3541 }
3542 } self.skip_non_blank();
3547 let end_mark = self.mark;
3548
3549 self.skip_ws_to_eol(SkipTabs::Yes)?;
3551 self.ensure_current_char_is_printable()?;
3552 match self.input.peek() {
3553 ',' | '}' | ']' if self.flow_level > 0 => {}
3555 c if is_breakz(c) => {}
3557 ':' if self.flow_level == 0 && start_mark.line == self.mark.line => {}
3560 ':' if self.flow_level > 0 => {}
3562 _ => {
3563 let kind = if single {
3564 ErrorKind::InvalidTrailingSingleQuotedScalar
3565 } else {
3566 ErrorKind::InvalidTrailingDoubleQuotedScalar
3567 };
3568 return Err(self.scan_error(kind));
3569 }
3570 }
3571
3572 let style = if single {
3573 ScalarStyle::SingleQuoted
3574 } else {
3575 ScalarStyle::DoubleQuoted
3576 };
3577
3578 let contents = match buf {
3579 FlowScalarBuf::Owned(string) => Cow::Owned(string),
3580 FlowScalarBuf::Borrowed {
3581 start,
3582 mut end,
3583 pending_ws_start,
3584 pending_ws_end,
3585 } => {
3586 if pending_ws_start.is_some() {
3588 end = pending_ws_end;
3589 }
3590 if let Some(slice) = self.try_borrow_slice(start, end) {
3591 Cow::Borrowed(slice)
3592 } else {
3593 let slice = self.input.slice_bytes(start, end).ok_or_else(|| {
3594 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
3595 })?;
3596 Cow::Owned(slice.to_owned())
3597 }
3598 }
3599 };
3600
3601 Ok(Token(
3602 Span::new(start_mark, end_mark),
3603 TokenType::Scalar(style, contents),
3604 ))
3605 }
3606
3607 fn consume_flow_scalar_non_whitespace_chars(
3616 &mut self,
3617 single: bool,
3618 buf: &mut FlowScalarBuf,
3619 leading_blanks: &mut bool,
3620 start_mark: &Marker,
3621 ) -> Result<(), ScanError> {
3622 self.input.lookahead(2);
3623 while !is_blank_or_breakz(self.input.peek()) && is_printable(self.input.peek()) {
3624 match self.input.peek() {
3625 '\'' if self.input.peek_nth(1) == '\'' && single => {
3627 if matches!(buf, FlowScalarBuf::Borrowed { .. }) {
3628 buf.commit_pending_ws();
3629 self.promote_flow_scalar_buf_to_owned(start_mark, buf)?;
3630 }
3631 let Some(string) = buf.as_owned_mut() else {
3632 unreachable!()
3633 };
3634 string.push('\'');
3635 self.skip_n_non_blank(2);
3636 }
3637 '\'' if single => break,
3639 '"' if !single => break,
3640 '\\' if !single && is_break(self.input.peek_nth(1)) => {
3642 self.input.lookahead(3);
3643 if matches!(buf, FlowScalarBuf::Borrowed { .. }) {
3644 buf.commit_pending_ws();
3645 self.promote_flow_scalar_buf_to_owned(start_mark, buf)?;
3646 }
3647 self.skip_non_blank();
3648 self.skip_linebreak();
3649 *leading_blanks = true;
3650 break;
3651 }
3652 '\\' if !single => {
3654 if matches!(buf, FlowScalarBuf::Borrowed { .. }) {
3655 buf.commit_pending_ws();
3656 self.promote_flow_scalar_buf_to_owned(start_mark, buf)?;
3657 }
3658 let Some(string) = buf.as_owned_mut() else {
3659 unreachable!()
3660 };
3661 string.push(self.resolve_flow_scalar_escape_sequence(start_mark)?);
3662 }
3663 c => {
3664 match buf {
3665 FlowScalarBuf::Owned(ref mut string) => {
3666 string.push(c);
3667 }
3668 FlowScalarBuf::Borrowed { .. } => {
3669 buf.commit_pending_ws();
3670 }
3671 }
3672 self.skip_non_blank();
3673
3674 if let Some(new_end) = self.input.byte_offset() {
3675 if let FlowScalarBuf::Borrowed { end, .. } = buf {
3676 *end = new_end;
3677 }
3678 }
3679 }
3680 }
3681 self.input.lookahead(2);
3682 }
3683 Ok(())
3684 }
3685
3686 fn resolve_flow_scalar_escape_sequence(
3693 &mut self,
3694 start_mark: &Marker,
3695 ) -> Result<char, ScanError> {
3696 let mut code_length = 0usize;
3697 let mut ret = '\0';
3698
3699 match self.input.peek_nth(1) {
3700 '0' => ret = '\0',
3701 'a' => ret = '\x07',
3702 'b' => ret = '\x08',
3703 't' | '\t' => ret = '\t',
3704 'n' => ret = '\n',
3705 'v' => ret = '\x0b',
3706 'f' => ret = '\x0c',
3707 'r' => ret = '\x0d',
3708 'e' => ret = '\x1b',
3709 ' ' => ret = '\x20',
3710 '"' => ret = '"',
3711 '/' => ret = '/',
3712 '\\' => ret = '\\',
3713 'N' => ret = char::from_u32(0x85).unwrap(),
3715 '_' => ret = char::from_u32(0xA0).unwrap(),
3717 'L' => ret = char::from_u32(0x2028).unwrap(),
3719 'P' => ret = char::from_u32(0x2029).unwrap(),
3721 'x' => code_length = 2,
3722 'u' => code_length = 4,
3723 'U' => code_length = 8,
3724 _ => {
3725 return Err(ScanError::from_kind(
3726 *start_mark,
3727 ErrorKind::UnknownQuotedScalarEscape,
3728 ))
3729 }
3730 }
3731 self.skip_n_non_blank(2);
3732
3733 if code_length > 0 {
3735 self.input.lookahead(code_length);
3736 let mut value = 0u32;
3737 for i in 0..code_length {
3738 let c = self.input.peek_nth(i);
3739 if !is_hex(c) {
3740 return Err(ScanError::from_kind(
3741 *start_mark,
3742 ErrorKind::InvalidQuotedScalarHexEscape,
3743 ));
3744 }
3745 value = (value << 4) + as_hex(c);
3746 }
3747
3748 self.skip_n_non_blank(code_length);
3749
3750 if code_length == 4 && (0xD800..=0xDBFF).contains(&value) {
3752 self.input.lookahead(2);
3753 if self.input.peek() == '\\' && self.input.peek_nth(1) == 'u' {
3754 self.skip_n_non_blank(2);
3755 self.input.lookahead(4);
3756 let mut low_value = 0u32;
3757 for i in 0..4 {
3758 let c = self.input.peek_nth(i);
3759 if !is_hex(c) {
3760 return Err(ScanError::from_kind(
3761 *start_mark,
3762 ErrorKind::InvalidLowSurrogateHexEscape,
3763 ));
3764 }
3765 low_value = (low_value << 4) + as_hex(c);
3766 }
3767 if (0xDC00..=0xDFFF).contains(&low_value) {
3768 value = 0x10000 + (((value - 0xD800) << 10) | (low_value - 0xDC00));
3769 self.skip_n_non_blank(4);
3770 } else {
3771 return Err(ScanError::from_kind(
3772 *start_mark,
3773 ErrorKind::InvalidLowSurrogate,
3774 ));
3775 }
3776 } else {
3777 return Err(ScanError::from_kind(
3778 *start_mark,
3779 ErrorKind::MissingLowSurrogate,
3780 ));
3781 }
3782 } else if code_length == 4 && (0xDC00..=0xDFFF).contains(&value) {
3783 return Err(ScanError::from_kind(
3784 *start_mark,
3785 ErrorKind::UnpairedLowSurrogate,
3786 ));
3787 }
3788
3789 let Some(ch) = char::from_u32(value) else {
3790 return Err(ScanError::from_kind(
3791 *start_mark,
3792 ErrorKind::InvalidUnicodeEscape,
3793 ));
3794 };
3795 ret = ch;
3796 }
3797 Ok(ret)
3798 }
3799
3800 fn fetch_plain_scalar(&mut self) -> ScanResult {
3801 self.save_simple_key();
3802 self.disallow_simple_key();
3803
3804 let token_index = self.tokens.len();
3805 let tok = self.scan_plain_scalar()?;
3806
3807 self.insert_token(token_index, tok);
3808 Ok(())
3809 }
3810
3811 #[allow(clippy::too_many_lines)]
3816 fn scan_plain_scalar(&mut self) -> Result<Token<'input>, ScanError> {
3817 self.unroll_non_block_indents();
3818 let indent = self.indent + 1;
3819 let start_mark = self.mark;
3820
3821 let borrow_start = start_mark
3822 .byte_offset()
3823 .filter(|start| self.try_borrow_slice(*start, *start).is_some());
3824 let mut string = borrow_start.is_none().then(|| String::with_capacity(32));
3825 let mut has_content = false;
3826 self.buf_whitespaces.clear();
3827 self.buf_leading_break.clear();
3828 self.buf_trailing_breaks.clear();
3829 let mut end_mark = self.mark;
3830
3831 loop {
3832 self.input.lookahead(4);
3833 if (self.mark.col == 0 && self.input.next_is_document_indicator())
3834 || self.input.peek() == '#'
3835 {
3836 if self.input.peek() == '#'
3841 && has_content
3842 && !self.buf_whitespaces.is_empty()
3843 && self.flow_level == 0
3844 {
3845 self.interrupted_plain_by_comment = Some(self.mark);
3846 }
3847 break;
3848 }
3849
3850 if !has_content
3852 && self.flow_level > 0
3853 && self.input.peek() == '-'
3854 && is_flow(self.input.peek_nth(1))
3855 {
3856 return Err(self.scan_error(ErrorKind::PlainScalarStartsWithDashFlowIndicator));
3857 }
3858
3859 if !self.input.next_is_blank_or_breakz()
3860 && self.input.next_can_be_plain_scalar(self.flow_level > 0)
3861 {
3862 if self.leading_whitespace {
3863 if has_content && string.is_none() {
3864 let start = borrow_start.expect("borrowed scalar has a start offset");
3865 let end = end_mark.byte_offset().ok_or_else(|| {
3866 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
3867 })?;
3868 let prefix = self.try_borrow_slice(start, end).ok_or_else(|| {
3869 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
3870 })?;
3871 string = Some(prefix.to_owned());
3872 }
3873 if self.buf_leading_break.is_empty() {
3874 if let Some(output) = string.as_mut() {
3875 output.push_str(&self.buf_leading_break);
3876 output.push_str(&self.buf_trailing_breaks);
3877 }
3878 self.buf_trailing_breaks.clear();
3879 self.buf_leading_break.clear();
3880 } else {
3881 if self.buf_trailing_breaks.is_empty() {
3882 if let Some(output) = string.as_mut() {
3883 output.push(' ');
3884 }
3885 } else {
3886 if let Some(output) = string.as_mut() {
3887 output.push_str(&self.buf_trailing_breaks);
3888 }
3889 self.buf_trailing_breaks.clear();
3890 }
3891 self.buf_leading_break.clear();
3892 }
3893 self.leading_whitespace = false;
3894 } else if !self.buf_whitespaces.is_empty() {
3895 if let Some(output) = string.as_mut() {
3896 output.push_str(&self.buf_whitespaces);
3897 }
3898 self.buf_whitespaces.clear();
3899 }
3900
3901 has_content = true;
3903 if let Some(output) = string.as_mut() {
3904 output.push(self.input.peek());
3905 }
3906 self.skip_non_blank();
3907 if let Some(output) = string.as_mut() {
3908 output.reserve(self.input.bufmaxlen());
3909 }
3910
3911 let mut end = false;
3913 while !end {
3914 self.input.lookahead(self.input.bufmaxlen());
3919 let chunk_len = self.input.bufmaxlen().saturating_sub(1).max(1);
3920 let (stop, chars_consumed) = if let Some(output) = string.as_mut() {
3921 self.input
3922 .fetch_plain_scalar_chunk(output, chunk_len, self.flow_level > 0)
3923 } else {
3924 self.input
3925 .skip_plain_scalar_chunk(chunk_len, self.flow_level > 0)
3926 };
3927 end = stop;
3928 self.mark.offsets.chars += chars_consumed;
3929 self.mark.col += chars_consumed;
3930 self.mark.offsets.bytes = self.input.byte_offset();
3931 }
3932 end_mark = self.mark;
3933 }
3934
3935 if !(self.input.next_is_blank() || self.input.next_is_break()) {
3940 break;
3941 }
3942
3943 self.input.lookahead(2);
3945 while self.input.next_is_blank_or_break() {
3946 if self.input.next_is_blank() {
3947 if !self.leading_whitespace {
3948 self.buf_whitespaces.push(self.input.peek());
3949 self.skip_blank();
3950 } else if (self.mark.col as isize) < indent && self.input.peek() == '\t' {
3951 self.skip_ws_to_eol(SkipTabs::Yes)?;
3954 if !self.input.next_is_breakz() {
3955 return Err(ScanError::from_kind(
3956 start_mark,
3957 ErrorKind::TabInPlainScalar,
3958 ));
3959 }
3960 } else {
3961 self.skip_blank();
3962 }
3963 } else {
3964 if self.leading_whitespace {
3966 self.skip_break();
3967 self.buf_trailing_breaks.push('\n');
3968 } else {
3969 self.buf_whitespaces.clear();
3970 self.skip_break();
3971 self.buf_leading_break.push('\n');
3972 self.leading_whitespace = true;
3973 }
3974 }
3975 self.input.lookahead(2);
3976 }
3977
3978 if self.flow_level == 0 && (self.mark.col as isize) < indent {
3980 break;
3981 }
3982 }
3983
3984 if self.leading_whitespace {
3985 self.allow_simple_key();
3986 }
3987
3988 let borrowed_contents = if string.is_none() {
3989 let start = borrow_start.expect("borrowed scalar has a start offset");
3990 let end = end_mark.byte_offset().ok_or_else(|| {
3991 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
3992 })?;
3993 Some(self.try_borrow_slice(start, end).ok_or_else(|| {
3994 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
3995 })?)
3996 } else {
3997 None
3998 };
3999 let scalar_text = borrowed_contents.unwrap_or_else(|| {
4000 string
4001 .as_deref()
4002 .expect("owned plain scalar has an output buffer")
4003 });
4004 if let Some(character) = find_non_printable(scalar_text) {
4005 return Err(ScanError::from_kind(
4006 start_mark,
4007 ErrorKind::UnexpectedCharacter { character },
4008 ));
4009 }
4010 self.ensure_current_char_is_printable()?;
4011
4012 if has_content {
4013 let contents = if let Some(slice) = borrowed_contents {
4014 Cow::Borrowed(slice)
4015 } else {
4016 Cow::Owned(string.expect("owned plain scalar has an output buffer"))
4017 };
4018
4019 Ok(Token(
4020 Span::new(start_mark, end_mark),
4021 TokenType::Scalar(ScalarStyle::Plain, contents),
4022 ))
4023 } else {
4024 Err(ScanError::from_kind(
4028 start_mark,
4029 ErrorKind::UnexpectedEndOfPlainScalar,
4030 ))
4031 }
4032 }
4033
4034 fn fetch_key(&mut self) -> ScanResult {
4035 let start_mark = self.mark;
4036 if self.flow_level == 0 {
4037 if !self.simple_key_allowed {
4039 return Err(self.scan_error(ErrorKind::MappingKeyNotAllowed));
4040 }
4041 self.roll_indent(
4042 start_mark.col,
4043 None,
4044 TokenType::BlockMappingStart,
4045 start_mark,
4046 )?;
4047 } else {
4048 self.set_current_flow_mapping_started(true);
4050 }
4051
4052 self.remove_simple_key()?;
4053
4054 if self.flow_level == 0 {
4055 self.allow_simple_key();
4056 } else {
4057 self.disallow_simple_key();
4058 }
4059
4060 self.skip_non_blank();
4061 let end_mark = self.mark;
4062 let token_index = self.tokens.len();
4063 self.explicit_key_tab_check_pending = false;
4064 let stopped_after_comment = self.skip_yaml_whitespace()?;
4065 if self.input.peek() == '\t' {
4066 return Err(self.scan_error(ErrorKind::TabNotAllowed));
4067 }
4068 self.explicit_key_tab_check_pending = stopped_after_comment;
4069 self.insert_token(
4070 token_index,
4071 Token(Span::new(start_mark, end_mark), TokenType::Key),
4072 );
4073 Ok(())
4074 }
4075
4076 fn fetch_flow_value(&mut self) -> ScanResult {
4084 let nc = self.input.peek_nth(1);
4085
4086 if self.mark.index() != self.adjacent_value_allowed_at && (nc == '[' || nc == '{') {
4098 return Err(self.scan_error(ErrorKind::FlowMappingValueAdjacentCollection));
4099 }
4100
4101 self.fetch_value()
4102 }
4103
4104 fn fetch_value(&mut self) -> ScanResult {
4106 let sk = *self.simple_keys.last().unwrap();
4107 let start_mark = self.mark;
4108 let is_implicit_flow_mapping = self.current_flow_collection_is_sequence()
4109 && !self.current_flow_mapping_started()
4110 && !self.implicit_flow_mapping_states.is_empty();
4111 if is_implicit_flow_mapping {
4112 *self.implicit_flow_mapping_states.last_mut().unwrap() =
4113 ImplicitMappingState::Inside(self.flow_level);
4114 }
4115
4116 self.skip_non_blank();
4118 let mut trailing_tokens = VecDeque::new();
4121 let mut found_tabs = false;
4122 if matches!(self.input.look_ch(), ' ' | '\t') {
4123 let trailing_token_index = self.tokens.len();
4124 found_tabs = self.skip_ws_to_eol(SkipTabs::Yes)?.found_tabs();
4125 trailing_tokens = self.tokens.split_off(trailing_token_index);
4126 }
4127
4128 if sk.possible {
4129 let block_indent = self
4133 .indents
4134 .last()
4135 .filter(|indent| !indent.needs_block_end)
4136 .map_or(self.indent, |indent| indent.indent);
4137 if self.flow_level > 0
4138 && sk.mark.line < start_mark.line
4139 && (sk.mark.col as isize) <= block_indent
4140 {
4141 return Err(ScanError::from_kind(
4142 start_mark,
4143 ErrorKind::InvalidColonPlacement,
4144 ));
4145 }
4146 let token_index = self.simple_key_token_index(&sk, start_mark)?;
4147 self.validate_flow_key_indentation(&sk, token_index, start_mark)?;
4148 let tok = Token(Span::empty(sk.mark), TokenType::Key);
4150 self.insert_token(token_index, tok);
4151 if is_implicit_flow_mapping {
4152 if sk.mark.line < start_mark.line {
4153 return Err(ScanError::from_kind(
4154 start_mark,
4155 ErrorKind::InvalidColonPlacement,
4156 ));
4157 }
4158 self.insert_token(
4159 token_index,
4160 Token(Span::empty(sk.mark), TokenType::FlowMappingStart),
4161 );
4162 }
4163
4164 self.roll_indent(
4166 sk.mark.col,
4167 Some(sk.token_number),
4168 TokenType::BlockMappingStart,
4169 sk.mark,
4170 )?;
4171 self.roll_one_col_indent();
4172
4173 self.simple_keys.last_mut().unwrap().possible = false;
4174 self.disallow_simple_key();
4175 } else {
4176 if is_implicit_flow_mapping {
4177 self.tokens
4178 .push_back(Token(Span::empty(start_mark), TokenType::FlowMappingStart).into());
4179 }
4180 if self.flow_level == 0 {
4182 if !self.simple_key_allowed {
4183 return Err(ScanError::from_kind(
4184 start_mark,
4185 ErrorKind::MappingValueNotAllowed,
4186 ));
4187 }
4188
4189 self.roll_indent(
4190 start_mark.col,
4191 None,
4192 TokenType::BlockMappingStart,
4193 start_mark,
4194 )?;
4195 }
4196 self.roll_one_col_indent();
4197
4198 if self.flow_level == 0 && !found_tabs {
4201 self.allow_simple_key();
4202 } else {
4203 self.disallow_simple_key();
4204 }
4205 }
4206 self.tokens
4207 .push_back(Token(Span::empty(start_mark), TokenType::Value).into());
4208 self.tokens.append(&mut trailing_tokens);
4209
4210 Ok(())
4211 }
4212
4213 fn validate_flow_key_indentation(
4215 &self,
4216 key: &SimpleKey,
4217 token_index: usize,
4218 colon: Marker,
4219 ) -> ScanResult {
4220 if self.flow_level == 0 || key.mark.line == colon.line {
4221 return Ok(());
4222 }
4223
4224 let block_indent = self
4225 .indents
4226 .last()
4227 .filter(|indent| !indent.needs_block_end)
4228 .map_or(self.indent, |indent| indent.indent);
4229 let mut under_indented = [key.mark.col, colon.col]
4233 .iter()
4234 .any(|&col| (col as isize) <= block_indent);
4235 for token in self.tokens.iter().skip(token_index) {
4236 if matches!(token.1, QueuedTokenType::Comment(_)) {
4237 continue;
4238 }
4239 under_indented |= (token.0.start.col as isize) <= block_indent;
4240 if !matches!(
4241 token.1,
4242 QueuedTokenType::Anchor(_) | QueuedTokenType::Tag(..)
4243 ) {
4244 break;
4245 }
4246 }
4247 if under_indented {
4248 return Err(ScanError::from_kind(
4249 colon,
4250 ErrorKind::InvalidColonPlacement,
4251 ));
4252 }
4253 Ok(())
4254 }
4255
4256 fn roll_indent(
4262 &mut self,
4263 col: usize,
4264 number: Option<usize>,
4265 tok: TokenType<'input>,
4266 mark: Marker,
4267 ) -> ScanResult {
4268 if self.flow_level > 0 {
4269 return Ok(());
4270 }
4271
4272 if self.indent <= col as isize {
4276 if let Some(indent) = self.indents.last() {
4277 if !indent.needs_block_end {
4278 self.indent = indent.indent;
4279 self.indents.pop();
4280 }
4281 }
4282 }
4283
4284 if self.indent < col as isize {
4285 if self.block_level >= self.options.block_nesting_limit {
4286 return Err(ScanError::from_kind(
4287 mark,
4288 ErrorKind::RecursionLimitExceeded,
4289 ));
4290 }
4291 self.indents.push(Indent {
4292 indent: self.indent,
4293 needs_block_end: true,
4294 });
4295 self.block_level += 1;
4296 self.indent = col as isize;
4297 let tokens_parsed = self.tokens_parsed;
4298 match number {
4299 Some(n) => self.insert_token(n - tokens_parsed, Token(Span::empty(mark), tok)),
4300 None => self.tokens.push_back(Token(Span::empty(mark), tok).into()),
4301 }
4302 }
4303 Ok(())
4304 }
4305
4306 fn unroll_indent(&mut self, col: isize) {
4312 if self.flow_level > 0 {
4313 return;
4314 }
4315 while self.indent > col {
4316 let indent = self.indents.pop().unwrap();
4317 self.indent = indent.indent;
4318 if indent.needs_block_end {
4319 debug_assert!(self.block_level > 0);
4320 self.block_level -= 1;
4321 self.tokens
4322 .push_back(Token(Span::empty(self.mark), TokenType::BlockEnd).into());
4323 }
4324 }
4325 }
4326
4327 fn roll_one_col_indent(&mut self) {
4333 if self.flow_level == 0 && self.indents.last().is_some_and(|x| x.needs_block_end) {
4334 self.indents.push(Indent {
4335 indent: self.indent,
4336 needs_block_end: false,
4337 });
4338 self.indent += 1;
4339 }
4340 }
4341
4342 fn unroll_non_block_indents(&mut self) {
4344 while let Some(indent) = self.indents.last() {
4345 if indent.needs_block_end {
4346 break;
4347 }
4348 self.indent = indent.indent;
4349 self.indents.pop();
4350 }
4351 }
4352
4353 fn save_simple_key(&mut self) {
4355 if self.simple_key_allowed {
4356 let required = self.flow_level == 0
4357 && self.indent == (self.mark.col as isize)
4358 && self.indents.last().unwrap().needs_block_end;
4359
4360 if let Some(last) = self.simple_keys.last_mut() {
4361 *last = SimpleKey {
4362 mark: self.mark,
4363 possible: true,
4364 required,
4365 token_number: self.tokens_parsed + self.tokens.len(),
4366 };
4367 }
4368 }
4369 }
4370
4371 fn remove_simple_key(&mut self) -> ScanResult {
4372 let last = self.simple_keys.last_mut().unwrap();
4373 if last.possible && last.required {
4374 return Err(Self::simple_key_expected(last.mark));
4375 }
4376
4377 last.possible = false;
4378 Ok(())
4379 }
4380
4381 fn is_within_block(&self) -> bool {
4383 !self.indents.is_empty()
4384 }
4385
4386 fn end_implicit_mapping(&mut self, mark: Marker, flow_level: usize) {
4392 if self
4393 .implicit_flow_mapping_states
4394 .last()
4395 .is_some_and(|state| *state == ImplicitMappingState::Inside(flow_level))
4396 {
4397 *self.implicit_flow_mapping_states.last_mut().unwrap() = ImplicitMappingState::Possible;
4398 self.set_current_flow_mapping_started(false);
4399 self.tokens
4400 .push_back(Token(Span::empty(mark), TokenType::FlowMappingEnd).into());
4401 }
4402 }
4403
4404 fn current_flow_collection_is_sequence(&self) -> bool {
4405 self.flow_markers
4406 .last()
4407 .is_some_and(|(_, bracket)| *bracket == '[')
4408 }
4409
4410 fn current_flow_mapping_started(&self) -> bool {
4411 self.flow_mapping_started.last().copied().unwrap_or(false)
4412 }
4413
4414 fn set_current_flow_mapping_started(&mut self, started: bool) {
4415 if let Some(current) = self.flow_mapping_started.last_mut() {
4416 *current = started;
4417 }
4418 }
4419}
4420
4421#[derive(PartialEq, Eq)]
4425enum Chomping {
4426 Strip,
4428 Clip,
4430 Keep,
4432}
4433
4434#[cfg(test)]
4435mod test {
4436 use alloc::{
4437 borrow::{Cow, ToOwned},
4438 rc::Rc,
4439 string::String,
4440 vec,
4441 vec::Vec,
4442 };
4443 use core::cell::Cell;
4444
4445 use crate::error::{ErrorKind, ScanError};
4446 use crate::{
4447 input::{str::StrInput, BorrowedInput, BufferedInput, Input},
4448 scanner::{
4449 Comment, Marker, Placement, QueuedToken, QueuedTokenType, ScalarStyle, Scanner, Span,
4450 Token, TokenType,
4451 },
4452 };
4453
4454 struct CountingChars {
4455 chars: alloc::vec::IntoIter<char>,
4456 read: Rc<Cell<usize>>,
4457 }
4458
4459 impl Iterator for CountingChars {
4460 type Item = char;
4461
4462 fn next(&mut self) -> Option<Self::Item> {
4463 let next = self.chars.next();
4464 if next.is_some() {
4465 self.read.set(self.read.get() + 1);
4466 }
4467 next
4468 }
4469 }
4470
4471 struct SlicingOnlyInput<'input> {
4472 inner: StrInput<'input>,
4473 expose_slice: bool,
4474 }
4475
4476 impl<'input> SlicingOnlyInput<'input> {
4477 fn new(source: &'input str, expose_slice: bool) -> Self {
4478 Self {
4479 inner: StrInput::new(source),
4480 expose_slice,
4481 }
4482 }
4483 }
4484
4485 impl Input for SlicingOnlyInput<'_> {
4486 fn lookahead(&mut self, count: usize) {
4487 self.inner.lookahead(count);
4488 }
4489
4490 fn buflen(&self) -> usize {
4491 self.inner.buflen()
4492 }
4493
4494 fn bufmaxlen(&self) -> usize {
4495 self.inner.bufmaxlen()
4496 }
4497
4498 fn raw_read_ch(&mut self) -> char {
4499 self.inner.raw_read_ch()
4500 }
4501
4502 fn raw_read_non_breakz_ch(&mut self) -> Option<char> {
4503 self.inner.raw_read_non_breakz_ch()
4504 }
4505
4506 fn skip(&mut self) {
4507 self.inner.skip();
4508 }
4509
4510 fn skip_n(&mut self, count: usize) {
4511 self.inner.skip_n(count);
4512 }
4513
4514 fn peek(&self) -> char {
4515 self.inner.peek()
4516 }
4517
4518 fn peek_nth(&self, n: usize) -> char {
4519 self.inner.peek_nth(n)
4520 }
4521
4522 fn byte_offset(&self) -> Option<usize> {
4523 self.inner.byte_offset()
4524 }
4525
4526 fn slice_bytes(&self, start: usize, end: usize) -> Option<&str> {
4527 if self.expose_slice {
4528 self.inner.slice_bytes(start, end)
4529 } else {
4530 None
4531 }
4532 }
4533 }
4534
4535 impl<'input> BorrowedInput<'input> for SlicingOnlyInput<'input> {
4536 fn slice_borrowed(&self, _start: usize, _end: usize) -> Option<&'input str> {
4537 None
4538 }
4539 }
4540
4541 struct SmallReportedBufferInput<'input> {
4542 inner: StrInput<'input>,
4543 reported_bufmaxlen: usize,
4544 }
4545
4546 impl<'input> SmallReportedBufferInput<'input> {
4547 fn new(source: &'input str, reported_bufmaxlen: usize) -> Self {
4548 Self {
4549 inner: StrInput::new(source),
4550 reported_bufmaxlen,
4551 }
4552 }
4553 }
4554
4555 impl Input for SmallReportedBufferInput<'_> {
4556 fn lookahead(&mut self, count: usize) {
4557 self.inner.lookahead(count);
4558 }
4559
4560 fn buflen(&self) -> usize {
4561 self.inner.buflen()
4562 }
4563
4564 fn bufmaxlen(&self) -> usize {
4565 self.reported_bufmaxlen
4566 }
4567
4568 fn raw_read_ch(&mut self) -> char {
4569 self.inner.raw_read_ch()
4570 }
4571
4572 fn raw_read_non_breakz_ch(&mut self) -> Option<char> {
4573 self.inner.raw_read_non_breakz_ch()
4574 }
4575
4576 fn skip(&mut self) {
4577 self.inner.skip();
4578 }
4579
4580 fn skip_n(&mut self, count: usize) {
4581 self.inner.skip_n(count);
4582 }
4583
4584 fn peek(&self) -> char {
4585 self.inner.peek()
4586 }
4587
4588 fn peek_nth(&self, n: usize) -> char {
4589 self.inner.peek_nth(n)
4590 }
4591 }
4592
4593 impl<'input> BorrowedInput<'input> for SmallReportedBufferInput<'input> {
4594 fn slice_borrowed(&self, start: usize, end: usize) -> Option<&'input str> {
4595 self.inner.slice_borrowed(start, end)
4596 }
4597 }
4598
4599 #[test]
4600 fn anchor_character_set_allows_colon_and_rejects_flow_indicators() {
4601 use super::is_anchor_char;
4602
4603 assert!(is_anchor_char('x'));
4604 assert!(is_anchor_char('-'));
4605 assert!(is_anchor_char('_'));
4606 assert!(is_anchor_char(':'));
4607 assert!(is_anchor_char('#'));
4608 assert!(is_anchor_char('/'));
4609 assert!(is_anchor_char('?'));
4610
4611 for c in [',', '[', ']', '{', '}', ' ', '\t', '\n', '\r', '\0'] {
4612 assert!(
4613 !is_anchor_char(c),
4614 "character {c:?} must not be accepted in anchor/alias names"
4615 );
4616 }
4617 }
4618
4619 #[test]
4620 fn flow_simple_key_length_limit_bounds_buffering() {
4621 let mut yaml = String::from("[\n\"start\"\n");
4622 for _ in 0..600 {
4623 yaml.push_str("\"x\"\n");
4624 }
4625 let total_chars = yaml.chars().count();
4626 let read = Rc::new(Cell::new(0));
4627 let chars = yaml.chars().collect::<Vec<_>>().into_iter();
4628 let mut scanner = Scanner::new(BufferedInput::new(CountingChars {
4629 chars,
4630 read: Rc::clone(&read),
4631 }));
4632
4633 assert!(matches!(
4634 scanner.next_token().unwrap().unwrap().1,
4635 TokenType::StreamStart
4636 ));
4637
4638 let token = scanner.next_token().unwrap().unwrap();
4639 assert!(matches!(token.1, TokenType::FlowSequenceStart));
4640
4641 let token = scanner.next_token().unwrap().unwrap();
4642 assert!(matches!(
4643 token.1,
4644 TokenType::Scalar(_, ref value) if value == "start"
4645 ));
4646 assert!(
4647 read.get() < total_chars,
4648 "scanner consumed all {total_chars} chars before yielding the first flow scalar"
4649 );
4650 assert!(
4651 read.get() <= scanner.options.simple_key_max_lookahead + 128,
4652 "scanner read {} chars before yielding the first flow scalar",
4653 read.get()
4654 );
4655 }
4656
4657 #[test]
4658 fn block_scalar_indent_tolerates_small_reported_bufmaxlen() {
4659 let mut scanner = Scanner::new(SmallReportedBufferInput::new("|\n value\n", 0));
4660
4661 let scalar = scanner
4662 .find_map(
4663 |token| match token.expect("valid YAML should scan without errors") {
4664 Token(_, TokenType::Scalar(ScalarStyle::Literal, value)) => {
4665 Some(value.into_owned())
4666 }
4667 _ => None,
4668 },
4669 )
4670 .expect("expected block scalar token");
4671
4672 assert_eq!(scalar, "value\n");
4673 }
4674
4675 #[test]
4676 fn plain_scalar_chunk_tolerates_small_reported_bufmaxlen() {
4677 let mut scanner = Scanner::new(SmallReportedBufferInput::new("plain\n", 0));
4678
4679 let scalar = scanner
4680 .find_map(
4681 |token| match token.expect("valid YAML should scan without errors") {
4682 Token(_, TokenType::Scalar(ScalarStyle::Plain, value)) => {
4683 Some(value.into_owned())
4684 }
4685 _ => None,
4686 },
4687 )
4688 .expect("expected plain scalar token");
4689
4690 assert_eq!(scalar, "plain");
4691 }
4692
4693 fn first_token_slice(
4694 yaml: &str,
4695 matches_token: impl Fn(&TokenType<'_>) -> bool,
4696 ) -> Option<String> {
4697 let mut scanner = Scanner::new(StrInput::new(yaml));
4698
4699 loop {
4700 let token = scanner
4701 .next_token()
4702 .expect("scanner should accept the test YAML")?;
4703 if matches_token(&token.1) {
4704 return token.0.slice(yaml).map(ToOwned::to_owned);
4705 }
4706 }
4707 }
4708
4709 #[test]
4710 fn flow_indicator_token_spans_cover_only_the_indicator() {
4711 assert_eq!(
4712 first_token_slice("[ # c\n a]\n", |token| matches!(
4713 token,
4714 TokenType::FlowSequenceStart
4715 ))
4716 .as_deref(),
4717 Some("[")
4718 );
4719 assert_eq!(
4720 first_token_slice("{ # c\n a: b}\n", |token| matches!(
4721 token,
4722 TokenType::FlowMappingStart
4723 ))
4724 .as_deref(),
4725 Some("{")
4726 );
4727 assert_eq!(
4728 first_token_slice("[a] # c\n", |token| matches!(
4729 token,
4730 TokenType::FlowSequenceEnd
4731 ))
4732 .as_deref(),
4733 Some("]")
4734 );
4735 assert_eq!(
4736 first_token_slice("{a: b} # c\n", |token| matches!(
4737 token,
4738 TokenType::FlowMappingEnd
4739 ))
4740 .as_deref(),
4741 Some("}")
4742 );
4743 assert_eq!(
4744 first_token_slice("[a, # c\nb]\n", |token| matches!(
4745 token,
4746 TokenType::FlowEntry
4747 ))
4748 .as_deref(),
4749 Some(",")
4750 );
4751 }
4752
4753 #[test]
4754 fn explicit_key_token_span_covers_only_the_indicator() {
4755 assert_eq!(
4756 first_token_slice("? # c\n: value\n", |token| matches!(token, TokenType::Key))
4757 .as_deref(),
4758 Some("?")
4759 );
4760 }
4761
4762 #[test]
4763 fn comment_capture_does_not_change_leading_whitespace() {
4764 let mut scanner = Scanner::new(StrInput::new("# comment\n"));
4765
4766 let token = scanner.scan_comment_token().unwrap();
4767
4768 assert!(scanner.leading_whitespace);
4769 assert!(matches!(token.1, TokenType::Comment(ref comment) if comment.text == " comment"));
4770
4771 let mut scanner = Scanner::new(BufferedInput::new("# streaming\n".chars()));
4772 scanner.input.lookahead(1);
4773
4774 let token = scanner.scan_comment_token().unwrap();
4775
4776 assert!(scanner.leading_whitespace);
4777 assert!(matches!(token.1, TokenType::Comment(ref comment) if comment.text == " streaming"));
4778 }
4779
4780 #[test]
4781 fn comment_capture_falls_back_to_owned_slice_when_borrow_unavailable() {
4782 let mut scanner = Scanner::new(SlicingOnlyInput::new("# sliced\n", true));
4783 scanner.input.lookahead(2);
4784 assert_eq!(scanner.input.peek_nth(1), ' ');
4785
4786 let token = scanner.scan_comment_token().unwrap();
4787
4788 assert!(matches!(token.1, TokenType::Comment(ref comment)
4789 if matches!(comment.text, Cow::Owned(ref text) if text == " sliced")));
4790 }
4791
4792 #[test]
4793 fn comment_capture_errors_when_offsets_have_no_slice() {
4794 let mut scanner = Scanner::new(SlicingOnlyInput::new("# broken\n", false));
4795
4796 let error = scanner.scan_comment_token().unwrap_err();
4797
4798 assert_eq!(error.kind(), &ErrorKind::InputOffsetsWithoutSlice);
4799 }
4800
4801 #[test]
4802 fn disabled_comments_do_not_require_input_slicing() {
4803 let options = crate::options! { emit_comments: false };
4804 let scanner = Scanner::with_options(
4805 SlicingOnlyInput::new("# ignored\nkey: value\n", false),
4806 options,
4807 );
4808
4809 let tokens = scanner
4810 .collect::<Result<Vec<_>, _>>()
4811 .expect("ignored comments should not capture their payload");
4812
4813 assert!(tokens
4814 .iter()
4815 .all(|token| !matches!(token.1, TokenType::Comment(_))));
4816 }
4817
4818 #[test]
4819 fn queued_token_roundtrips_public_token_variants() {
4820 let span = Span::new(Marker::new(0, 1, 0), Marker::new(7, 1, 7));
4821 let tokens = [
4822 Token(span, TokenType::StreamStart),
4823 Token(span, TokenType::StreamEnd),
4824 Token(span, TokenType::VersionDirective(1, 2)),
4825 Token(
4826 span,
4827 TokenType::TagDirective(Cow::Borrowed("!app!"), Cow::Borrowed("tag:app.example,")),
4828 ),
4829 Token(span, TokenType::DocumentStart),
4830 Token(span, TokenType::DocumentEnd),
4831 Token(span, TokenType::BlockSequenceStart),
4832 Token(span, TokenType::BlockMappingStart),
4833 Token(span, TokenType::BlockEnd),
4834 Token(span, TokenType::FlowSequenceStart),
4835 Token(span, TokenType::FlowSequenceEnd),
4836 Token(span, TokenType::FlowMappingStart),
4837 Token(span, TokenType::FlowMappingEnd),
4838 Token(span, TokenType::BlockEntry),
4839 Token(span, TokenType::FlowEntry),
4840 Token(span, TokenType::Key),
4841 Token(span, TokenType::Value),
4842 Token(span, TokenType::Alias(Cow::Borrowed("alias"))),
4843 Token(span, TokenType::Anchor(Cow::Borrowed("anchor"))),
4844 Token(
4845 span,
4846 TokenType::Tag(Cow::Borrowed("!"), Cow::Borrowed("tag")),
4847 ),
4848 Token(
4849 span,
4850 TokenType::Scalar(ScalarStyle::Literal, Cow::Borrowed("scalar")),
4851 ),
4852 Token(
4853 span,
4854 TokenType::Comment(
4855 Comment::new(Cow::Borrowed(" comment")).with_placement(Placement::Right),
4856 ),
4857 ),
4858 Token(
4859 span,
4860 TokenType::ReservedDirective(
4861 "reserved".to_owned(),
4862 vec!["one".to_owned(), "two".to_owned()],
4863 ),
4864 ),
4865 ];
4866
4867 for token in tokens {
4868 let queued: QueuedToken = token.clone().into();
4869
4870 assert_eq!(queued.into_public(), token);
4871 }
4872 }
4873
4874 #[test]
4875 fn comment_skipping_path_consumes_comment_without_tokenizing_it() {
4876 let mut scanner = Scanner::new(StrInput::new("# skipped\nnext: value\n"));
4877
4878 scanner.skip_yaml_whitespace().unwrap();
4879
4880 assert!(scanner.tokens.is_empty());
4881 assert_eq!(scanner.mark.line(), 2);
4882 assert_eq!(scanner.mark.col(), 0);
4883 }
4884
4885 #[test]
4886 fn yaml_whitespace_can_stop_after_queued_comment() {
4887 let mut scanner = Scanner::new(StrInput::new(" # queued\n# later\n"));
4888
4889 assert!(scanner.skip_yaml_whitespace().unwrap());
4890
4891 assert_eq!(scanner.tokens.len(), 1);
4892 assert!(matches!(
4893 scanner.tokens.front().unwrap().1,
4894 QueuedTokenType::Comment(ref comment) if comment.text == " queued"
4895 ));
4896 assert_eq!(scanner.mark.line(), 1);
4897 assert_eq!(scanner.mark.col(), 9);
4898 }
4899
4900 #[test]
4901 fn token_skip_can_stop_after_queued_comment() {
4902 let mut scanner = Scanner::new(StrInput::new("# first\n# second\n"));
4903
4904 assert!(scanner.skip_to_next_token().unwrap().queued_comment);
4905
4906 assert_eq!(scanner.tokens.len(), 1);
4907 assert!(matches!(
4908 scanner.tokens.front().unwrap().1,
4909 QueuedTokenType::Comment(ref comment) if comment.text == " first"
4910 ));
4911 assert_eq!(scanner.mark.line(), 2);
4912 assert_eq!(scanner.mark.col(), 0);
4913 }
4914
4915 #[test]
4916 fn token_skip_stops_after_one_ignored_comment_without_queuing_it() {
4917 let options = crate::options! { emit_comments: false };
4918 let mut scanner =
4919 Scanner::with_options(StrInput::new("# first\n# second\nvalue\n"), options);
4920
4921 let outcome = scanner.skip_to_next_token().unwrap();
4922
4923 assert!(outcome.saw_comment);
4924 assert!(!outcome.queued_comment);
4925 assert!(scanner.tokens.is_empty());
4926 assert_eq!((scanner.mark.line(), scanner.mark.col()), (2, 0));
4927 assert_eq!(scanner.input.look_ch(), '#');
4928 }
4929
4930 #[test]
4931 fn yaml_whitespace_stops_after_one_separated_ignored_comment() {
4932 let options = crate::options! { emit_comments: false };
4933 let mut scanner =
4934 Scanner::with_options(StrInput::new(" # first\n# second\nvalue\n"), options);
4935
4936 assert!(scanner.skip_yaml_whitespace().unwrap());
4937
4938 assert!(scanner.tokens.is_empty());
4939 assert_eq!((scanner.mark.line(), scanner.mark.col()), (1, 8));
4940 assert_eq!(scanner.input.look_ch(), '\n');
4941 }
4942
4943 #[test]
4944 fn scanner_skips_consecutive_leading_ignored_comments_before_next_token() {
4945 let options = crate::options! { emit_comments: false };
4946 let mut scanner =
4947 Scanner::with_options(StrInput::new("# first\n# second\nvalue\n"), options);
4948
4949 assert!(matches!(
4950 scanner.next_token().unwrap().unwrap().1,
4951 TokenType::StreamStart
4952 ));
4953 assert!(matches!(
4954 scanner.next_token().unwrap().unwrap().1,
4955 TokenType::Scalar(ScalarStyle::Plain, ref value) if value == "value"
4956 ));
4957 assert!(scanner
4958 .tokens
4959 .iter()
4960 .all(|QueuedToken(_, token)| !matches!(token, QueuedTokenType::Comment(_))));
4961 }
4962
4963 #[test]
4964 fn scanner_emits_first_leading_comment_before_scanning_next_comment() {
4965 let mut scanner = Scanner::new(StrInput::new("# first\n# second\nkey: value\n"));
4966
4967 assert!(matches!(
4968 scanner.next_token().unwrap().unwrap().1,
4969 TokenType::StreamStart
4970 ));
4971 assert!(matches!(
4972 scanner.next_token().unwrap().unwrap().1,
4973 TokenType::Comment(ref comment) if comment.text == " first"
4974 ));
4975 assert!(scanner.tokens.is_empty());
4976 assert!(matches!(
4977 scanner.next_token().unwrap().unwrap().1,
4978 TokenType::Comment(ref comment) if comment.text == " second"
4979 ));
4980 }
4981
4982 #[test]
4983 fn scanner_emits_quoted_scalar_comment_before_scanning_following_value() {
4984 let mut scanner = Scanner::new(StrInput::new("\"key\" # quoted\n: value\n"));
4985
4986 assert!(matches!(
4987 scanner.next_token().unwrap().unwrap().1,
4988 TokenType::StreamStart
4989 ));
4990 assert!(matches!(
4991 scanner.next_token().unwrap().unwrap().1,
4992 TokenType::Scalar(ScalarStyle::DoubleQuoted, ref value) if value == "key"
4993 ));
4994 assert!(matches!(
4995 scanner.next_token().unwrap().unwrap().1,
4996 TokenType::Comment(ref comment) if comment.text == " quoted"
4997 ));
4998 }
4999
5000 #[test]
5001 fn flow_scalar_comment_disables_adjacent_value_lookahead() {
5002 let mut scanner = Scanner::new(StrInput::new("\"key\"\n# quoted\n: value\n"));
5003
5004 scanner.fetch_flow_scalar(false).unwrap();
5005
5006 assert_eq!(scanner.adjacent_value_allowed_at, usize::MAX);
5007 assert!(matches!(
5008 scanner.tokens.front().unwrap().1,
5009 QueuedTokenType::Scalar(ScalarStyle::DoubleQuoted, ref value) if value == "key"
5010 ));
5011 assert!(scanner.tokens.iter().any(|QueuedToken(_, token)| matches!(
5012 token,
5013 QueuedTokenType::Comment(comment) if comment.text == " quoted"
5014 )));
5015 }
5016
5017 #[test]
5018 fn ignored_flow_scalar_comment_still_disables_adjacent_value_lookahead() {
5019 let options = crate::options! { emit_comments: false };
5020 let mut scanner =
5021 Scanner::with_options(StrInput::new("\"key\"\n# ignored\n: value\n"), options);
5022
5023 scanner.fetch_flow_scalar(false).unwrap();
5024
5025 assert_eq!(scanner.adjacent_value_allowed_at, usize::MAX);
5026 assert!(scanner
5027 .tokens
5028 .iter()
5029 .all(|QueuedToken(_, token)| { !matches!(token, QueuedTokenType::Comment(_)) }));
5030 }
5031
5032 #[test]
5033 fn deferred_error_waits_for_all_comment_tokens() {
5034 let mut scanner = Scanner::new(StrInput::new("# first\n# second\n@\n"));
5035
5036 assert!(matches!(
5037 scanner.next_token().unwrap().unwrap().1,
5038 TokenType::StreamStart
5039 ));
5040 assert!(matches!(
5041 scanner.next_token().unwrap().unwrap().1,
5042 TokenType::Comment(ref comment) if comment.text == " first"
5043 ));
5044 assert!(matches!(
5045 scanner.next_token().unwrap().unwrap().1,
5046 TokenType::Comment(ref comment) if comment.text == " second"
5047 ));
5048
5049 let error = scanner.next_token().unwrap_err();
5050
5051 assert_eq!(
5052 error.kind(),
5053 &ErrorKind::UnexpectedCharacter { character: '@' }
5054 );
5055 }
5056
5057 #[test]
5059 fn anchor_name_is_borrowed_for_str_input() {
5060 let mut scanner = Scanner::new(StrInput::new("&anch\n"));
5061
5062 loop {
5063 let tok = scanner
5064 .next_token()
5065 .expect("valid YAML must scan without errors")
5066 .expect("scanner must eventually produce a token");
5067 if let TokenType::Anchor(name) = tok.1 {
5068 assert!(matches!(name, Cow::Borrowed("anch")));
5069 break;
5070 }
5071 }
5072 }
5073
5074 #[test]
5075 fn anchor_name_rejects_non_printable_control_chars() {
5076 let mut scanner = Scanner::new(StrInput::new("&foo\u{0001}\n"));
5077
5078 scanner.next_token().unwrap();
5079 assert_eq!(
5080 scanner.next_token().unwrap_err().kind(),
5081 &ErrorKind::UnexpectedCharacter {
5082 character: '\u{0001}'
5083 }
5084 );
5085 }
5086
5087 #[test]
5088 fn alias_name_rejects_non_printable_control_chars() {
5089 let mut scanner = Scanner::new(StrInput::new("*foo\u{0001}\n"));
5090
5091 scanner.next_token().unwrap();
5092 assert_eq!(
5093 scanner.next_token().unwrap_err().kind(),
5094 &ErrorKind::UnexpectedCharacter {
5095 character: '\u{0001}'
5096 }
5097 );
5098 }
5099
5100 #[test]
5101 fn alias_name_is_borrowed_for_str_input() {
5102 let mut scanner = Scanner::new(StrInput::new("*anch\n"));
5103
5104 loop {
5105 let tok = scanner
5106 .next_token()
5107 .expect("valid YAML must scan without errors")
5108 .expect("scanner must eventually produce a token");
5109 if let TokenType::Alias(name) = tok.1 {
5110 assert!(matches!(name, Cow::Borrowed("anch")));
5111 break;
5112 }
5113 }
5114 }
5115
5116 #[test]
5117 fn alias_name_scans_colon_as_part_of_name() {
5118 let mut scanner = Scanner::new(StrInput::new("*foo: bar\n"));
5119
5120 loop {
5121 let tok = scanner
5122 .next_token()
5123 .expect("scanner must not fail before alias token")
5124 .expect("scanner must eventually emit an alias token");
5125
5126 if let TokenType::Alias(name) = tok.1 {
5127 assert_eq!(name.as_ref(), "foo:");
5128 break;
5129 }
5130 }
5131 }
5132
5133 #[test]
5134 fn anchor_name_scans_colon_as_part_of_name() {
5135 let mut scanner = Scanner::new(StrInput::new("&foo: bar\n"));
5136
5137 loop {
5138 let tok = scanner
5139 .next_token()
5140 .expect("scanner must not fail before anchor token")
5141 .expect("scanner must eventually emit an anchor token");
5142
5143 if let TokenType::Anchor(name) = tok.1 {
5144 assert_eq!(name.as_ref(), "foo:");
5145 break;
5146 }
5147 }
5148 }
5149
5150 #[test]
5152 fn tag_directive_parts_are_borrowed_for_str_input() {
5153 let mut scanner = Scanner::new(StrInput::new("%TAG !e! tag:example.com,2000:app/\n"));
5154
5155 loop {
5156 let tok = scanner
5157 .next_token()
5158 .expect("valid YAML must scan without errors")
5159 .expect("scanner must eventually produce a token");
5160 if let TokenType::TagDirective(handle, prefix) = tok.1 {
5161 assert!(matches!(handle, Cow::Borrowed("!e!")));
5162 assert!(matches!(prefix, Cow::Borrowed("tag:example.com,2000:app/")));
5163 break;
5164 }
5165 }
5166 }
5167
5168 #[test]
5169 fn tag_directive_parts_are_owned_for_buffered_input() {
5170 let mut scanner = Scanner::new(BufferedInput::new(
5171 "%TAG !e! tag:example.com,2000:app/\n".chars(),
5172 ));
5173
5174 loop {
5175 let tok = scanner
5176 .next_token()
5177 .expect("valid YAML must scan without errors")
5178 .expect("scanner must eventually produce a token");
5179 if let TokenType::TagDirective(handle, prefix) = tok.1 {
5180 assert!(matches!(handle, Cow::Owned(_)));
5181 assert_eq!(&*handle, "!e!");
5182 assert!(matches!(prefix, Cow::Owned(_)));
5183 assert_eq!(&*prefix, "tag:example.com,2000:app/");
5184 break;
5185 }
5186 }
5187 }
5188
5189 #[test]
5190 fn buffered_tag_directive_decodes_prefix_escape() {
5191 let mut scanner = Scanner::new(BufferedInput::new(
5192 "%TAG !e! %74ag:example.com,2000:app/\n".chars(),
5193 ));
5194
5195 loop {
5196 let tok = scanner
5197 .next_token()
5198 .expect("valid YAML must scan without errors")
5199 .expect("scanner must eventually produce a token");
5200 if let TokenType::TagDirective(handle, prefix) = tok.1 {
5201 assert_eq!(&*handle, "!e!");
5202 assert!(matches!(prefix, Cow::Owned(_)));
5203 assert_eq!(&*prefix, "tag:example.com,2000:app/");
5204 break;
5205 }
5206 }
5207 }
5208
5209 #[test]
5210 fn local_tag_combines_handle_text_with_escaped_suffix() {
5211 let mut scanner = Scanner::new(StrInput::new("!foo%20bar value\n"));
5212
5213 loop {
5214 let tok = scanner
5215 .next_token()
5216 .expect("valid YAML must scan without errors")
5217 .expect("scanner must eventually produce a token");
5218 if let TokenType::Tag(handle, suffix) = tok.1 {
5219 assert!(matches!(handle, Cow::Borrowed("!")));
5220 assert!(matches!(suffix, Cow::Owned(_)));
5221 assert_eq!(&*suffix, "foo bar");
5222 break;
5223 }
5224 }
5225 }
5226
5227 #[test]
5228 fn secondary_tag_requires_suffix_in_borrowed_and_buffered_paths() {
5229 assert_eq!(
5230 first_scanner_error_kind("!! value\n"),
5231 ErrorKind::MissingTagUri
5232 );
5233 assert_eq!(
5234 first_buffered_scanner_error_kind("!! value\n"),
5235 ErrorKind::MissingTagUri
5236 );
5237 }
5238
5239 #[test]
5240 fn plain_scalar_is_borrowed_when_whitespace_free_for_str_input() {
5241 let mut scanner = Scanner::new(StrInput::new("foo\n"));
5242
5243 loop {
5244 let tok = scanner
5245 .next_token()
5246 .expect("valid YAML must scan without errors")
5247 .expect("scanner must eventually produce a token");
5248 if let TokenType::Scalar(_, value) = tok.1 {
5249 assert!(matches!(value, Cow::Borrowed("foo")));
5250 break;
5251 }
5252 }
5253 }
5254
5255 #[test]
5256 fn plain_scalar_is_borrowed_when_whitespace_present_for_str_input() {
5257 let mut scanner = Scanner::new(StrInput::new("foo bar\n"));
5258
5259 loop {
5260 let tok = scanner
5261 .next_token()
5262 .expect("valid YAML must scan without errors")
5263 .expect("scanner must eventually produce a token");
5264 if let TokenType::Scalar(_, value) = tok.1 {
5265 assert!(matches!(value, Cow::Borrowed("foo bar")));
5266 break;
5267 }
5268 }
5269 }
5270
5271 #[test]
5272 fn single_quoted_scalar_is_borrowed_when_verbatim_for_str_input() {
5273 let mut scanner = Scanner::new(StrInput::new("'foo bar'\n"));
5274
5275 loop {
5276 let tok = scanner
5277 .next_token()
5278 .expect("valid YAML must scan without errors")
5279 .expect("scanner must eventually produce a token");
5280 if let TokenType::Scalar(_, value) = tok.1 {
5281 assert!(matches!(value, Cow::Borrowed("foo bar")));
5282 break;
5283 }
5284 }
5285 }
5286
5287 #[test]
5288 fn single_quoted_scalar_is_owned_when_quote_is_escaped_for_str_input() {
5289 let mut scanner = Scanner::new(StrInput::new("'foo''bar'\n"));
5290
5291 loop {
5292 let tok = scanner
5293 .next_token()
5294 .expect("valid YAML must scan without errors")
5295 .expect("scanner must eventually produce a token");
5296 if let TokenType::Scalar(_, value) = tok.1 {
5297 assert!(matches!(value, Cow::Owned(_)));
5298 assert_eq!(&*value, "foo'bar");
5299 break;
5300 }
5301 }
5302 }
5303
5304 #[test]
5305 fn double_quoted_scalar_is_borrowed_when_verbatim_for_str_input() {
5306 let mut scanner = Scanner::new(StrInput::new("\"foo bar\"\n"));
5307
5308 loop {
5309 let tok = scanner
5310 .next_token()
5311 .expect("valid YAML must scan without errors")
5312 .expect("scanner must eventually produce a token");
5313 if let TokenType::Scalar(_, value) = tok.1 {
5314 assert!(matches!(value, Cow::Borrowed("foo bar")));
5315 break;
5316 }
5317 }
5318 }
5319
5320 #[test]
5321 fn double_quoted_scalar_is_owned_when_escape_sequence_present_for_str_input() {
5322 let mut scanner = Scanner::new(StrInput::new("\"foo\\nbar\"\n"));
5323
5324 loop {
5325 let tok = scanner
5326 .next_token()
5327 .expect("valid YAML must scan without errors")
5328 .expect("scanner must eventually produce a token");
5329 if let TokenType::Scalar(_, value) = tok.1 {
5330 assert!(matches!(value, Cow::Owned(_)));
5331 assert_eq!(&*value, "foo\nbar");
5332 break;
5333 }
5334 }
5335 }
5336
5337 #[test]
5338 fn plain_key_is_borrowed_for_str_input() {
5339 let mut scanner = Scanner::new(StrInput::new("mykey: value\n"));
5341
5342 let mut found_key = false;
5343 let mut key_value: Option<Cow<'_, str>> = None;
5344
5345 loop {
5346 let tok = scanner
5347 .next_token()
5348 .expect("valid YAML must scan without errors");
5349 let Some(tok) = tok else { break };
5350
5351 if matches!(tok.1, TokenType::Key) {
5352 found_key = true;
5353 } else if found_key {
5354 if let TokenType::Scalar(_, value) = tok.1 {
5355 key_value = Some(value);
5356 break;
5357 }
5358 }
5359 }
5360
5361 assert!(found_key, "expected to find a Key token");
5362 let key_value = key_value.expect("expected to find a scalar after Key token");
5363 assert!(
5364 matches!(key_value, Cow::Borrowed("mykey")),
5365 "key should be borrowed, got: {key_value:?}"
5366 );
5367 }
5368
5369 #[test]
5370 fn quoted_key_is_borrowed_when_verbatim_for_str_input() {
5371 let mut scanner = Scanner::new(StrInput::new("\"mykey\": value\n"));
5372
5373 let mut found_key = false;
5374 let mut key_value: Option<Cow<'_, str>> = None;
5375
5376 loop {
5377 let tok = scanner
5378 .next_token()
5379 .expect("valid YAML must scan without errors");
5380 let Some(tok) = tok else { break };
5381
5382 if matches!(tok.1, TokenType::Key) {
5383 found_key = true;
5384 } else if found_key {
5385 if let TokenType::Scalar(_, value) = tok.1 {
5386 key_value = Some(value);
5387 break;
5388 }
5389 }
5390 }
5391
5392 assert!(found_key, "expected to find a Key token");
5393 let key_value = key_value.expect("expected to find a scalar after Key token");
5394 assert!(
5395 matches!(key_value, Cow::Borrowed("mykey")),
5396 "quoted key should be borrowed when verbatim, got: {key_value:?}"
5397 );
5398 }
5399
5400 #[test]
5401 fn tag_handle_and_suffix_are_borrowed_for_str_input() {
5402 let mut scanner = Scanner::new(StrInput::new("!!str foo\n"));
5404
5405 loop {
5406 let tok = scanner
5407 .next_token()
5408 .expect("valid YAML must scan without errors")
5409 .expect("scanner must eventually produce a token");
5410 if let TokenType::Tag(handle, suffix) = tok.1 {
5411 assert!(
5412 matches!(handle, Cow::Borrowed("!!")),
5413 "tag handle should be borrowed, got: {handle:?}"
5414 );
5415 assert!(
5416 matches!(suffix, Cow::Borrowed("str")),
5417 "tag suffix should be borrowed, got: {suffix:?}"
5418 );
5419 break;
5420 }
5421 }
5422 }
5423
5424 #[test]
5425 fn local_tag_suffix_is_borrowed_for_str_input() {
5426 let mut scanner = Scanner::new(StrInput::new("!mytag foo\n"));
5428
5429 loop {
5430 let tok = scanner
5431 .next_token()
5432 .expect("valid YAML must scan without errors")
5433 .expect("scanner must eventually produce a token");
5434 if let TokenType::Tag(handle, suffix) = tok.1 {
5435 assert!(
5436 matches!(handle, Cow::Borrowed("!")),
5437 "local tag handle should be '!', got: {handle:?}"
5438 );
5439 assert!(
5440 matches!(suffix, Cow::Borrowed("mytag")),
5441 "local tag suffix should be borrowed, got: {suffix:?}"
5442 );
5443 break;
5444 }
5445 }
5446 }
5447
5448 #[test]
5449 fn local_tag_suffix_with_punctuation_is_borrowed_for_str_input() {
5450 let mut scanner = Scanner::new(StrInput::new("!mytag/part foo\n"));
5451
5452 loop {
5453 let tok = scanner
5454 .next_token()
5455 .expect("valid YAML must scan without errors")
5456 .expect("scanner must eventually produce a token");
5457 if let TokenType::Tag(handle, suffix) = tok.1 {
5458 assert!(matches!(handle, Cow::Borrowed("!")));
5459 assert!(matches!(suffix, Cow::Borrowed("mytag/part")));
5460 break;
5461 }
5462 }
5463 }
5464
5465 #[test]
5466 fn tag_with_uri_escape_is_owned_for_str_input() {
5467 let mut scanner = Scanner::new(StrInput::new("!!my%20tag foo\n"));
5469
5470 loop {
5471 let tok = scanner
5472 .next_token()
5473 .expect("valid YAML must scan without errors")
5474 .expect("scanner must eventually produce a token");
5475 if let TokenType::Tag(handle, suffix) = tok.1 {
5476 assert!(
5477 matches!(handle, Cow::Borrowed("!!")),
5478 "tag handle should still be borrowed, got: {handle:?}"
5479 );
5480 assert!(
5481 matches!(suffix, Cow::Owned(_)),
5482 "tag suffix with URI escape should be owned, got: {suffix:?}"
5483 );
5484 assert_eq!(&*suffix, "my tag");
5485 break;
5486 }
5487 }
5488 }
5489
5490 #[test]
5491 fn flow_scalar_buffer_tracks_pending_whitespace() {
5492 let mut borrowed = super::FlowScalarBuf::new_borrowed(2);
5493
5494 borrowed.note_pending_ws(5, 8);
5495 borrowed.commit_pending_ws();
5496 assert!(matches!(
5497 borrowed,
5498 super::FlowScalarBuf::Borrowed {
5499 end: 8,
5500 pending_ws_start: None,
5501 pending_ws_end: 8,
5502 ..
5503 }
5504 ));
5505
5506 borrowed.note_pending_ws(9, 11);
5507 borrowed.discard_pending_ws();
5508 assert!(matches!(
5509 borrowed,
5510 super::FlowScalarBuf::Borrowed {
5511 end: 8,
5512 pending_ws_start: None,
5513 pending_ws_end: 8,
5514 ..
5515 }
5516 ));
5517 assert!(borrowed.as_owned_mut().is_none());
5518
5519 let mut owned = super::FlowScalarBuf::new_owned();
5520 owned.as_owned_mut().unwrap().push_str("owned");
5521 assert!(matches!(owned, super::FlowScalarBuf::Owned(ref s) if s == "owned"));
5522 }
5523
5524 fn first_scanner_error_kind(input: &str) -> ErrorKind {
5525 first_scanner_error(input).kind().clone()
5526 }
5527
5528 fn first_buffered_scanner_error_kind(input: &str) -> ErrorKind {
5529 let mut scanner = Scanner::new(BufferedInput::new(input.chars()));
5530 loop {
5531 match scanner.next_token() {
5532 Ok(Some(_)) => {}
5533 Ok(None) => panic!("expected scanner error"),
5534 Err(error) => return error.kind().clone(),
5535 }
5536 }
5537 }
5538
5539 fn first_scanner_error(input: &str) -> ScanError {
5540 let mut scanner = Scanner::new(StrInput::new(input));
5541 loop {
5542 match scanner.next_token() {
5543 Ok(Some(_)) => {}
5544 Ok(None) => panic!("expected scanner error"),
5545 Err(error) => return error,
5546 }
5547 }
5548 }
5549
5550 fn first_scalar_value(input: &str) -> String {
5551 let mut scanner = Scanner::new(StrInput::new(input));
5552 loop {
5553 match scanner.next_token().expect("scanner should not error") {
5554 Some(Token(_, TokenType::Scalar(_, value))) => return value.into_owned(),
5555 Some(_) => {}
5556 None => panic!("expected scalar token"),
5557 }
5558 }
5559 }
5560
5561 fn first_buffered_scalar_value(input: &str) -> String {
5562 let mut scanner = Scanner::new(BufferedInput::new(input.chars()));
5563 loop {
5564 match scanner.next_token().expect("scanner should not error") {
5565 Some(Token(_, TokenType::Scalar(_, value))) => return value.into_owned(),
5566 Some(_) => {}
5567 None => panic!("expected scalar token"),
5568 }
5569 }
5570 }
5571
5572 #[test]
5573 fn iterator_next_emits_error_and_then_stays_empty() {
5574 let mut scanner = Scanner::new(StrInput::new("\"unterminated"));
5575
5576 let error = scanner
5577 .by_ref()
5578 .find_map(Result::err)
5579 .expect("scanner should emit the error");
5580 assert_eq!(error.kind(), &ErrorKind::UnclosedQuotedScalar);
5581 assert!(scanner.next().is_none());
5582 }
5583
5584 #[test]
5585 fn next_token_returns_none_after_stream_end() {
5586 let mut scanner = Scanner::new(StrInput::new(""));
5587
5588 while let Some(token) = scanner.next_token().unwrap() {
5589 if matches!(token.1, TokenType::StreamEnd) {
5590 break;
5591 }
5592 }
5593
5594 assert!(scanner.stream_started());
5595 assert!(scanner.stream_ended());
5596 assert!(scanner.next_token().unwrap().is_none());
5597 }
5598
5599 #[test]
5600 fn directive_name_must_be_present() {
5601 assert_eq!(
5602 first_scanner_error_kind("%\n"),
5603 ErrorKind::MissingDirectiveName
5604 );
5605 }
5606
5607 #[test]
5608 fn yaml_directive_requires_dot_between_version_numbers() {
5609 assert_eq!(
5610 first_scanner_error_kind("%YAML 1\n"),
5611 ErrorKind::MissingYamlVersionSeparator
5612 );
5613 }
5614
5615 #[test]
5616 fn yaml_directive_requires_major_version_number() {
5617 assert_eq!(
5618 first_scanner_error_kind("%YAML .2\n"),
5619 ErrorKind::MissingYamlVersion
5620 );
5621 }
5622
5623 #[test]
5624 fn yaml_directive_rejects_extremely_long_version_number() {
5625 assert_eq!(
5626 first_scanner_error_kind("%YAML 1234567890.2\n"),
5627 ErrorKind::YamlVersionTooLong
5628 );
5629 }
5630
5631 #[test]
5632 fn tag_directive_handle_must_end_with_bang() {
5633 assert_eq!(
5634 first_scanner_error_kind("%TAG !bad tag:example.com,2024:\n"),
5635 ErrorKind::ExpectedTagDirectiveBang
5636 );
5637 }
5638
5639 #[test]
5640 fn tag_directive_handle_must_start_with_bang() {
5641 assert_eq!(
5642 first_scanner_error_kind("%TAG bad! tag:example.com,2024:\n"),
5643 ErrorKind::ExpectedTagBang
5644 );
5645 assert_eq!(
5646 first_buffered_scanner_error_kind("%TAG bad! tag:example.com,2024:\n"),
5647 ErrorKind::ExpectedTagBang
5648 );
5649 }
5650
5651 #[test]
5652 fn tag_directive_prefix_must_start_with_tag_character() {
5653 assert_eq!(
5654 first_scanner_error_kind("%TAG !e! `bad\n"),
5655 ErrorKind::InvalidGlobalTagCharacter
5656 );
5657 }
5658
5659 #[test]
5660 fn tag_directive_prefix_must_end_before_invalid_content() {
5661 assert_eq!(
5662 first_scanner_error_kind("%TAG !e! tag:example.com^suffix\n"),
5663 ErrorKind::InvalidTagDirectiveTerminator
5664 );
5665 }
5666
5667 #[test]
5668 fn tag_directive_prefix_with_uri_escape_is_owned_and_decoded() {
5669 let mut scanner =
5670 Scanner::new(StrInput::new("%TAG !e! tag:example.com,2024:some%20app/\n"));
5671
5672 loop {
5673 let token = scanner
5674 .next_token()
5675 .expect("valid directive should scan")
5676 .expect("scanner must produce a directive token");
5677 if let TokenType::TagDirective(handle, prefix) = token.1 {
5678 assert!(matches!(handle, Cow::Borrowed("!e!")));
5679 assert!(matches!(prefix, Cow::Owned(_)));
5680 assert_eq!(&*prefix, "tag:example.com,2024:some app/");
5681 break;
5682 }
5683 }
5684 }
5685
5686 #[test]
5687 fn bare_bang_tag_scans_as_non_specific_tag() {
5688 let mut scanner = Scanner::new(StrInput::new("! foo\n"));
5689
5690 loop {
5691 let token = scanner
5692 .next_token()
5693 .expect("valid tag should scan")
5694 .expect("scanner must produce a tag token");
5695 if let TokenType::Tag(handle, suffix) = token.1 {
5696 assert_eq!(&*handle, "");
5697 assert_eq!(&*suffix, "!");
5698 break;
5699 }
5700 }
5701 }
5702
5703 #[test]
5704 fn tag_requires_separation_after_suffix() {
5705 assert_eq!(
5706 first_scanner_error_kind("!foo,bar\n"),
5707 ErrorKind::InvalidTagTerminator
5708 );
5709 }
5710
5711 #[test]
5712 fn verbatim_tag_requires_uri() {
5713 assert_eq!(
5714 first_scanner_error_kind("!<> foo\n"),
5715 ErrorKind::MissingTagUri
5716 );
5717 }
5718
5719 #[test]
5720 fn verbatim_tag_requires_closing_angle_bracket() {
5721 assert_eq!(
5722 first_scanner_error_kind("!<tag:yaml.org,2002:str foo\n"),
5723 ErrorKind::UnclosedVerbatimTag
5724 );
5725 }
5726
5727 #[test]
5728 fn tag_uri_escape_requires_hex_digits() {
5729 assert_eq!(
5730 first_scanner_error_kind("!!bad%zz foo\n"),
5731 ErrorKind::InvalidTagEscape
5732 );
5733 }
5734
5735 #[test]
5736 fn tag_uri_escape_rejects_bad_leading_utf8_byte() {
5737 assert_eq!(
5738 first_scanner_error_kind("!!bad%80 foo\n"),
5739 ErrorKind::InvalidTagUtf8LeadingByte
5740 );
5741 }
5742
5743 #[test]
5744 fn tag_uri_escape_rejects_bad_trailing_utf8_byte() {
5745 assert_eq!(
5746 first_scanner_error_kind("!!bad%C2%41 foo\n"),
5747 ErrorKind::InvalidTagUtf8TrailingByte
5748 );
5749 }
5750
5751 #[test]
5752 fn tag_uri_escape_rejects_invalid_utf8_codepoint() {
5753 assert_eq!(
5754 first_scanner_error_kind("!!bad%F4%90%80%80 foo\n"),
5755 ErrorKind::InvalidTagUtf8
5756 );
5757 }
5758
5759 #[test]
5760 fn anchors_and_aliases_require_names() {
5761 assert_eq!(
5762 first_scanner_error_kind("& \n"),
5763 ErrorKind::MissingAnchorOrAliasName
5764 );
5765 assert_eq!(
5766 first_scanner_error_kind("* \n"),
5767 ErrorKind::MissingAnchorOrAliasName
5768 );
5769 }
5770
5771 #[test]
5772 fn document_end_marker_rejects_trailing_content() {
5773 assert_eq!(
5774 first_scanner_error_kind("... trailing\n"),
5775 ErrorKind::InvalidDocumentEnd
5776 );
5777 }
5778
5779 #[test]
5780 fn reserved_indicators_are_rejected_outside_directives() {
5781 let error = first_scanner_error(" @\n");
5782
5783 assert_eq!(
5784 error.kind(),
5785 &ErrorKind::UnexpectedCharacter { character: '@' }
5786 );
5787 }
5788
5789 #[test]
5790 fn flow_block_entry_indicator_is_rejected() {
5791 assert_eq!(
5792 first_scanner_error_kind("[- ]\n"),
5793 ErrorKind::BlockEntryInFlowCollection
5794 );
5795 }
5796
5797 #[test]
5798 fn block_entry_after_tabbed_separator_reports_specific_error() {
5799 assert_eq!(
5800 first_scanner_error_kind("-\t- value\n"),
5801 ErrorKind::InvalidBlockEntryWhitespace
5802 );
5803 }
5804
5805 #[test]
5806 fn document_indicator_reports_unclosed_flow_collection() {
5807 let error = first_scanner_error("[\n---\n");
5808
5809 assert_eq!(
5810 error.kind(),
5811 &ErrorKind::UnclosedFlowCollection { open: '[' }
5812 );
5813 }
5814
5815 #[test]
5816 fn block_scalar_header_rejects_trailing_content() {
5817 assert_eq!(
5818 first_scanner_error_kind("|+ trailing\n"),
5819 ErrorKind::InvalidBlockScalarHeader
5820 );
5821 }
5822
5823 #[test]
5824 fn block_scalar_rejects_zero_indent_indicator() {
5825 assert_eq!(
5826 first_scanner_error_kind("|0\n"),
5827 ErrorKind::ZeroBlockScalarIndent
5828 );
5829 assert_eq!(
5830 first_scanner_error_kind("|+0\n"),
5831 ErrorKind::ZeroBlockScalarIndent
5832 );
5833 }
5834
5835 #[test]
5836 fn empty_block_scalar_at_eof_honors_chomping() {
5837 assert_eq!(first_scalar_value("|\n"), "");
5838 assert_eq!(first_scalar_value("|-\n"), "");
5839 assert_eq!(first_scalar_value("|+\n"), "");
5840 assert_eq!(first_scalar_value("|+\n\n"), "\n");
5841 assert_eq!(first_scalar_value("|+\n "), "\n");
5842 }
5843
5844 #[test]
5845 fn buffered_block_scalar_reads_content_past_lookahead_window() {
5846 assert_eq!(
5847 first_buffered_scalar_value("|\n abcdefghijklmnopqrstuvwxyz\n"),
5848 "abcdefghijklmnopqrstuvwxyz\n"
5849 );
5850 }
5851
5852 #[test]
5853 fn explicit_indent_block_scalar_can_end_at_document_marker() {
5854 assert_eq!(first_scalar_value("|1\n...\n"), "");
5855 }
5856
5857 #[test]
5858 fn root_explicit_indent_block_scalar_rejects_underindented_content() {
5859 assert_eq!(
5860 first_scanner_error_kind("|2\nx\n"),
5861 ErrorKind::InvalidBlockScalarIndent
5862 );
5863 }
5864
5865 #[test]
5866 fn quoted_scalar_rejects_document_indicator_at_line_start() {
5867 assert_eq!(
5868 first_scanner_error_kind("\"one\n---\ntwo\"\n"),
5869 ErrorKind::DocumentIndicatorInQuotedScalar
5870 );
5871 }
5872
5873 #[test]
5874 fn quoted_scalar_rejects_tab_indentation_after_line_break() {
5875 assert_eq!(
5876 first_scanner_error_kind("a: \"one\n\tbad\"\n"),
5877 ErrorKind::TabInIndentation
5878 );
5879 }
5880
5881 #[test]
5882 fn quoted_scalar_rejects_underindented_continuation() {
5883 assert_eq!(
5884 first_scanner_error_kind("a: \"one\nbad\"\n"),
5885 ErrorKind::InvalidQuotedScalarIndent
5886 );
5887 }
5888
5889 #[test]
5890 fn quoted_scalar_trailing_content_error_names_quote_style() {
5891 assert_eq!(
5892 first_scanner_error_kind("'foo' trailing\n"),
5893 ErrorKind::InvalidTrailingSingleQuotedScalar
5894 );
5895 assert_eq!(
5896 first_scanner_error_kind("\"foo\" trailing\n"),
5897 ErrorKind::InvalidTrailingDoubleQuotedScalar
5898 );
5899 }
5900
5901 #[test]
5902 fn quoted_scalar_escape_errors_cover_hex_and_surrogate_edges() {
5903 assert_eq!(
5904 first_scanner_error_kind("\"\\xG0\"\n"),
5905 ErrorKind::InvalidQuotedScalarHexEscape
5906 );
5907 assert_eq!(
5908 first_scanner_error_kind("\"\\uD800\\uGGGG\"\n"),
5909 ErrorKind::InvalidLowSurrogateHexEscape
5910 );
5911 assert_eq!(
5912 first_scanner_error_kind("\"\\uD800\\u0041\"\n"),
5913 ErrorKind::InvalidLowSurrogate
5914 );
5915 assert_eq!(
5916 first_scanner_error_kind("\"\\U00110000\"\n"),
5917 ErrorKind::InvalidUnicodeEscape
5918 );
5919 }
5920
5921 #[test]
5922 fn under_indented_flow_scalar_is_accepted() {
5923 let tokens: Vec<_> = Scanner::new(StrInput::new("a:\n [\nfoo]\n"))
5924 .map(|result| result.expect("under-indented flow entry should scan").1)
5925 .collect();
5926 assert_eq!(
5927 tokens,
5928 [
5929 TokenType::StreamStart,
5930 TokenType::BlockMappingStart,
5931 TokenType::Key,
5932 TokenType::Scalar(ScalarStyle::Plain, "a".into()),
5933 TokenType::Value,
5934 TokenType::FlowSequenceStart,
5935 TokenType::Scalar(ScalarStyle::Plain, "foo".into()),
5936 TokenType::FlowSequenceEnd,
5937 TokenType::BlockEnd,
5938 TokenType::StreamEnd,
5939 ]
5940 );
5941 }
5942
5943 #[test]
5944 fn required_simple_key_requires_value_at_stream_end() {
5945 let error = first_scanner_error("a:\n&b\n- c\n");
5946
5947 assert_eq!(error.kind(), &ErrorKind::SimpleKeyExpected);
5948 assert_eq!(error.marker().index(), 3);
5949 assert_eq!(error.marker().line(), 2);
5950 assert_eq!(error.marker().col(), 0);
5951 }
5952
5953 #[test]
5954 fn plain_scalar_rejects_dash_before_flow_indicator() {
5955 assert_eq!(
5956 first_scanner_error_kind("[-]\n"),
5957 ErrorKind::PlainScalarStartsWithDashFlowIndicator
5958 );
5959 }
5960
5961 #[test]
5962 fn explicit_key_rejects_tab_after_indicator() {
5963 assert_eq!(
5964 first_scanner_error_kind("? \tfoo\n"),
5965 ErrorKind::TabNotAllowed
5966 );
5967 }
5968
5969 #[test]
5970 fn flow_mapping_rejects_adjacent_collection_value_after_plain_key() {
5971 assert_eq!(
5972 first_scanner_error_kind("[a:[]]\n"),
5973 ErrorKind::FlowMappingValueAdjacentCollection
5974 );
5975 }
5976
5977 #[test]
5978 fn implicit_flow_mapping_colon_cannot_move_to_next_line() {
5979 assert_eq!(
5980 first_scanner_error_kind("[foo\n: bar]\n"),
5981 ErrorKind::InvalidColonPlacement
5982 );
5983 }
5984
5985 #[test]
5986 fn invalid_simple_key_token_positions_are_scan_errors() {
5987 for (tokens_parsed, token_number) in [(1, 0), (0, 1)] {
5988 let mut scanner = Scanner::new(StrInput::new(": value\n"));
5989 scanner.fetch_stream_start();
5990 scanner.tokens.clear();
5991 scanner.tokens_parsed = tokens_parsed;
5992
5993 let simple_key = scanner
5994 .simple_keys
5995 .last_mut()
5996 .expect("stream start should create a simple key slot");
5997 simple_key.possible = true;
5998 simple_key.token_number = token_number;
5999
6000 let error = scanner
6001 .fetch_value()
6002 .expect_err("invalid simple key position should be reported as a scan error");
6003 assert_eq!(error.kind(), &ErrorKind::InvalidSimpleKey);
6004 assert_eq!(error.marker(), &Marker::new(0, 1, 0));
6005 }
6006 }
6007
6008 #[test]
6009 fn issue14_alias_scanner_consumes_colon_as_name_character() {
6010 let mut scanner = Scanner::new(StrInput::new("*foo: bar\n"));
6011
6012 assert!(matches!(
6013 scanner.next_token().unwrap().unwrap().1,
6014 TokenType::StreamStart
6015 ));
6016
6017 let token = scanner.next_token().unwrap().unwrap();
6018
6019 assert!(
6020 matches!(token.1, TokenType::Alias(ref name) if name.as_ref() == "foo:"),
6021 "expected `*foo: bar` to start with Alias(\"foo:\"), got {token:?}"
6022 );
6023 }
6024
6025 #[test]
6026 fn issue14_anchor_scanner_consumes_colon_as_name_character() {
6027 let mut scanner = Scanner::new(StrInput::new("&foo: bar\n"));
6028
6029 assert!(matches!(
6030 scanner.next_token().unwrap().unwrap().1,
6031 TokenType::StreamStart
6032 ));
6033
6034 let token = scanner.next_token().unwrap().unwrap();
6035
6036 assert!(
6037 matches!(token.1, TokenType::Anchor(ref name) if name.as_ref() == "foo:"),
6038 "expected `&foo: bar` to start with Anchor(\"foo:\"), got {token:?}"
6039 );
6040 }
6041}