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, WhitespaceResult},
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#[non_exhaustive]
174#[derive(Clone, Copy, PartialEq, Debug, Eq, Default)]
175pub struct Span {
176 pub start: Marker,
178 pub end: Marker,
180
181 pub indent: Option<usize>,
186
187 pub tag_start: Option<Marker>,
196
197 tag_length: u32,
200}
201
202impl Span {
203 #[must_use]
205 pub fn new(start: Marker, end: Marker) -> Span {
206 Span {
207 start,
208 end,
209 indent: None,
210 tag_start: None,
211 tag_length: 0,
212 }
213 }
214
215 #[must_use]
222 pub fn empty(mark: Marker) -> Span {
223 Span {
224 start: mark,
225 end: mark,
226 indent: None,
227 tag_start: None,
228 tag_length: 0,
229 }
230 }
231
232 #[must_use]
234 pub fn with_indent(mut self, indent: Option<usize>) -> Span {
235 self.indent = indent;
236 self
237 }
238
239 #[deprecated(
245 since = "2.1.0",
246 note = "Only sets a tag start (.tag_span() then returns None). Use with_tag_span."
247 )]
248 #[must_use]
249 pub fn with_tag_start(mut self, tag_start: Option<Marker>) -> Span {
250 self.tag_start = tag_start;
251 self.tag_length = 0;
252 self
253 }
254
255 #[must_use]
257 pub fn with_tag_span(mut self, tag_span: Option<(Marker, u32)>) -> Span {
258 let (start, length) = tag_span.map_or((None, 0), |(start, length)| (Some(start), length));
259 self.tag_start = start;
260 self.tag_length = length;
261 self
262 }
263
264 #[must_use]
270 pub fn tag_start(&self) -> Option<Marker> {
271 self.tag_start
272 }
273
274 #[must_use]
296 pub fn tag_span(&self) -> Option<Span> {
297 let start = self.tag_start?;
298 if self.tag_length == 0 {
299 return None;
300 }
301 let length = usize::try_from(self.tag_length).ok()?;
302 let end = Marker::new(
305 start.index().checked_add(length)?,
306 start.line(),
307 start.col().checked_add(length)?,
308 )
309 .with_byte_offset(match start.byte_offset() {
310 Some(byte_offset) => Some(byte_offset.checked_add(length)?),
311 None => None,
312 });
313 Some(Span::new(start, end))
314 }
315
316 #[track_caller]
321 #[must_use]
322 pub fn len(&self) -> usize {
323 self.end.index() - self.start.index()
324 }
325
326 #[track_caller]
331 #[must_use]
332 pub fn is_empty(&self) -> bool {
333 self.len() == 0
334 }
335
336 #[must_use]
347 pub fn byte_range(&self) -> Option<core::ops::Range<usize>> {
348 Some(self.start.byte_offset()?..self.end.byte_offset()?)
349 }
350
351 #[must_use]
358 pub fn slice<'source>(&self, source: &'source str) -> Option<&'source str> {
359 source.get(self.byte_range()?)
360 }
361}
362
363#[non_exhaustive]
378#[derive(Clone, Copy, PartialEq, Debug, Eq, Default)]
379#[cfg(feature = "comments")]
380pub enum Placement {
381 Above,
387 Right,
390 #[default]
396 Free,
397 Last,
402}
403
404#[derive(Clone, PartialEq, Debug, Eq)]
409#[cfg(feature = "comments")]
410pub struct Comment<'input> {
411 text: Cow<'input, str>,
415 placement: Placement,
417}
418
419#[cfg(feature = "comments")]
420impl<'input> Comment<'input> {
421 #[must_use]
426 pub fn new(text: impl Into<Cow<'input, str>>) -> Self {
427 Self {
428 text: text.into(),
429 placement: Placement::Free,
430 }
431 }
432
433 #[must_use]
435 pub fn with_placement(mut self, placement: Placement) -> Self {
436 self.placement = placement;
437 self
438 }
439
440 #[must_use]
442 pub fn text(&self) -> &str {
443 self.text.as_ref()
444 }
445
446 #[must_use]
448 pub const fn placement(&self) -> Placement {
449 self.placement
450 }
451
452 #[must_use]
454 pub fn into_text(self) -> Cow<'input, str> {
455 self.text
456 }
457
458 #[must_use]
462 pub fn trimmed_text(&self) -> &str {
463 self.text.trim()
464 }
465}
466
467#[cfg(feature = "comments")]
468impl AsRef<str> for Comment<'_> {
469 fn as_ref(&self) -> &str {
470 self.text.as_ref()
471 }
472}
473
474#[non_exhaustive]
476#[derive(Clone, PartialEq, Debug, Eq)]
477pub enum TokenType<'input> {
478 StreamStart,
480 StreamEnd,
482 VersionDirective(
484 u32,
486 u32,
488 ),
489 TagDirective(
491 Cow<'input, str>,
493 Cow<'input, str>,
495 ),
496 DocumentStart,
498 DocumentEnd,
500 BlockSequenceStart,
504 BlockMappingStart,
508 BlockEnd,
510 FlowSequenceStart,
512 FlowSequenceEnd,
514 FlowMappingStart,
516 FlowMappingEnd,
518 BlockEntry,
520 FlowEntry,
522 Key,
524 Value,
526 Alias(Cow<'input, str>),
528 Anchor(Cow<'input, str>),
530 Tag(
532 Cow<'input, str>,
534 Cow<'input, str>,
536 ),
537 Scalar(ScalarStyle, Cow<'input, str>),
539 #[cfg(feature = "comments")]
545 Comment(
546 Comment<'input>,
548 ),
549 ReservedDirective(
551 String,
553 Vec<String>,
555 ),
556}
557
558#[derive(Clone, PartialEq, Debug, Eq)]
560pub struct Token<'input>(Span, TokenType<'input>);
561
562impl<'input> Token<'input> {
563 #[must_use]
565 pub const fn new(span: Span, token_type: TokenType<'input>) -> Self {
566 Self(span, token_type)
567 }
568
569 #[must_use]
571 pub const fn span(&self) -> Span {
572 self.0
573 }
574
575 #[must_use]
577 pub const fn token_type(&self) -> &TokenType<'input> {
578 &self.1
579 }
580
581 #[must_use]
583 pub fn into_parts(self) -> (Span, TokenType<'input>) {
584 (self.0, self.1)
585 }
586}
587
588#[derive(Clone, PartialEq, Debug, Eq)]
592pub(crate) enum QueuedTokenType<'input> {
593 StreamStart,
594 StreamEnd,
595 VersionDirective(u32, u32),
596 TagDirective(Cow<'input, str>, Cow<'input, str>),
597 DocumentStart,
598 DocumentEnd,
599 BlockSequenceStart,
600 BlockMappingStart,
601 BlockEnd,
602 FlowSequenceStart,
603 FlowSequenceEnd,
604 FlowMappingStart,
605 FlowMappingEnd,
606 BlockEntry,
607 FlowEntry,
608 Key,
609 Value,
610 Alias(Cow<'input, str>),
611 Anchor(Cow<'input, str>),
612 Tag(Cow<'input, str>, Cow<'input, str>),
613 Scalar(ScalarStyle, Cow<'input, str>),
614 #[cfg(feature = "comments")]
615 Comment(Comment<'input>),
616 ReservedDirective(String, Vec<String>),
617}
618
619impl<'input> QueuedTokenType<'input> {
620 fn into_public(self) -> TokenType<'input> {
621 match self {
622 Self::StreamStart => TokenType::StreamStart,
623 Self::StreamEnd => TokenType::StreamEnd,
624 Self::VersionDirective(major, minor) => TokenType::VersionDirective(major, minor),
625 Self::TagDirective(handle, prefix) => TokenType::TagDirective(handle, prefix),
626 Self::DocumentStart => TokenType::DocumentStart,
627 Self::DocumentEnd => TokenType::DocumentEnd,
628 Self::BlockSequenceStart => TokenType::BlockSequenceStart,
629 Self::BlockMappingStart => TokenType::BlockMappingStart,
630 Self::BlockEnd => TokenType::BlockEnd,
631 Self::FlowSequenceStart => TokenType::FlowSequenceStart,
632 Self::FlowSequenceEnd => TokenType::FlowSequenceEnd,
633 Self::FlowMappingStart => TokenType::FlowMappingStart,
634 Self::FlowMappingEnd => TokenType::FlowMappingEnd,
635 Self::BlockEntry => TokenType::BlockEntry,
636 Self::FlowEntry => TokenType::FlowEntry,
637 Self::Key => TokenType::Key,
638 Self::Value => TokenType::Value,
639 Self::Alias(name) => TokenType::Alias(name),
640 Self::Anchor(name) => TokenType::Anchor(name),
641 Self::Tag(handle, suffix) => TokenType::Tag(handle, suffix),
642 Self::Scalar(style, value) => TokenType::Scalar(style, value),
643 #[cfg(feature = "comments")]
644 Self::Comment(comment) => TokenType::Comment(comment),
645 Self::ReservedDirective(name, params) => TokenType::ReservedDirective(name, params),
646 }
647 }
648}
649
650impl<'input> From<TokenType<'input>> for QueuedTokenType<'input> {
651 fn from(token: TokenType<'input>) -> Self {
652 match token {
653 TokenType::StreamStart => Self::StreamStart,
654 TokenType::StreamEnd => Self::StreamEnd,
655 TokenType::VersionDirective(major, minor) => Self::VersionDirective(major, minor),
656 TokenType::TagDirective(handle, prefix) => Self::TagDirective(handle, prefix),
657 TokenType::DocumentStart => Self::DocumentStart,
658 TokenType::DocumentEnd => Self::DocumentEnd,
659 TokenType::BlockSequenceStart => Self::BlockSequenceStart,
660 TokenType::BlockMappingStart => Self::BlockMappingStart,
661 TokenType::BlockEnd => Self::BlockEnd,
662 TokenType::FlowSequenceStart => Self::FlowSequenceStart,
663 TokenType::FlowSequenceEnd => Self::FlowSequenceEnd,
664 TokenType::FlowMappingStart => Self::FlowMappingStart,
665 TokenType::FlowMappingEnd => Self::FlowMappingEnd,
666 TokenType::BlockEntry => Self::BlockEntry,
667 TokenType::FlowEntry => Self::FlowEntry,
668 TokenType::Key => Self::Key,
669 TokenType::Value => Self::Value,
670 TokenType::Alias(name) => Self::Alias(name),
671 TokenType::Anchor(name) => Self::Anchor(name),
672 TokenType::Tag(handle, suffix) => Self::Tag(handle, suffix),
673 TokenType::Scalar(style, value) => Self::Scalar(style, value),
674 #[cfg(feature = "comments")]
675 TokenType::Comment(comment) => Self::Comment(comment),
676 TokenType::ReservedDirective(name, params) => Self::ReservedDirective(name, params),
677 }
678 }
679}
680
681#[derive(Clone, PartialEq, Debug, Eq)]
683pub(crate) struct QueuedToken<'input>(pub(crate) Span, pub(crate) QueuedTokenType<'input>);
684
685impl<'input> QueuedToken<'input> {
686 fn into_public(self) -> Token<'input> {
687 Token(self.0, self.1.into_public())
688 }
689}
690
691impl<'input> From<Token<'input>> for QueuedToken<'input> {
692 fn from(token: Token<'input>) -> Self {
693 Self(token.0, token.1.into())
694 }
695}
696
697#[derive(Clone, Copy, PartialEq, Debug, Eq)]
732struct SimpleKey {
733 possible: bool,
746 required: bool,
755 token_number: usize,
761 mark: Marker,
763}
764
765impl SimpleKey {
766 fn new(mark: Marker) -> SimpleKey {
768 SimpleKey {
769 possible: false,
770 required: false,
771 token_number: 0,
772 mark,
773 }
774 }
775}
776
777#[derive(Clone, Debug, Default)]
779struct Indent {
780 indent: isize,
782 needs_block_end: bool,
800}
801
802#[derive(Debug, PartialEq)]
824enum ImplicitMappingState {
825 Possible,
830 Inside(usize),
834}
835
836#[derive(Debug)]
849#[allow(clippy::struct_excessive_bools)]
850pub struct Scanner<'input, T> {
851 input: T,
855 options: Options,
857 mark: Marker,
859 tokens: VecDeque<QueuedToken<'input>>,
866 failed: bool,
868 source_error: Option<ScanError>,
870 #[cfg(feature = "comments")]
872 deferred_error: Option<ScanError>,
873 #[cfg(feature = "comments")]
879 comments_possible: bool,
880
881 stream_start_produced: bool,
883 stream_end_produced: bool,
885 document_prefix_allowed: bool,
891 in_directive_prefix: bool,
893 adjacent_value_allowed_at: usize,
896 simple_key_allowed: bool,
900 simple_keys: smallvec::SmallVec<[SimpleKey; 8]>,
905 first_simple_key: Option<usize>,
911 indent: isize,
913 indents: smallvec::SmallVec<[Indent; 8]>,
915 flow_level: usize,
917 block_level: usize,
919 tokens_parsed: usize,
923 token_available: bool,
925 leading_whitespace: bool,
927 flow_mapping_started: smallvec::SmallVec<[bool; 8]>,
934 implicit_flow_mapping_states: smallvec::SmallVec<[ImplicitMappingState; 8]>,
947 interrupted_plain_by_comment: Option<Marker>,
950 explicit_key_tab_check_pending: bool,
956 flow_markers: smallvec::SmallVec<[(Marker, char); 8]>,
958 buf_leading_break: String,
959 buf_trailing_breaks: String,
960 buf_whitespaces: String,
961}
962
963impl<'input, T: BorrowedInput<'input>> Iterator for Scanner<'input, T> {
964 type Item = Result<Token<'input>, ScanError>;
965
966 fn next(&mut self) -> Option<Self::Item> {
967 if self.failed {
968 return None;
969 }
970 match self.next_token() {
971 Ok(Some(tok)) => {
972 debug_print!(
973 " \x1B[;32m\u{21B3} {:?} \x1B[;36m{:?}\x1B[;m",
974 tok.1,
975 tok.0
976 );
977 Some(Ok(tok))
978 }
979 Ok(None) => None,
980 Err(error) => {
981 self.failed = true;
982 Some(Err(error))
983 }
984 }
985 }
986}
987
988impl<'input, T: BorrowedInput<'input>> core::iter::FusedIterator for Scanner<'input, T> {}
989
990type ScanResult = Result<(), ScanError>;
992
993#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
998struct SkipToNextTokenOutcome {
999 saw_comment: bool,
1000 #[cfg(feature = "comments")]
1001 queued_comment: bool,
1002}
1003
1004#[derive(Debug)]
1005enum FlowScalarBuf {
1006 Borrowed {
1012 start: usize,
1013 end: usize,
1014 pending_ws_start: Option<usize>,
1015 pending_ws_end: usize,
1016 },
1017 Owned(String),
1018}
1019
1020impl FlowScalarBuf {
1021 #[inline]
1022 fn new_borrowed(start: usize) -> Self {
1023 Self::Borrowed {
1024 start,
1025 end: start,
1026 pending_ws_start: None,
1027 pending_ws_end: start,
1028 }
1029 }
1030
1031 #[inline]
1032 fn new_owned() -> Self {
1033 Self::Owned(String::new())
1034 }
1035
1036 #[inline]
1037 fn as_owned_mut(&mut self) -> Option<&mut String> {
1038 match self {
1039 Self::Owned(s) => Some(s),
1040 Self::Borrowed { .. } => None,
1041 }
1042 }
1043
1044 #[inline]
1045 fn commit_pending_ws(&mut self) {
1046 if let Self::Borrowed {
1047 end,
1048 pending_ws_start,
1049 pending_ws_end,
1050 ..
1051 } = self
1052 {
1053 if pending_ws_start.is_some() {
1054 *end = *pending_ws_end;
1055 *pending_ws_start = None;
1056 }
1057 }
1058 }
1059
1060 #[inline]
1061 fn note_pending_ws(&mut self, ws_start: usize, ws_end: usize) {
1062 if let Self::Borrowed {
1063 pending_ws_start,
1064 pending_ws_end,
1065 ..
1066 } = self
1067 {
1068 if pending_ws_start.is_none() {
1069 *pending_ws_start = Some(ws_start);
1070 }
1071 *pending_ws_end = ws_end;
1072 }
1073 }
1074
1075 #[inline]
1076 fn discard_pending_ws(&mut self) {
1077 if let Self::Borrowed {
1078 pending_ws_start,
1079 pending_ws_end,
1080 end,
1081 ..
1082 } = self
1083 {
1084 *pending_ws_start = None;
1085 *pending_ws_end = *end;
1086 }
1087 }
1088}
1089
1090impl<'input, T: BorrowedInput<'input>> Scanner<'input, T> {
1091 #[inline]
1092 fn promote_flow_scalar_buf_to_owned(
1093 &self,
1094 start_mark: &Marker,
1095 buf: &mut FlowScalarBuf,
1096 ) -> Result<(), ScanError> {
1097 let FlowScalarBuf::Borrowed {
1098 start,
1099 end,
1100 pending_ws_start: _,
1101 pending_ws_end: _,
1102 } = *buf
1103 else {
1104 return Ok(());
1105 };
1106
1107 let slice = self.input.slice_bytes(start, end).ok_or_else(|| {
1108 ScanError::from_kind(*start_mark, ErrorKind::InputOffsetsWithoutSlice)
1109 })?;
1110 *buf = FlowScalarBuf::Owned(slice.to_owned());
1111 Ok(())
1112 }
1113 #[inline]
1119 fn try_borrow_slice(&self, start: usize, end: usize) -> Option<&'input str> {
1120 self.input.slice_borrowed(start, end)
1121 }
1122
1123 fn scan_tag_handle_directive_cow(
1128 &mut self,
1129 mark: &Marker,
1130 budget: &mut usize,
1131 ) -> Result<Cow<'input, str>, ScanError> {
1132 let Some(start) = self.input.byte_offset() else {
1133 return Ok(Cow::Owned(
1134 self.scan_tag_handle_directive_owned(mark, budget)?,
1135 ));
1136 };
1137
1138 if self.input.look_ch() != '!' {
1139 return Err(ScanError::from_kind(*mark, ErrorKind::ExpectedTagBang));
1140 }
1141
1142 self.spend_directive_bytes(budget, 1, *mark)?;
1144 self.skip_non_blank();
1145
1146 self.input.lookahead(1);
1149 while self.input.next_is_alpha() {
1150 self.spend_directive_bytes(budget, 1, *mark)?;
1151 self.skip_non_blank();
1152 self.input.lookahead(1);
1153 }
1154
1155 if self.input.peek() == '!' {
1157 self.spend_directive_bytes(budget, 1, *mark)?;
1158 self.skip_non_blank();
1159 }
1160
1161 let Some(end) = self.input.byte_offset() else {
1162 return Err(ScanError::from_kind(
1163 *mark,
1164 ErrorKind::InputSlicingUnavailable,
1165 ));
1166 };
1167
1168 let Some(slice) = self.try_borrow_slice(start, end) else {
1169 let slice = self
1171 .input
1172 .slice_bytes(start, end)
1173 .ok_or_else(|| ScanError::from_kind(*mark, ErrorKind::InputSlicingUnavailable))?;
1174 if !slice.ends_with('!') && slice != "!" {
1175 return Err(ScanError::from_kind(
1176 *mark,
1177 ErrorKind::ExpectedTagDirectiveBang,
1178 ));
1179 }
1180 return Ok(Cow::Owned(slice.to_owned()));
1181 };
1182
1183 if !slice.ends_with('!') && slice != "!" {
1184 return Err(ScanError::from_kind(
1185 *mark,
1186 ErrorKind::ExpectedTagDirectiveBang,
1187 ));
1188 }
1189
1190 Ok(Cow::Borrowed(slice))
1191 }
1192
1193 fn scan_tag_handle_directive_owned(
1195 &mut self,
1196 mark: &Marker,
1197 budget: &mut usize,
1198 ) -> Result<String, ScanError> {
1199 let mut string = String::new();
1200 if self.input.look_ch() != '!' {
1201 return Err(ScanError::from_kind(*mark, ErrorKind::ExpectedTagBang));
1202 }
1203
1204 self.spend_directive_bytes(budget, 1, *mark)?;
1205 string.push('!');
1206 self.skip_non_blank();
1207
1208 self.input.lookahead(1);
1209 while self.input.next_is_alpha() {
1210 self.spend_directive_bytes(budget, 1, *mark)?;
1211 string.push(self.input.peek());
1212 self.skip_non_blank();
1213 self.input.lookahead(1);
1214 }
1215
1216 if self.input.peek() == '!' {
1217 self.spend_directive_bytes(budget, 1, *mark)?;
1218 string.push('!');
1219 self.skip_non_blank();
1220 } else if string != "!" {
1221 return Err(ScanError::from_kind(
1222 *mark,
1223 ErrorKind::ExpectedTagDirectiveBang,
1224 ));
1225 }
1226
1227 Ok(string)
1228 }
1229
1230 fn scan_tag_prefix_directive_cow(
1235 &mut self,
1236 start_mark: &Marker,
1237 budget: &mut usize,
1238 ) -> Result<Cow<'input, str>, ScanError> {
1239 let Some(start) = self.input.byte_offset() else {
1240 return Ok(Cow::Owned(
1241 self.scan_tag_prefix_directive_owned(start_mark, budget)?,
1242 ));
1243 };
1244
1245 if self.input.look_ch() == '!' {
1247 self.spend_directive_bytes(budget, 1, *start_mark)?;
1248 self.skip_non_blank();
1249 } else if !is_tag_char(self.input.peek()) {
1250 return Err(ScanError::from_kind(
1251 *start_mark,
1252 ErrorKind::InvalidGlobalTagCharacter,
1253 ));
1254 } else if self.input.peek() == '%' {
1255 } else {
1257 self.spend_directive_bytes(budget, 1, *start_mark)?;
1258 self.skip_non_blank();
1259 }
1260
1261 while is_uri_char(self.input.look_ch()) {
1263 if self.input.peek() == '%' {
1264 break;
1265 }
1266 self.spend_directive_bytes(budget, 1, *start_mark)?;
1267 self.skip_non_blank();
1268 }
1269
1270 if self.input.peek() == '%' {
1272 let current = self
1273 .input
1274 .byte_offset()
1275 .expect("byte_offset() must remain available once enabled");
1276 let mut out = if let Some(slice) = self.input.slice_bytes(start, current) {
1277 slice.to_owned()
1278 } else {
1279 String::new()
1280 };
1281
1282 while is_uri_char(self.input.look_ch()) {
1283 if self.input.peek() == '%' {
1284 out.push(self.scan_uri_escapes_with_budget(start_mark, Some(budget))?);
1285 } else {
1286 self.spend_directive_bytes(budget, 1, *start_mark)?;
1287 out.push(self.input.peek());
1288 self.skip_non_blank();
1289 }
1290 }
1291 return Ok(Cow::Owned(out));
1292 }
1293
1294 let Some(end) = self.input.byte_offset() else {
1295 return Err(ScanError::from_kind(
1296 *start_mark,
1297 ErrorKind::InputSlicingUnavailable,
1298 ));
1299 };
1300
1301 let Some(slice) = self.try_borrow_slice(start, end) else {
1302 let slice = self.input.slice_bytes(start, end).ok_or_else(|| {
1304 ScanError::from_kind(*start_mark, ErrorKind::InputSlicingUnavailable)
1305 })?;
1306 return Ok(Cow::Owned(slice.to_owned()));
1307 };
1308
1309 Ok(Cow::Borrowed(slice))
1310 }
1311
1312 fn scan_tag_prefix_directive_owned(
1314 &mut self,
1315 start_mark: &Marker,
1316 budget: &mut usize,
1317 ) -> Result<String, ScanError> {
1318 let mut string = String::new();
1319
1320 if self.input.look_ch() == '!' {
1321 self.spend_directive_bytes(budget, 1, *start_mark)?;
1322 string.push('!');
1323 self.skip_non_blank();
1324 } else if !is_tag_char(self.input.peek()) {
1325 return Err(ScanError::from_kind(
1326 *start_mark,
1327 ErrorKind::InvalidGlobalTagCharacter,
1328 ));
1329 } else if self.input.peek() == '%' {
1330 string.push(self.scan_uri_escapes_with_budget(start_mark, Some(budget))?);
1331 } else {
1332 self.spend_directive_bytes(budget, 1, *start_mark)?;
1333 string.push(self.input.peek());
1334 self.skip_non_blank();
1335 }
1336
1337 while is_uri_char(self.input.look_ch()) {
1338 if self.input.peek() == '%' {
1339 string.push(self.scan_uri_escapes_with_budget(start_mark, Some(budget))?);
1340 } else {
1341 self.spend_directive_bytes(budget, 1, *start_mark)?;
1342 string.push(self.input.peek());
1343 self.skip_non_blank();
1344 }
1345 }
1346
1347 Ok(string)
1348 }
1349 #[must_use]
1351 pub fn new(input: T) -> Self {
1352 Self::with_options(input, Options::default())
1353 }
1354
1355 #[cfg_attr(
1357 feature = "comments",
1358 doc = "\n[`Options::emit_comments`] controls comment emission. \
1359 [`Options::max_buffered_comment_events`] has no effect when using a scanner \
1360 directly, because comment-event buffering is performed by [`crate::Parser`]."
1361 )]
1362 #[must_use]
1363 pub fn with_options(input: T, options: Options) -> Self {
1364 let initial_byte_offset = input.byte_offset();
1365 #[cfg(feature = "comments")]
1368 let comments_possible = !options.emit_comments || input.may_contain_comments();
1369 Scanner {
1370 input,
1371 options,
1372 mark: Marker::new(0, 1, 0).with_byte_offset(initial_byte_offset),
1373 tokens: VecDeque::with_capacity(64),
1374 failed: false,
1375 source_error: None,
1376 #[cfg(feature = "comments")]
1377 deferred_error: None,
1378 #[cfg(feature = "comments")]
1379 comments_possible,
1380
1381 stream_start_produced: false,
1382 stream_end_produced: false,
1383 document_prefix_allowed: true,
1384 in_directive_prefix: false,
1385 adjacent_value_allowed_at: 0,
1386 simple_key_allowed: true,
1387 simple_keys: smallvec::SmallVec::new(),
1388 first_simple_key: None,
1389 indent: -1,
1390 indents: smallvec::SmallVec::new(),
1391 flow_level: 0,
1392 block_level: 0,
1393 tokens_parsed: 0,
1394 token_available: false,
1395 leading_whitespace: true,
1396 flow_mapping_started: smallvec::SmallVec::new(),
1397 implicit_flow_mapping_states: smallvec::SmallVec::new(),
1398 flow_markers: smallvec::SmallVec::new(),
1399 interrupted_plain_by_comment: None,
1400 explicit_key_tab_check_pending: false,
1401
1402 buf_leading_break: String::with_capacity(128),
1403 buf_trailing_breaks: String::with_capacity(128),
1404 buf_whitespaces: String::with_capacity(128),
1405 }
1406 }
1407
1408 #[cold]
1409 fn scan_error(&self, kind: ErrorKind) -> ScanError {
1410 ScanError::from_kind(self.mark, kind)
1411 }
1412
1413 #[inline]
1414 fn ensure_current_char_is_printable(&self) -> ScanResult {
1415 let character = self.input.peek();
1416 if self.input.next_is_z() || is_printable(character) {
1417 Ok(())
1418 } else {
1419 Err(self.scan_error(ErrorKind::UnexpectedCharacter { character }))
1420 }
1421 }
1422
1423 #[cold]
1424 fn simple_key_expected(mark: Marker) -> ScanError {
1425 ScanError::from_kind(mark, ErrorKind::SimpleKeyExpected)
1426 }
1427
1428 #[cold]
1429 fn unclosed_bracket(mark: Marker, bracket: char) -> ScanError {
1430 ScanError::from_kind(mark, ErrorKind::UnclosedFlowCollection { open: bracket })
1431 }
1432
1433 #[inline]
1435 fn skip_blank(&mut self) {
1436 self.input.skip();
1437
1438 self.mark.offsets.chars += 1;
1439 self.mark.col += 1;
1440 self.mark.offsets.bytes = self.input.byte_offset();
1441 }
1442
1443 #[inline]
1445 fn skip_non_blank(&mut self) {
1446 self.input.skip();
1447
1448 self.mark.offsets.chars += 1;
1449 self.mark.col += 1;
1450 self.mark.offsets.bytes = self.input.byte_offset();
1451 self.leading_whitespace = false;
1452 }
1453
1454 #[inline]
1459 fn skip_bom(&mut self) {
1460 self.input.skip();
1461
1462 self.mark.offsets.chars += 1;
1463 self.mark.offsets.bytes = self.input.byte_offset();
1464 }
1465
1466 #[inline]
1472 fn skip_comment_char(&mut self) {
1473 self.input.skip();
1474
1475 self.mark.offsets.chars += 1;
1476 self.mark.col += 1;
1477 self.mark.offsets.bytes = self.input.byte_offset();
1478 }
1479
1480 #[inline]
1482 fn skip_n_non_blank(&mut self, count: usize) {
1483 for _ in 0..count {
1484 self.input.skip();
1485 self.mark.offsets.chars += 1;
1486 self.mark.col += 1;
1487 }
1488 self.mark.offsets.bytes = self.input.byte_offset();
1489 self.leading_whitespace = false;
1490 }
1491
1492 #[inline]
1494 fn skip_nl(&mut self) {
1495 self.input.skip();
1496
1497 self.mark.offsets.chars += 1;
1498 self.mark.col = 0;
1499 self.mark.line += 1;
1500 self.mark.offsets.bytes = self.input.byte_offset();
1501 self.leading_whitespace = true;
1502 }
1503
1504 #[inline]
1506 fn skip_linebreak(&mut self) {
1507 if self.input.next_2_are('\r', '\n') {
1508 self.skip_blank();
1511 self.skip_nl();
1512 } else if self.input.next_is_break() {
1513 self.skip_nl();
1514 }
1515 }
1516
1517 #[cfg(all(test, feature = "comments"))]
1518 fn scan_comment_token(&mut self) -> Result<Token<'input>, ScanError> {
1519 Ok(self.scan_comment_queued_token()?.into_public())
1520 }
1521
1522 #[cfg(feature = "comments")]
1523 fn scan_comment_queued_token(&mut self) -> Result<QueuedToken<'input>, ScanError> {
1524 let start_mark = self.mark;
1525 debug_assert_eq!(self.input.peek(), '#');
1526 let placement = if self.leading_whitespace {
1527 Placement::Free
1528 } else {
1529 Placement::Right
1530 };
1531
1532 self.skip_comment_char();
1533
1534 let text = if let Some(start) = self.input.byte_offset() {
1535 let n = self.input.skip_while_non_breakz();
1537 self.mark.offsets.chars += n;
1538 self.mark.col += n;
1539 let byte_offset = self.input.byte_offset();
1540 self.mark.offsets.bytes = byte_offset;
1541 let end = byte_offset.expect("byte_offset must remain available once enabled");
1542
1543 if let Some(slice) = self.try_borrow_slice(start, end) {
1544 Cow::Borrowed(slice)
1545 } else if let Some(slice) = self.input.slice_bytes(start, end) {
1546 Cow::Owned(slice.to_owned())
1548 } else {
1549 return Err(ScanError::from_kind(
1550 start_mark,
1551 ErrorKind::InputOffsetsWithoutSlice,
1552 ));
1553 }
1554 } else {
1555 let mut owned = String::new();
1557 while {
1558 let character = self.input.look_ch();
1559 !is_breakz(character) && is_printable(character)
1560 } {
1561 owned.push(self.input.peek());
1562 self.skip_comment_char();
1563 }
1564 Cow::Owned(owned)
1565 };
1566
1567 self.ensure_current_char_is_printable()?;
1568
1569 let end_mark = self.mark;
1570 let span = Span::new(start_mark, end_mark);
1571 Ok(QueuedToken(
1572 span,
1573 QueuedTokenType::Comment(Comment { text, placement }),
1574 ))
1575 }
1576
1577 #[cfg(feature = "comments")]
1578 fn push_comment_token(&mut self) -> ScanResult {
1579 let token = self.scan_comment_queued_token()?;
1580 self.tokens.push_back(token);
1581 Ok(())
1582 }
1583
1584 fn consume_comment(&mut self) -> Result<bool, ScanError> {
1586 #[cfg(feature = "comments")]
1587 if self.options.emit_comments {
1588 self.push_comment_token()?;
1589 return Ok(true);
1590 }
1591 self.skip_comment()?;
1592 Ok(false)
1593 }
1594
1595 fn skip_comment(&mut self) -> ScanResult {
1596 debug_assert_eq!(self.input.peek(), '#');
1597
1598 self.skip_comment_char();
1599 let n = self.input.skip_while_non_breakz();
1600 self.mark.offsets.chars += n;
1601 self.mark.col += n;
1602 self.mark.offsets.bytes = self.input.byte_offset();
1603 self.ensure_current_char_is_printable()
1604 }
1605
1606 #[inline]
1608 #[must_use]
1609 pub fn stream_started(&self) -> bool {
1610 self.stream_start_produced
1611 }
1612
1613 #[inline]
1615 #[must_use]
1616 pub fn stream_ended(&self) -> bool {
1617 self.stream_end_produced
1618 }
1619
1620 #[inline]
1622 #[must_use]
1623 pub fn mark(&self) -> Marker {
1624 self.mark
1625 }
1626
1627 pub(crate) fn in_directive_prefix(&self) -> bool {
1629 self.in_directive_prefix
1630 }
1631
1632 pub(crate) fn proceed_next_document(
1637 &mut self,
1638 token: Option<QueuedToken<'input>>,
1639 ) -> ScanResult {
1640 self.check_source_error()?;
1641 self.input.lookahead(4);
1642 self.check_source_error()?;
1643
1644 let cursor = self.mark;
1645 let at_line_end = self.input.next_is_z() || self.input.next_is_break();
1646 let at_real_nul = !self.input.next_is_z() && self.input.peek() == '\0';
1647 let boundary = token
1648 .into_iter()
1649 .chain(self.tokens.drain(..))
1650 .find(|token| {
1651 match token.1 {
1652 QueuedTokenType::DocumentStart => {
1653 cursor != token.0.end || !at_real_nul
1655 }
1656 QueuedTokenType::DocumentEnd => {
1657 cursor.line > token.0.end.line || at_line_end
1660 }
1661 QueuedTokenType::StreamEnd => true,
1662 _ => false,
1663 }
1664 });
1665 self.reset_document_state();
1666 if let Some(boundary) = boundary {
1667 self.queue_recovery_boundary(boundary);
1668 return Ok(());
1669 }
1670
1671 loop {
1672 self.input.lookahead(4);
1673 self.check_source_error()?;
1674 if self.input.next_is_z() {
1675 self.queue_recovery_boundary(QueuedToken(
1676 Span::empty(self.mark),
1677 QueuedTokenType::StreamEnd,
1678 ));
1679 return Ok(());
1680 }
1681
1682 if self.mark.col == 0 {
1683 if is_bom(self.input.peek()) {
1684 self.skip_bom();
1685 continue;
1686 }
1687 let start = self.input.next_is_document_start();
1688 if start || self.input.next_is_document_end() {
1689 let mark = self.mark;
1690 self.skip_n_non_blank(3);
1691 let span = Span::new(mark, self.mark);
1692 self.input.lookahead(1);
1693 self.check_source_error()?;
1694 if self.input.next_is_z()
1697 || matches!(self.input.peek(), ' ' | '\t' | '\r' | '\n')
1698 {
1699 if start {
1700 self.queue_recovery_boundary(QueuedToken(
1701 span,
1702 QueuedTokenType::DocumentStart,
1703 ));
1704 return Ok(());
1705 }
1706 if self.skip_recovery_document_end_line()? {
1707 self.queue_recovery_boundary(QueuedToken(
1708 span,
1709 QueuedTokenType::DocumentEnd,
1710 ));
1711 return Ok(());
1712 }
1713 }
1714 }
1715 }
1716
1717 self.input.lookahead(2);
1718 self.check_source_error()?;
1719 if self.input.next_is_break() {
1720 self.skip_linebreak();
1721 } else if !self.input.next_is_z() {
1722 self.skip_non_blank();
1723 }
1724 }
1725 }
1726
1727 fn skip_recovery_document_end_line(&mut self) -> Result<bool, ScanError> {
1729 let mut comment = false;
1730 loop {
1731 self.input.lookahead(2);
1732 self.check_source_error()?;
1733 if self.input.next_is_z() || self.input.next_is_break() {
1734 return Ok(true);
1735 }
1736 let ch = self.input.peek();
1737 if !is_printable(ch) {
1738 return Ok(false);
1739 }
1740 if comment || matches!(ch, ' ' | '\t') {
1741 self.skip_non_blank();
1742 } else if ch == '#' {
1743 comment = true;
1744 self.skip_non_blank();
1745 } else {
1746 return Ok(false);
1747 }
1748 }
1749 }
1750
1751 fn reset_document_state(&mut self) {
1753 self.tokens.clear();
1754 self.failed = false;
1755 #[cfg(feature = "comments")]
1756 {
1757 self.deferred_error = None;
1758 }
1759 self.stream_start_produced = true;
1760 self.stream_end_produced = false;
1761 self.document_prefix_allowed = true;
1762 self.in_directive_prefix = false;
1763 self.adjacent_value_allowed_at = 0;
1764 self.simple_key_allowed = true;
1765 self.simple_keys.clear();
1766 self.simple_keys.push(SimpleKey::new(self.mark));
1767 self.first_simple_key = None;
1768 self.indent = -1;
1769 self.indents.clear();
1770 self.flow_level = 0;
1771 self.block_level = 0;
1772 self.tokens_parsed = 0;
1773 self.token_available = false;
1774 self.leading_whitespace = self.mark.col == 0;
1775 self.flow_mapping_started.clear();
1776 self.implicit_flow_mapping_states.clear();
1777 self.flow_markers.clear();
1778 self.interrupted_plain_by_comment = None;
1779 self.explicit_key_tab_check_pending = false;
1780 self.buf_leading_break.clear();
1781 self.buf_trailing_breaks.clear();
1782 self.buf_whitespaces.clear();
1783 }
1784
1785 fn queue_recovery_boundary(&mut self, token: QueuedToken<'input>) {
1786 self.document_prefix_allowed = matches!(token.1, QueuedTokenType::DocumentEnd);
1787 self.disallow_simple_key();
1788 self.tokens.push_back(token);
1789 self.token_available = true;
1790 }
1791
1792 pub(crate) fn check_source_error(&mut self) -> ScanResult {
1794 if let Some(kind) = self.input.take_source_error() {
1795 self.source_error
1796 .get_or_insert_with(|| ScanError::from_kind(self.mark, kind));
1797 }
1798 match &self.source_error {
1799 Some(error) => Err(error.clone()),
1800 None => Ok(()),
1801 }
1802 }
1803
1804 #[cfg(feature = "comments")]
1806 #[inline]
1807 pub(crate) fn comments_possible(&self) -> bool {
1808 self.options.emit_comments && self.comments_possible
1809 }
1810
1811 #[track_caller]
1818 #[inline]
1819 fn read_break(&mut self, s: &mut String) {
1820 self.skip_break();
1821 s.push('\n');
1822 }
1823
1824 #[track_caller]
1829 #[inline]
1830 fn skip_break(&mut self) {
1831 let c = self.input.peek();
1832 let nc = self.input.peek_nth(1);
1833 debug_assert!(is_break(c));
1834 if c == '\r' && nc == '\n' {
1835 self.skip_blank();
1836 }
1837 self.skip_nl();
1838 }
1839
1840 #[track_caller]
1845 fn insert_token(&mut self, pos: usize, tok: Token<'input>) {
1846 let old_len = self.tokens.len();
1847 assert!(pos <= old_len);
1848 self.tokens.insert(pos, tok.into());
1849 }
1850
1851 fn simple_key_token_index(&self, sk: &SimpleKey, mark: Marker) -> Result<usize, ScanError> {
1852 let Some(index) = sk.token_number.checked_sub(self.tokens_parsed) else {
1853 return Err(ScanError::from_kind(mark, ErrorKind::InvalidSimpleKey));
1854 };
1855 if index > self.tokens.len() {
1856 return Err(ScanError::from_kind(mark, ErrorKind::InvalidSimpleKey));
1857 }
1858 Ok(index)
1859 }
1860
1861 #[inline]
1862 fn allow_simple_key(&mut self) {
1863 self.simple_key_allowed = true;
1864 }
1865
1866 #[inline]
1867 fn disallow_simple_key(&mut self) {
1868 self.simple_key_allowed = false;
1869 }
1870
1871 fn fetch_next_token(&mut self) -> ScanResult {
1876 let result = self.fetch_next_token_impl();
1877 self.check_source_error()?;
1878 result
1879 }
1880
1881 fn fetch_next_token_impl(&mut self) -> ScanResult {
1882 self.input.lookahead(1);
1883
1884 if !self.stream_start_produced {
1885 self.fetch_stream_start();
1886 return Ok(());
1887 }
1888 loop {
1889 let outcome = self.skip_to_next_token()?;
1890 #[cfg(feature = "comments")]
1891 if outcome.queued_comment {
1892 return Ok(());
1893 }
1894 if outcome.saw_comment && !self.tokens.is_empty() {
1895 return Ok(());
1896 }
1897 if !outcome.saw_comment {
1898 break;
1899 }
1900 }
1903
1904 debug_print!(
1905 " \x1B[38;5;244m\u{2192} fetch_next_token after whitespace {:?} {:?}\x1B[m",
1906 self.mark,
1907 self.input.peek()
1908 );
1909
1910 self.stale_simple_keys()?;
1911
1912 let mark = self.mark;
1913 self.unroll_indent(mark.col as isize);
1914
1915 self.input.lookahead(4);
1916
1917 if self.input.next_is_z() {
1918 self.fetch_stream_end()?;
1919 return Ok(());
1920 }
1921
1922 self.ensure_current_char_is_printable()?;
1923
1924 if self.mark.col == 0 {
1925 if self.input.next_char_is('%') {
1926 self.in_directive_prefix = true;
1927 return self.fetch_directive();
1928 } else if self.input.next_is_document_start() {
1929 return self.fetch_document_indicator(TokenType::DocumentStart);
1930 } else if self.input.next_is_document_end() {
1931 self.fetch_document_indicator(TokenType::DocumentEnd)?;
1932 self.skip_ws_to_eol(SkipTabs::Yes)?;
1933 if !self.input.next_is_breakz() {
1934 return Err(self.scan_error(ErrorKind::InvalidDocumentEnd));
1935 }
1936 return Ok(());
1937 }
1938 }
1939
1940 self.in_directive_prefix = false;
1941 if self.document_prefix_allowed {
1942 self.document_prefix_allowed = false;
1943 }
1944
1945 if self.options.strict_indentation
1948 && self.flow_level > 0
1949 && (self.mark.col as isize) <= self.flow_block_indent()
1950 {
1951 return Err(self.scan_error(ErrorKind::InvalidIndentation));
1952 }
1953
1954 let c = self.input.peek();
1955 let nc = self.input.peek_nth(1);
1956 match c {
1957 '[' => self.fetch_flow_collection_start(TokenType::FlowSequenceStart),
1958 '{' => self.fetch_flow_collection_start(TokenType::FlowMappingStart),
1959 ']' => self.fetch_flow_collection_end(TokenType::FlowSequenceEnd, '[', ']'),
1960 '}' => self.fetch_flow_collection_end(TokenType::FlowMappingEnd, '{', '}'),
1961 ',' => self.fetch_flow_entry(),
1962 '-' if is_blank_or_breakz(nc) => self.fetch_block_entry(),
1963 '?' if is_blank_or_breakz(nc) => self.fetch_key(),
1964 ':' if is_blank_or_breakz(nc) => self.fetch_value(),
1965 ':' if self.flow_level > 0
1966 && (is_flow(nc) || self.mark.index() == self.adjacent_value_allowed_at) =>
1967 {
1968 self.fetch_flow_value()
1969 }
1970 '*' => self.fetch_anchor(true),
1972 '&' => self.fetch_anchor(false),
1974 '!' => self.fetch_tag(),
1975 '|' if self.flow_level == 0 => self.fetch_block_scalar(true),
1977 '>' if self.flow_level == 0 => self.fetch_block_scalar(false),
1979 '\'' => self.fetch_flow_scalar(true),
1980 '"' => self.fetch_flow_scalar(false),
1981 '-' if !is_blank_or_breakz(nc) => self.fetch_plain_scalar(),
1983 ':' | '?' if !is_blank_or_breakz(nc) && self.flow_level == 0 => {
1984 self.fetch_plain_scalar()
1985 }
1986 c if is_bom(c) => Err(self.scan_error(ErrorKind::BomInsideDocument)),
1987 '|' | '>' | '%' | '@' | '`' => {
1988 Err(self.scan_error(ErrorKind::UnexpectedCharacter { character: c }))
1989 }
1990 _ => self.fetch_plain_scalar(),
1991 }
1992 }
1993
1994 pub(crate) fn next_queued_token(&mut self) -> Result<Option<QueuedToken<'input>>, ScanError> {
1999 #[cfg(feature = "comments")]
2000 if self.deferred_error.is_some() {
2001 if !matches!(
2002 self.tokens.front().map(|token| &token.1),
2003 Some(QueuedTokenType::Comment(_))
2004 ) {
2005 if let Some(error) = self.deferred_error.take() {
2006 return error.into_result();
2007 }
2008 }
2009 self.token_available = true;
2010 }
2011
2012 if self.stream_end_produced {
2013 return Ok(None);
2014 }
2015
2016 if !self.token_available {
2017 #[cfg(feature = "comments")]
2018 if let Err(error) = self.fetch_more_tokens() {
2019 if matches!(
2020 self.tokens.front().map(|token| &token.1),
2021 Some(QueuedTokenType::Comment(_))
2022 ) {
2023 self.deferred_error = Some(error);
2024 } else {
2025 return Err(error);
2026 }
2027 }
2028 #[cfg(not(feature = "comments"))]
2029 self.fetch_more_tokens()?;
2030 }
2031 let Some(t) = self.tokens.pop_front() else {
2032 unreachable!("fetch_more_tokens succeeded without producing a token")
2033 };
2034 self.token_available = false;
2035 self.tokens_parsed += 1;
2036
2037 let is_stream_end = matches!(t.1, QueuedTokenType::StreamEnd);
2038 if is_stream_end {
2039 self.stream_end_produced = true;
2040 }
2041 Ok(Some(t))
2042 }
2043
2044 fn next_token(&mut self) -> Result<Option<Token<'input>>, ScanError> {
2049 Ok(self.next_queued_token()?.map(QueuedToken::into_public))
2050 }
2051
2052 fn fetch_more_tokens(&mut self) -> ScanResult {
2057 let mut need_more;
2058 loop {
2059 if self.tokens.is_empty() {
2060 need_more = true;
2061 } else {
2062 #[cfg(feature = "comments")]
2063 {
2064 need_more = false;
2065 }
2066 self.stale_simple_keys()?;
2068 #[cfg(feature = "comments")]
2069 if !matches!(
2070 self.tokens.front().map(|token| &token.1),
2071 Some(QueuedTokenType::Comment(_))
2072 ) {
2073 need_more = self.first_simple_key.is_some_and(|index| {
2075 self.simple_keys[index].token_number == self.tokens_parsed
2076 });
2077 }
2078 #[cfg(not(feature = "comments"))]
2079 {
2080 need_more = self.first_simple_key.is_some_and(|index| {
2081 self.simple_keys[index].token_number == self.tokens_parsed
2082 });
2083 }
2084 }
2085
2086 if let Some(token) = self.tokens.back() {
2089 if matches!(
2090 token.1,
2091 QueuedTokenType::DocumentEnd | QueuedTokenType::DocumentStart
2092 ) {
2093 break;
2094 }
2095 }
2096
2097 if !need_more {
2098 break;
2099 }
2100 self.fetch_next_token()?;
2101 }
2102 self.token_available = true;
2103
2104 Ok(())
2105 }
2106
2107 fn stale_simple_keys(&mut self) -> ScanResult {
2116 while let Some(index) = self.first_simple_key {
2117 let sk = &mut self.simple_keys[index];
2118 debug_assert!(sk.possible);
2119 let is_line_stale = self.flow_level == 0 && sk.mark.line < self.mark.line;
2120 let is_length_stale = self.mark.index().saturating_sub(sk.mark.index())
2123 > self.options.simple_key_max_lookahead;
2124
2125 if !is_line_stale && !is_length_stale {
2126 break;
2128 }
2129 if sk.required {
2130 return Err(Self::simple_key_expected(sk.mark));
2131 }
2132 sk.possible = false;
2133 self.first_simple_key = self.simple_keys[index + 1..]
2136 .iter()
2137 .position(|key| key.possible)
2138 .map(|offset| index + 1 + offset);
2139 }
2140 Ok(())
2141 }
2142
2143 #[cfg_attr(
2148 feature = "comments",
2149 doc = "\nWhen comment emission is enabled, encountered comments are queued as \
2150 [`TokenType::Comment`] tokens so the parser can emit presentation events."
2151 )]
2152 fn skip_to_next_token(&mut self) -> Result<SkipToNextTokenOutcome, ScanError> {
2157 let consume_linebreak = |this: &mut Self| {
2160 this.input.lookahead(2);
2161 this.skip_linebreak();
2162 if this.flow_level == 0 {
2163 this.allow_simple_key();
2164 }
2165 };
2166
2167 let mut outcome = SkipToNextTokenOutcome::default();
2168 loop {
2169 let ch = self.input.look_ch();
2170 if self.explicit_key_tab_check_pending {
2171 match ch {
2172 '\t' => {
2173 self.skip_explicit_key_tab()?;
2174 continue;
2175 }
2176 ' ' | '\n' | '\r' | '#' => {}
2177 '&' | '!' if self.options.strict_indentation => {}
2178 _ => self.explicit_key_tab_check_pending = false,
2179 }
2180 }
2181
2182 match ch {
2183 '\t' => {
2185 let indent = if self.options.strict_indentation && self.flow_level > 0 {
2186 self.flow_block_indent() + 1
2187 } else {
2188 self.indent
2189 };
2190 if self.is_within_block()
2191 && self.leading_whitespace
2192 && (self.mark.col as isize) < indent
2193 {
2194 let (chars_consumed, _) = self.input.skip_ws_to_eol_blanks(SkipTabs::Yes);
2195 self.mark.col += chars_consumed;
2196 self.mark.offsets.chars += chars_consumed;
2197 self.mark.offsets.bytes = self.input.byte_offset();
2198
2199 if self.input.look_ch() == '#' {
2201 continue;
2202 }
2203
2204 if !self.input.next_is_breakz() {
2206 return Err(self.scan_error(ErrorKind::TabInBlockIndentation));
2207 }
2208
2209 if matches!(self.input.look_ch(), '\n' | '\r') {
2211 consume_linebreak(self);
2212 }
2213 } else {
2214 self.skip_blank();
2216 }
2217 }
2218
2219 ' ' => self.skip_blank(),
2220
2221 '\n' | '\r' => consume_linebreak(self),
2222
2223 c if is_bom(c)
2224 && self.document_prefix_allowed
2225 && self.flow_level == 0
2226 && self.mark.col == 0 =>
2227 {
2228 self.skip_bom();
2229 }
2230
2231 '#' => {
2232 outcome.saw_comment = true;
2233 #[cfg(feature = "comments")]
2234 {
2235 outcome.queued_comment |= self.consume_comment()?;
2236 }
2237 #[cfg(not(feature = "comments"))]
2238 self.skip_comment()?;
2239
2240 if matches!(self.input.look_ch(), '\n' | '\r') {
2242 consume_linebreak(self);
2243 }
2244 return Ok(outcome);
2245 }
2246
2247 _ => break,
2248 }
2249 }
2250
2251 if let Some(err_mark) = self.interrupted_plain_by_comment.take() {
2254 let is_immediate_next_line = self.mark.line == err_mark.line + 1;
2258
2259 if self.flow_level == 0
2261 && is_immediate_next_line
2262 && (self.mark.col as isize) > self.indent
2263 {
2264 self.input.lookahead(4);
2268
2269 if !self.input.next_is_z()
2270 && !self.input.next_is_document_indicator()
2271 && self.input.next_can_be_plain_scalar(false)
2272 {
2273 return Err(ScanError::from_kind(
2274 err_mark,
2275 ErrorKind::CommentInterceptedScalar,
2276 ));
2277 }
2278 }
2279 }
2280
2281 Ok(outcome)
2282 }
2283
2284 fn skip_explicit_key_tab(&mut self) -> ScanResult {
2286 if self.leading_whitespace && (self.mark.col as isize) <= self.indent {
2287 let tab_mark = self.mark;
2288 let (chars_consumed, _) = self.input.skip_ws_to_eol_blanks(SkipTabs::Yes);
2289 self.mark.col += chars_consumed;
2290 self.mark.offsets.chars += chars_consumed;
2291 self.mark.offsets.bytes = self.input.byte_offset();
2292 if self.input.look_ch() != '#' && !self.input.next_is_breakz() {
2294 return Err(ScanError::from_kind(tab_mark, ErrorKind::TabNotAllowed));
2295 }
2296 } else {
2297 self.skip_blank();
2298 }
2299 self.disallow_simple_key();
2302 Ok(())
2303 }
2304
2305 fn skip_yaml_whitespace(&mut self) -> Result<bool, ScanError> {
2314 let mut need_whitespace = true;
2315 loop {
2316 match self.input.look_ch() {
2317 ' ' => {
2318 self.skip_blank();
2319
2320 need_whitespace = false;
2321 }
2322 '\t' => {
2323 self.skip_explicit_key_tab()?;
2324 need_whitespace = false;
2325 }
2326 '\n' | '\r' => {
2327 self.input.lookahead(2);
2328 self.skip_linebreak();
2329 if self.flow_level == 0 {
2330 self.allow_simple_key();
2331 }
2332 need_whitespace = false;
2333 }
2334 '#' => {
2335 if need_whitespace {
2336 self.skip_comment()?;
2337 } else {
2338 self.consume_comment()?;
2339 return Ok(true);
2340 }
2341 }
2342 _ => break,
2343 }
2344 }
2345
2346 if need_whitespace && !self.input.next_is_z() {
2350 Err(self.scan_error(ErrorKind::ExpectedWhitespace))
2351 } else {
2352 Ok(false)
2353 }
2354 }
2355
2356 fn skip_ws_to_eol(&mut self, skip_tabs: SkipTabs) -> Result<WhitespaceResult, ScanError> {
2358 #[cfg(feature = "comments")]
2359 if !self.comments_possible {
2360 let (chars_consumed, result) = self.input.skip_ws_to_eol(skip_tabs);
2361 self.mark.col += chars_consumed;
2362 self.mark.offsets.chars += chars_consumed;
2363 self.mark.offsets.bytes = self.input.byte_offset();
2364 return result.map_err(|kind| self.scan_error(kind));
2365 }
2366
2367 let (chars_consumed, whitespace) = self.input.skip_ws_to_eol_blanks(skip_tabs);
2368 self.mark.col += chars_consumed;
2369 self.mark.offsets.chars += chars_consumed;
2370 self.mark.offsets.bytes = self.input.byte_offset();
2371
2372 if self.input.look_ch() != '#' {
2373 return Ok(whitespace);
2374 }
2375
2376 if !whitespace.found_tabs() && !whitespace.has_valid_yaml_ws() {
2377 return Err(self.scan_error(ErrorKind::CommentNotSeparated));
2378 }
2379
2380 self.consume_comment()?;
2381 Ok(whitespace)
2382 }
2383
2384 fn fetch_stream_start(&mut self) {
2385 let mark = self.mark;
2386 self.indent = -1;
2387 self.stream_start_produced = true;
2388 self.allow_simple_key();
2389 self.tokens
2390 .push_back(Token(Span::empty(mark), TokenType::StreamStart).into());
2391 self.simple_keys.push(SimpleKey::new(Marker::new(0, 0, 0)));
2392 }
2393
2394 fn fetch_stream_end(&mut self) -> ScanResult {
2395 if let Some((mark, bracket)) = self.flow_markers.pop() {
2398 return Err(Self::unclosed_bracket(mark, bracket));
2399 }
2400
2401 for sk in &mut self.simple_keys {
2404 if sk.required && sk.possible {
2405 return Err(Self::simple_key_expected(sk.mark));
2406 }
2407 sk.possible = false;
2408 }
2409 self.first_simple_key = None;
2410
2411 self.unroll_indent(-1);
2412 self.remove_simple_key()?;
2413 self.disallow_simple_key();
2414
2415 self.tokens
2416 .push_back(Token(Span::empty(self.mark), TokenType::StreamEnd).into());
2417 Ok(())
2418 }
2419
2420 fn fetch_directive(&mut self) -> ScanResult {
2421 if self.options.disallow_directives {
2422 return Err(self.scan_error(ErrorKind::DirectiveDisallowed));
2423 }
2424
2425 self.unroll_indent(-1);
2426 self.remove_simple_key()?;
2427
2428 self.disallow_simple_key();
2429
2430 let token_index = self.tokens.len();
2431 let tok = self.scan_directive()?;
2432 self.insert_token(token_index, tok);
2433
2434 Ok(())
2435 }
2436
2437 fn scan_directive(&mut self) -> Result<Token<'input>, ScanError> {
2438 let start_mark = self.mark;
2439 self.skip_non_blank();
2440
2441 let mut budget = self.options.max_directive_bytes;
2444
2445 let name = self.scan_directive_name(&mut budget)?;
2446 let mut separated_comment = false;
2447 let tok = match name.as_ref() {
2448 "YAML" => self.scan_version_directive_value(&start_mark)?,
2449 "TAG" => self.scan_tag_directive_value(&start_mark, &mut budget)?,
2450 _ => {
2451 let mut params = Vec::new();
2452 while self.input.next_is_blank() {
2453 let n_blanks = self.input.skip_while_blank();
2454 self.mark.offsets.chars += n_blanks;
2455 self.mark.col += n_blanks;
2456 self.mark.offsets.bytes = self.input.byte_offset();
2457
2458 if self.input.peek() == '#' {
2459 separated_comment = true;
2460 break;
2461 }
2462
2463 if !is_blank_or_breakz(self.input.peek()) {
2464 self.spend_directive_bytes(&mut budget, n_blanks, start_mark)?;
2465 if params.len() >= self.options.max_reserved_directive_params {
2466 return Err(ScanError::from_kind(
2467 start_mark,
2468 ErrorKind::TooManyReservedDirectiveParams {
2469 limit: self.options.max_reserved_directive_params,
2470 },
2471 ));
2472 }
2473
2474 let mut param = String::new();
2475 let Some(n_chars) = self.fetch_directive_word(&mut param, &mut budget)
2476 else {
2477 return Err(self.directive_byte_limit(start_mark));
2478 };
2479 self.mark.offsets.chars += n_chars;
2480 self.mark.col += n_chars;
2481 self.mark.offsets.bytes = self.input.byte_offset();
2482 params.push(param);
2483 }
2484 }
2485
2486 Token(
2487 Span::new(start_mark, self.mark),
2488 TokenType::ReservedDirective(name, params),
2489 )
2490 }
2491 };
2492
2493 if separated_comment {
2494 self.consume_comment()?;
2496 } else {
2497 self.skip_ws_to_eol(SkipTabs::Yes)?;
2498 }
2499
2500 if self.input.next_is_breakz() {
2501 self.input.lookahead(2);
2502 self.skip_linebreak();
2503 Ok(tok)
2504 } else {
2505 Err(ScanError::from_kind(
2506 start_mark,
2507 ErrorKind::InvalidDirectiveTerminator,
2508 ))
2509 }
2510 }
2511
2512 fn scan_version_directive_value(&mut self, mark: &Marker) -> Result<Token<'input>, ScanError> {
2513 let n_blanks = self.input.skip_while_blank();
2514 self.mark.offsets.chars += n_blanks;
2515 self.mark.col += n_blanks;
2516 self.mark.offsets.bytes = self.input.byte_offset();
2517
2518 let major = self.scan_version_directive_number(mark)?;
2519
2520 if self.input.peek() != '.' {
2521 return Err(ScanError::from_kind(
2522 *mark,
2523 ErrorKind::MissingYamlVersionSeparator,
2524 ));
2525 }
2526 self.skip_non_blank();
2527
2528 let minor = self.scan_version_directive_number(mark)?;
2529
2530 Ok(Token(
2531 Span::new(*mark, self.mark),
2532 TokenType::VersionDirective(major, minor),
2533 ))
2534 }
2535
2536 fn directive_byte_limit(&self, start_mark: Marker) -> ScanError {
2538 ScanError::from_kind(
2539 start_mark,
2540 ErrorKind::DirectiveByteLimitExceeded {
2541 limit: self.options.max_directive_bytes,
2542 },
2543 )
2544 }
2545
2546 fn spend_directive_bytes(
2548 &self,
2549 budget: &mut usize,
2550 bytes: usize,
2551 start_mark: Marker,
2552 ) -> ScanResult {
2553 let Some(remaining) = budget.checked_sub(bytes) else {
2554 return Err(self.directive_byte_limit(start_mark));
2555 };
2556 *budget = remaining;
2557 Ok(())
2558 }
2559
2560 fn fetch_directive_word(&mut self, out: &mut String, budget: &mut usize) -> Option<usize> {
2567 let mut n_chars = 0;
2568
2569 loop {
2570 let c = self.input.look_ch();
2571 if !is_yaml_non_space(c) || is_z(c) {
2572 break;
2573 }
2574
2575 let len = c.len_utf8();
2576 if len > *budget {
2577 return None;
2578 }
2579 *budget -= len;
2580
2581 out.push(c);
2582 self.input.skip();
2583 n_chars += 1;
2584 }
2585
2586 Some(n_chars)
2587 }
2588
2589 fn scan_directive_name(&mut self, budget: &mut usize) -> Result<String, ScanError> {
2590 let start_mark = self.mark;
2591 let mut string = String::new();
2592
2593 let Some(n_chars) = self.fetch_directive_word(&mut string, budget) else {
2594 return Err(self.directive_byte_limit(start_mark));
2595 };
2596 self.mark.offsets.chars += n_chars;
2597 self.mark.col += n_chars;
2598 self.mark.offsets.bytes = self.input.byte_offset();
2599
2600 if string.is_empty() {
2601 return Err(ScanError::from_kind(
2602 start_mark,
2603 ErrorKind::MissingDirectiveName,
2604 ));
2605 }
2606
2607 if !is_blank_or_breakz(self.input.peek()) {
2608 return Err(ScanError::from_kind(
2609 start_mark,
2610 ErrorKind::InvalidDirectiveName,
2611 ));
2612 }
2613
2614 Ok(string)
2615 }
2616
2617 fn scan_version_directive_number(&mut self, mark: &Marker) -> Result<u32, ScanError> {
2618 let mut val = 0u32;
2619 let mut length = 0usize;
2620 while let Some(digit) = self.input.look_ch().to_digit(10) {
2621 if length + 1 > 9 {
2622 return Err(ScanError::from_kind(*mark, ErrorKind::YamlVersionTooLong));
2623 }
2624 length += 1;
2625 val = val * 10 + digit;
2626 self.skip_non_blank();
2627 }
2628
2629 if length == 0 {
2630 return Err(ScanError::from_kind(*mark, ErrorKind::MissingYamlVersion));
2631 }
2632
2633 Ok(val)
2634 }
2635
2636 fn scan_tag_directive_value(
2637 &mut self,
2638 mark: &Marker,
2639 budget: &mut usize,
2640 ) -> Result<Token<'input>, ScanError> {
2641 let n_blanks = self.input.skip_while_blank();
2642 self.mark.offsets.chars += n_blanks;
2643 self.mark.col += n_blanks;
2644 self.mark.offsets.bytes = self.input.byte_offset();
2645 self.spend_directive_bytes(budget, n_blanks, *mark)?;
2646
2647 let handle = self.scan_tag_handle_directive_cow(mark, budget)?;
2648
2649 let n_blanks = self.input.skip_while_blank();
2650 self.mark.offsets.chars += n_blanks;
2651 self.mark.col += n_blanks;
2652 self.mark.offsets.bytes = self.input.byte_offset();
2653 self.spend_directive_bytes(budget, n_blanks, *mark)?;
2654
2655 let prefix = self.scan_tag_prefix_directive_cow(mark, budget)?;
2656
2657 self.input.lookahead(1);
2658
2659 if self.input.next_is_blank_or_breakz() {
2660 Ok(Token(
2661 Span::new(*mark, self.mark),
2662 TokenType::TagDirective(handle, prefix),
2663 ))
2664 } else {
2665 Err(ScanError::from_kind(
2666 *mark,
2667 ErrorKind::InvalidTagDirectiveTerminator,
2668 ))
2669 }
2670 }
2671
2672 fn fetch_tag(&mut self) -> ScanResult {
2673 self.save_simple_key();
2674 self.disallow_simple_key();
2675
2676 let tok = self.scan_tag()?;
2677 self.tokens.push_back(tok.into());
2678 Ok(())
2679 }
2680
2681 fn scan_tag(&mut self) -> Result<Token<'input>, ScanError> {
2682 let start_mark = self.mark;
2683
2684 self.input.lookahead(2);
2686
2687 if self.input.byte_offset().is_none() {
2689 return self.scan_tag_owned(&start_mark);
2690 }
2691
2692 let (handle, suffix): (Cow<'input, str>, Cow<'input, str>) = if self
2693 .input
2694 .nth_char_is(1, '<')
2695 {
2696 let suffix = self.scan_verbatim_tag(&start_mark)?;
2698 (Cow::Owned(String::new()), Cow::Owned(suffix))
2699 } else {
2700 let handle = self.scan_tag_handle_cow(&start_mark)?;
2702 if handle.len() >= 2 && handle.starts_with('!') && handle.ends_with('!') {
2704 let suffix = self.scan_tag_shorthand_suffix_cow(&start_mark, true)?;
2706 (handle, suffix)
2707 } else {
2708 let remaining_suffix = self.scan_tag_shorthand_suffix_cow(&start_mark, false)?;
2713
2714 let suffix = self.combine_local_tag_suffix(&start_mark, handle, remaining_suffix);
2715
2716 if suffix.is_empty() {
2719 (Cow::Borrowed(""), Cow::Borrowed("!"))
2720 } else {
2721 (Cow::Borrowed("!"), suffix)
2722 }
2723 }
2724 };
2725
2726 if is_blank_or_breakz(self.input.look_ch())
2727 || (self.flow_level > 0 && matches!(self.input.peek(), ',' | ']' | '}'))
2728 {
2729 Ok(Token(
2732 Span::new(start_mark, self.mark),
2733 TokenType::Tag(handle, suffix),
2734 ))
2735 } else {
2736 Err(ScanError::from_kind(
2737 start_mark,
2738 ErrorKind::InvalidTagTerminator,
2739 ))
2740 }
2741 }
2742
2743 fn combine_local_tag_suffix(
2744 &self,
2745 start_mark: &Marker,
2746 handle: Cow<'input, str>,
2747 remaining: Cow<'input, str>,
2748 ) -> Cow<'input, str> {
2749 if handle.len() == 1 {
2750 return remaining;
2751 }
2752
2753 match (handle, remaining) {
2754 (Cow::Borrowed(handle), Cow::Borrowed(remaining)) => {
2755 if remaining.is_empty() {
2756 return Cow::Borrowed(&handle[1..]);
2757 }
2758
2759 let borrowed = start_mark
2760 .byte_offset()
2761 .and_then(|start| start.checked_add(1))
2762 .zip(self.input.byte_offset())
2763 .and_then(|(start, end)| self.try_borrow_slice(start, end));
2764 borrowed.map_or_else(
2765 || {
2766 let mut combined =
2767 String::with_capacity(handle.len() - 1 + remaining.len());
2768 combined.push_str(&handle[1..]);
2769 combined.push_str(remaining);
2770 Cow::Owned(combined)
2771 },
2772 Cow::Borrowed,
2773 )
2774 }
2775 (Cow::Borrowed(handle), Cow::Owned(mut remaining)) => {
2776 remaining.reserve(handle.len() - 1);
2777 remaining.insert_str(0, &handle[1..]);
2778 Cow::Owned(remaining)
2779 }
2780 (Cow::Owned(mut handle), remaining) => {
2781 handle.remove(0);
2782 handle.push_str(&remaining);
2783 Cow::Owned(handle)
2784 }
2785 }
2786 }
2787
2788 fn scan_tag_owned(&mut self, start_mark: &Marker) -> Result<Token<'input>, ScanError> {
2790 let mut handle = String::new();
2791 let mut suffix;
2792
2793 if self.input.nth_char_is(1, '<') {
2794 suffix = self.scan_verbatim_tag(start_mark)?;
2795 } else {
2796 handle = self.scan_tag_handle(false, start_mark)?;
2798 if handle.len() >= 2 && handle.starts_with('!') && handle.ends_with('!') {
2800 let is_secondary_handle = handle == "!!";
2802 suffix =
2803 self.scan_tag_shorthand_suffix(false, is_secondary_handle, "", start_mark)?;
2804 } else {
2805 suffix = self.scan_tag_shorthand_suffix(false, false, &handle, start_mark)?;
2806 "!".clone_into(&mut handle);
2807 if suffix.is_empty() {
2810 handle.clear();
2811 "!".clone_into(&mut suffix);
2812 }
2813 }
2814 }
2815
2816 if is_blank_or_breakz(self.input.look_ch())
2817 || (self.flow_level > 0 && matches!(self.input.peek(), ',' | ']' | '}'))
2818 {
2819 Ok(Token(
2822 Span::new(*start_mark, self.mark),
2823 TokenType::Tag(handle.into(), suffix.into()),
2824 ))
2825 } else {
2826 Err(ScanError::from_kind(
2827 *start_mark,
2828 ErrorKind::InvalidTagTerminator,
2829 ))
2830 }
2831 }
2832
2833 fn scan_tag_handle_cow(&mut self, mark: &Marker) -> Result<Cow<'input, str>, ScanError> {
2838 let Some(start) = self.input.byte_offset() else {
2839 return Ok(Cow::Owned(self.scan_tag_handle(false, mark)?));
2840 };
2841
2842 if self.input.look_ch() != '!' {
2843 return Err(ScanError::from_kind(*mark, ErrorKind::ExpectedTagBang));
2844 }
2845
2846 self.skip_non_blank();
2848
2849 self.input.lookahead(1);
2851 while self.input.next_is_alpha() {
2852 self.skip_non_blank();
2853 self.input.lookahead(1);
2854 }
2855
2856 if self.input.peek() == '!' {
2858 self.skip_non_blank();
2859 }
2860
2861 let Some(end) = self.input.byte_offset() else {
2862 return Ok(Cow::Owned(self.scan_tag_handle(false, mark)?));
2863 };
2864
2865 if let Some(slice) = self.try_borrow_slice(start, end) {
2866 Ok(Cow::Borrowed(slice))
2867 } else {
2868 let slice = self
2869 .input
2870 .slice_bytes(start, end)
2871 .ok_or_else(|| ScanError::from_kind(*mark, ErrorKind::InputSlicingUnavailable))?;
2872 Ok(Cow::Owned(slice.to_owned()))
2873 }
2874 }
2875
2876 fn scan_tag_shorthand_suffix_cow(
2880 &mut self,
2881 mark: &Marker,
2882 require_non_empty: bool,
2883 ) -> Result<Cow<'input, str>, ScanError> {
2884 let Some(start) = self.input.byte_offset() else {
2885 return Ok(Cow::Owned(
2886 self.scan_tag_shorthand_suffix(false, false, "", mark)?,
2887 ));
2888 };
2889
2890 while is_tag_char(self.input.look_ch()) {
2892 if self.input.peek() == '%' {
2893 let current = self
2895 .input
2896 .byte_offset()
2897 .expect("byte_offset() must remain available once enabled");
2898 let mut out = if let Some(slice) = self.input.slice_bytes(start, current) {
2899 slice.to_owned()
2900 } else {
2901 String::new()
2902 };
2903
2904 while is_tag_char(self.input.look_ch()) {
2906 if self.input.peek() == '%' {
2907 out.push(self.scan_uri_escapes(mark)?);
2908 } else {
2909 out.push(self.input.peek());
2910 self.skip_non_blank();
2911 }
2912 }
2913 return Ok(Cow::Owned(out));
2914 }
2915 self.skip_non_blank();
2916 }
2917
2918 let Some(end) = self.input.byte_offset() else {
2919 return Ok(Cow::Owned(
2920 self.scan_tag_shorthand_suffix(false, false, "", mark)?,
2921 ));
2922 };
2923
2924 if require_non_empty && start == end {
2925 return Err(ScanError::from_kind(*mark, ErrorKind::MissingTagUri));
2926 }
2927
2928 if let Some(slice) = self.try_borrow_slice(start, end) {
2929 Ok(Cow::Borrowed(slice))
2930 } else {
2931 let slice = self
2932 .input
2933 .slice_bytes(start, end)
2934 .ok_or_else(|| ScanError::from_kind(*mark, ErrorKind::InputSlicingUnavailable))?;
2935 Ok(Cow::Owned(slice.to_owned()))
2936 }
2937 }
2938
2939 fn scan_tag_handle(&mut self, directive: bool, mark: &Marker) -> Result<String, ScanError> {
2940 let mut string = String::new();
2941 if self.input.look_ch() != '!' {
2942 return Err(ScanError::from_kind(*mark, ErrorKind::ExpectedTagBang));
2943 }
2944
2945 string.push(self.input.peek());
2946 self.skip_non_blank();
2947
2948 let n_chars = self.input.fetch_while_is_alpha(&mut string);
2949 self.mark.offsets.chars += n_chars;
2950 self.mark.col += n_chars;
2951 self.mark.offsets.bytes = self.input.byte_offset();
2952
2953 if self.input.peek() == '!' {
2955 string.push(self.input.peek());
2956 self.skip_non_blank();
2957 } else if directive && string != "!" {
2958 return Err(ScanError::from_kind(
2962 *mark,
2963 ErrorKind::ExpectedTagDirectiveBang,
2964 ));
2965 }
2966 Ok(string)
2967 }
2968
2969 fn scan_verbatim_tag(&mut self, start_mark: &Marker) -> Result<String, ScanError> {
2973 self.skip_non_blank();
2975 self.skip_non_blank();
2976
2977 let mut string = String::new();
2978 while is_uri_char(self.input.look_ch()) {
2979 if self.input.peek() == '%' {
2980 string.push(self.scan_uri_escapes(start_mark)?);
2981 } else {
2982 string.push(self.input.peek());
2983 self.skip_non_blank();
2984 }
2985 }
2986
2987 if string.is_empty() {
2988 return Err(ScanError::from_kind(*start_mark, ErrorKind::MissingTagUri));
2989 }
2990
2991 if self.input.peek() != '>' {
2992 return Err(ScanError::from_kind(
2993 *start_mark,
2994 ErrorKind::UnclosedVerbatimTag,
2995 ));
2996 }
2997 self.skip_non_blank();
2998
2999 Ok(string)
3000 }
3001
3002 fn scan_tag_shorthand_suffix(
3003 &mut self,
3004 _directive: bool,
3005 _is_secondary: bool,
3006 head: &str,
3007 mark: &Marker,
3008 ) -> Result<String, ScanError> {
3009 let mut length = head.len();
3010 let mut string = String::new();
3011
3012 if length > 1 {
3015 string.extend(head.chars().skip(1));
3016 }
3017
3018 while is_tag_char(self.input.look_ch()) {
3019 if self.input.peek() == '%' {
3021 string.push(self.scan_uri_escapes(mark)?);
3022 } else {
3023 string.push(self.input.peek());
3024 self.skip_non_blank();
3025 }
3026
3027 length += 1;
3028 }
3029
3030 if length == 0 {
3031 return Err(ScanError::from_kind(*mark, ErrorKind::MissingTagUri));
3032 }
3033
3034 Ok(string)
3035 }
3036
3037 fn scan_uri_escapes(&mut self, mark: &Marker) -> Result<char, ScanError> {
3038 self.scan_uri_escapes_with_budget(mark, None)
3039 }
3040
3041 fn scan_uri_escapes_with_budget(
3042 &mut self,
3043 mark: &Marker,
3044 mut directive_budget: Option<&mut usize>,
3045 ) -> Result<char, ScanError> {
3046 let mut width = 0usize;
3047 let mut bytes = [0u8; 4];
3048 let mut bytes_len = 0usize;
3049 loop {
3050 self.input.lookahead(3);
3051
3052 let c = self.input.peek_nth(1);
3053 let nc = self.input.peek_nth(2);
3054
3055 if !(self.input.peek() == '%' && is_hex(c) && is_hex(nc)) {
3056 return Err(ScanError::from_kind(*mark, ErrorKind::InvalidTagEscape));
3057 }
3058
3059 if let Some(budget) = directive_budget.as_deref_mut() {
3060 self.spend_directive_bytes(budget, 3, *mark)?;
3061 }
3062
3063 let byte = u8::try_from((as_hex(c) << 4) + as_hex(nc))
3064 .expect("two hex nibbles always fit in a byte");
3065 if width == 0 {
3066 width = match byte {
3067 _ if byte & 0x80 == 0x00 => 1,
3068 _ if byte & 0xE0 == 0xC0 => 2,
3069 _ if byte & 0xF0 == 0xE0 => 3,
3070 _ if byte & 0xF8 == 0xF0 => 4,
3071 _ => {
3072 return Err(ScanError::from_kind(
3073 *mark,
3074 ErrorKind::InvalidTagUtf8LeadingByte,
3075 ));
3076 }
3077 };
3078 } else if byte & 0xc0 != 0x80 {
3079 return Err(ScanError::from_kind(
3080 *mark,
3081 ErrorKind::InvalidTagUtf8TrailingByte,
3082 ));
3083 }
3084
3085 bytes[bytes_len] = byte;
3086 bytes_len += 1;
3087
3088 self.skip_n_non_blank(3);
3089
3090 width -= 1;
3091 if width == 0 {
3092 break;
3093 }
3094 }
3095
3096 let s = core::str::from_utf8(&bytes[..bytes_len])
3097 .map_err(|_| ScanError::from_kind(*mark, ErrorKind::InvalidTagUtf8))?;
3098
3099 let Some(ch) = s.chars().next() else {
3100 unreachable!("a validated URI escape cannot decode to an empty string")
3101 };
3102 Ok(ch)
3103 }
3104
3105 fn fetch_anchor(&mut self, alias: bool) -> ScanResult {
3106 self.save_simple_key();
3107 self.disallow_simple_key();
3108
3109 let tok = self.scan_anchor(alias)?;
3110
3111 self.tokens.push_back(tok.into());
3112
3113 Ok(())
3114 }
3115
3116 fn scan_anchor(&mut self, alias: bool) -> Result<Token<'input>, ScanError> {
3117 let start_mark = self.mark;
3118
3119 self.skip_non_blank();
3121
3122 if let Some(start) = self.input.byte_offset() {
3124 while is_anchor_char(self.input.look_ch()) {
3125 self.skip_non_blank();
3126 }
3127
3128 let end = self
3129 .input
3130 .byte_offset()
3131 .expect("byte_offset() must remain available once enabled");
3132
3133 if start == end {
3134 return Err(ScanError::from_kind(
3135 start_mark,
3136 ErrorKind::MissingAnchorOrAliasName,
3137 ));
3138 }
3139
3140 let cow = if let Some(slice) = self.try_borrow_slice(start, end) {
3141 Cow::Borrowed(slice)
3142 } else if let Some(slice) = self.input.slice_bytes(start, end) {
3143 Cow::Owned(slice.to_owned())
3144 } else {
3145 return Err(ScanError::from_kind(
3146 start_mark,
3147 ErrorKind::InputSlicingUnavailable,
3148 ));
3149 };
3150
3151 let tok = if alias {
3152 TokenType::Alias(cow)
3153 } else {
3154 TokenType::Anchor(cow)
3155 };
3156 return Ok(Token(Span::new(start_mark, self.mark), tok));
3157 }
3158
3159 let mut string = String::new();
3160 while is_anchor_char(self.input.look_ch()) {
3161 string.push(self.input.peek());
3162 self.skip_non_blank();
3163 }
3164
3165 if string.is_empty() {
3166 return Err(ScanError::from_kind(
3167 start_mark,
3168 ErrorKind::MissingAnchorOrAliasName,
3169 ));
3170 }
3171
3172 let tok = if alias {
3173 TokenType::Alias(string.into())
3174 } else {
3175 TokenType::Anchor(string.into())
3176 };
3177 Ok(Token(Span::new(start_mark, self.mark), tok))
3178 }
3179
3180 fn fetch_flow_collection_start(&mut self, tok: TokenType<'input>) -> ScanResult {
3181 self.save_simple_key();
3183
3184 let start_mark = self.mark;
3185 let indicator = self.input.peek();
3186 self.flow_markers.push((start_mark, indicator));
3187
3188 self.roll_one_col_indent();
3189 self.increase_flow_level()?;
3190
3191 self.allow_simple_key();
3192
3193 self.skip_non_blank();
3194 let end_mark = self.mark;
3195
3196 if tok == TokenType::FlowMappingStart {
3197 self.flow_mapping_started.push(true);
3198 } else {
3199 self.flow_mapping_started.push(false);
3200 self.implicit_flow_mapping_states
3201 .push(ImplicitMappingState::Possible);
3202 }
3203
3204 let token_index = self.tokens.len();
3205 self.skip_ws_to_eol(SkipTabs::Yes)?;
3206
3207 self.insert_token(token_index, Token(Span::new(start_mark, end_mark), tok));
3208 Ok(())
3209 }
3210
3211 fn fetch_flow_collection_end(
3212 &mut self,
3213 tok: TokenType<'input>,
3214 expected_open: char,
3215 actual_close: char,
3216 ) -> ScanResult {
3217 if self.flow_level == 0 {
3219 return Err(self.scan_error(ErrorKind::MisplacedFlowCollectionEnd));
3220 }
3221
3222 let Some((open_mark, open_ch)) = self.flow_markers.pop() else {
3223 return Err(self.scan_error(ErrorKind::MisplacedFlowCollectionEnd));
3224 };
3225
3226 if open_ch != expected_open {
3227 return Err(ScanError::from_kind(
3228 open_mark,
3229 ErrorKind::MismatchedFlowCollectionEnd {
3230 open: open_ch,
3231 close: actual_close,
3232 },
3233 ));
3234 }
3235
3236 let flow_level = self.flow_level;
3237
3238 self.remove_simple_key()?;
3239
3240 if matches!(tok, TokenType::FlowSequenceEnd) {
3241 self.end_implicit_mapping(self.mark, flow_level);
3242 self.implicit_flow_mapping_states.pop();
3244 }
3245 self.flow_mapping_started.pop();
3246
3247 self.decrease_flow_level();
3248
3249 self.disallow_simple_key();
3250
3251 let start_mark = self.mark;
3252 self.skip_non_blank();
3253 let end_mark = self.mark;
3254 let token_index = self.tokens.len();
3255 self.skip_ws_to_eol(SkipTabs::Yes)?;
3256
3257 if self.flow_level > 0 {
3263 self.adjacent_value_allowed_at = self.mark.index();
3264 }
3265
3266 self.insert_token(token_index, Token(Span::new(start_mark, end_mark), tok));
3267 Ok(())
3268 }
3269
3270 fn fetch_flow_entry(&mut self) -> ScanResult {
3272 self.remove_simple_key()?;
3273 self.allow_simple_key();
3274
3275 self.end_implicit_mapping(self.mark, self.flow_level);
3276 if self.current_flow_collection_is_sequence() {
3277 self.set_current_flow_mapping_started(false);
3278 }
3279
3280 let start_mark = self.mark;
3281 self.skip_non_blank();
3282 let end_mark = self.mark;
3283 let token_index = self.tokens.len();
3284 self.skip_ws_to_eol(SkipTabs::Yes)?;
3285
3286 self.insert_token(
3287 token_index,
3288 Token(Span::new(start_mark, end_mark), TokenType::FlowEntry),
3289 );
3290 Ok(())
3291 }
3292
3293 fn increase_flow_level(&mut self) -> ScanResult {
3294 if self.flow_level >= self.options.flow_nesting_limit {
3295 return Err(self.scan_error(ErrorKind::RecursionLimitExceeded));
3296 }
3297
3298 self.simple_keys.push(SimpleKey::new(Marker::new(0, 0, 0)));
3299 self.flow_level += 1;
3300 Ok(())
3301 }
3302
3303 fn decrease_flow_level(&mut self) {
3304 if self.flow_level > 0 {
3305 debug_assert!(!self.simple_keys.last().unwrap().possible);
3307 debug_assert_ne!(self.first_simple_key, Some(self.flow_level));
3308 self.flow_level -= 1;
3309 self.simple_keys.pop().unwrap();
3310 }
3311 }
3312
3313 fn fetch_block_entry(&mut self) -> ScanResult {
3319 if self.flow_level > 0 {
3320 return Err(self.scan_error(ErrorKind::BlockEntryInFlowCollection));
3322 }
3323 if !self.simple_key_allowed {
3325 return Err(self.scan_error(ErrorKind::BlockSequenceEntryNotAllowed));
3326 }
3327
3328 let mark = self.mark;
3330 self.skip_non_blank();
3331
3332 self.roll_indent(mark.col, None, TokenType::BlockSequenceStart, mark)?;
3334 let token_index = self.tokens.len();
3335 let found_tabs = self.skip_ws_to_eol(SkipTabs::Yes)?.found_tabs();
3336 self.input.lookahead(2);
3337 if found_tabs && self.input.next_char_is('-') && is_blank_or_breakz(self.input.peek_nth(1))
3338 {
3339 return Err(self.scan_error(ErrorKind::InvalidBlockEntryWhitespace));
3340 }
3341
3342 self.skip_ws_to_eol(SkipTabs::No)?;
3343 self.input.lookahead(1);
3344 if self.input.next_is_break() || self.input.next_is_flow() {
3345 self.roll_one_col_indent();
3346 }
3347
3348 self.remove_simple_key()?;
3349 self.allow_simple_key();
3350
3351 self.insert_token(
3352 token_index,
3353 Token(Span::empty(self.mark), TokenType::BlockEntry),
3354 );
3355
3356 Ok(())
3357 }
3358
3359 fn fetch_document_indicator(&mut self, t: TokenType<'input>) -> ScanResult {
3360 self.in_directive_prefix = false;
3361 if let Some((mark, bracket)) = self.flow_markers.pop() {
3362 return Err(ScanError::from_kind(
3363 mark,
3364 ErrorKind::UnclosedFlowCollection { open: bracket },
3365 ));
3366 }
3367
3368 self.unroll_indent(-1);
3369 self.remove_simple_key()?;
3370 self.disallow_simple_key();
3371
3372 let mark = self.mark;
3373
3374 self.skip_n_non_blank(3);
3375
3376 self.document_prefix_allowed = matches!(t, TokenType::DocumentEnd);
3377 self.tokens
3378 .push_back(Token(Span::new(mark, self.mark), t).into());
3379 Ok(())
3380 }
3381
3382 fn fetch_block_scalar(&mut self, literal: bool) -> ScanResult {
3383 self.save_simple_key();
3384 self.allow_simple_key();
3385 let token_index = self.tokens.len();
3386 let tok = self.scan_block_scalar(literal)?;
3387
3388 self.insert_token(token_index, tok);
3389 Ok(())
3390 }
3391
3392 #[allow(clippy::too_many_lines)]
3393 fn scan_block_scalar(&mut self, literal: bool) -> Result<Token<'input>, ScanError> {
3394 let start_mark = self.mark;
3395 let mut chomping = Chomping::Clip;
3396 let mut increment: usize = 0;
3397 let mut indent: usize = 0;
3398 let mut trailing_blank: bool;
3399 let mut leading_blank: bool = false;
3400 let style = if literal {
3401 ScalarStyle::Literal
3402 } else {
3403 ScalarStyle::Folded
3404 };
3405
3406 let mut string = String::new();
3407 let mut leading_break = false;
3408 let mut trailing_breaks = 0;
3409 let mut chomping_break = false;
3410
3411 self.skip_non_blank();
3413 self.unroll_non_block_indents();
3414
3415 if self.input.look_ch() == '+' || self.input.peek() == '-' {
3416 if self.input.peek() == '+' {
3417 chomping = Chomping::Keep;
3418 } else {
3419 chomping = Chomping::Strip;
3420 }
3421 self.skip_non_blank();
3422 self.input.lookahead(1);
3423 if self.input.next_is_digit() {
3424 if self.input.peek() == '0' {
3425 return Err(ScanError::from_kind(
3426 start_mark,
3427 ErrorKind::ZeroBlockScalarIndent,
3428 ));
3429 }
3430 increment = (self.input.peek() as usize) - ('0' as usize);
3431 self.skip_non_blank();
3432 }
3433 } else if self.input.next_is_digit() {
3434 if self.input.peek() == '0' {
3435 return Err(ScanError::from_kind(
3436 start_mark,
3437 ErrorKind::ZeroBlockScalarIndent,
3438 ));
3439 }
3440
3441 increment = (self.input.peek() as usize) - ('0' as usize);
3442 self.skip_non_blank();
3443 self.input.lookahead(1);
3444 if self.input.peek() == '+' || self.input.peek() == '-' {
3445 if self.input.peek() == '+' {
3446 chomping = Chomping::Keep;
3447 } else {
3448 chomping = Chomping::Strip;
3449 }
3450 self.skip_non_blank();
3451 }
3452 }
3453
3454 self.skip_ws_to_eol(SkipTabs::Yes)?;
3455
3456 self.input.lookahead(1);
3458 self.ensure_current_char_is_printable()?;
3459 if !self.input.next_is_breakz() {
3460 return Err(ScanError::from_kind(
3461 start_mark,
3462 ErrorKind::InvalidBlockScalarHeader,
3463 ));
3464 }
3465
3466 if self.input.next_is_break() {
3467 self.input.lookahead(2);
3468 self.skip_break();
3469 chomping_break = true;
3470 }
3471
3472 self.ensure_current_char_is_printable()?;
3473
3474 if self.input.look_ch() == '\t' {
3475 return Err(ScanError::from_kind(
3476 start_mark,
3477 ErrorKind::TabAtBlockScalarStart,
3478 ));
3479 }
3480
3481 if increment > 0 {
3482 indent = if self.indent >= 0 {
3483 (self.indent + increment as isize) as usize
3484 } else {
3485 increment
3486 }
3487 }
3488
3489 if indent == 0 {
3491 self.skip_block_scalar_first_line_indent(&mut indent, &mut trailing_breaks);
3492 } else {
3493 self.skip_block_scalar_indent(indent, &mut trailing_breaks);
3494 }
3495
3496 self.ensure_current_char_is_printable()?;
3497
3498 if self.input.next_is_z() {
3503 let contents = match chomping {
3504 Chomping::Strip => String::new(),
3506 _ if self.mark.line == start_mark.line() => String::new(),
3508 Chomping::Clip => String::new(),
3510 Chomping::Keep if trailing_breaks == 0 && self.mark.col > 0 => {
3512 if chomping_break {
3513 "\n".into()
3514 } else {
3515 String::new()
3516 }
3517 }
3518 Chomping::Keep => "\n".repeat(trailing_breaks),
3520 };
3521
3522 return Ok(Token(
3523 Span::new(start_mark, self.mark),
3524 TokenType::Scalar(style, contents.into()),
3525 ));
3526 }
3527
3528 if self.mark.col < indent && (self.mark.col as isize) > self.indent {
3529 if self.indent < 0 && self.mark.col == 0 {
3530 self.input.lookahead(4);
3531 if self.input.next_is_document_start()
3532 || self.input.next_is_document_end()
3533 || self.input.peek() == '#'
3534 {
3535 } else {
3539 return Err(self.scan_error(ErrorKind::InvalidBlockScalarIndent));
3540 }
3541 } else {
3542 return Err(self.scan_error(ErrorKind::InvalidBlockScalarIndent));
3543 }
3544 }
3545
3546 let start_mark = self.mark;
3547 while self.mark.col == indent && !self.input.next_is_z() {
3548 self.ensure_current_char_is_printable()?;
3549
3550 if indent == 0 {
3551 self.input.lookahead(4);
3552 if self.input.next_is_document_indicator() {
3553 break;
3554 }
3555 }
3556
3557 trailing_blank = self.input.next_is_blank();
3559 if !literal && leading_break && !leading_blank && !trailing_blank {
3560 string.extend(core::iter::repeat('\n').take(trailing_breaks));
3561 if trailing_breaks == 0 {
3562 string.push(' ');
3563 }
3564 } else {
3565 if leading_break {
3566 string.push('\n');
3567 }
3568 string.extend(core::iter::repeat('\n').take(trailing_breaks));
3569 }
3570
3571 leading_break = false;
3572 trailing_breaks = 0;
3573
3574 leading_blank = self.input.next_is_blank();
3575
3576 self.scan_block_scalar_content_line(&mut string);
3577
3578 self.input.lookahead(2);
3580 if self.input.next_is_z() {
3581 break;
3582 }
3583
3584 self.ensure_current_char_is_printable()?;
3585
3586 self.skip_break();
3587 leading_break = true;
3588
3589 self.skip_block_scalar_indent(indent, &mut trailing_breaks);
3591 }
3592
3593 self.ensure_current_char_is_printable()?;
3594
3595 if chomping != Chomping::Strip {
3597 if !leading_break && self.input.next_is_z() && self.mark.col >= indent.max(1) {
3603 string.push('\n');
3604 }
3605 if leading_break {
3606 string.push('\n');
3607 }
3608 }
3609
3610 if chomping == Chomping::Keep {
3611 string.extend(core::iter::repeat('\n').take(trailing_breaks));
3612 }
3613
3614 if let Some(character) = find_non_printable(&string) {
3615 return Err(ScanError::from_kind(
3616 start_mark,
3617 ErrorKind::UnexpectedCharacter { character },
3618 ));
3619 }
3620
3621 let span = if string.trim().is_empty() {
3622 Span::new(start_mark, self.mark)
3623 } else {
3624 Span::new(start_mark, self.mark).with_indent(Some(indent))
3625 };
3626
3627 Ok(Token(span, TokenType::Scalar(style, string.into())))
3628 }
3629
3630 fn scan_block_scalar_content_line(&mut self, string: &mut String) {
3635 let chars_consumed = self.input.fetch_block_scalar_line(string);
3636 self.mark.col += chars_consumed;
3637 self.mark.offsets.chars += chars_consumed;
3638 self.mark.offsets.bytes = self.input.byte_offset();
3639 }
3642
3643 fn skip_block_scalar_indent(&mut self, indent: usize, breaks: &mut usize) {
3645 loop {
3646 if indent < self.input.bufmaxlen().saturating_sub(2) {
3648 self.input.lookahead(self.input.bufmaxlen());
3649 while self.mark.col < indent && self.input.peek() == ' ' {
3650 self.skip_blank();
3651 }
3652 } else {
3653 loop {
3654 self.input.lookahead(self.input.bufmaxlen());
3655 while !self.input.buf_is_empty()
3656 && self.mark.col < indent
3657 && self.input.peek() == ' '
3658 {
3659 self.skip_blank();
3660 }
3661 if self.mark.col == indent
3665 || (!self.input.buf_is_empty() && self.input.peek() != ' ')
3666 {
3667 break;
3668 }
3669 }
3670 self.input.lookahead(2);
3671 }
3672
3673 if self.input.next_is_break() {
3675 self.skip_break();
3676 *breaks += 1;
3677 } else {
3678 break;
3680 }
3681 }
3682 }
3683
3684 fn skip_block_scalar_first_line_indent(&mut self, indent: &mut usize, breaks: &mut usize) {
3689 let mut max_indent = 0;
3690 loop {
3691 while self.input.look_ch() == ' ' {
3693 self.skip_blank();
3694 }
3695
3696 if self.mark.col > max_indent {
3697 max_indent = self.mark.col;
3698 }
3699
3700 if self.input.next_is_break() {
3701 self.input.lookahead(2);
3703 self.skip_break();
3704 *breaks += 1;
3705 } else {
3706 break;
3708 }
3709 }
3710
3711 *indent = max_indent.max((self.indent + 1) as usize);
3720 }
3721
3722 fn fetch_flow_scalar(&mut self, single: bool) -> ScanResult {
3723 self.save_simple_key();
3724 self.disallow_simple_key();
3725
3726 let token_index = self.tokens.len();
3727 let tok = self.scan_flow_scalar(single)?;
3728
3729 if self.skip_to_next_token()?.saw_comment {
3732 self.adjacent_value_allowed_at = usize::MAX;
3733 } else {
3734 self.adjacent_value_allowed_at = self.mark.index();
3735 }
3736
3737 self.insert_token(token_index, tok);
3738 Ok(())
3739 }
3740
3741 #[allow(clippy::too_many_lines)]
3742 fn scan_flow_scalar(&mut self, single: bool) -> Result<Token<'input>, ScanError> {
3743 let start_mark = self.mark;
3744
3745 let mut buf = match self.input.byte_offset() {
3747 Some(off) => FlowScalarBuf::new_borrowed(off + self.input.peek().len_utf8()),
3748 None => FlowScalarBuf::new_owned(),
3749 };
3750
3751 self.skip_non_blank();
3753
3754 loop {
3755 self.input.lookahead(4);
3757
3758 if self.mark.col == 0 && self.input.next_is_document_indicator() {
3759 return Err(ScanError::from_kind(
3760 start_mark,
3761 ErrorKind::DocumentIndicatorInQuotedScalar,
3762 ));
3763 }
3764
3765 if self.input.next_is_z() {
3766 return Err(ScanError::from_kind(
3767 start_mark,
3768 ErrorKind::UnclosedQuotedScalar,
3769 ));
3770 }
3771
3772 self.ensure_current_char_is_printable()?;
3773
3774 let mut leading_blanks = false;
3776 self.consume_flow_scalar_non_whitespace_chars(
3777 single,
3778 &mut buf,
3779 &mut leading_blanks,
3780 &start_mark,
3781 )?;
3782
3783 match self.input.look_ch() {
3784 '\'' if single && !leading_blanks => break,
3785 '"' if !single && !leading_blanks => break,
3786 _ => {}
3787 }
3788
3789 let mut trailing_ws_start: Option<usize> = None;
3805 let mut has_leading_break = false;
3806 let mut has_trailing_breaks = false;
3807
3808 let mut pending_ws_start: Option<usize> = None;
3810
3811 while self.input.next_is_blank() || self.input.next_is_break() {
3813 if self.input.next_is_blank() {
3814 if leading_blanks {
3816 let indent = if self.options.strict_indentation && self.flow_level > 0 {
3817 self.flow_block_indent() + 1
3818 } else {
3819 self.indent
3820 };
3821 if self.input.peek() == '\t' && (self.mark.col as isize) < indent {
3822 return Err(self.scan_error(ErrorKind::TabInIndentation));
3823 }
3824 self.skip_blank();
3825 } else {
3826 match buf {
3828 FlowScalarBuf::Owned(ref mut string) => {
3829 if trailing_ws_start.is_none() {
3830 trailing_ws_start = Some(string.len());
3831 }
3832 string.push(self.input.peek());
3833 }
3834 FlowScalarBuf::Borrowed { .. } => {
3835 if pending_ws_start.is_none() {
3836 pending_ws_start = self.input.byte_offset();
3837 }
3838 }
3839 }
3840 self.skip_blank();
3841
3842 if let (FlowScalarBuf::Borrowed { .. }, Some(ws_start), Some(ws_end)) =
3843 (&mut buf, pending_ws_start, self.input.byte_offset())
3844 {
3845 buf.note_pending_ws(ws_start, ws_end);
3846 }
3847 }
3848 } else {
3849 self.input.lookahead(2);
3850
3851 if leading_blanks {
3853 match buf {
3855 FlowScalarBuf::Owned(ref mut string) => self.read_break(string),
3856 FlowScalarBuf::Borrowed { .. } => {
3857 self.promote_flow_scalar_buf_to_owned(&start_mark, &mut buf)?;
3858 let Some(string) = buf.as_owned_mut() else {
3859 unreachable!()
3860 };
3861 self.read_break(string);
3862 }
3863 }
3864 has_trailing_breaks = true;
3865 } else {
3866 if let Some(pos) = trailing_ws_start.take() {
3868 if let FlowScalarBuf::Owned(ref mut string) = buf {
3869 string.truncate(pos);
3870 }
3871 }
3872
3873 if pending_ws_start.take().is_some() {
3874 if matches!(buf, FlowScalarBuf::Borrowed { .. }) {
3876 self.promote_flow_scalar_buf_to_owned(&start_mark, &mut buf)?;
3877 }
3878 buf.discard_pending_ws();
3879 } else {
3880 buf.commit_pending_ws();
3881 }
3882
3883 self.skip_break();
3884
3885 has_leading_break = true;
3886 leading_blanks = true;
3887 }
3888 }
3889
3890 self.input.lookahead(1);
3891 }
3892
3893 if leading_blanks
3896 && self.options.strict_indentation
3897 && self.flow_level > 0
3898 && (self.mark.col as isize) <= self.flow_block_indent()
3899 {
3900 return Err(self.scan_error(ErrorKind::InvalidIndentation));
3901 }
3902
3903 if leading_blanks && has_leading_break && self.flow_level == 0 {
3908 let next_ch = self.input.peek();
3909 let is_closing_quote = (single && next_ch == '\'') || (!single && next_ch == '"');
3910 if (!is_closing_quote || self.options.strict_indentation)
3911 && (self.mark.col as isize) <= self.flow_block_indent()
3912 {
3913 return Err(self.scan_error(ErrorKind::InvalidQuotedScalarIndent));
3914 }
3915 }
3916
3917 if leading_blanks
3922 && !has_leading_break
3923 && self.options.strict_indentation
3924 && self.flow_level == 0
3925 && (self.mark.col as isize) <= self.flow_block_indent()
3926 {
3927 return Err(self.scan_error(ErrorKind::InvalidIndentation));
3928 }
3929
3930 if leading_blanks {
3932 if has_leading_break && !has_trailing_breaks {
3937 match buf {
3938 FlowScalarBuf::Owned(ref mut string) => string.push(' '),
3939 FlowScalarBuf::Borrowed { .. } => {
3940 self.promote_flow_scalar_buf_to_owned(&start_mark, &mut buf)?;
3941 let Some(string) = buf.as_owned_mut() else {
3942 unreachable!()
3943 };
3944 string.push(' ');
3945 }
3946 }
3947 }
3948 }
3949 } self.skip_non_blank();
3954 let end_mark = self.mark;
3955
3956 self.skip_ws_to_eol(SkipTabs::Yes)?;
3958 self.ensure_current_char_is_printable()?;
3959 match self.input.peek() {
3960 ',' | '}' | ']' if self.flow_level > 0 => {}
3962 c if is_breakz(c) => {}
3964 ':' if self.flow_level == 0 && start_mark.line == self.mark.line => {}
3967 ':' if self.flow_level > 0 => {}
3969 _ => {
3970 let kind = if single {
3971 ErrorKind::InvalidTrailingSingleQuotedScalar
3972 } else {
3973 ErrorKind::InvalidTrailingDoubleQuotedScalar
3974 };
3975 return Err(self.scan_error(kind));
3976 }
3977 }
3978
3979 let style = if single {
3980 ScalarStyle::SingleQuoted
3981 } else {
3982 ScalarStyle::DoubleQuoted
3983 };
3984
3985 let contents = match buf {
3986 FlowScalarBuf::Owned(string) => Cow::Owned(string),
3987 FlowScalarBuf::Borrowed {
3988 start,
3989 mut end,
3990 pending_ws_start,
3991 pending_ws_end,
3992 } => {
3993 if pending_ws_start.is_some() {
3995 end = pending_ws_end;
3996 }
3997 if let Some(slice) = self.try_borrow_slice(start, end) {
3998 Cow::Borrowed(slice)
3999 } else {
4000 let slice = self.input.slice_bytes(start, end).ok_or_else(|| {
4001 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
4002 })?;
4003 Cow::Owned(slice.to_owned())
4004 }
4005 }
4006 };
4007
4008 Ok(Token(
4009 Span::new(start_mark, end_mark),
4010 TokenType::Scalar(style, contents),
4011 ))
4012 }
4013
4014 fn consume_flow_scalar_chunk(&mut self, single: bool, buf: &mut FlowScalarBuf) -> bool {
4016 let (chunk, consumed) = self.input.take_quoted_scalar_chunk(single);
4017 if consumed == 0 {
4018 return false;
4019 }
4020
4021 match buf {
4022 FlowScalarBuf::Owned(string) => string.push_str(chunk),
4023 FlowScalarBuf::Borrowed { .. } => buf.commit_pending_ws(),
4024 }
4025
4026 self.mark.offsets.chars += consumed;
4028 self.mark.col += consumed;
4029 self.mark.offsets.bytes = self.input.byte_offset();
4030 self.leading_whitespace = false;
4031 if let (Some(new_end), FlowScalarBuf::Borrowed { end, .. }) = (self.mark.offsets.bytes, buf)
4032 {
4033 *end = new_end;
4034 }
4035 true
4036 }
4037
4038 fn consume_flow_scalar_non_whitespace_chars(
4047 &mut self,
4048 single: bool,
4049 buf: &mut FlowScalarBuf,
4050 leading_blanks: &mut bool,
4051 start_mark: &Marker,
4052 ) -> Result<(), ScanError> {
4053 self.input.lookahead(2);
4054 loop {
4055 let character = self.input.peek();
4056 if is_blank_or_breakz(character) || !is_printable(character) {
4057 break;
4058 }
4059 match character {
4060 '\'' if self.input.peek_nth(1) == '\'' && single => {
4062 if matches!(buf, FlowScalarBuf::Borrowed { .. }) {
4063 buf.commit_pending_ws();
4064 self.promote_flow_scalar_buf_to_owned(start_mark, buf)?;
4065 }
4066 let Some(string) = buf.as_owned_mut() else {
4067 unreachable!()
4068 };
4069 string.push('\'');
4070 self.skip_n_non_blank(2);
4071 }
4072 '\'' if single => break,
4074 '"' if !single => break,
4075 '\\' if !single && is_break(self.input.peek_nth(1)) => {
4077 self.input.lookahead(3);
4078 if matches!(buf, FlowScalarBuf::Borrowed { .. }) {
4079 buf.commit_pending_ws();
4080 self.promote_flow_scalar_buf_to_owned(start_mark, buf)?;
4081 }
4082 self.skip_non_blank();
4083 self.skip_linebreak();
4084 *leading_blanks = true;
4085 break;
4086 }
4087 '\\' if !single => {
4089 if matches!(buf, FlowScalarBuf::Borrowed { .. }) {
4090 buf.commit_pending_ws();
4091 self.promote_flow_scalar_buf_to_owned(start_mark, buf)?;
4092 }
4093 let Some(string) = buf.as_owned_mut() else {
4094 unreachable!()
4095 };
4096 string.push(self.resolve_flow_scalar_escape_sequence(start_mark)?);
4097 }
4098 c => {
4099 if self.consume_flow_scalar_chunk(single, buf) {
4100 self.input.lookahead(2);
4101 continue;
4102 }
4103 match buf {
4104 FlowScalarBuf::Owned(ref mut string) => {
4105 string.push(c);
4106 }
4107 FlowScalarBuf::Borrowed { .. } => {
4108 buf.commit_pending_ws();
4109 }
4110 }
4111 self.skip_non_blank();
4112
4113 if let Some(new_end) = self.input.byte_offset() {
4114 if let FlowScalarBuf::Borrowed { end, .. } = buf {
4115 *end = new_end;
4116 }
4117 }
4118 }
4119 }
4120 self.input.lookahead(2);
4121 }
4122 Ok(())
4123 }
4124
4125 fn resolve_flow_scalar_escape_sequence(
4132 &mut self,
4133 start_mark: &Marker,
4134 ) -> Result<char, ScanError> {
4135 let mut code_length = 0usize;
4136 let mut ret = '\0';
4137
4138 match self.input.peek_nth(1) {
4139 '0' => ret = '\0',
4140 'a' => ret = '\x07',
4141 'b' => ret = '\x08',
4142 't' | '\t' => ret = '\t',
4143 'n' => ret = '\n',
4144 'v' => ret = '\x0b',
4145 'f' => ret = '\x0c',
4146 'r' => ret = '\x0d',
4147 'e' => ret = '\x1b',
4148 ' ' => ret = '\x20',
4149 '"' => ret = '"',
4150 '/' => ret = '/',
4151 '\\' => ret = '\\',
4152 'N' => ret = char::from_u32(0x85).unwrap(),
4154 '_' => ret = char::from_u32(0xA0).unwrap(),
4156 'L' => ret = char::from_u32(0x2028).unwrap(),
4158 'P' => ret = char::from_u32(0x2029).unwrap(),
4160 'x' => code_length = 2,
4161 'u' => code_length = 4,
4162 'U' => code_length = 8,
4163 _ => {
4164 return Err(ScanError::from_kind(
4165 *start_mark,
4166 ErrorKind::UnknownQuotedScalarEscape,
4167 ))
4168 }
4169 }
4170 self.skip_n_non_blank(2);
4171
4172 if code_length > 0 {
4174 self.input.lookahead(code_length);
4175 let mut value = 0u32;
4176 for i in 0..code_length {
4177 let c = self.input.peek_nth(i);
4178 if !is_hex(c) {
4179 return Err(ScanError::from_kind(
4180 *start_mark,
4181 ErrorKind::InvalidQuotedScalarHexEscape,
4182 ));
4183 }
4184 value = (value << 4) + as_hex(c);
4185 }
4186
4187 self.skip_n_non_blank(code_length);
4188
4189 if code_length == 4 && (0xD800..=0xDBFF).contains(&value) {
4191 self.input.lookahead(2);
4192 if self.input.peek() == '\\' && self.input.peek_nth(1) == 'u' {
4193 self.skip_n_non_blank(2);
4194 self.input.lookahead(4);
4195 let mut low_value = 0u32;
4196 for i in 0..4 {
4197 let c = self.input.peek_nth(i);
4198 if !is_hex(c) {
4199 return Err(ScanError::from_kind(
4200 *start_mark,
4201 ErrorKind::InvalidLowSurrogateHexEscape,
4202 ));
4203 }
4204 low_value = (low_value << 4) + as_hex(c);
4205 }
4206 if (0xDC00..=0xDFFF).contains(&low_value) {
4207 value = 0x10000 + (((value - 0xD800) << 10) | (low_value - 0xDC00));
4208 self.skip_n_non_blank(4);
4209 } else {
4210 return Err(ScanError::from_kind(
4211 *start_mark,
4212 ErrorKind::InvalidLowSurrogate,
4213 ));
4214 }
4215 } else {
4216 return Err(ScanError::from_kind(
4217 *start_mark,
4218 ErrorKind::MissingLowSurrogate,
4219 ));
4220 }
4221 } else if code_length == 4 && (0xDC00..=0xDFFF).contains(&value) {
4222 return Err(ScanError::from_kind(
4223 *start_mark,
4224 ErrorKind::UnpairedLowSurrogate,
4225 ));
4226 }
4227
4228 let Some(ch) = char::from_u32(value) else {
4229 return Err(ScanError::from_kind(
4230 *start_mark,
4231 ErrorKind::InvalidUnicodeEscape,
4232 ));
4233 };
4234 ret = ch;
4235 }
4236 Ok(ret)
4237 }
4238
4239 fn fetch_plain_scalar(&mut self) -> ScanResult {
4240 self.save_simple_key();
4241 self.disallow_simple_key();
4242
4243 let token_index = self.tokens.len();
4244 let tok = self.scan_plain_scalar()?;
4245
4246 self.insert_token(token_index, tok);
4247 Ok(())
4248 }
4249
4250 #[allow(clippy::too_many_lines)]
4255 fn scan_plain_scalar(&mut self) -> Result<Token<'input>, ScanError> {
4256 self.unroll_non_block_indents();
4257 let indent = self.indent + 1;
4258 let start_mark = self.mark;
4259
4260 let borrow_start = start_mark
4261 .byte_offset()
4262 .filter(|start| self.try_borrow_slice(*start, *start).is_some());
4263 let mut string = borrow_start.is_none().then(|| String::with_capacity(32));
4264 let mut has_content = false;
4265 self.buf_whitespaces.clear();
4266 self.buf_leading_break.clear();
4267 self.buf_trailing_breaks.clear();
4268 let mut end_mark = self.mark;
4269
4270 loop {
4271 self.input.lookahead(4);
4272 if (self.mark.col == 0 && self.input.next_is_document_indicator())
4273 || self.input.peek() == '#'
4274 {
4275 if self.input.peek() == '#'
4280 && has_content
4281 && !self.buf_whitespaces.is_empty()
4282 && self.flow_level == 0
4283 {
4284 self.interrupted_plain_by_comment = Some(self.mark);
4285 }
4286 break;
4287 }
4288
4289 if !has_content
4291 && self.flow_level > 0
4292 && self.input.peek() == '-'
4293 && is_flow(self.input.peek_nth(1))
4294 {
4295 return Err(self.scan_error(ErrorKind::PlainScalarStartsWithDashFlowIndicator));
4296 }
4297
4298 if !self.input.next_is_blank_or_breakz()
4299 && self.input.next_can_be_plain_scalar(self.flow_level > 0)
4300 {
4301 if self.leading_whitespace {
4302 if has_content && string.is_none() {
4303 let start = borrow_start.expect("borrowed scalar has a start offset");
4304 let end = end_mark.byte_offset().ok_or_else(|| {
4305 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
4306 })?;
4307 let prefix = self.try_borrow_slice(start, end).ok_or_else(|| {
4308 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
4309 })?;
4310 string = Some(prefix.to_owned());
4311 }
4312 if self.buf_leading_break.is_empty() {
4313 if let Some(output) = string.as_mut() {
4314 output.push_str(&self.buf_leading_break);
4315 output.push_str(&self.buf_trailing_breaks);
4316 }
4317 self.buf_trailing_breaks.clear();
4318 self.buf_leading_break.clear();
4319 } else {
4320 if self.buf_trailing_breaks.is_empty() {
4321 if let Some(output) = string.as_mut() {
4322 output.push(' ');
4323 }
4324 } else {
4325 if let Some(output) = string.as_mut() {
4326 output.push_str(&self.buf_trailing_breaks);
4327 }
4328 self.buf_trailing_breaks.clear();
4329 }
4330 self.buf_leading_break.clear();
4331 }
4332 self.leading_whitespace = false;
4333 } else if !self.buf_whitespaces.is_empty() {
4334 if let Some(output) = string.as_mut() {
4335 output.push_str(&self.buf_whitespaces);
4336 }
4337 self.buf_whitespaces.clear();
4338 }
4339
4340 has_content = true;
4342 if let Some(output) = string.as_mut() {
4343 output.push(self.input.peek());
4344 }
4345 self.skip_non_blank();
4346 if let Some(output) = string.as_mut() {
4347 output.reserve(self.input.bufmaxlen());
4348 }
4349
4350 let mut end = false;
4352 while !end {
4353 self.input.lookahead(self.input.bufmaxlen());
4358 let chunk_len = self.input.bufmaxlen().saturating_sub(1).max(1);
4359 let (stop, chars_consumed) = if let Some(output) = string.as_mut() {
4360 self.input
4361 .fetch_plain_scalar_chunk(output, chunk_len, self.flow_level > 0)
4362 } else {
4363 self.input
4364 .skip_plain_scalar_chunk(chunk_len, self.flow_level > 0)
4365 };
4366 end = stop;
4367 self.mark.offsets.chars += chars_consumed;
4368 self.mark.col += chars_consumed;
4369 self.mark.offsets.bytes = self.input.byte_offset();
4370 }
4371 end_mark = self.mark;
4372 }
4373
4374 if !(self.input.next_is_blank() || self.input.next_is_break()) {
4379 break;
4380 }
4381
4382 self.input.lookahead(2);
4384 while self.input.next_is_blank_or_break() {
4385 if self.input.next_is_blank() {
4386 if !self.leading_whitespace {
4387 self.buf_whitespaces.push(self.input.peek());
4388 self.skip_blank();
4389 } else if (self.mark.col as isize) < indent && self.input.peek() == '\t' {
4390 self.skip_ws_to_eol(SkipTabs::Yes)?;
4393 if !self.input.next_is_breakz() {
4394 return Err(ScanError::from_kind(
4395 start_mark,
4396 ErrorKind::TabInPlainScalar,
4397 ));
4398 }
4399 } else {
4400 self.skip_blank();
4401 }
4402 } else {
4403 if self.leading_whitespace {
4405 self.skip_break();
4406 self.buf_trailing_breaks.push('\n');
4407 } else {
4408 self.buf_whitespaces.clear();
4409 self.skip_break();
4410 self.buf_leading_break.push('\n');
4411 self.leading_whitespace = true;
4412 }
4413 }
4414 self.input.lookahead(2);
4415 }
4416
4417 if (self.flow_level == 0 || self.options.strict_indentation)
4420 && (self.mark.col as isize) < indent
4421 {
4422 break;
4423 }
4424 }
4425
4426 if self.leading_whitespace {
4427 self.allow_simple_key();
4428 }
4429
4430 let borrowed_contents = if string.is_none() {
4431 let start = borrow_start.expect("borrowed scalar has a start offset");
4432 let end = end_mark.byte_offset().ok_or_else(|| {
4433 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
4434 })?;
4435 Some(self.try_borrow_slice(start, end).ok_or_else(|| {
4436 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
4437 })?)
4438 } else {
4439 None
4440 };
4441 let scalar_text = borrowed_contents.unwrap_or_else(|| {
4442 string
4443 .as_deref()
4444 .expect("owned plain scalar has an output buffer")
4445 });
4446 if let Some(character) = find_non_printable(scalar_text) {
4447 return Err(ScanError::from_kind(
4448 start_mark,
4449 ErrorKind::UnexpectedCharacter { character },
4450 ));
4451 }
4452 self.ensure_current_char_is_printable()?;
4453
4454 if has_content {
4455 let contents = if let Some(slice) = borrowed_contents {
4456 Cow::Borrowed(slice)
4457 } else {
4458 Cow::Owned(string.expect("owned plain scalar has an output buffer"))
4459 };
4460
4461 Ok(Token(
4462 Span::new(start_mark, end_mark),
4463 TokenType::Scalar(ScalarStyle::Plain, contents),
4464 ))
4465 } else {
4466 Err(ScanError::from_kind(
4470 start_mark,
4471 ErrorKind::UnexpectedEndOfPlainScalar,
4472 ))
4473 }
4474 }
4475
4476 fn fetch_key(&mut self) -> ScanResult {
4477 let start_mark = self.mark;
4478 if self.flow_level == 0 {
4479 if !self.simple_key_allowed {
4481 return Err(self.scan_error(ErrorKind::MappingKeyNotAllowed));
4482 }
4483 self.roll_indent(
4484 start_mark.col,
4485 None,
4486 TokenType::BlockMappingStart,
4487 start_mark,
4488 )?;
4489 } else {
4490 self.set_current_flow_mapping_started(true);
4492 }
4493
4494 self.remove_simple_key()?;
4495
4496 if self.flow_level == 0 {
4497 self.allow_simple_key();
4498 } else {
4499 self.disallow_simple_key();
4500 }
4501
4502 self.skip_non_blank();
4503 let end_mark = self.mark;
4504 let token_index = self.tokens.len();
4505 self.explicit_key_tab_check_pending = false;
4506 if self.flow_level == 0 {
4511 let stopped_after_comment = self.skip_yaml_whitespace()?;
4512 self.explicit_key_tab_check_pending = stopped_after_comment
4513 || (self.options.strict_indentation && matches!(self.input.peek(), '&' | '!'));
4514 }
4515 self.insert_token(
4516 token_index,
4517 Token(Span::new(start_mark, end_mark), TokenType::Key),
4518 );
4519 Ok(())
4520 }
4521
4522 fn fetch_flow_value(&mut self) -> ScanResult {
4530 let nc = self.input.peek_nth(1);
4531
4532 if self.mark.index() != self.adjacent_value_allowed_at && (nc == '[' || nc == '{') {
4544 return Err(self.scan_error(ErrorKind::FlowMappingValueAdjacentCollection));
4545 }
4546
4547 self.fetch_value()
4548 }
4549
4550 fn fetch_value(&mut self) -> ScanResult {
4552 let sk = *self.simple_keys.last().unwrap();
4553 let start_mark = self.mark;
4554 let is_implicit_flow_mapping = self.current_flow_collection_is_sequence()
4555 && !self.current_flow_mapping_started()
4556 && !self.implicit_flow_mapping_states.is_empty();
4557 if is_implicit_flow_mapping {
4558 *self.implicit_flow_mapping_states.last_mut().unwrap() =
4559 ImplicitMappingState::Inside(self.flow_level);
4560 }
4561
4562 self.skip_non_blank();
4564 let mut trailing_comment = None;
4567 let mut found_tabs = false;
4568 if matches!(self.input.look_ch(), ' ' | '\t') {
4569 let trailing_token_index = self.tokens.len();
4570 found_tabs = self.skip_ws_to_eol(SkipTabs::Yes)?.found_tabs();
4571 debug_assert!(self.tokens.len() <= trailing_token_index + 1);
4573 if self.tokens.len() > trailing_token_index {
4574 trailing_comment = self.tokens.pop_back();
4575 }
4576 }
4577
4578 if sk.possible {
4579 let block_indent = self.flow_block_indent();
4583 if self.flow_level > 0
4584 && sk.mark.line < start_mark.line
4585 && (sk.mark.col as isize) <= block_indent
4586 {
4587 return Err(ScanError::from_kind(
4588 start_mark,
4589 ErrorKind::InvalidColonPlacement,
4590 ));
4591 }
4592 let token_index = self.simple_key_token_index(&sk, start_mark)?;
4593 self.validate_flow_key_indentation(&sk, token_index, start_mark)?;
4594 let tok = Token(Span::empty(sk.mark), TokenType::Key);
4596 self.insert_token(token_index, tok);
4597 if is_implicit_flow_mapping {
4598 if sk.mark.line < start_mark.line {
4599 return Err(ScanError::from_kind(
4600 start_mark,
4601 ErrorKind::InvalidColonPlacement,
4602 ));
4603 }
4604 self.insert_token(
4605 token_index,
4606 Token(Span::empty(sk.mark), TokenType::FlowMappingStart),
4607 );
4608 }
4609
4610 self.roll_indent(
4612 sk.mark.col,
4613 Some(sk.token_number),
4614 TokenType::BlockMappingStart,
4615 sk.mark,
4616 )?;
4617 self.roll_one_col_indent();
4618
4619 self.clear_simple_key();
4620 self.disallow_simple_key();
4621 } else {
4622 if is_implicit_flow_mapping {
4623 self.tokens
4624 .push_back(Token(Span::empty(start_mark), TokenType::FlowMappingStart).into());
4625 }
4626 if self.flow_level == 0 {
4628 if !self.simple_key_allowed {
4629 return Err(ScanError::from_kind(
4630 start_mark,
4631 ErrorKind::MappingValueNotAllowed,
4632 ));
4633 }
4634
4635 self.roll_indent(
4636 start_mark.col,
4637 None,
4638 TokenType::BlockMappingStart,
4639 start_mark,
4640 )?;
4641 }
4642 self.roll_one_col_indent();
4643
4644 if self.flow_level == 0 && !found_tabs {
4647 self.allow_simple_key();
4648 } else {
4649 self.disallow_simple_key();
4650 }
4651 }
4652 self.tokens
4653 .push_back(Token(Span::empty(start_mark), TokenType::Value).into());
4654 if let Some(comment) = trailing_comment {
4655 self.tokens.push_back(comment);
4656 }
4657
4658 Ok(())
4659 }
4660
4661 fn validate_flow_key_indentation(
4663 &self,
4664 key: &SimpleKey,
4665 token_index: usize,
4666 colon: Marker,
4667 ) -> ScanResult {
4668 if self.flow_level == 0 || key.mark.line == colon.line {
4669 return Ok(());
4670 }
4671
4672 let block_indent = self.flow_block_indent();
4673 let mut under_indented = [key.mark.col, colon.col]
4677 .iter()
4678 .any(|&col| (col as isize) <= block_indent);
4679 for token in self.tokens.iter().skip(token_index) {
4680 #[cfg(feature = "comments")]
4681 if matches!(token.1, QueuedTokenType::Comment(_)) {
4682 continue;
4683 }
4684 under_indented |= (token.0.start.col as isize) <= block_indent;
4685 if !matches!(
4686 token.1,
4687 QueuedTokenType::Anchor(_) | QueuedTokenType::Tag(..)
4688 ) {
4689 break;
4690 }
4691 }
4692 if under_indented {
4693 return Err(ScanError::from_kind(
4694 colon,
4695 ErrorKind::InvalidColonPlacement,
4696 ));
4697 }
4698 Ok(())
4699 }
4700
4701 fn flow_block_indent(&self) -> isize {
4703 self.indents
4704 .last()
4705 .filter(|indent| !indent.needs_block_end)
4706 .map_or(self.indent, |indent| indent.indent)
4707 }
4708
4709 fn roll_indent(
4715 &mut self,
4716 col: usize,
4717 number: Option<usize>,
4718 tok: TokenType<'input>,
4719 mark: Marker,
4720 ) -> ScanResult {
4721 if self.flow_level > 0 {
4722 return Ok(());
4723 }
4724
4725 if self.indent <= col as isize {
4729 if let Some(indent) = self.indents.last() {
4730 if !indent.needs_block_end {
4731 self.indent = indent.indent;
4732 self.indents.pop();
4733 }
4734 }
4735 }
4736
4737 if self.indent < col as isize {
4738 if self.block_level >= self.options.block_nesting_limit {
4739 return Err(ScanError::from_kind(
4740 mark,
4741 ErrorKind::RecursionLimitExceeded,
4742 ));
4743 }
4744 self.indents.push(Indent {
4745 indent: self.indent,
4746 needs_block_end: true,
4747 });
4748 self.block_level += 1;
4749 self.indent = col as isize;
4750 let tokens_parsed = self.tokens_parsed;
4751 match number {
4752 Some(n) => self.insert_token(n - tokens_parsed, Token(Span::empty(mark), tok)),
4753 None => self.tokens.push_back(Token(Span::empty(mark), tok).into()),
4754 }
4755 }
4756 Ok(())
4757 }
4758
4759 fn unroll_indent(&mut self, col: isize) {
4765 if self.flow_level > 0 {
4766 return;
4767 }
4768 while self.indent > col {
4769 let indent = self.indents.pop().unwrap();
4770 self.indent = indent.indent;
4771 if indent.needs_block_end {
4772 debug_assert!(self.block_level > 0);
4773 self.block_level -= 1;
4774 self.tokens
4775 .push_back(Token(Span::empty(self.mark), TokenType::BlockEnd).into());
4776 }
4777 }
4778 }
4779
4780 fn roll_one_col_indent(&mut self) {
4786 if self.flow_level == 0 && self.indents.last().is_some_and(|x| x.needs_block_end) {
4787 self.indents.push(Indent {
4788 indent: self.indent,
4789 needs_block_end: false,
4790 });
4791 self.indent += 1;
4792 }
4793 }
4794
4795 fn unroll_non_block_indents(&mut self) {
4797 while let Some(indent) = self.indents.last() {
4798 if indent.needs_block_end {
4799 break;
4800 }
4801 self.indent = indent.indent;
4802 self.indents.pop();
4803 }
4804 }
4805
4806 fn save_simple_key(&mut self) {
4808 if self.simple_key_allowed {
4809 let required = self.flow_level == 0
4810 && self.indent == (self.mark.col as isize)
4811 && self.indents.last().unwrap().needs_block_end;
4812
4813 if let Some(last) = self.simple_keys.last_mut() {
4814 *last = SimpleKey {
4815 mark: self.mark,
4816 possible: true,
4817 required,
4818 token_number: self.tokens_parsed + self.tokens.len(),
4819 };
4820 self.first_simple_key.get_or_insert(self.flow_level);
4821 }
4822 }
4823 }
4824
4825 fn remove_simple_key(&mut self) -> ScanResult {
4826 let last = self.simple_keys.last().unwrap();
4827 if last.possible && last.required {
4828 return Err(Self::simple_key_expected(last.mark));
4829 }
4830
4831 self.clear_simple_key();
4832 Ok(())
4833 }
4834
4835 fn clear_simple_key(&mut self) {
4837 self.simple_keys.last_mut().unwrap().possible = false;
4838 if self.first_simple_key == Some(self.flow_level) {
4839 self.first_simple_key = None;
4841 }
4842 }
4843
4844 fn is_within_block(&self) -> bool {
4846 !self.indents.is_empty()
4847 }
4848
4849 fn end_implicit_mapping(&mut self, mark: Marker, flow_level: usize) {
4855 if self
4856 .implicit_flow_mapping_states
4857 .last()
4858 .is_some_and(|state| *state == ImplicitMappingState::Inside(flow_level))
4859 {
4860 *self.implicit_flow_mapping_states.last_mut().unwrap() = ImplicitMappingState::Possible;
4861 self.set_current_flow_mapping_started(false);
4862 self.tokens
4863 .push_back(Token(Span::empty(mark), TokenType::FlowMappingEnd).into());
4864 }
4865 }
4866
4867 fn current_flow_collection_is_sequence(&self) -> bool {
4868 self.flow_markers
4869 .last()
4870 .is_some_and(|(_, bracket)| *bracket == '[')
4871 }
4872
4873 fn current_flow_mapping_started(&self) -> bool {
4874 self.flow_mapping_started.last().copied().unwrap_or(false)
4875 }
4876
4877 fn set_current_flow_mapping_started(&mut self, started: bool) {
4878 if let Some(current) = self.flow_mapping_started.last_mut() {
4879 *current = started;
4880 }
4881 }
4882}
4883
4884#[derive(PartialEq, Eq)]
4888enum Chomping {
4889 Strip,
4891 Clip,
4893 Keep,
4895}
4896
4897#[cfg(test)]
4898mod test {
4899 use alloc::{
4900 borrow::{Cow, ToOwned},
4901 rc::Rc,
4902 string::String,
4903 vec,
4904 vec::Vec,
4905 };
4906 use core::cell::Cell;
4907
4908 use crate::error::{ErrorKind, ScanError};
4909 #[cfg(feature = "comments")]
4910 use crate::scanner::{Comment, Placement};
4911 use crate::{
4912 input::{str::StrInput, BorrowedInput, BufferedInput, Input},
4913 scanner::{
4914 Marker, QueuedToken, QueuedTokenType, ScalarStyle, Scanner, Span, Token, TokenType,
4915 },
4916 };
4917
4918 #[test]
4919 fn tag_span_supports_the_full_u32_length_without_allocating() {
4920 let start = Marker::new(0, 1, 0).with_byte_offset(Some(0));
4921 let span = Span::empty(start).with_tag_span(Some((start, u32::MAX)));
4922 let tag_span = span.tag_span().unwrap();
4923 let length = usize::try_from(u32::MAX).unwrap();
4924 assert_eq!(tag_span.start, start);
4925 assert_eq!(tag_span.end.index(), length);
4926 assert_eq!(tag_span.end.col(), length);
4927 assert_eq!(tag_span.end.line(), 1);
4928 assert_eq!(tag_span.end.byte_offset(), Some(length));
4929 }
4930
4931 #[test]
4932 fn tag_span_rejects_overflow_in_each_reconstructed_coordinate() {
4933 for start in [
4934 Marker::new(usize::MAX, 1, 0),
4935 Marker::new(0, 1, usize::MAX),
4936 Marker::new(0, 1, 0).with_byte_offset(Some(usize::MAX)),
4937 ] {
4938 let span = Span::empty(start).with_tag_span(Some((start, 1)));
4939 assert_eq!(span.tag_span(), None);
4940 }
4941 }
4942
4943 #[test]
4944 fn recovery_ignores_invalid_document_marker_lookalikes() {
4945 let input = "@bad\n---# comment\n ---\n---\0\n... trailing\n--- healthy\n";
4946 let mut scanner = Scanner::new(StrInput::new(input));
4947 assert!(matches!(
4948 scanner.next_token().unwrap().unwrap().1,
4949 TokenType::StreamStart
4950 ));
4951 assert!(scanner.next_token().is_err());
4952
4953 scanner.proceed_next_document(None).unwrap();
4954 let token = scanner.next_token().unwrap().unwrap();
4955 assert_eq!(token.1, TokenType::DocumentStart);
4956 assert_eq!(token.0.start.line(), 6);
4957 assert!(matches!(
4958 scanner.next_token().unwrap().unwrap().1,
4959 TokenType::Scalar(_, ref value) if value == "healthy"
4960 ));
4961 }
4962
4963 #[test]
4964 fn recovery_rejects_end_marker_queued_before_its_line_failed() {
4965 let mut scanner =
4966 Scanner::new(StrInput::new("... trailing\nnot_a_document\n--- healthy\n"));
4967 scanner.next_token().unwrap();
4968 assert_eq!(
4969 scanner.next_token().unwrap_err().kind(),
4970 &ErrorKind::InvalidDocumentEnd
4971 );
4972 assert!(scanner
4973 .tokens
4974 .iter()
4975 .any(|token| matches!(token.1, QueuedTokenType::DocumentEnd)));
4976
4977 scanner.proceed_next_document(None).unwrap();
4978 let token = scanner.next_token().unwrap().unwrap();
4979 assert_eq!(token.1, TokenType::DocumentStart);
4980 assert_eq!(token.0.start.line(), 3);
4981 }
4982
4983 #[test]
4984 fn recovery_preserves_boundary_already_held_by_parser() {
4985 let mut scanner = Scanner::new(StrInput::new("first\n--- second\n"));
4986 let boundary = loop {
4987 let token = scanner.next_queued_token().unwrap().unwrap();
4988 if matches!(token.1, QueuedTokenType::DocumentStart) {
4989 break token;
4990 }
4991 };
4992 let span = boundary.0;
4993 scanner.proceed_next_document(Some(boundary)).unwrap();
4994 assert_eq!(
4995 scanner.next_token().unwrap().unwrap(),
4996 Token(span, TokenType::DocumentStart)
4997 );
4998 assert!(matches!(
4999 scanner.next_token().unwrap().unwrap().1,
5000 TokenType::Scalar(_, ref value) if value == "second"
5001 ));
5002 }
5003
5004 #[test]
5005 fn recovery_retains_previously_reported_source_error() {
5006 let kind = ErrorKind::InputDecoding {
5007 message: "invalid encoding".into(),
5008 };
5009 let input = crate::FallibleBufferedInput::new(
5010 vec![Ok('['), Err(kind.clone()), Ok('\n')].into_iter(),
5011 );
5012 let mut scanner = Scanner::new(input);
5013 scanner.next_token().unwrap();
5014 let error = scanner.next_token().unwrap_err();
5015 assert_eq!(error.kind(), &kind);
5016 assert_eq!(scanner.proceed_next_document(None), Err(error));
5017 }
5018
5019 #[test]
5020 fn recovery_distinguishes_directive_header_errors_from_document_content() {
5021 for input in ["%YAML bad\n--- body\n", "%TAG invalid\n--- body\n"] {
5022 let mut scanner = Scanner::new(StrInput::new(input));
5023 scanner.next_token().unwrap();
5024 assert!(scanner.next_token().is_err());
5025 assert!(scanner.in_directive_prefix());
5026 scanner.proceed_next_document(None).unwrap();
5027 assert!(!scanner.in_directive_prefix());
5028 }
5029
5030 let mut scanner = Scanner::with_options(
5031 StrInput::new("%YAML 1.2\n--- body\n"),
5032 crate::options! { disallow_directives: true },
5033 );
5034 scanner.next_token().unwrap();
5035 assert!(scanner.next_token().is_err());
5036 assert!(scanner.in_directive_prefix());
5037
5038 for input in ["%YAML 1.2\n--- body\n", "%YAML 1.2\nbare\n"] {
5039 let mut scanner = Scanner::new(StrInput::new(input));
5040 scanner.next_token().unwrap();
5041 scanner.next_token().unwrap();
5042 assert!(scanner.in_directive_prefix());
5043 scanner.next_token().unwrap();
5044 assert!(!scanner.in_directive_prefix());
5045 }
5046 }
5047
5048 fn ignoring_comments_options() -> crate::Options {
5049 #[cfg(feature = "comments")]
5050 {
5051 crate::options! { emit_comments: false }
5052 }
5053 #[cfg(not(feature = "comments"))]
5054 {
5055 crate::Options::default()
5056 }
5057 }
5058
5059 struct CountingChars {
5060 chars: alloc::vec::IntoIter<char>,
5061 read: Rc<Cell<usize>>,
5062 }
5063
5064 impl Iterator for CountingChars {
5065 type Item = char;
5066
5067 fn next(&mut self) -> Option<Self::Item> {
5068 let next = self.chars.next();
5069 if next.is_some() {
5070 self.read.set(self.read.get() + 1);
5071 }
5072 next
5073 }
5074 }
5075
5076 struct SlicingOnlyInput<'input> {
5077 inner: StrInput<'input>,
5078 expose_slice: bool,
5079 }
5080
5081 impl<'input> SlicingOnlyInput<'input> {
5082 fn new(source: &'input str, expose_slice: bool) -> Self {
5083 Self {
5084 inner: StrInput::new(source),
5085 expose_slice,
5086 }
5087 }
5088 }
5089
5090 impl Input for SlicingOnlyInput<'_> {
5091 fn lookahead(&mut self, count: usize) {
5092 self.inner.lookahead(count);
5093 }
5094
5095 fn buflen(&self) -> usize {
5096 self.inner.buflen()
5097 }
5098
5099 fn bufmaxlen(&self) -> usize {
5100 self.inner.bufmaxlen()
5101 }
5102
5103 fn raw_read_ch(&mut self) -> char {
5104 self.inner.raw_read_ch()
5105 }
5106
5107 fn raw_read_non_breakz_ch(&mut self) -> Option<char> {
5108 self.inner.raw_read_non_breakz_ch()
5109 }
5110
5111 fn skip(&mut self) {
5112 self.inner.skip();
5113 }
5114
5115 fn skip_n(&mut self, count: usize) {
5116 self.inner.skip_n(count);
5117 }
5118
5119 fn peek(&self) -> char {
5120 self.inner.peek()
5121 }
5122
5123 fn peek_nth(&self, n: usize) -> char {
5124 self.inner.peek_nth(n)
5125 }
5126
5127 fn byte_offset(&self) -> Option<usize> {
5128 self.inner.byte_offset()
5129 }
5130
5131 fn slice_bytes(&self, start: usize, end: usize) -> Option<&str> {
5132 if self.expose_slice {
5133 self.inner.slice_bytes(start, end)
5134 } else {
5135 None
5136 }
5137 }
5138 }
5139
5140 impl<'input> BorrowedInput<'input> for SlicingOnlyInput<'input> {
5141 fn slice_borrowed(&self, _start: usize, _end: usize) -> Option<&'input str> {
5142 None
5143 }
5144 }
5145
5146 struct SmallReportedBufferInput<'input> {
5147 inner: StrInput<'input>,
5148 reported_bufmaxlen: usize,
5149 }
5150
5151 impl<'input> SmallReportedBufferInput<'input> {
5152 fn new(source: &'input str, reported_bufmaxlen: usize) -> Self {
5153 Self {
5154 inner: StrInput::new(source),
5155 reported_bufmaxlen,
5156 }
5157 }
5158 }
5159
5160 impl Input for SmallReportedBufferInput<'_> {
5161 fn lookahead(&mut self, count: usize) {
5162 self.inner.lookahead(count);
5163 }
5164
5165 fn buflen(&self) -> usize {
5166 self.inner.buflen()
5167 }
5168
5169 fn bufmaxlen(&self) -> usize {
5170 self.reported_bufmaxlen
5171 }
5172
5173 fn raw_read_ch(&mut self) -> char {
5174 self.inner.raw_read_ch()
5175 }
5176
5177 fn raw_read_non_breakz_ch(&mut self) -> Option<char> {
5178 self.inner.raw_read_non_breakz_ch()
5179 }
5180
5181 fn skip(&mut self) {
5182 self.inner.skip();
5183 }
5184
5185 fn skip_n(&mut self, count: usize) {
5186 self.inner.skip_n(count);
5187 }
5188
5189 fn peek(&self) -> char {
5190 self.inner.peek()
5191 }
5192
5193 fn peek_nth(&self, n: usize) -> char {
5194 self.inner.peek_nth(n)
5195 }
5196 }
5197
5198 impl<'input> BorrowedInput<'input> for SmallReportedBufferInput<'input> {
5199 fn slice_borrowed(&self, start: usize, end: usize) -> Option<&'input str> {
5200 self.inner.slice_borrowed(start, end)
5201 }
5202 }
5203
5204 #[test]
5205 fn anchor_character_set_allows_colon_and_rejects_flow_indicators() {
5206 use super::is_anchor_char;
5207
5208 assert!(is_anchor_char('x'));
5209 assert!(is_anchor_char('-'));
5210 assert!(is_anchor_char('_'));
5211 assert!(is_anchor_char(':'));
5212 assert!(is_anchor_char('#'));
5213 assert!(is_anchor_char('/'));
5214 assert!(is_anchor_char('?'));
5215
5216 for c in [',', '[', ']', '{', '}', ' ', '\t', '\n', '\r', '\0'] {
5217 assert!(
5218 !is_anchor_char(c),
5219 "character {c:?} must not be accepted in anchor/alias names"
5220 );
5221 }
5222 }
5223
5224 #[test]
5225 fn flow_simple_key_length_limit_bounds_buffering() {
5226 let mut yaml = String::from("[\n\"start\"\n");
5227 for _ in 0..600 {
5228 yaml.push_str("\"x\"\n");
5229 }
5230 let total_chars = yaml.chars().count();
5231 let read = Rc::new(Cell::new(0));
5232 let chars = yaml.chars().collect::<Vec<_>>().into_iter();
5233 let mut scanner = Scanner::new(BufferedInput::new(CountingChars {
5234 chars,
5235 read: Rc::clone(&read),
5236 }));
5237
5238 assert!(matches!(
5239 scanner.next_token().unwrap().unwrap().1,
5240 TokenType::StreamStart
5241 ));
5242
5243 let token = scanner.next_token().unwrap().unwrap();
5244 assert!(matches!(token.1, TokenType::FlowSequenceStart));
5245
5246 let token = scanner.next_token().unwrap().unwrap();
5247 assert!(matches!(
5248 token.1,
5249 TokenType::Scalar(_, ref value) if value == "start"
5250 ));
5251 assert!(
5252 read.get() < total_chars,
5253 "scanner consumed all {total_chars} chars before yielding the first flow scalar"
5254 );
5255 assert!(
5256 read.get() <= scanner.options.simple_key_max_lookahead + 128,
5257 "scanner read {} chars before yielding the first flow scalar",
5258 read.get()
5259 );
5260 }
5261
5262 #[test]
5263 fn block_scalar_indent_tolerates_small_reported_bufmaxlen() {
5264 let mut scanner = Scanner::new(SmallReportedBufferInput::new("|\n value\n", 0));
5265
5266 let scalar = scanner
5267 .find_map(
5268 |token| match token.expect("valid YAML should scan without errors") {
5269 Token(_, TokenType::Scalar(ScalarStyle::Literal, value)) => {
5270 Some(value.into_owned())
5271 }
5272 _ => None,
5273 },
5274 )
5275 .expect("expected block scalar token");
5276
5277 assert_eq!(scalar, "value\n");
5278 }
5279
5280 #[test]
5281 fn plain_scalar_chunk_tolerates_small_reported_bufmaxlen() {
5282 let mut scanner = Scanner::new(SmallReportedBufferInput::new("plain\n", 0));
5283
5284 let scalar = scanner
5285 .find_map(
5286 |token| match token.expect("valid YAML should scan without errors") {
5287 Token(_, TokenType::Scalar(ScalarStyle::Plain, value)) => {
5288 Some(value.into_owned())
5289 }
5290 _ => None,
5291 },
5292 )
5293 .expect("expected plain scalar token");
5294
5295 assert_eq!(scalar, "plain");
5296 }
5297
5298 fn first_token_slice(
5299 yaml: &str,
5300 matches_token: impl Fn(&TokenType<'_>) -> bool,
5301 ) -> Option<String> {
5302 let mut scanner = Scanner::new(StrInput::new(yaml));
5303
5304 loop {
5305 let token = scanner
5306 .next_token()
5307 .expect("scanner should accept the test YAML")?;
5308 if matches_token(&token.1) {
5309 return token.0.slice(yaml).map(ToOwned::to_owned);
5310 }
5311 }
5312 }
5313
5314 #[test]
5315 fn flow_indicator_token_spans_cover_only_the_indicator() {
5316 assert_eq!(
5317 first_token_slice("[ # c\n a]\n", |token| matches!(
5318 token,
5319 TokenType::FlowSequenceStart
5320 ))
5321 .as_deref(),
5322 Some("[")
5323 );
5324 assert_eq!(
5325 first_token_slice("{ # c\n a: b}\n", |token| matches!(
5326 token,
5327 TokenType::FlowMappingStart
5328 ))
5329 .as_deref(),
5330 Some("{")
5331 );
5332 assert_eq!(
5333 first_token_slice("[a] # c\n", |token| matches!(
5334 token,
5335 TokenType::FlowSequenceEnd
5336 ))
5337 .as_deref(),
5338 Some("]")
5339 );
5340 assert_eq!(
5341 first_token_slice("{a: b} # c\n", |token| matches!(
5342 token,
5343 TokenType::FlowMappingEnd
5344 ))
5345 .as_deref(),
5346 Some("}")
5347 );
5348 assert_eq!(
5349 first_token_slice("[a, # c\nb]\n", |token| matches!(
5350 token,
5351 TokenType::FlowEntry
5352 ))
5353 .as_deref(),
5354 Some(",")
5355 );
5356 }
5357
5358 #[test]
5359 fn explicit_key_token_span_covers_only_the_indicator() {
5360 assert_eq!(
5361 first_token_slice("? # c\n: value\n", |token| matches!(token, TokenType::Key))
5362 .as_deref(),
5363 Some("?")
5364 );
5365 }
5366
5367 #[test]
5368 #[cfg(feature = "comments")]
5369 fn comment_capture_does_not_change_leading_whitespace() {
5370 let mut scanner = Scanner::new(StrInput::new("# comment\n"));
5371
5372 let token = scanner.scan_comment_token().unwrap();
5373
5374 assert!(scanner.leading_whitespace);
5375 assert!(matches!(token.1, TokenType::Comment(ref comment) if comment.text == " comment"));
5376
5377 let mut scanner = Scanner::new(BufferedInput::new("# streaming\n".chars()));
5378 scanner.input.lookahead(1);
5379
5380 let token = scanner.scan_comment_token().unwrap();
5381
5382 assert!(scanner.leading_whitespace);
5383 assert!(matches!(token.1, TokenType::Comment(ref comment) if comment.text == " streaming"));
5384 }
5385
5386 #[test]
5387 #[cfg(feature = "comments")]
5388 fn comment_capture_falls_back_to_owned_slice_when_borrow_unavailable() {
5389 let mut scanner = Scanner::new(SlicingOnlyInput::new("# sliced\n", true));
5390 scanner.input.lookahead(2);
5391 assert_eq!(scanner.input.peek_nth(1), ' ');
5392
5393 let token = scanner.scan_comment_token().unwrap();
5394
5395 assert!(matches!(token.1, TokenType::Comment(ref comment)
5396 if matches!(comment.text, Cow::Owned(ref text) if text == " sliced")));
5397 }
5398
5399 #[test]
5400 #[cfg(feature = "comments")]
5401 fn comment_capture_errors_when_offsets_have_no_slice() {
5402 let mut scanner = Scanner::new(SlicingOnlyInput::new("# broken\n", false));
5403
5404 let error = scanner.scan_comment_token().unwrap_err();
5405
5406 assert_eq!(error.kind(), &ErrorKind::InputOffsetsWithoutSlice);
5407 }
5408
5409 #[test]
5410 fn disabled_comments_do_not_require_input_slicing() {
5411 let options = ignoring_comments_options();
5412 let scanner = Scanner::with_options(
5413 SlicingOnlyInput::new("# ignored\nkey: value\n", false),
5414 options,
5415 );
5416
5417 let tokens = scanner
5418 .collect::<Result<Vec<_>, _>>()
5419 .expect("ignored comments should not capture their payload");
5420
5421 #[cfg(feature = "comments")]
5422 assert!(tokens
5423 .iter()
5424 .all(|token| !matches!(token.1, TokenType::Comment(_))));
5425 assert!(tokens.iter().any(|token| matches!(
5426 token.1,
5427 TokenType::Scalar(ScalarStyle::Plain, ref text) if text == "value"
5428 )));
5429 }
5430
5431 #[test]
5432 fn queued_token_roundtrips_public_token_variants() {
5433 let span = Span::new(Marker::new(0, 1, 0), Marker::new(7, 1, 7));
5434 let tokens = [
5435 Token(span, TokenType::StreamStart),
5436 Token(span, TokenType::StreamEnd),
5437 Token(span, TokenType::VersionDirective(1, 2)),
5438 Token(
5439 span,
5440 TokenType::TagDirective(Cow::Borrowed("!app!"), Cow::Borrowed("tag:app.example,")),
5441 ),
5442 Token(span, TokenType::DocumentStart),
5443 Token(span, TokenType::DocumentEnd),
5444 Token(span, TokenType::BlockSequenceStart),
5445 Token(span, TokenType::BlockMappingStart),
5446 Token(span, TokenType::BlockEnd),
5447 Token(span, TokenType::FlowSequenceStart),
5448 Token(span, TokenType::FlowSequenceEnd),
5449 Token(span, TokenType::FlowMappingStart),
5450 Token(span, TokenType::FlowMappingEnd),
5451 Token(span, TokenType::BlockEntry),
5452 Token(span, TokenType::FlowEntry),
5453 Token(span, TokenType::Key),
5454 Token(span, TokenType::Value),
5455 Token(span, TokenType::Alias(Cow::Borrowed("alias"))),
5456 Token(span, TokenType::Anchor(Cow::Borrowed("anchor"))),
5457 Token(
5458 span,
5459 TokenType::Tag(Cow::Borrowed("!"), Cow::Borrowed("tag")),
5460 ),
5461 Token(
5462 span,
5463 TokenType::Scalar(ScalarStyle::Literal, Cow::Borrowed("scalar")),
5464 ),
5465 #[cfg(feature = "comments")]
5466 Token(
5467 span,
5468 TokenType::Comment(
5469 Comment::new(Cow::Borrowed(" comment")).with_placement(Placement::Right),
5470 ),
5471 ),
5472 Token(
5473 span,
5474 TokenType::ReservedDirective(
5475 "reserved".to_owned(),
5476 vec!["one".to_owned(), "two".to_owned()],
5477 ),
5478 ),
5479 ];
5480
5481 for token in tokens {
5482 let queued: QueuedToken = token.clone().into();
5483
5484 assert_eq!(queued.into_public(), token);
5485 }
5486 }
5487
5488 #[test]
5489 fn comment_skipping_path_consumes_comment_without_tokenizing_it() {
5490 let mut scanner = Scanner::new(StrInput::new("# skipped\nnext: value\n"));
5491
5492 scanner.skip_yaml_whitespace().unwrap();
5493
5494 assert!(scanner.tokens.is_empty());
5495 assert_eq!(scanner.mark.line(), 2);
5496 assert_eq!(scanner.mark.col(), 0);
5497 }
5498
5499 #[test]
5500 #[cfg(feature = "comments")]
5501 fn yaml_whitespace_can_stop_after_queued_comment() {
5502 let mut scanner = Scanner::new(StrInput::new(" # queued\n# later\n"));
5503
5504 assert!(scanner.skip_yaml_whitespace().unwrap());
5505
5506 assert_eq!(scanner.tokens.len(), 1);
5507 assert!(matches!(
5508 scanner.tokens.front().unwrap().1,
5509 QueuedTokenType::Comment(ref comment) if comment.text == " queued"
5510 ));
5511 assert_eq!(scanner.mark.line(), 1);
5512 assert_eq!(scanner.mark.col(), 9);
5513 }
5514
5515 #[test]
5516 #[cfg(feature = "comments")]
5517 fn token_skip_can_stop_after_queued_comment() {
5518 let mut scanner = Scanner::new(StrInput::new("# first\n# second\n"));
5519
5520 assert!(scanner.skip_to_next_token().unwrap().queued_comment);
5521
5522 assert_eq!(scanner.tokens.len(), 1);
5523 assert!(matches!(
5524 scanner.tokens.front().unwrap().1,
5525 QueuedTokenType::Comment(ref comment) if comment.text == " first"
5526 ));
5527 assert_eq!(scanner.mark.line(), 2);
5528 assert_eq!(scanner.mark.col(), 0);
5529 }
5530
5531 #[test]
5532 fn token_skip_stops_after_one_ignored_comment_without_queuing_it() {
5533 let options = ignoring_comments_options();
5534 let mut scanner =
5535 Scanner::with_options(StrInput::new("# first\n# second\nvalue\n"), options);
5536
5537 let outcome = scanner.skip_to_next_token().unwrap();
5538
5539 assert!(outcome.saw_comment);
5540 #[cfg(feature = "comments")]
5541 assert!(!outcome.queued_comment);
5542 assert!(scanner.tokens.is_empty());
5543 assert_eq!((scanner.mark.line(), scanner.mark.col()), (2, 0));
5544 assert_eq!(scanner.input.look_ch(), '#');
5545 }
5546
5547 #[test]
5548 fn yaml_whitespace_stops_after_one_separated_ignored_comment() {
5549 let options = ignoring_comments_options();
5550 let mut scanner =
5551 Scanner::with_options(StrInput::new(" # first\n# second\nvalue\n"), options);
5552
5553 assert!(scanner.skip_yaml_whitespace().unwrap());
5554
5555 assert!(scanner.tokens.is_empty());
5556 assert_eq!((scanner.mark.line(), scanner.mark.col()), (1, 8));
5557 assert_eq!(scanner.input.look_ch(), '\n');
5558 }
5559
5560 #[test]
5561 fn scanner_skips_consecutive_leading_ignored_comments_before_next_token() {
5562 let options = ignoring_comments_options();
5563 let mut scanner =
5564 Scanner::with_options(StrInput::new("# first\n# second\nvalue\n"), options);
5565
5566 assert!(matches!(
5567 scanner.next_token().unwrap().unwrap().1,
5568 TokenType::StreamStart
5569 ));
5570 assert!(matches!(
5571 scanner.next_token().unwrap().unwrap().1,
5572 TokenType::Scalar(ScalarStyle::Plain, ref value) if value == "value"
5573 ));
5574 #[cfg(feature = "comments")]
5575 assert!(scanner
5576 .tokens
5577 .iter()
5578 .all(|QueuedToken(_, token)| !matches!(token, QueuedTokenType::Comment(_))));
5579 }
5580
5581 #[test]
5582 #[cfg(feature = "comments")]
5583 fn scanner_emits_first_leading_comment_before_scanning_next_comment() {
5584 let mut scanner = Scanner::new(StrInput::new("# first\n# second\nkey: value\n"));
5585
5586 assert!(matches!(
5587 scanner.next_token().unwrap().unwrap().1,
5588 TokenType::StreamStart
5589 ));
5590 assert!(matches!(
5591 scanner.next_token().unwrap().unwrap().1,
5592 TokenType::Comment(ref comment) if comment.text == " first"
5593 ));
5594 assert!(scanner.tokens.is_empty());
5595 assert!(matches!(
5596 scanner.next_token().unwrap().unwrap().1,
5597 TokenType::Comment(ref comment) if comment.text == " second"
5598 ));
5599 }
5600
5601 #[test]
5602 #[cfg(feature = "comments")]
5603 fn scanner_emits_quoted_scalar_comment_before_scanning_following_value() {
5604 let mut scanner = Scanner::new(StrInput::new("\"key\" # quoted\n: value\n"));
5605
5606 assert!(matches!(
5607 scanner.next_token().unwrap().unwrap().1,
5608 TokenType::StreamStart
5609 ));
5610 assert!(matches!(
5611 scanner.next_token().unwrap().unwrap().1,
5612 TokenType::Scalar(ScalarStyle::DoubleQuoted, ref value) if value == "key"
5613 ));
5614 assert!(matches!(
5615 scanner.next_token().unwrap().unwrap().1,
5616 TokenType::Comment(ref comment) if comment.text == " quoted"
5617 ));
5618 }
5619
5620 #[test]
5621 #[cfg(feature = "comments")]
5622 fn flow_scalar_comment_disables_adjacent_value_lookahead() {
5623 let mut scanner = Scanner::new(StrInput::new("\"key\"\n# quoted\n: value\n"));
5624
5625 scanner.fetch_flow_scalar(false).unwrap();
5626
5627 assert_eq!(scanner.adjacent_value_allowed_at, usize::MAX);
5628 assert!(matches!(
5629 scanner.tokens.front().unwrap().1,
5630 QueuedTokenType::Scalar(ScalarStyle::DoubleQuoted, ref value) if value == "key"
5631 ));
5632 assert!(scanner.tokens.iter().any(|QueuedToken(_, token)| matches!(
5633 token,
5634 QueuedTokenType::Comment(comment) if comment.text == " quoted"
5635 )));
5636 }
5637
5638 #[test]
5639 fn ignored_flow_scalar_comment_still_disables_adjacent_value_lookahead() {
5640 let options = ignoring_comments_options();
5641 let mut scanner =
5642 Scanner::with_options(StrInput::new("\"key\"\n# ignored\n: value\n"), options);
5643
5644 scanner.fetch_flow_scalar(false).unwrap();
5645
5646 assert_eq!(scanner.adjacent_value_allowed_at, usize::MAX);
5647 assert_eq!(scanner.tokens.len(), 1);
5648 assert!(matches!(
5649 scanner.tokens.front().unwrap().1,
5650 QueuedTokenType::Scalar(ScalarStyle::DoubleQuoted, ref value) if value == "key"
5651 ));
5652 }
5653
5654 #[test]
5655 #[cfg(feature = "comments")]
5656 fn deferred_error_waits_for_all_comment_tokens() {
5657 let mut scanner = Scanner::new(StrInput::new("# first\n# second\n@\n"));
5658
5659 assert!(matches!(
5660 scanner.next_token().unwrap().unwrap().1,
5661 TokenType::StreamStart
5662 ));
5663 assert!(matches!(
5664 scanner.next_token().unwrap().unwrap().1,
5665 TokenType::Comment(ref comment) if comment.text == " first"
5666 ));
5667 assert!(matches!(
5668 scanner.next_token().unwrap().unwrap().1,
5669 TokenType::Comment(ref comment) if comment.text == " second"
5670 ));
5671
5672 let error = scanner.next_token().unwrap_err();
5673
5674 assert_eq!(
5675 error.kind(),
5676 &ErrorKind::UnexpectedCharacter { character: '@' }
5677 );
5678 }
5679
5680 #[test]
5682 fn anchor_name_is_borrowed_for_str_input() {
5683 let mut scanner = Scanner::new(StrInput::new("&anch\n"));
5684
5685 loop {
5686 let tok = scanner
5687 .next_token()
5688 .expect("valid YAML must scan without errors")
5689 .expect("scanner must eventually produce a token");
5690 if let TokenType::Anchor(name) = tok.1 {
5691 assert!(matches!(name, Cow::Borrowed("anch")));
5692 break;
5693 }
5694 }
5695 }
5696
5697 #[test]
5698 fn anchor_name_rejects_non_printable_control_chars() {
5699 let mut scanner = Scanner::new(StrInput::new("&foo\u{0001}\n"));
5700
5701 scanner.next_token().unwrap();
5702 assert_eq!(
5703 scanner.next_token().unwrap_err().kind(),
5704 &ErrorKind::UnexpectedCharacter {
5705 character: '\u{0001}'
5706 }
5707 );
5708 }
5709
5710 #[test]
5711 fn alias_name_rejects_non_printable_control_chars() {
5712 let mut scanner = Scanner::new(StrInput::new("*foo\u{0001}\n"));
5713
5714 scanner.next_token().unwrap();
5715 assert_eq!(
5716 scanner.next_token().unwrap_err().kind(),
5717 &ErrorKind::UnexpectedCharacter {
5718 character: '\u{0001}'
5719 }
5720 );
5721 }
5722
5723 #[test]
5724 fn alias_name_is_borrowed_for_str_input() {
5725 let mut scanner = Scanner::new(StrInput::new("*anch\n"));
5726
5727 loop {
5728 let tok = scanner
5729 .next_token()
5730 .expect("valid YAML must scan without errors")
5731 .expect("scanner must eventually produce a token");
5732 if let TokenType::Alias(name) = tok.1 {
5733 assert!(matches!(name, Cow::Borrowed("anch")));
5734 break;
5735 }
5736 }
5737 }
5738
5739 #[test]
5740 fn alias_name_scans_colon_as_part_of_name() {
5741 let mut scanner = Scanner::new(StrInput::new("*foo: bar\n"));
5742
5743 loop {
5744 let tok = scanner
5745 .next_token()
5746 .expect("scanner must not fail before alias token")
5747 .expect("scanner must eventually emit an alias token");
5748
5749 if let TokenType::Alias(name) = tok.1 {
5750 assert_eq!(name.as_ref(), "foo:");
5751 break;
5752 }
5753 }
5754 }
5755
5756 #[test]
5757 fn anchor_name_scans_colon_as_part_of_name() {
5758 let mut scanner = Scanner::new(StrInput::new("&foo: bar\n"));
5759
5760 loop {
5761 let tok = scanner
5762 .next_token()
5763 .expect("scanner must not fail before anchor token")
5764 .expect("scanner must eventually emit an anchor token");
5765
5766 if let TokenType::Anchor(name) = tok.1 {
5767 assert_eq!(name.as_ref(), "foo:");
5768 break;
5769 }
5770 }
5771 }
5772
5773 #[test]
5775 fn tag_directive_parts_are_borrowed_for_str_input() {
5776 let mut scanner = Scanner::new(StrInput::new("%TAG !e! tag:example.com,2000:app/\n"));
5777
5778 loop {
5779 let tok = scanner
5780 .next_token()
5781 .expect("valid YAML must scan without errors")
5782 .expect("scanner must eventually produce a token");
5783 if let TokenType::TagDirective(handle, prefix) = tok.1 {
5784 assert!(matches!(handle, Cow::Borrowed("!e!")));
5785 assert!(matches!(prefix, Cow::Borrowed("tag:example.com,2000:app/")));
5786 break;
5787 }
5788 }
5789 }
5790
5791 #[test]
5792 fn tag_directive_parts_are_owned_for_buffered_input() {
5793 let mut scanner = Scanner::new(BufferedInput::new(
5794 "%TAG !e! tag:example.com,2000:app/\n".chars(),
5795 ));
5796
5797 loop {
5798 let tok = scanner
5799 .next_token()
5800 .expect("valid YAML must scan without errors")
5801 .expect("scanner must eventually produce a token");
5802 if let TokenType::TagDirective(handle, prefix) = tok.1 {
5803 assert!(matches!(handle, Cow::Owned(_)));
5804 assert_eq!(&*handle, "!e!");
5805 assert!(matches!(prefix, Cow::Owned(_)));
5806 assert_eq!(&*prefix, "tag:example.com,2000:app/");
5807 break;
5808 }
5809 }
5810 }
5811
5812 #[test]
5813 fn buffered_tag_directive_decodes_prefix_escape() {
5814 let mut scanner = Scanner::new(BufferedInput::new(
5815 "%TAG !e! %74ag:example.com,2000:app/\n".chars(),
5816 ));
5817
5818 loop {
5819 let tok = scanner
5820 .next_token()
5821 .expect("valid YAML must scan without errors")
5822 .expect("scanner must eventually produce a token");
5823 if let TokenType::TagDirective(handle, prefix) = tok.1 {
5824 assert_eq!(&*handle, "!e!");
5825 assert!(matches!(prefix, Cow::Owned(_)));
5826 assert_eq!(&*prefix, "tag:example.com,2000:app/");
5827 break;
5828 }
5829 }
5830 }
5831
5832 #[test]
5833 fn local_tag_combines_handle_text_with_escaped_suffix() {
5834 let mut scanner = Scanner::new(StrInput::new("!foo%20bar value\n"));
5835
5836 loop {
5837 let tok = scanner
5838 .next_token()
5839 .expect("valid YAML must scan without errors")
5840 .expect("scanner must eventually produce a token");
5841 if let TokenType::Tag(handle, suffix) = tok.1 {
5842 assert!(matches!(handle, Cow::Borrowed("!")));
5843 assert!(matches!(suffix, Cow::Owned(_)));
5844 assert_eq!(&*suffix, "foo bar");
5845 break;
5846 }
5847 }
5848 }
5849
5850 #[test]
5851 fn secondary_tag_requires_suffix_in_borrowed_and_buffered_paths() {
5852 assert_eq!(
5853 first_scanner_error_kind("!! value\n"),
5854 ErrorKind::MissingTagUri
5855 );
5856 assert_eq!(
5857 first_buffered_scanner_error_kind("!! value\n"),
5858 ErrorKind::MissingTagUri
5859 );
5860 }
5861
5862 #[test]
5863 fn plain_scalar_is_borrowed_when_whitespace_free_for_str_input() {
5864 let mut scanner = Scanner::new(StrInput::new("foo\n"));
5865
5866 loop {
5867 let tok = scanner
5868 .next_token()
5869 .expect("valid YAML must scan without errors")
5870 .expect("scanner must eventually produce a token");
5871 if let TokenType::Scalar(_, value) = tok.1 {
5872 assert!(matches!(value, Cow::Borrowed("foo")));
5873 break;
5874 }
5875 }
5876 }
5877
5878 #[test]
5879 fn plain_scalar_is_borrowed_when_whitespace_present_for_str_input() {
5880 let mut scanner = Scanner::new(StrInput::new("foo bar\n"));
5881
5882 loop {
5883 let tok = scanner
5884 .next_token()
5885 .expect("valid YAML must scan without errors")
5886 .expect("scanner must eventually produce a token");
5887 if let TokenType::Scalar(_, value) = tok.1 {
5888 assert!(matches!(value, Cow::Borrowed("foo bar")));
5889 break;
5890 }
5891 }
5892 }
5893
5894 #[test]
5895 fn single_quoted_scalar_is_borrowed_when_verbatim_for_str_input() {
5896 let mut scanner = Scanner::new(StrInput::new("'foo bar'\n"));
5897
5898 loop {
5899 let tok = scanner
5900 .next_token()
5901 .expect("valid YAML must scan without errors")
5902 .expect("scanner must eventually produce a token");
5903 if let TokenType::Scalar(_, value) = tok.1 {
5904 assert!(matches!(value, Cow::Borrowed("foo bar")));
5905 break;
5906 }
5907 }
5908 }
5909
5910 #[test]
5911 fn single_quoted_scalar_is_owned_when_quote_is_escaped_for_str_input() {
5912 let mut scanner = Scanner::new(StrInput::new("'foo''bar'\n"));
5913
5914 loop {
5915 let tok = scanner
5916 .next_token()
5917 .expect("valid YAML must scan without errors")
5918 .expect("scanner must eventually produce a token");
5919 if let TokenType::Scalar(_, value) = tok.1 {
5920 assert!(matches!(value, Cow::Owned(_)));
5921 assert_eq!(&*value, "foo'bar");
5922 break;
5923 }
5924 }
5925 }
5926
5927 #[test]
5928 fn double_quoted_scalar_is_borrowed_when_verbatim_for_str_input() {
5929 let mut scanner = Scanner::new(StrInput::new("\"foo bar\"\n"));
5930
5931 loop {
5932 let tok = scanner
5933 .next_token()
5934 .expect("valid YAML must scan without errors")
5935 .expect("scanner must eventually produce a token");
5936 if let TokenType::Scalar(_, value) = tok.1 {
5937 assert!(matches!(value, Cow::Borrowed("foo bar")));
5938 break;
5939 }
5940 }
5941 }
5942
5943 #[test]
5944 fn double_quoted_scalar_is_owned_when_escape_sequence_present_for_str_input() {
5945 let mut scanner = Scanner::new(StrInput::new("\"foo\\nbar\"\n"));
5946
5947 loop {
5948 let tok = scanner
5949 .next_token()
5950 .expect("valid YAML must scan without errors")
5951 .expect("scanner must eventually produce a token");
5952 if let TokenType::Scalar(_, value) = tok.1 {
5953 assert!(matches!(value, Cow::Owned(_)));
5954 assert_eq!(&*value, "foo\nbar");
5955 break;
5956 }
5957 }
5958 }
5959
5960 #[test]
5961 fn plain_key_is_borrowed_for_str_input() {
5962 let mut scanner = Scanner::new(StrInput::new("mykey: value\n"));
5964
5965 let mut found_key = false;
5966 let mut key_value: Option<Cow<'_, str>> = None;
5967
5968 loop {
5969 let tok = scanner
5970 .next_token()
5971 .expect("valid YAML must scan without errors");
5972 let Some(tok) = tok else { break };
5973
5974 if matches!(tok.1, TokenType::Key) {
5975 found_key = true;
5976 } else if found_key {
5977 if let TokenType::Scalar(_, value) = tok.1 {
5978 key_value = Some(value);
5979 break;
5980 }
5981 }
5982 }
5983
5984 assert!(found_key, "expected to find a Key token");
5985 let key_value = key_value.expect("expected to find a scalar after Key token");
5986 assert!(
5987 matches!(key_value, Cow::Borrowed("mykey")),
5988 "key should be borrowed, got: {key_value:?}"
5989 );
5990 }
5991
5992 #[test]
5993 fn quoted_key_is_borrowed_when_verbatim_for_str_input() {
5994 let mut scanner = Scanner::new(StrInput::new("\"mykey\": value\n"));
5995
5996 let mut found_key = false;
5997 let mut key_value: Option<Cow<'_, str>> = None;
5998
5999 loop {
6000 let tok = scanner
6001 .next_token()
6002 .expect("valid YAML must scan without errors");
6003 let Some(tok) = tok else { break };
6004
6005 if matches!(tok.1, TokenType::Key) {
6006 found_key = true;
6007 } else if found_key {
6008 if let TokenType::Scalar(_, value) = tok.1 {
6009 key_value = Some(value);
6010 break;
6011 }
6012 }
6013 }
6014
6015 assert!(found_key, "expected to find a Key token");
6016 let key_value = key_value.expect("expected to find a scalar after Key token");
6017 assert!(
6018 matches!(key_value, Cow::Borrowed("mykey")),
6019 "quoted key should be borrowed when verbatim, got: {key_value:?}"
6020 );
6021 }
6022
6023 #[test]
6024 fn tag_handle_and_suffix_are_borrowed_for_str_input() {
6025 let mut scanner = Scanner::new(StrInput::new("!!str foo\n"));
6027
6028 loop {
6029 let tok = scanner
6030 .next_token()
6031 .expect("valid YAML must scan without errors")
6032 .expect("scanner must eventually produce a token");
6033 if let TokenType::Tag(handle, suffix) = tok.1 {
6034 assert!(
6035 matches!(handle, Cow::Borrowed("!!")),
6036 "tag handle should be borrowed, got: {handle:?}"
6037 );
6038 assert!(
6039 matches!(suffix, Cow::Borrowed("str")),
6040 "tag suffix should be borrowed, got: {suffix:?}"
6041 );
6042 break;
6043 }
6044 }
6045 }
6046
6047 #[test]
6048 fn local_tag_suffix_is_borrowed_for_str_input() {
6049 let mut scanner = Scanner::new(StrInput::new("!mytag foo\n"));
6051
6052 loop {
6053 let tok = scanner
6054 .next_token()
6055 .expect("valid YAML must scan without errors")
6056 .expect("scanner must eventually produce a token");
6057 if let TokenType::Tag(handle, suffix) = tok.1 {
6058 assert!(
6059 matches!(handle, Cow::Borrowed("!")),
6060 "local tag handle should be '!', got: {handle:?}"
6061 );
6062 assert!(
6063 matches!(suffix, Cow::Borrowed("mytag")),
6064 "local tag suffix should be borrowed, got: {suffix:?}"
6065 );
6066 break;
6067 }
6068 }
6069 }
6070
6071 #[test]
6072 fn local_tag_suffix_with_punctuation_is_borrowed_for_str_input() {
6073 let mut scanner = Scanner::new(StrInput::new("!mytag/part foo\n"));
6074
6075 loop {
6076 let tok = scanner
6077 .next_token()
6078 .expect("valid YAML must scan without errors")
6079 .expect("scanner must eventually produce a token");
6080 if let TokenType::Tag(handle, suffix) = tok.1 {
6081 assert!(matches!(handle, Cow::Borrowed("!")));
6082 assert!(matches!(suffix, Cow::Borrowed("mytag/part")));
6083 break;
6084 }
6085 }
6086 }
6087
6088 #[test]
6089 fn tag_with_uri_escape_is_owned_for_str_input() {
6090 let mut scanner = Scanner::new(StrInput::new("!!my%20tag foo\n"));
6092
6093 loop {
6094 let tok = scanner
6095 .next_token()
6096 .expect("valid YAML must scan without errors")
6097 .expect("scanner must eventually produce a token");
6098 if let TokenType::Tag(handle, suffix) = tok.1 {
6099 assert!(
6100 matches!(handle, Cow::Borrowed("!!")),
6101 "tag handle should still be borrowed, got: {handle:?}"
6102 );
6103 assert!(
6104 matches!(suffix, Cow::Owned(_)),
6105 "tag suffix with URI escape should be owned, got: {suffix:?}"
6106 );
6107 assert_eq!(&*suffix, "my tag");
6108 break;
6109 }
6110 }
6111 }
6112
6113 #[test]
6114 fn flow_scalar_buffer_tracks_pending_whitespace() {
6115 let mut borrowed = super::FlowScalarBuf::new_borrowed(2);
6116
6117 borrowed.note_pending_ws(5, 8);
6118 borrowed.commit_pending_ws();
6119 assert!(matches!(
6120 borrowed,
6121 super::FlowScalarBuf::Borrowed {
6122 end: 8,
6123 pending_ws_start: None,
6124 pending_ws_end: 8,
6125 ..
6126 }
6127 ));
6128
6129 borrowed.note_pending_ws(9, 11);
6130 borrowed.discard_pending_ws();
6131 assert!(matches!(
6132 borrowed,
6133 super::FlowScalarBuf::Borrowed {
6134 end: 8,
6135 pending_ws_start: None,
6136 pending_ws_end: 8,
6137 ..
6138 }
6139 ));
6140 assert!(borrowed.as_owned_mut().is_none());
6141
6142 let mut owned = super::FlowScalarBuf::new_owned();
6143 owned.as_owned_mut().unwrap().push_str("owned");
6144 assert!(matches!(owned, super::FlowScalarBuf::Owned(ref s) if s == "owned"));
6145 }
6146
6147 fn first_scanner_error_kind(input: &str) -> ErrorKind {
6148 first_scanner_error(input).kind().clone()
6149 }
6150
6151 fn first_buffered_scanner_error_kind(input: &str) -> ErrorKind {
6152 let mut scanner = Scanner::new(BufferedInput::new(input.chars()));
6153 loop {
6154 match scanner.next_token() {
6155 Ok(Some(_)) => {}
6156 Ok(None) => panic!("expected scanner error"),
6157 Err(error) => return error.kind().clone(),
6158 }
6159 }
6160 }
6161
6162 fn first_scanner_error(input: &str) -> ScanError {
6163 let mut scanner = Scanner::new(StrInput::new(input));
6164 loop {
6165 match scanner.next_token() {
6166 Ok(Some(_)) => {}
6167 Ok(None) => panic!("expected scanner error"),
6168 Err(error) => return error,
6169 }
6170 }
6171 }
6172
6173 fn first_scalar_value(input: &str) -> String {
6174 let mut scanner = Scanner::new(StrInput::new(input));
6175 loop {
6176 match scanner.next_token().expect("scanner should not error") {
6177 Some(Token(_, TokenType::Scalar(_, value))) => return value.into_owned(),
6178 Some(_) => {}
6179 None => panic!("expected scalar token"),
6180 }
6181 }
6182 }
6183
6184 fn first_buffered_scalar_value(input: &str) -> String {
6185 let mut scanner = Scanner::new(BufferedInput::new(input.chars()));
6186 loop {
6187 match scanner.next_token().expect("scanner should not error") {
6188 Some(Token(_, TokenType::Scalar(_, value))) => return value.into_owned(),
6189 Some(_) => {}
6190 None => panic!("expected scalar token"),
6191 }
6192 }
6193 }
6194
6195 #[test]
6196 fn iterator_next_emits_error_and_then_stays_empty() {
6197 let mut scanner = Scanner::new(StrInput::new("\"unterminated"));
6198
6199 let error = scanner
6200 .by_ref()
6201 .find_map(Result::err)
6202 .expect("scanner should emit the error");
6203 assert_eq!(error.kind(), &ErrorKind::UnclosedQuotedScalar);
6204 assert!(scanner.next().is_none());
6205 }
6206
6207 #[test]
6208 fn next_token_returns_none_after_stream_end() {
6209 let mut scanner = Scanner::new(StrInput::new(""));
6210
6211 while let Some(token) = scanner.next_token().unwrap() {
6212 if matches!(token.1, TokenType::StreamEnd) {
6213 break;
6214 }
6215 }
6216
6217 assert!(scanner.stream_started());
6218 assert!(scanner.stream_ended());
6219 assert!(scanner.next_token().unwrap().is_none());
6220 }
6221
6222 #[test]
6223 fn directive_name_must_be_present() {
6224 assert_eq!(
6225 first_scanner_error_kind("%\n"),
6226 ErrorKind::MissingDirectiveName
6227 );
6228 }
6229
6230 #[test]
6231 fn yaml_directive_requires_dot_between_version_numbers() {
6232 assert_eq!(
6233 first_scanner_error_kind("%YAML 1\n"),
6234 ErrorKind::MissingYamlVersionSeparator
6235 );
6236 }
6237
6238 #[test]
6239 fn yaml_directive_requires_major_version_number() {
6240 assert_eq!(
6241 first_scanner_error_kind("%YAML .2\n"),
6242 ErrorKind::MissingYamlVersion
6243 );
6244 }
6245
6246 #[test]
6247 fn yaml_directive_rejects_extremely_long_version_number() {
6248 assert_eq!(
6249 first_scanner_error_kind("%YAML 1234567890.2\n"),
6250 ErrorKind::YamlVersionTooLong
6251 );
6252 }
6253
6254 #[test]
6255 fn tag_directive_handle_must_end_with_bang() {
6256 assert_eq!(
6257 first_scanner_error_kind("%TAG !bad tag:example.com,2024:\n"),
6258 ErrorKind::ExpectedTagDirectiveBang
6259 );
6260 }
6261
6262 #[test]
6263 fn tag_directive_handle_must_start_with_bang() {
6264 assert_eq!(
6265 first_scanner_error_kind("%TAG bad! tag:example.com,2024:\n"),
6266 ErrorKind::ExpectedTagBang
6267 );
6268 assert_eq!(
6269 first_buffered_scanner_error_kind("%TAG bad! tag:example.com,2024:\n"),
6270 ErrorKind::ExpectedTagBang
6271 );
6272 }
6273
6274 #[test]
6275 fn tag_directive_prefix_must_start_with_tag_character() {
6276 assert_eq!(
6277 first_scanner_error_kind("%TAG !e! `bad\n"),
6278 ErrorKind::InvalidGlobalTagCharacter
6279 );
6280 }
6281
6282 #[test]
6283 fn tag_directive_prefix_must_end_before_invalid_content() {
6284 assert_eq!(
6285 first_scanner_error_kind("%TAG !e! tag:example.com^suffix\n"),
6286 ErrorKind::InvalidTagDirectiveTerminator
6287 );
6288 }
6289
6290 #[test]
6291 fn tag_directive_prefix_with_uri_escape_is_owned_and_decoded() {
6292 let mut scanner =
6293 Scanner::new(StrInput::new("%TAG !e! tag:example.com,2024:some%20app/\n"));
6294
6295 loop {
6296 let token = scanner
6297 .next_token()
6298 .expect("valid directive should scan")
6299 .expect("scanner must produce a directive token");
6300 if let TokenType::TagDirective(handle, prefix) = token.1 {
6301 assert!(matches!(handle, Cow::Borrowed("!e!")));
6302 assert!(matches!(prefix, Cow::Owned(_)));
6303 assert_eq!(&*prefix, "tag:example.com,2024:some app/");
6304 break;
6305 }
6306 }
6307 }
6308
6309 #[test]
6310 fn bare_bang_tag_scans_as_non_specific_tag() {
6311 let mut scanner = Scanner::new(StrInput::new("! foo\n"));
6312
6313 loop {
6314 let token = scanner
6315 .next_token()
6316 .expect("valid tag should scan")
6317 .expect("scanner must produce a tag token");
6318 if let TokenType::Tag(handle, suffix) = token.1 {
6319 assert_eq!(&*handle, "");
6320 assert_eq!(&*suffix, "!");
6321 break;
6322 }
6323 }
6324 }
6325
6326 #[test]
6327 fn tag_requires_separation_after_suffix() {
6328 assert_eq!(
6329 first_scanner_error_kind("!foo,bar\n"),
6330 ErrorKind::InvalidTagTerminator
6331 );
6332 }
6333
6334 #[test]
6335 fn verbatim_tag_requires_uri() {
6336 assert_eq!(
6337 first_scanner_error_kind("!<> foo\n"),
6338 ErrorKind::MissingTagUri
6339 );
6340 }
6341
6342 #[test]
6343 fn verbatim_tag_requires_closing_angle_bracket() {
6344 assert_eq!(
6345 first_scanner_error_kind("!<tag:yaml.org,2002:str foo\n"),
6346 ErrorKind::UnclosedVerbatimTag
6347 );
6348 }
6349
6350 #[test]
6351 fn tag_uri_escape_requires_hex_digits() {
6352 assert_eq!(
6353 first_scanner_error_kind("!!bad%zz foo\n"),
6354 ErrorKind::InvalidTagEscape
6355 );
6356 }
6357
6358 #[test]
6359 fn tag_uri_escape_rejects_bad_leading_utf8_byte() {
6360 assert_eq!(
6361 first_scanner_error_kind("!!bad%80 foo\n"),
6362 ErrorKind::InvalidTagUtf8LeadingByte
6363 );
6364 }
6365
6366 #[test]
6367 fn tag_uri_escape_rejects_bad_trailing_utf8_byte() {
6368 assert_eq!(
6369 first_scanner_error_kind("!!bad%C2%41 foo\n"),
6370 ErrorKind::InvalidTagUtf8TrailingByte
6371 );
6372 }
6373
6374 #[test]
6375 fn tag_uri_escape_rejects_invalid_utf8_codepoint() {
6376 assert_eq!(
6377 first_scanner_error_kind("!!bad%F4%90%80%80 foo\n"),
6378 ErrorKind::InvalidTagUtf8
6379 );
6380 }
6381
6382 #[test]
6383 fn anchors_and_aliases_require_names() {
6384 assert_eq!(
6385 first_scanner_error_kind("& \n"),
6386 ErrorKind::MissingAnchorOrAliasName
6387 );
6388 assert_eq!(
6389 first_scanner_error_kind("* \n"),
6390 ErrorKind::MissingAnchorOrAliasName
6391 );
6392 }
6393
6394 #[test]
6395 fn document_end_marker_rejects_trailing_content() {
6396 assert_eq!(
6397 first_scanner_error_kind("... trailing\n"),
6398 ErrorKind::InvalidDocumentEnd
6399 );
6400 }
6401
6402 #[test]
6403 fn reserved_indicators_are_rejected_outside_directives() {
6404 let error = first_scanner_error(" @\n");
6405
6406 assert_eq!(
6407 error.kind(),
6408 &ErrorKind::UnexpectedCharacter { character: '@' }
6409 );
6410 }
6411
6412 #[test]
6413 fn flow_block_entry_indicator_is_rejected() {
6414 assert_eq!(
6415 first_scanner_error_kind("[- ]\n"),
6416 ErrorKind::BlockEntryInFlowCollection
6417 );
6418 }
6419
6420 #[test]
6421 fn block_entry_after_tabbed_separator_reports_specific_error() {
6422 assert_eq!(
6423 first_scanner_error_kind("-\t- value\n"),
6424 ErrorKind::InvalidBlockEntryWhitespace
6425 );
6426 }
6427
6428 #[test]
6429 fn document_indicator_reports_unclosed_flow_collection() {
6430 let error = first_scanner_error("[\n---\n");
6431
6432 assert_eq!(
6433 error.kind(),
6434 &ErrorKind::UnclosedFlowCollection { open: '[' }
6435 );
6436 }
6437
6438 #[test]
6439 fn block_scalar_header_rejects_trailing_content() {
6440 assert_eq!(
6441 first_scanner_error_kind("|+ trailing\n"),
6442 ErrorKind::InvalidBlockScalarHeader
6443 );
6444 }
6445
6446 #[test]
6447 fn block_scalar_rejects_zero_indent_indicator() {
6448 assert_eq!(
6449 first_scanner_error_kind("|0\n"),
6450 ErrorKind::ZeroBlockScalarIndent
6451 );
6452 assert_eq!(
6453 first_scanner_error_kind("|+0\n"),
6454 ErrorKind::ZeroBlockScalarIndent
6455 );
6456 }
6457
6458 #[test]
6459 fn empty_block_scalar_at_eof_honors_chomping() {
6460 assert_eq!(first_scalar_value("|\n"), "");
6461 assert_eq!(first_scalar_value("|-\n"), "");
6462 assert_eq!(first_scalar_value("|+\n"), "");
6463 assert_eq!(first_scalar_value("|+\n\n"), "\n");
6464 assert_eq!(first_scalar_value("|+\n "), "\n");
6465 }
6466
6467 #[test]
6468 fn buffered_block_scalar_reads_content_past_lookahead_window() {
6469 assert_eq!(
6470 first_buffered_scalar_value("|\n abcdefghijklmnopqrstuvwxyz\n"),
6471 "abcdefghijklmnopqrstuvwxyz\n"
6472 );
6473 }
6474
6475 #[test]
6476 fn explicit_indent_block_scalar_can_end_at_document_marker() {
6477 assert_eq!(first_scalar_value("|1\n...\n"), "");
6478 }
6479
6480 #[test]
6481 fn root_explicit_indent_block_scalar_rejects_underindented_content() {
6482 assert_eq!(
6483 first_scanner_error_kind("|2\nx\n"),
6484 ErrorKind::InvalidBlockScalarIndent
6485 );
6486 }
6487
6488 #[test]
6489 fn quoted_scalar_rejects_document_indicator_at_line_start() {
6490 assert_eq!(
6491 first_scanner_error_kind("\"one\n---\ntwo\"\n"),
6492 ErrorKind::DocumentIndicatorInQuotedScalar
6493 );
6494 }
6495
6496 #[test]
6497 fn quoted_scalar_rejects_tab_indentation_after_line_break() {
6498 assert_eq!(
6499 first_scanner_error_kind("a: \"one\n\tbad\"\n"),
6500 ErrorKind::TabInIndentation
6501 );
6502 }
6503
6504 #[test]
6505 fn quoted_scalar_rejects_underindented_continuation() {
6506 assert_eq!(
6507 first_scanner_error_kind("a: \"one\nbad\"\n"),
6508 ErrorKind::InvalidQuotedScalarIndent
6509 );
6510 assert_eq!(
6511 first_scanner_error_kind("a:\n b: 'one\n bad'\n"),
6512 ErrorKind::InvalidQuotedScalarIndent
6513 );
6514 }
6515
6516 #[test]
6517 fn quoted_scalar_continuation_one_column_past_block_indent() {
6518 for input in [
6521 "a: \"one\n two\"\n",
6522 "a: 'one\n two'\n",
6523 "a:\n b: \"one\n two\"\n",
6524 "- a: \"one\n two\"\n",
6525 "? k\n: \"one\n two\"\n",
6526 ] {
6527 let mut scanner = Scanner::new(StrInput::new(input));
6528 loop {
6529 match scanner.next_token() {
6530 Ok(Some(_)) => {}
6531 Ok(None) => break,
6532 Err(error) => panic!("{input:?}: {error}"),
6533 }
6534 }
6535 }
6536 }
6537
6538 #[test]
6539 fn quoted_scalar_trailing_content_error_names_quote_style() {
6540 assert_eq!(
6541 first_scanner_error_kind("'foo' trailing\n"),
6542 ErrorKind::InvalidTrailingSingleQuotedScalar
6543 );
6544 assert_eq!(
6545 first_scanner_error_kind("\"foo\" trailing\n"),
6546 ErrorKind::InvalidTrailingDoubleQuotedScalar
6547 );
6548 }
6549
6550 #[test]
6551 fn quoted_scalar_escape_errors_cover_hex_and_surrogate_edges() {
6552 assert_eq!(
6553 first_scanner_error_kind("\"\\xG0\"\n"),
6554 ErrorKind::InvalidQuotedScalarHexEscape
6555 );
6556 assert_eq!(
6557 first_scanner_error_kind("\"\\uD800\\uGGGG\"\n"),
6558 ErrorKind::InvalidLowSurrogateHexEscape
6559 );
6560 assert_eq!(
6561 first_scanner_error_kind("\"\\uD800\\u0041\"\n"),
6562 ErrorKind::InvalidLowSurrogate
6563 );
6564 assert_eq!(
6565 first_scanner_error_kind("\"\\U00110000\"\n"),
6566 ErrorKind::InvalidUnicodeEscape
6567 );
6568 }
6569
6570 #[test]
6571 fn under_indented_flow_scalar_is_accepted() {
6572 let tokens: Vec<_> = Scanner::new(StrInput::new("a:\n [\nfoo]\n"))
6573 .map(|result| result.expect("under-indented flow entry should scan").1)
6574 .collect();
6575 assert_eq!(
6576 tokens,
6577 [
6578 TokenType::StreamStart,
6579 TokenType::BlockMappingStart,
6580 TokenType::Key,
6581 TokenType::Scalar(ScalarStyle::Plain, "a".into()),
6582 TokenType::Value,
6583 TokenType::FlowSequenceStart,
6584 TokenType::Scalar(ScalarStyle::Plain, "foo".into()),
6585 TokenType::FlowSequenceEnd,
6586 TokenType::BlockEnd,
6587 TokenType::StreamEnd,
6588 ]
6589 );
6590 }
6591
6592 #[test]
6593 fn required_simple_key_requires_value_at_stream_end() {
6594 let error = first_scanner_error("a:\n&b\n- c\n");
6595
6596 assert_eq!(error.kind(), &ErrorKind::SimpleKeyExpected);
6597 assert_eq!(error.marker().index(), 3);
6598 assert_eq!(error.marker().line(), 2);
6599 assert_eq!(error.marker().col(), 0);
6600 }
6601
6602 #[test]
6603 fn first_simple_key_tracks_ordered_possible_candidates() {
6604 let deep = alloc::format!(
6605 "{}{}{{[é, z]: v}}{}\n",
6606 "[".repeat(240),
6607 "a, ".repeat(400),
6608 "]".repeat(240)
6609 );
6610 for source in [
6611 "",
6612 "a: b\nmissing",
6613 "a: b\nmissing\n# after\nnext: value\n",
6614 "{[a, {b: c}]: [d, e], []: f}\n",
6615 "[&a !tag [x, y], *a, {z: []}]\n",
6616 "key: {\n&anchor\nname: value}\n",
6617 "---\n{[a, b]: c} # end\n...\n# next\n---\nx: y\n",
6618 "a: b\n[unterminated",
6619 &deep,
6620 ] {
6621 for limit in [0, 4, 1024, usize::MAX] {
6622 #[cfg(feature = "comments")]
6623 let emission_modes = [false, true].as_slice();
6624 #[cfg(not(feature = "comments"))]
6625 let emission_modes = [false].as_slice();
6626 for &emit_comments in emission_modes {
6627 let options = crate::options! { simple_key_max_lookahead: limit };
6628 #[cfg(feature = "comments")]
6629 let options = {
6630 let mut options = options;
6631 options.emit_comments = emit_comments;
6632 options
6633 };
6634 let mut scanner = Scanner::with_options(StrInput::new(source), options);
6635 loop {
6636 let result = scanner.next();
6637 assert_eq!(
6638 scanner.first_simple_key,
6639 scanner.simple_keys.iter().position(|key| key.possible),
6640 "source: {source:?}, limit: {limit}, comments: {emit_comments}"
6641 );
6642 let mut previous = None;
6643 for key in scanner.simple_keys.iter().filter(|key| key.possible) {
6644 assert!(key.token_number >= scanner.tokens_parsed);
6645 if let Some((index, token_number)) = previous {
6646 assert!(key.mark.index() >= index);
6647 assert!(key.token_number >= token_number);
6648 }
6649 previous = Some((key.mark.index(), key.token_number));
6650 }
6651 if result.is_none() {
6652 break;
6653 }
6654 }
6655 }
6656 }
6657 }
6658 }
6659
6660 #[test]
6661 fn plain_scalar_rejects_dash_before_flow_indicator() {
6662 assert_eq!(
6663 first_scanner_error_kind("[-]\n"),
6664 ErrorKind::PlainScalarStartsWithDashFlowIndicator
6665 );
6666 }
6667
6668 #[test]
6669 fn explicit_key_rejects_tab_used_as_indentation() {
6670 assert_eq!(
6671 first_scanner_error_kind("?\n\tfoo\n"),
6672 ErrorKind::TabNotAllowed
6673 );
6674 }
6675
6676 #[test]
6677 fn flow_mapping_rejects_adjacent_collection_value_after_plain_key() {
6678 assert_eq!(
6679 first_scanner_error_kind("[a:[]]\n"),
6680 ErrorKind::FlowMappingValueAdjacentCollection
6681 );
6682 }
6683
6684 #[test]
6685 fn implicit_flow_mapping_colon_cannot_move_to_next_line() {
6686 assert_eq!(
6687 first_scanner_error_kind("[foo\n: bar]\n"),
6688 ErrorKind::InvalidColonPlacement
6689 );
6690 }
6691
6692 #[test]
6693 fn invalid_simple_key_token_positions_are_scan_errors() {
6694 for (tokens_parsed, token_number) in [(1, 0), (0, 1)] {
6695 let mut scanner = Scanner::new(StrInput::new(": value\n"));
6696 scanner.fetch_stream_start();
6697 scanner.tokens.clear();
6698 scanner.tokens_parsed = tokens_parsed;
6699
6700 let simple_key = scanner
6701 .simple_keys
6702 .last_mut()
6703 .expect("stream start should create a simple key slot");
6704 simple_key.possible = true;
6705 simple_key.token_number = token_number;
6706
6707 let error = scanner
6708 .fetch_value()
6709 .expect_err("invalid simple key position should be reported as a scan error");
6710 assert_eq!(error.kind(), &ErrorKind::InvalidSimpleKey);
6711 assert_eq!(error.marker(), &Marker::new(0, 1, 0));
6712 }
6713 }
6714
6715 #[test]
6716 fn issue14_alias_scanner_consumes_colon_as_name_character() {
6717 let mut scanner = Scanner::new(StrInput::new("*foo: bar\n"));
6718
6719 assert!(matches!(
6720 scanner.next_token().unwrap().unwrap().1,
6721 TokenType::StreamStart
6722 ));
6723
6724 let token = scanner.next_token().unwrap().unwrap();
6725
6726 assert!(
6727 matches!(token.1, TokenType::Alias(ref name) if name.as_ref() == "foo:"),
6728 "expected `*foo: bar` to start with Alias(\"foo:\"), got {token:?}"
6729 );
6730 }
6731
6732 #[test]
6733 fn issue14_anchor_scanner_consumes_colon_as_name_character() {
6734 let mut scanner = Scanner::new(StrInput::new("&foo: bar\n"));
6735
6736 assert!(matches!(
6737 scanner.next_token().unwrap().unwrap().1,
6738 TokenType::StreamStart
6739 ));
6740
6741 let token = scanner.next_token().unwrap().unwrap();
6742
6743 assert!(
6744 matches!(token.1, TokenType::Anchor(ref name) if name.as_ref() == "foo:"),
6745 "expected `&foo: bar` to start with Anchor(\"foo:\"), got {token:?}"
6746 );
6747 }
6748}