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,
24 },
25 error::{ErrorKind, ScanError},
26 input::{BorrowedInput, SkipTabs},
27};
28
29const SIMPLE_KEY_MAX_LOOKAHEAD: usize = 1024;
31
32#[derive(Clone, Copy, PartialEq, Debug, Eq, Hash, PartialOrd, Ord)]
34pub enum ScalarStyle {
35 Plain,
37 SingleQuoted,
39 DoubleQuoted,
41
42 Literal,
48 Folded,
55}
56
57#[derive(Clone, Copy, Debug, Default)]
64struct MarkerOffsets {
65 chars: usize,
67 bytes: Option<usize>,
69}
70
71impl PartialEq for MarkerOffsets {
72 fn eq(&self, other: &Self) -> bool {
73 self.chars == other.chars
77 }
78}
79
80impl Eq for MarkerOffsets {}
81
82#[derive(Clone, Copy, PartialEq, Debug, Eq, Default)]
84pub struct Marker {
85 offsets: MarkerOffsets,
87 line: usize,
89 col: usize,
91}
92
93impl Marker {
94 #[must_use]
96 pub fn new(index: usize, line: usize, col: usize) -> Marker {
97 Marker {
98 offsets: MarkerOffsets {
99 chars: index,
100 bytes: None,
101 },
102 line,
103 col,
104 }
105 }
106
107 #[must_use]
109 pub fn with_byte_offset(mut self, byte_offset: Option<usize>) -> Marker {
110 self.offsets.bytes = byte_offset;
111 self
112 }
113
114 #[must_use]
116 pub fn index(&self) -> usize {
117 self.offsets.chars
118 }
119
120 #[must_use]
122 pub fn byte_offset(&self) -> Option<usize> {
123 self.offsets.bytes
124 }
125
126 #[must_use]
128 pub fn line(&self) -> usize {
129 self.line
130 }
131
132 #[must_use]
134 pub fn col(&self) -> usize {
135 self.col
136 }
137}
138
139#[derive(Clone, Copy, PartialEq, Debug, Eq, Default)]
141pub struct Span {
142 pub start: Marker,
144 pub end: Marker,
146
147 pub indent: Option<usize>,
152
153 pub tag_start: Option<Marker>,
162}
163
164impl Span {
165 #[must_use]
167 pub fn new(start: Marker, end: Marker) -> Span {
168 Span {
169 start,
170 end,
171 indent: None,
172 tag_start: None,
173 }
174 }
175
176 #[must_use]
183 pub fn empty(mark: Marker) -> Span {
184 Span {
185 start: mark,
186 end: mark,
187 indent: None,
188 tag_start: None,
189 }
190 }
191
192 #[must_use]
194 pub fn with_indent(mut self, indent: Option<usize>) -> Span {
195 self.indent = indent;
196 self
197 }
198
199 #[must_use]
201 pub fn with_tag_start(mut self, tag_start: Option<Marker>) -> Span {
202 self.tag_start = tag_start;
203 self
204 }
205
206 #[must_use]
212 pub fn tag_start(&self) -> Option<Marker> {
213 self.tag_start
214 }
215
216 #[track_caller]
221 #[must_use]
222 pub fn len(&self) -> usize {
223 self.end.index() - self.start.index()
224 }
225
226 #[track_caller]
231 #[must_use]
232 pub fn is_empty(&self) -> bool {
233 self.len() == 0
234 }
235
236 #[must_use]
238 pub fn byte_range(&self) -> Option<core::ops::Range<usize>> {
239 let start = self.start.byte_offset()?;
240 let end = self.end.byte_offset()?;
241 Some(start..end)
242 }
243
244 #[must_use]
247 pub fn slice<'source>(&self, source: &'source str) -> Option<&'source str> {
248 source.get(self.byte_range()?)
249 }
250}
251
252#[non_exhaustive]
267#[derive(Clone, Copy, PartialEq, Debug, Eq, Default)]
268pub enum Placement {
269 Above,
275 Right,
278 #[default]
284 Free,
285 Last,
290}
291
292#[derive(Clone, PartialEq, Debug, Eq)]
297pub struct Comment<'input> {
298 text: Cow<'input, str>,
302 placement: Placement,
304}
305
306impl<'input> Comment<'input> {
307 #[must_use]
312 pub fn new(text: impl Into<Cow<'input, str>>) -> Self {
313 Self {
314 text: text.into(),
315 placement: Placement::Free,
316 }
317 }
318
319 #[must_use]
321 pub fn with_placement(mut self, placement: Placement) -> Self {
322 self.placement = placement;
323 self
324 }
325
326 #[must_use]
328 pub fn text(&self) -> &str {
329 self.text.as_ref()
330 }
331
332 #[must_use]
334 pub const fn placement(&self) -> Placement {
335 self.placement
336 }
337
338 #[must_use]
340 pub fn into_text(self) -> Cow<'input, str> {
341 self.text
342 }
343
344 #[must_use]
348 pub fn trimmed_text(&self) -> &str {
349 self.text.trim()
350 }
351}
352
353impl AsRef<str> for Comment<'_> {
354 fn as_ref(&self) -> &str {
355 self.text.as_ref()
356 }
357}
358
359#[non_exhaustive]
361#[derive(Clone, PartialEq, Debug, Eq)]
362pub enum TokenType<'input> {
363 StreamStart,
365 StreamEnd,
367 VersionDirective(
369 u32,
371 u32,
373 ),
374 TagDirective(
376 Cow<'input, str>,
378 Cow<'input, str>,
380 ),
381 DocumentStart,
383 DocumentEnd,
385 BlockSequenceStart,
389 BlockMappingStart,
393 BlockEnd,
395 FlowSequenceStart,
397 FlowSequenceEnd,
399 FlowMappingStart,
401 FlowMappingEnd,
403 BlockEntry,
405 FlowEntry,
407 Key,
409 Value,
411 Alias(Cow<'input, str>),
413 Anchor(Cow<'input, str>),
415 Tag(
417 Cow<'input, str>,
419 Cow<'input, str>,
421 ),
422 Scalar(ScalarStyle, Cow<'input, str>),
424 Comment(
430 Comment<'input>,
432 ),
433 ReservedDirective(
435 String,
437 Vec<String>,
439 ),
440}
441
442#[derive(Clone, PartialEq, Debug, Eq)]
444pub struct Token<'input>(Span, TokenType<'input>);
445
446impl<'input> Token<'input> {
447 #[must_use]
449 pub const fn new(span: Span, token_type: TokenType<'input>) -> Self {
450 Self(span, token_type)
451 }
452
453 #[must_use]
455 pub const fn span(&self) -> Span {
456 self.0
457 }
458
459 #[must_use]
461 pub const fn token_type(&self) -> &TokenType<'input> {
462 &self.1
463 }
464
465 #[must_use]
467 pub fn into_parts(self) -> (Span, TokenType<'input>) {
468 (self.0, self.1)
469 }
470}
471
472#[derive(Clone, PartialEq, Debug, Eq)]
476pub(crate) enum QueuedTokenType<'input> {
477 StreamStart,
478 StreamEnd,
479 VersionDirective(u32, u32),
480 TagDirective(Cow<'input, str>, Cow<'input, str>),
481 DocumentStart,
482 DocumentEnd,
483 BlockSequenceStart,
484 BlockMappingStart,
485 BlockEnd,
486 FlowSequenceStart,
487 FlowSequenceEnd,
488 FlowMappingStart,
489 FlowMappingEnd,
490 BlockEntry,
491 FlowEntry,
492 Key,
493 Value,
494 Alias(Cow<'input, str>),
495 Anchor(Cow<'input, str>),
496 Tag(Cow<'input, str>, Cow<'input, str>),
497 Scalar(ScalarStyle, Cow<'input, str>),
498 Comment(Comment<'input>),
499 ReservedDirective(String, Vec<String>),
500}
501
502impl<'input> QueuedTokenType<'input> {
503 fn into_public(self) -> TokenType<'input> {
504 match self {
505 Self::StreamStart => TokenType::StreamStart,
506 Self::StreamEnd => TokenType::StreamEnd,
507 Self::VersionDirective(major, minor) => TokenType::VersionDirective(major, minor),
508 Self::TagDirective(handle, prefix) => TokenType::TagDirective(handle, prefix),
509 Self::DocumentStart => TokenType::DocumentStart,
510 Self::DocumentEnd => TokenType::DocumentEnd,
511 Self::BlockSequenceStart => TokenType::BlockSequenceStart,
512 Self::BlockMappingStart => TokenType::BlockMappingStart,
513 Self::BlockEnd => TokenType::BlockEnd,
514 Self::FlowSequenceStart => TokenType::FlowSequenceStart,
515 Self::FlowSequenceEnd => TokenType::FlowSequenceEnd,
516 Self::FlowMappingStart => TokenType::FlowMappingStart,
517 Self::FlowMappingEnd => TokenType::FlowMappingEnd,
518 Self::BlockEntry => TokenType::BlockEntry,
519 Self::FlowEntry => TokenType::FlowEntry,
520 Self::Key => TokenType::Key,
521 Self::Value => TokenType::Value,
522 Self::Alias(name) => TokenType::Alias(name),
523 Self::Anchor(name) => TokenType::Anchor(name),
524 Self::Tag(handle, suffix) => TokenType::Tag(handle, suffix),
525 Self::Scalar(style, value) => TokenType::Scalar(style, value),
526 Self::Comment(comment) => TokenType::Comment(comment),
527 Self::ReservedDirective(name, params) => TokenType::ReservedDirective(name, params),
528 }
529 }
530}
531
532impl<'input> From<TokenType<'input>> for QueuedTokenType<'input> {
533 fn from(token: TokenType<'input>) -> Self {
534 match token {
535 TokenType::StreamStart => Self::StreamStart,
536 TokenType::StreamEnd => Self::StreamEnd,
537 TokenType::VersionDirective(major, minor) => Self::VersionDirective(major, minor),
538 TokenType::TagDirective(handle, prefix) => Self::TagDirective(handle, prefix),
539 TokenType::DocumentStart => Self::DocumentStart,
540 TokenType::DocumentEnd => Self::DocumentEnd,
541 TokenType::BlockSequenceStart => Self::BlockSequenceStart,
542 TokenType::BlockMappingStart => Self::BlockMappingStart,
543 TokenType::BlockEnd => Self::BlockEnd,
544 TokenType::FlowSequenceStart => Self::FlowSequenceStart,
545 TokenType::FlowSequenceEnd => Self::FlowSequenceEnd,
546 TokenType::FlowMappingStart => Self::FlowMappingStart,
547 TokenType::FlowMappingEnd => Self::FlowMappingEnd,
548 TokenType::BlockEntry => Self::BlockEntry,
549 TokenType::FlowEntry => Self::FlowEntry,
550 TokenType::Key => Self::Key,
551 TokenType::Value => Self::Value,
552 TokenType::Alias(name) => Self::Alias(name),
553 TokenType::Anchor(name) => Self::Anchor(name),
554 TokenType::Tag(handle, suffix) => Self::Tag(handle, suffix),
555 TokenType::Scalar(style, value) => Self::Scalar(style, value),
556 TokenType::Comment(comment) => Self::Comment(comment),
557 TokenType::ReservedDirective(name, params) => Self::ReservedDirective(name, params),
558 }
559 }
560}
561
562#[derive(Clone, PartialEq, Debug, Eq)]
564pub(crate) struct QueuedToken<'input>(pub(crate) Span, pub(crate) QueuedTokenType<'input>);
565
566impl<'input> QueuedToken<'input> {
567 fn into_public(self) -> Token<'input> {
568 Token(self.0, self.1.into_public())
569 }
570}
571
572impl<'input> From<Token<'input>> for QueuedToken<'input> {
573 fn from(token: Token<'input>) -> Self {
574 Self(token.0, token.1.into())
575 }
576}
577
578#[derive(Clone, Copy, PartialEq, Debug, Eq)]
613struct SimpleKey {
614 possible: bool,
627 required: bool,
636 token_number: usize,
642 mark: Marker,
644}
645
646impl SimpleKey {
647 fn new(mark: Marker) -> SimpleKey {
649 SimpleKey {
650 possible: false,
651 required: false,
652 token_number: 0,
653 mark,
654 }
655 }
656}
657
658#[derive(Clone, Debug, Default)]
660struct Indent {
661 indent: isize,
663 needs_block_end: bool,
681}
682
683#[derive(Debug, PartialEq)]
705enum ImplicitMappingState {
706 Possible,
711 Inside(u8),
715}
716
717#[derive(Debug)]
730#[allow(clippy::struct_excessive_bools)]
731pub struct Scanner<'input, T> {
732 input: T,
736 mark: Marker,
738 tokens: VecDeque<QueuedToken<'input>>,
745 failed: bool,
747 deferred_error: Option<ScanError>,
749 comments_possible: bool,
751
752 stream_start_produced: bool,
754 stream_end_produced: bool,
756 document_prefix_allowed: bool,
762 adjacent_value_allowed_at: usize,
765 simple_key_allowed: bool,
769 simple_keys: smallvec::SmallVec<[SimpleKey; 8]>,
774 indent: isize,
776 indents: smallvec::SmallVec<[Indent; 8]>,
778 flow_level: u8,
780 tokens_parsed: usize,
784 token_available: bool,
786 leading_whitespace: bool,
788 flow_mapping_started: smallvec::SmallVec<[bool; 8]>,
795 implicit_flow_mapping_states: smallvec::SmallVec<[ImplicitMappingState; 8]>,
808 interrupted_plain_by_comment: Option<Marker>,
811 explicit_key_tab_check_pending: bool,
816 flow_markers: smallvec::SmallVec<[(Marker, char); 8]>,
818 buf_leading_break: String,
819 buf_trailing_breaks: String,
820 buf_whitespaces: String,
821}
822
823impl<'input, T: BorrowedInput<'input>> Iterator for Scanner<'input, T> {
824 type Item = Result<Token<'input>, ScanError>;
825
826 fn next(&mut self) -> Option<Self::Item> {
827 if self.failed {
828 return None;
829 }
830 match self.next_token() {
831 Ok(Some(tok)) => {
832 debug_print!(
833 " \x1B[;32m\u{21B3} {:?} \x1B[;36m{:?}\x1B[;m",
834 tok.1,
835 tok.0
836 );
837 Some(Ok(tok))
838 }
839 Ok(None) => None,
840 Err(error) => {
841 self.failed = true;
842 Some(Err(error))
843 }
844 }
845 }
846}
847
848impl<'input, T: BorrowedInput<'input>> core::iter::FusedIterator for Scanner<'input, T> {}
849
850type ScanResult = Result<(), ScanError>;
852
853#[derive(Debug)]
854enum FlowScalarBuf {
855 Borrowed {
861 start: usize,
862 end: usize,
863 pending_ws_start: Option<usize>,
864 pending_ws_end: usize,
865 },
866 Owned(String),
867}
868
869impl FlowScalarBuf {
870 #[inline]
871 fn new_borrowed(start: usize) -> Self {
872 Self::Borrowed {
873 start,
874 end: start,
875 pending_ws_start: None,
876 pending_ws_end: start,
877 }
878 }
879
880 #[inline]
881 fn new_owned() -> Self {
882 Self::Owned(String::new())
883 }
884
885 #[inline]
886 fn as_owned_mut(&mut self) -> Option<&mut String> {
887 match self {
888 Self::Owned(s) => Some(s),
889 Self::Borrowed { .. } => None,
890 }
891 }
892
893 #[inline]
894 fn commit_pending_ws(&mut self) {
895 if let Self::Borrowed {
896 end,
897 pending_ws_start,
898 pending_ws_end,
899 ..
900 } = self
901 {
902 if pending_ws_start.is_some() {
903 *end = *pending_ws_end;
904 *pending_ws_start = None;
905 }
906 }
907 }
908
909 #[inline]
910 fn note_pending_ws(&mut self, ws_start: usize, ws_end: usize) {
911 if let Self::Borrowed {
912 pending_ws_start,
913 pending_ws_end,
914 ..
915 } = self
916 {
917 if pending_ws_start.is_none() {
918 *pending_ws_start = Some(ws_start);
919 }
920 *pending_ws_end = ws_end;
921 }
922 }
923
924 #[inline]
925 fn discard_pending_ws(&mut self) {
926 if let Self::Borrowed {
927 pending_ws_start,
928 pending_ws_end,
929 end,
930 ..
931 } = self
932 {
933 *pending_ws_start = None;
934 *pending_ws_end = *end;
935 }
936 }
937}
938
939impl<'input, T: BorrowedInput<'input>> Scanner<'input, T> {
940 #[inline]
941 fn promote_flow_scalar_buf_to_owned(
942 &self,
943 start_mark: &Marker,
944 buf: &mut FlowScalarBuf,
945 ) -> Result<(), ScanError> {
946 let FlowScalarBuf::Borrowed {
947 start,
948 end,
949 pending_ws_start: _,
950 pending_ws_end: _,
951 } = *buf
952 else {
953 return Ok(());
954 };
955
956 let slice = self.input.slice_bytes(start, end).ok_or_else(|| {
957 ScanError::from_kind(*start_mark, ErrorKind::InputOffsetsWithoutSlice)
958 })?;
959 *buf = FlowScalarBuf::Owned(slice.to_owned());
960 Ok(())
961 }
962 #[inline]
968 fn try_borrow_slice(&self, start: usize, end: usize) -> Option<&'input str> {
969 self.input.slice_borrowed(start, end)
970 }
971
972 fn scan_tag_handle_directive_cow(
977 &mut self,
978 mark: &Marker,
979 ) -> Result<Cow<'input, str>, ScanError> {
980 let Some(start) = self.input.byte_offset() else {
981 return Ok(Cow::Owned(self.scan_tag_handle(true, mark)?));
982 };
983
984 if self.input.look_ch() != '!' {
985 return Err(ScanError::from_kind(*mark, ErrorKind::ExpectedTagBang));
986 }
987
988 self.skip_non_blank();
990
991 self.input.lookahead(1);
994 while self.input.next_is_alpha() {
995 self.skip_non_blank();
996 self.input.lookahead(1);
997 }
998
999 if self.input.peek() == '!' {
1001 self.skip_non_blank();
1002 }
1003
1004 let Some(end) = self.input.byte_offset() else {
1005 return Ok(Cow::Owned(self.scan_tag_handle(true, mark)?));
1007 };
1008
1009 let Some(slice) = self.try_borrow_slice(start, end) else {
1010 let slice = self
1012 .input
1013 .slice_bytes(start, end)
1014 .ok_or_else(|| ScanError::from_kind(*mark, ErrorKind::InputSlicingUnavailable))?;
1015 if !slice.ends_with('!') && slice != "!" {
1016 return Err(ScanError::from_kind(
1017 *mark,
1018 ErrorKind::ExpectedTagDirectiveBang,
1019 ));
1020 }
1021 return Ok(Cow::Owned(slice.to_owned()));
1022 };
1023
1024 if !slice.ends_with('!') && slice != "!" {
1025 return Err(ScanError::from_kind(
1026 *mark,
1027 ErrorKind::ExpectedTagDirectiveBang,
1028 ));
1029 }
1030
1031 Ok(Cow::Borrowed(slice))
1032 }
1033
1034 fn scan_tag_prefix_directive_cow(
1039 &mut self,
1040 start_mark: &Marker,
1041 ) -> Result<Cow<'input, str>, ScanError> {
1042 let Some(start) = self.input.byte_offset() else {
1043 return Ok(Cow::Owned(self.scan_tag_prefix(start_mark)?));
1044 };
1045
1046 if self.input.look_ch() == '!' {
1048 self.skip_non_blank();
1049 } else if !is_tag_char(self.input.peek()) {
1050 return Err(ScanError::from_kind(
1051 *start_mark,
1052 ErrorKind::InvalidGlobalTagCharacter,
1053 ));
1054 } else if self.input.peek() == '%' {
1055 } else {
1057 self.skip_non_blank();
1058 }
1059
1060 while is_uri_char(self.input.look_ch()) {
1062 if self.input.peek() == '%' {
1063 break;
1064 }
1065 self.skip_non_blank();
1066 }
1067
1068 if self.input.peek() == '%' {
1070 let current = self
1071 .input
1072 .byte_offset()
1073 .expect("byte_offset() must remain available once enabled");
1074 let mut out = if let Some(slice) = self.input.slice_bytes(start, current) {
1075 slice.to_owned()
1076 } else {
1077 String::new()
1078 };
1079
1080 while is_uri_char(self.input.look_ch()) {
1081 if self.input.peek() == '%' {
1082 out.push(self.scan_uri_escapes(start_mark)?);
1083 } else {
1084 out.push(self.input.peek());
1085 self.skip_non_blank();
1086 }
1087 }
1088 return Ok(Cow::Owned(out));
1089 }
1090
1091 let Some(end) = self.input.byte_offset() else {
1092 return Ok(Cow::Owned(self.scan_tag_prefix(start_mark)?));
1093 };
1094
1095 let Some(slice) = self.try_borrow_slice(start, end) else {
1096 let slice = self.input.slice_bytes(start, end).ok_or_else(|| {
1098 ScanError::from_kind(*start_mark, ErrorKind::InputSlicingUnavailable)
1099 })?;
1100 return Ok(Cow::Owned(slice.to_owned()));
1101 };
1102
1103 Ok(Cow::Borrowed(slice))
1104 }
1105 #[must_use]
1107 pub fn new(input: T) -> Self {
1108 let initial_byte_offset = input.byte_offset();
1109 let comments_possible = input.may_contain_comments();
1110 Scanner {
1111 input,
1112 mark: Marker::new(0, 1, 0).with_byte_offset(initial_byte_offset),
1113 tokens: VecDeque::with_capacity(64),
1114 failed: false,
1115 deferred_error: None,
1116 comments_possible,
1117
1118 stream_start_produced: false,
1119 stream_end_produced: false,
1120 document_prefix_allowed: true,
1121 adjacent_value_allowed_at: 0,
1122 simple_key_allowed: true,
1123 simple_keys: smallvec::SmallVec::new(),
1124 indent: -1,
1125 indents: smallvec::SmallVec::new(),
1126 flow_level: 0,
1127 tokens_parsed: 0,
1128 token_available: false,
1129 leading_whitespace: true,
1130 flow_mapping_started: smallvec::SmallVec::new(),
1131 implicit_flow_mapping_states: smallvec::SmallVec::new(),
1132 flow_markers: smallvec::SmallVec::new(),
1133 interrupted_plain_by_comment: None,
1134 explicit_key_tab_check_pending: false,
1135
1136 buf_leading_break: String::with_capacity(128),
1137 buf_trailing_breaks: String::with_capacity(128),
1138 buf_whitespaces: String::with_capacity(128),
1139 }
1140 }
1141
1142 #[cold]
1143 fn scan_error(&self, kind: ErrorKind) -> ScanError {
1144 ScanError::from_kind(self.mark, kind)
1145 }
1146
1147 #[inline]
1148 fn ensure_current_char_is_printable(&self) -> ScanResult {
1149 let character = self.input.peek();
1150 if self.input.next_is_z() || is_printable(character) {
1151 Ok(())
1152 } else {
1153 Err(self.scan_error(ErrorKind::UnexpectedCharacter { character }))
1154 }
1155 }
1156
1157 #[cold]
1158 fn simple_key_expected(mark: Marker) -> ScanError {
1159 ScanError::from_kind(mark, ErrorKind::SimpleKeyExpected)
1160 }
1161
1162 #[cold]
1163 fn unclosed_bracket(mark: Marker, bracket: char) -> ScanError {
1164 ScanError::from_kind(mark, ErrorKind::UnclosedFlowCollection { open: bracket })
1165 }
1166
1167 #[inline]
1169 fn skip_blank(&mut self) {
1170 self.input.skip();
1171
1172 self.mark.offsets.chars += 1;
1173 self.mark.col += 1;
1174 self.mark.offsets.bytes = self.input.byte_offset();
1175 }
1176
1177 #[inline]
1179 fn skip_non_blank(&mut self) {
1180 self.input.skip();
1181
1182 self.mark.offsets.chars += 1;
1183 self.mark.col += 1;
1184 self.mark.offsets.bytes = self.input.byte_offset();
1185 self.leading_whitespace = false;
1186 }
1187
1188 #[inline]
1193 fn skip_bom(&mut self) {
1194 self.input.skip();
1195
1196 self.mark.offsets.chars += 1;
1197 self.mark.offsets.bytes = self.input.byte_offset();
1198 }
1199
1200 #[inline]
1206 fn skip_comment_char(&mut self) {
1207 self.input.skip();
1208
1209 self.mark.offsets.chars += 1;
1210 self.mark.col += 1;
1211 self.mark.offsets.bytes = self.input.byte_offset();
1212 }
1213
1214 #[inline]
1216 fn skip_n_non_blank(&mut self, count: usize) {
1217 for _ in 0..count {
1218 self.input.skip();
1219 self.mark.offsets.chars += 1;
1220 self.mark.col += 1;
1221 }
1222 self.mark.offsets.bytes = self.input.byte_offset();
1223 self.leading_whitespace = false;
1224 }
1225
1226 #[inline]
1228 fn skip_nl(&mut self) {
1229 self.input.skip();
1230
1231 self.mark.offsets.chars += 1;
1232 self.mark.col = 0;
1233 self.mark.line += 1;
1234 self.mark.offsets.bytes = self.input.byte_offset();
1235 self.leading_whitespace = true;
1236 }
1237
1238 #[inline]
1240 fn skip_linebreak(&mut self) {
1241 if self.input.next_2_are('\r', '\n') {
1242 self.skip_blank();
1245 self.skip_nl();
1246 } else if self.input.next_is_break() {
1247 self.skip_nl();
1248 }
1249 }
1250
1251 #[cfg(test)]
1252 fn scan_comment_token(&mut self) -> Result<Token<'input>, ScanError> {
1253 Ok(self.scan_comment_queued_token()?.into_public())
1254 }
1255
1256 fn scan_comment_queued_token(&mut self) -> Result<QueuedToken<'input>, ScanError> {
1257 let start_mark = self.mark;
1258 debug_assert_eq!(self.input.peek(), '#');
1259 let placement = if self.leading_whitespace {
1260 Placement::Free
1261 } else {
1262 Placement::Right
1263 };
1264
1265 self.skip_comment_char();
1266
1267 let text = if let Some(start) = self.input.byte_offset() {
1268 let n = self.input.skip_while_non_breakz();
1270 self.mark.offsets.chars += n;
1271 self.mark.col += n;
1272 let byte_offset = self.input.byte_offset();
1273 self.mark.offsets.bytes = byte_offset;
1274 let end = byte_offset.expect("byte_offset must remain available once enabled");
1275
1276 if let Some(slice) = self.try_borrow_slice(start, end) {
1277 Cow::Borrowed(slice)
1278 } else if let Some(slice) = self.input.slice_bytes(start, end) {
1279 Cow::Owned(slice.to_owned())
1281 } else {
1282 return Err(ScanError::from_kind(
1283 start_mark,
1284 ErrorKind::InputOffsetsWithoutSlice,
1285 ));
1286 }
1287 } else {
1288 let mut owned = String::new();
1290 while {
1291 let character = self.input.look_ch();
1292 !is_breakz(character) && is_printable(character)
1293 } {
1294 owned.push(self.input.peek());
1295 self.skip_comment_char();
1296 }
1297 Cow::Owned(owned)
1298 };
1299
1300 self.ensure_current_char_is_printable()?;
1301
1302 let end_mark = self.mark;
1303 let span = Span::new(start_mark, end_mark);
1304 Ok(QueuedToken(
1305 span,
1306 QueuedTokenType::Comment(Comment { text, placement }),
1307 ))
1308 }
1309
1310 fn push_comment_token(&mut self) -> ScanResult {
1311 let token = self.scan_comment_queued_token()?;
1312 self.tokens.push_back(token);
1313 Ok(())
1314 }
1315
1316 fn skip_comment(&mut self) -> ScanResult {
1317 debug_assert_eq!(self.input.peek(), '#');
1318
1319 self.skip_comment_char();
1320 let n = self.input.skip_while_non_breakz();
1321 self.mark.offsets.chars += n;
1322 self.mark.col += n;
1323 self.mark.offsets.bytes = self.input.byte_offset();
1324 self.ensure_current_char_is_printable()
1325 }
1326
1327 #[inline]
1329 #[must_use]
1330 pub fn stream_started(&self) -> bool {
1331 self.stream_start_produced
1332 }
1333
1334 #[inline]
1336 #[must_use]
1337 pub fn stream_ended(&self) -> bool {
1338 self.stream_end_produced
1339 }
1340
1341 #[inline]
1343 #[must_use]
1344 pub fn mark(&self) -> Marker {
1345 self.mark
1346 }
1347
1348 #[inline]
1350 pub(crate) fn comments_possible(&self) -> bool {
1351 self.comments_possible
1352 }
1353
1354 #[track_caller]
1361 #[inline]
1362 fn read_break(&mut self, s: &mut String) {
1363 self.skip_break();
1364 s.push('\n');
1365 }
1366
1367 #[track_caller]
1372 #[inline]
1373 fn skip_break(&mut self) {
1374 let c = self.input.peek();
1375 let nc = self.input.peek_nth(1);
1376 debug_assert!(is_break(c));
1377 if c == '\r' && nc == '\n' {
1378 self.skip_blank();
1379 }
1380 self.skip_nl();
1381 }
1382
1383 #[track_caller]
1388 fn insert_token(&mut self, pos: usize, tok: Token<'input>) {
1389 let old_len = self.tokens.len();
1390 assert!(pos <= old_len);
1391 self.tokens.insert(pos, tok.into());
1392 }
1393
1394 fn simple_key_token_index(&self, sk: &SimpleKey, mark: Marker) -> Result<usize, ScanError> {
1395 let Some(index) = sk.token_number.checked_sub(self.tokens_parsed) else {
1396 return Err(ScanError::from_kind(mark, ErrorKind::InvalidSimpleKey));
1397 };
1398 if index > self.tokens.len() {
1399 return Err(ScanError::from_kind(mark, ErrorKind::InvalidSimpleKey));
1400 }
1401 Ok(index)
1402 }
1403
1404 #[inline]
1405 fn allow_simple_key(&mut self) {
1406 self.simple_key_allowed = true;
1407 }
1408
1409 #[inline]
1410 fn disallow_simple_key(&mut self) {
1411 self.simple_key_allowed = false;
1412 }
1413
1414 fn fetch_next_token(&mut self) -> ScanResult {
1419 let result = self.fetch_next_token_impl();
1420 if let Some(kind) = self.input.take_source_error() {
1421 return Err(ScanError::from_kind(self.mark, kind));
1422 }
1423 result
1424 }
1425
1426 fn fetch_next_token_impl(&mut self) -> ScanResult {
1427 self.input.lookahead(1);
1428
1429 if !self.stream_start_produced {
1430 self.fetch_stream_start();
1431 return Ok(());
1432 }
1433 if self.skip_to_next_token(true)? {
1434 return Ok(());
1435 }
1436
1437 debug_print!(
1438 " \x1B[38;5;244m\u{2192} fetch_next_token after whitespace {:?} {:?}\x1B[m",
1439 self.mark,
1440 self.input.peek()
1441 );
1442
1443 self.stale_simple_keys()?;
1444
1445 let mark = self.mark;
1446 self.unroll_indent(mark.col as isize);
1447
1448 self.input.lookahead(4);
1449
1450 if self.input.next_is_z() {
1451 self.fetch_stream_end()?;
1452 return Ok(());
1453 }
1454
1455 self.ensure_current_char_is_printable()?;
1456
1457 if self.mark.col == 0 {
1458 if self.input.next_char_is('%') {
1459 return self.fetch_directive();
1460 } else if self.input.next_is_document_start() {
1461 return self.fetch_document_indicator(TokenType::DocumentStart);
1462 } else if self.input.next_is_document_end() {
1463 self.fetch_document_indicator(TokenType::DocumentEnd)?;
1464 self.skip_ws_to_eol(SkipTabs::Yes)?;
1465 if !self.input.next_is_breakz() {
1466 return Err(self.scan_error(ErrorKind::InvalidDocumentEnd));
1467 }
1468 return Ok(());
1469 }
1470 }
1471
1472 if self.document_prefix_allowed {
1473 self.document_prefix_allowed = false;
1474 }
1475
1476 if (self.mark.col as isize) < self.indent {
1477 self.input.lookahead(1);
1478 let c = self.input.peek();
1479 if self.flow_level == 0 || !matches!(c, ']' | '}' | ',') {
1480 return Err(self.scan_error(ErrorKind::InvalidIndentation));
1481 }
1482 }
1483
1484 let c = self.input.peek();
1485 let nc = self.input.peek_nth(1);
1486 match c {
1487 '[' => self.fetch_flow_collection_start(TokenType::FlowSequenceStart),
1488 '{' => self.fetch_flow_collection_start(TokenType::FlowMappingStart),
1489 ']' => self.fetch_flow_collection_end(TokenType::FlowSequenceEnd, '[', ']'),
1490 '}' => self.fetch_flow_collection_end(TokenType::FlowMappingEnd, '{', '}'),
1491 ',' => self.fetch_flow_entry(),
1492 '-' if is_blank_or_breakz(nc) => self.fetch_block_entry(),
1493 '?' if is_blank_or_breakz(nc) => self.fetch_key(),
1494 ':' if is_blank_or_breakz(nc) => self.fetch_value(),
1495 ':' if self.flow_level > 0
1496 && (is_flow(nc) || self.mark.index() == self.adjacent_value_allowed_at) =>
1497 {
1498 self.fetch_flow_value()
1499 }
1500 '*' => self.fetch_anchor(true),
1502 '&' => self.fetch_anchor(false),
1504 '!' => self.fetch_tag(),
1505 '|' if self.flow_level == 0 => self.fetch_block_scalar(true),
1507 '>' if self.flow_level == 0 => self.fetch_block_scalar(false),
1509 '\'' => self.fetch_flow_scalar(true),
1510 '"' => self.fetch_flow_scalar(false),
1511 '-' if !is_blank_or_breakz(nc) => self.fetch_plain_scalar(),
1513 ':' | '?' if !is_blank_or_breakz(nc) && self.flow_level == 0 => {
1514 self.fetch_plain_scalar()
1515 }
1516 c if is_bom(c) => Err(self.scan_error(ErrorKind::BomInsideDocument)),
1517 '%' | '@' | '`' => {
1518 Err(self.scan_error(ErrorKind::UnexpectedCharacter { character: c }))
1519 }
1520 _ => self.fetch_plain_scalar(),
1521 }
1522 }
1523
1524 pub(crate) fn next_queued_token(&mut self) -> Result<Option<QueuedToken<'input>>, ScanError> {
1529 if self.deferred_error.is_some() {
1530 if !matches!(
1531 self.tokens.front().map(|token| &token.1),
1532 Some(QueuedTokenType::Comment(_))
1533 ) {
1534 if let Some(error) = self.deferred_error.take() {
1535 return error.into_result();
1536 }
1537 }
1538 self.token_available = true;
1539 }
1540
1541 if self.stream_end_produced {
1542 return Ok(None);
1543 }
1544
1545 if !self.token_available {
1546 if let Err(error) = self.fetch_more_tokens() {
1547 if matches!(
1548 self.tokens.front().map(|token| &token.1),
1549 Some(QueuedTokenType::Comment(_))
1550 ) {
1551 self.deferred_error = Some(error);
1552 } else {
1553 return Err(error);
1554 }
1555 }
1556 }
1557 let Some(t) = self.tokens.pop_front() else {
1558 unreachable!("fetch_more_tokens succeeded without producing a token")
1559 };
1560 self.token_available = false;
1561 self.tokens_parsed += 1;
1562
1563 let is_stream_end = matches!(t.1, QueuedTokenType::StreamEnd);
1564 if is_stream_end {
1565 self.stream_end_produced = true;
1566 }
1567 Ok(Some(t))
1568 }
1569
1570 fn next_token(&mut self) -> Result<Option<Token<'input>>, ScanError> {
1575 Ok(self.next_queued_token()?.map(QueuedToken::into_public))
1576 }
1577
1578 fn fetch_more_tokens(&mut self) -> ScanResult {
1583 let mut need_more;
1584 loop {
1585 if self.tokens.is_empty() {
1586 need_more = true;
1587 } else {
1588 need_more = false;
1589 self.stale_simple_keys()?;
1591 if !matches!(
1592 self.tokens.front().map(|token| &token.1),
1593 Some(QueuedTokenType::Comment(_))
1594 ) {
1595 for sk in &self.simple_keys {
1597 if sk.possible && sk.token_number == self.tokens_parsed {
1598 need_more = true;
1599 break;
1600 }
1601 }
1602 }
1603 }
1604
1605 if let Some(token) = self.tokens.back() {
1608 if matches!(
1609 token.1,
1610 QueuedTokenType::DocumentEnd | QueuedTokenType::DocumentStart
1611 ) {
1612 break;
1613 }
1614 }
1615
1616 if !need_more {
1617 break;
1618 }
1619 self.fetch_next_token()?;
1620 }
1621 self.token_available = true;
1622
1623 Ok(())
1624 }
1625
1626 fn stale_simple_keys(&mut self) -> ScanResult {
1635 for sk in &mut self.simple_keys {
1636 let is_line_stale = self.flow_level == 0 && sk.mark.line < self.mark.line;
1637 let is_length_stale =
1640 self.mark.index().saturating_sub(sk.mark.index()) > SIMPLE_KEY_MAX_LOOKAHEAD;
1641
1642 if sk.possible && (is_line_stale || is_length_stale) {
1643 if sk.required {
1644 return Err(Self::simple_key_expected(sk.mark));
1645 }
1646 sk.possible = false;
1647 }
1648 }
1649 Ok(())
1650 }
1651
1652 fn skip_to_next_token(&mut self, stop_after_comment: bool) -> Result<bool, ScanError> {
1662 let consume_linebreak = |this: &mut Self| {
1665 this.input.lookahead(2);
1666 this.skip_linebreak();
1667 if this.flow_level == 0 {
1668 this.allow_simple_key();
1669 }
1670 };
1671
1672 loop {
1673 let ch = self.input.look_ch();
1674 if self.explicit_key_tab_check_pending {
1675 match ch {
1676 '\t' => {
1677 return Err(self.scan_error(ErrorKind::TabNotAllowed));
1678 }
1679 ' ' | '\n' | '\r' | '#' => {}
1680 _ => self.explicit_key_tab_check_pending = false,
1681 }
1682 }
1683
1684 match ch {
1685 '\t' => {
1687 if self.is_within_block()
1688 && self.leading_whitespace
1689 && (self.mark.col as isize) < self.indent
1690 {
1691 self.skip_ws_to_eol(SkipTabs::Yes)?;
1692
1693 if !self.input.next_is_breakz() {
1695 return Err(self.scan_error(ErrorKind::TabInBlockIndentation));
1696 }
1697
1698 if matches!(self.input.look_ch(), '\n' | '\r') {
1700 consume_linebreak(self);
1701 }
1702 } else {
1703 self.skip_blank();
1705 }
1706 }
1707
1708 ' ' => self.skip_blank(),
1709
1710 '\n' | '\r' => consume_linebreak(self),
1711
1712 c if is_bom(c)
1713 && self.document_prefix_allowed
1714 && self.flow_level == 0
1715 && self.mark.col == 0 =>
1716 {
1717 self.skip_bom();
1718 }
1719
1720 '#' => {
1721 self.push_comment_token()?;
1722
1723 if matches!(self.input.look_ch(), '\n' | '\r') {
1725 consume_linebreak(self);
1726 }
1727 if stop_after_comment {
1728 return Ok(true);
1729 }
1730 }
1731
1732 _ => break,
1733 }
1734 }
1735
1736 if let Some(err_mark) = self.interrupted_plain_by_comment.take() {
1739 let is_immediate_next_line = self.mark.line == err_mark.line + 1;
1743
1744 if self.flow_level == 0
1746 && is_immediate_next_line
1747 && (self.mark.col as isize) > self.indent
1748 {
1749 self.input.lookahead(4);
1753
1754 if !self.input.next_is_z()
1755 && !self.input.next_is_document_indicator()
1756 && self.input.next_can_be_plain_scalar(false)
1757 {
1758 return Err(ScanError::from_kind(
1759 err_mark,
1760 ErrorKind::CommentInterceptedScalar,
1761 ));
1762 }
1763 }
1764 }
1765
1766 Ok(false)
1767 }
1768
1769 fn skip_yaml_whitespace(&mut self, stop_after_comment: bool) -> Result<bool, ScanError> {
1777 let mut need_whitespace = true;
1778 loop {
1779 match self.input.look_ch() {
1780 ' ' => {
1781 self.skip_blank();
1782
1783 need_whitespace = false;
1784 }
1785 '\n' | '\r' => {
1786 self.input.lookahead(2);
1787 self.skip_linebreak();
1788 if self.flow_level == 0 {
1789 self.allow_simple_key();
1790 }
1791 need_whitespace = false;
1792 }
1793 '#' => {
1794 if need_whitespace {
1795 self.skip_comment()?;
1796 } else {
1797 self.push_comment_token()?;
1798 if stop_after_comment {
1799 return Ok(true);
1800 }
1801 }
1802 }
1803 _ => break,
1804 }
1805 }
1806
1807 if need_whitespace {
1808 Err(self.scan_error(ErrorKind::ExpectedWhitespace))
1809 } else {
1810 Ok(false)
1811 }
1812 }
1813
1814 #[track_caller]
1819 fn skip_ws_to_eol(&mut self, skip_tabs: SkipTabs) -> Result<SkipTabs, ScanError> {
1820 debug_assert!(!matches!(skip_tabs, SkipTabs::Result(..)));
1821
1822 if !self.comments_possible {
1823 let (chars_consumed, result) = self.input.skip_ws_to_eol(skip_tabs);
1824 self.mark.col += chars_consumed;
1825 self.mark.offsets.chars += chars_consumed;
1826 self.mark.offsets.bytes = self.input.byte_offset();
1827 return result.map_err(|kind| self.scan_error(kind));
1828 }
1829
1830 let (chars_consumed, whitespace) = self.input.skip_ws_to_eol_blanks(skip_tabs);
1831 self.mark.col += chars_consumed;
1832 self.mark.offsets.chars += chars_consumed;
1833 self.mark.offsets.bytes = self.input.byte_offset();
1834
1835 if self.input.look_ch() != '#' {
1836 return Ok(whitespace);
1837 }
1838
1839 if !whitespace.found_tabs() && !whitespace.has_valid_yaml_ws() {
1840 return Err(self.scan_error(ErrorKind::CommentNotSeparated));
1841 }
1842
1843 self.push_comment_token()?;
1844 Ok(whitespace)
1845 }
1846
1847 fn fetch_stream_start(&mut self) {
1848 let mark = self.mark;
1849 self.indent = -1;
1850 self.stream_start_produced = true;
1851 self.allow_simple_key();
1852 self.tokens
1853 .push_back(Token(Span::empty(mark), TokenType::StreamStart).into());
1854 self.simple_keys.push(SimpleKey::new(Marker::new(0, 0, 0)));
1855 }
1856
1857 fn fetch_stream_end(&mut self) -> ScanResult {
1858 if self.mark.col != 0 {
1860 self.mark.col = 0;
1861 self.mark.line += 1;
1862 }
1863
1864 if let Some((mark, bracket)) = self.flow_markers.pop() {
1865 return Err(Self::unclosed_bracket(mark, bracket));
1866 }
1867
1868 for sk in &mut self.simple_keys {
1871 if sk.required && sk.possible {
1872 return Err(Self::simple_key_expected(sk.mark));
1873 }
1874 sk.possible = false;
1875 }
1876
1877 self.unroll_indent(-1);
1878 self.remove_simple_key()?;
1879 self.disallow_simple_key();
1880
1881 self.tokens
1882 .push_back(Token(Span::empty(self.mark), TokenType::StreamEnd).into());
1883 Ok(())
1884 }
1885
1886 fn fetch_directive(&mut self) -> ScanResult {
1887 self.unroll_indent(-1);
1888 self.remove_simple_key()?;
1889
1890 self.disallow_simple_key();
1891
1892 let token_index = self.tokens.len();
1893 let tok = self.scan_directive()?;
1894 self.insert_token(token_index, tok);
1895
1896 Ok(())
1897 }
1898
1899 fn scan_directive(&mut self) -> Result<Token<'input>, ScanError> {
1900 let start_mark = self.mark;
1901 self.skip_non_blank();
1902
1903 let name = self.scan_directive_name()?;
1904 let tok = match name.as_ref() {
1905 "YAML" => self.scan_version_directive_value(&start_mark)?,
1906 "TAG" => self.scan_tag_directive_value(&start_mark)?,
1907 _ => {
1908 let mut params = Vec::new();
1909 while self.input.next_is_blank() {
1910 let n_blanks = self.input.skip_while_blank();
1911 self.mark.offsets.chars += n_blanks;
1912 self.mark.col += n_blanks;
1913 self.mark.offsets.bytes = self.input.byte_offset();
1914
1915 if !is_blank_or_breakz(self.input.peek()) {
1916 let mut param = String::new();
1917 let n_chars = self.input.fetch_while_is_yaml_non_space(&mut param);
1918 self.mark.offsets.chars += n_chars;
1919 self.mark.col += n_chars;
1920 self.mark.offsets.bytes = self.input.byte_offset();
1921 params.push(param);
1922 }
1923 }
1924
1925 Token(
1926 Span::new(start_mark, self.mark),
1927 TokenType::ReservedDirective(name, params),
1928 )
1929 }
1930 };
1931
1932 self.skip_ws_to_eol(SkipTabs::Yes)?;
1933
1934 if self.input.next_is_breakz() {
1935 self.input.lookahead(2);
1936 self.skip_linebreak();
1937 Ok(tok)
1938 } else {
1939 Err(ScanError::from_kind(
1940 start_mark,
1941 ErrorKind::InvalidDirectiveTerminator,
1942 ))
1943 }
1944 }
1945
1946 fn scan_version_directive_value(&mut self, mark: &Marker) -> Result<Token<'input>, ScanError> {
1947 let n_blanks = self.input.skip_while_blank();
1948 self.mark.offsets.chars += n_blanks;
1949 self.mark.col += n_blanks;
1950 self.mark.offsets.bytes = self.input.byte_offset();
1951
1952 let major = self.scan_version_directive_number(mark)?;
1953
1954 if self.input.peek() != '.' {
1955 return Err(ScanError::from_kind(
1956 *mark,
1957 ErrorKind::MissingYamlVersionSeparator,
1958 ));
1959 }
1960 self.skip_non_blank();
1961
1962 let minor = self.scan_version_directive_number(mark)?;
1963
1964 Ok(Token(
1965 Span::new(*mark, self.mark),
1966 TokenType::VersionDirective(major, minor),
1967 ))
1968 }
1969
1970 fn scan_directive_name(&mut self) -> Result<String, ScanError> {
1971 let start_mark = self.mark;
1972 let mut string = String::new();
1973
1974 let n_chars = self.input.fetch_while_is_yaml_non_space(&mut string);
1975 self.mark.offsets.chars += n_chars;
1976 self.mark.col += n_chars;
1977 self.mark.offsets.bytes = self.input.byte_offset();
1978
1979 if string.is_empty() {
1980 return Err(ScanError::from_kind(
1981 start_mark,
1982 ErrorKind::MissingDirectiveName,
1983 ));
1984 }
1985
1986 if !is_blank_or_breakz(self.input.peek()) {
1987 return Err(ScanError::from_kind(
1988 start_mark,
1989 ErrorKind::InvalidDirectiveName,
1990 ));
1991 }
1992
1993 Ok(string)
1994 }
1995
1996 fn scan_version_directive_number(&mut self, mark: &Marker) -> Result<u32, ScanError> {
1997 let mut val = 0u32;
1998 let mut length = 0usize;
1999 while let Some(digit) = self.input.look_ch().to_digit(10) {
2000 if length + 1 > 9 {
2001 return Err(ScanError::from_kind(*mark, ErrorKind::YamlVersionTooLong));
2002 }
2003 length += 1;
2004 val = val * 10 + digit;
2005 self.skip_non_blank();
2006 }
2007
2008 if length == 0 {
2009 return Err(ScanError::from_kind(*mark, ErrorKind::MissingYamlVersion));
2010 }
2011
2012 Ok(val)
2013 }
2014
2015 fn scan_tag_directive_value(&mut self, mark: &Marker) -> Result<Token<'input>, ScanError> {
2016 let n_blanks = self.input.skip_while_blank();
2017 self.mark.offsets.chars += n_blanks;
2018 self.mark.col += n_blanks;
2019 self.mark.offsets.bytes = self.input.byte_offset();
2020
2021 let handle = self.scan_tag_handle_directive_cow(mark)?;
2022
2023 let n_blanks = self.input.skip_while_blank();
2024 self.mark.offsets.chars += n_blanks;
2025 self.mark.col += n_blanks;
2026 self.mark.offsets.bytes = self.input.byte_offset();
2027
2028 let prefix = self.scan_tag_prefix_directive_cow(mark)?;
2029
2030 self.input.lookahead(1);
2031
2032 if self.input.next_is_blank_or_breakz() {
2033 Ok(Token(
2034 Span::new(*mark, self.mark),
2035 TokenType::TagDirective(handle, prefix),
2036 ))
2037 } else {
2038 Err(ScanError::from_kind(
2039 *mark,
2040 ErrorKind::InvalidTagDirectiveTerminator,
2041 ))
2042 }
2043 }
2044
2045 fn fetch_tag(&mut self) -> ScanResult {
2046 self.save_simple_key();
2047 self.disallow_simple_key();
2048
2049 let tok = self.scan_tag()?;
2050 self.tokens.push_back(tok.into());
2051 Ok(())
2052 }
2053
2054 fn scan_tag(&mut self) -> Result<Token<'input>, ScanError> {
2055 let start_mark = self.mark;
2056
2057 self.input.lookahead(2);
2059
2060 if self.input.byte_offset().is_none() {
2062 return self.scan_tag_owned(&start_mark);
2063 }
2064
2065 let (handle, suffix): (Cow<'input, str>, Cow<'input, str>) = if self
2066 .input
2067 .nth_char_is(1, '<')
2068 {
2069 let suffix = self.scan_verbatim_tag(&start_mark)?;
2071 (Cow::Owned(String::new()), Cow::Owned(suffix))
2072 } else {
2073 let handle = self.scan_tag_handle_cow(&start_mark)?;
2075 if handle.len() >= 2 && handle.starts_with('!') && handle.ends_with('!') {
2077 let suffix = self.scan_tag_shorthand_suffix_cow(&start_mark, true)?;
2079 (handle, suffix)
2080 } else {
2081 let remaining_suffix = self.scan_tag_shorthand_suffix_cow(&start_mark, false)?;
2086
2087 let suffix = self.combine_local_tag_suffix(&start_mark, handle, remaining_suffix);
2088
2089 if suffix.is_empty() {
2092 (Cow::Borrowed(""), Cow::Borrowed("!"))
2093 } else {
2094 (Cow::Borrowed("!"), suffix)
2095 }
2096 }
2097 };
2098
2099 if is_blank_or_breakz(self.input.look_ch())
2100 || (self.flow_level > 0 && matches!(self.input.peek(), ',' | ']' | '}'))
2101 {
2102 Ok(Token(
2105 Span::new(start_mark, self.mark),
2106 TokenType::Tag(handle, suffix),
2107 ))
2108 } else {
2109 Err(ScanError::from_kind(
2110 start_mark,
2111 ErrorKind::InvalidTagTerminator,
2112 ))
2113 }
2114 }
2115
2116 fn combine_local_tag_suffix(
2117 &self,
2118 start_mark: &Marker,
2119 handle: Cow<'input, str>,
2120 remaining: Cow<'input, str>,
2121 ) -> Cow<'input, str> {
2122 if handle.len() == 1 {
2123 return remaining;
2124 }
2125
2126 match (handle, remaining) {
2127 (Cow::Borrowed(handle), Cow::Borrowed(remaining)) => {
2128 if remaining.is_empty() {
2129 return Cow::Borrowed(&handle[1..]);
2130 }
2131
2132 let borrowed = start_mark
2133 .byte_offset()
2134 .and_then(|start| start.checked_add(1))
2135 .zip(self.input.byte_offset())
2136 .and_then(|(start, end)| self.try_borrow_slice(start, end));
2137 borrowed.map_or_else(
2138 || {
2139 let mut combined =
2140 String::with_capacity(handle.len() - 1 + remaining.len());
2141 combined.push_str(&handle[1..]);
2142 combined.push_str(remaining);
2143 Cow::Owned(combined)
2144 },
2145 Cow::Borrowed,
2146 )
2147 }
2148 (Cow::Borrowed(handle), Cow::Owned(mut remaining)) => {
2149 remaining.reserve(handle.len() - 1);
2150 remaining.insert_str(0, &handle[1..]);
2151 Cow::Owned(remaining)
2152 }
2153 (Cow::Owned(mut handle), remaining) => {
2154 handle.remove(0);
2155 handle.push_str(&remaining);
2156 Cow::Owned(handle)
2157 }
2158 }
2159 }
2160
2161 fn scan_tag_owned(&mut self, start_mark: &Marker) -> Result<Token<'input>, ScanError> {
2163 let mut handle = String::new();
2164 let mut suffix;
2165
2166 if self.input.nth_char_is(1, '<') {
2167 suffix = self.scan_verbatim_tag(start_mark)?;
2168 } else {
2169 handle = self.scan_tag_handle(false, start_mark)?;
2171 if handle.len() >= 2 && handle.starts_with('!') && handle.ends_with('!') {
2173 let is_secondary_handle = handle == "!!";
2175 suffix =
2176 self.scan_tag_shorthand_suffix(false, is_secondary_handle, "", start_mark)?;
2177 } else {
2178 suffix = self.scan_tag_shorthand_suffix(false, false, &handle, start_mark)?;
2179 "!".clone_into(&mut handle);
2180 if suffix.is_empty() {
2183 handle.clear();
2184 "!".clone_into(&mut suffix);
2185 }
2186 }
2187 }
2188
2189 if is_blank_or_breakz(self.input.look_ch())
2190 || (self.flow_level > 0 && matches!(self.input.peek(), ',' | ']' | '}'))
2191 {
2192 Ok(Token(
2195 Span::new(*start_mark, self.mark),
2196 TokenType::Tag(handle.into(), suffix.into()),
2197 ))
2198 } else {
2199 Err(ScanError::from_kind(
2200 *start_mark,
2201 ErrorKind::InvalidTagTerminator,
2202 ))
2203 }
2204 }
2205
2206 fn scan_tag_handle_cow(&mut self, mark: &Marker) -> Result<Cow<'input, str>, ScanError> {
2211 let Some(start) = self.input.byte_offset() else {
2212 return Ok(Cow::Owned(self.scan_tag_handle(false, mark)?));
2213 };
2214
2215 if self.input.look_ch() != '!' {
2216 return Err(ScanError::from_kind(*mark, ErrorKind::ExpectedTagBang));
2217 }
2218
2219 self.skip_non_blank();
2221
2222 self.input.lookahead(1);
2224 while self.input.next_is_alpha() {
2225 self.skip_non_blank();
2226 self.input.lookahead(1);
2227 }
2228
2229 if self.input.peek() == '!' {
2231 self.skip_non_blank();
2232 }
2233
2234 let Some(end) = self.input.byte_offset() else {
2235 return Ok(Cow::Owned(self.scan_tag_handle(false, mark)?));
2236 };
2237
2238 if let Some(slice) = self.try_borrow_slice(start, end) {
2239 Ok(Cow::Borrowed(slice))
2240 } else {
2241 let slice = self
2242 .input
2243 .slice_bytes(start, end)
2244 .ok_or_else(|| ScanError::from_kind(*mark, ErrorKind::InputSlicingUnavailable))?;
2245 Ok(Cow::Owned(slice.to_owned()))
2246 }
2247 }
2248
2249 fn scan_tag_shorthand_suffix_cow(
2253 &mut self,
2254 mark: &Marker,
2255 require_non_empty: bool,
2256 ) -> Result<Cow<'input, str>, ScanError> {
2257 let Some(start) = self.input.byte_offset() else {
2258 return Ok(Cow::Owned(
2259 self.scan_tag_shorthand_suffix(false, false, "", mark)?,
2260 ));
2261 };
2262
2263 while is_tag_char(self.input.look_ch()) {
2265 if self.input.peek() == '%' {
2266 let current = self
2268 .input
2269 .byte_offset()
2270 .expect("byte_offset() must remain available once enabled");
2271 let mut out = if let Some(slice) = self.input.slice_bytes(start, current) {
2272 slice.to_owned()
2273 } else {
2274 String::new()
2275 };
2276
2277 while is_tag_char(self.input.look_ch()) {
2279 if self.input.peek() == '%' {
2280 out.push(self.scan_uri_escapes(mark)?);
2281 } else {
2282 out.push(self.input.peek());
2283 self.skip_non_blank();
2284 }
2285 }
2286 return Ok(Cow::Owned(out));
2287 }
2288 self.skip_non_blank();
2289 }
2290
2291 let Some(end) = self.input.byte_offset() else {
2292 return Ok(Cow::Owned(
2293 self.scan_tag_shorthand_suffix(false, false, "", mark)?,
2294 ));
2295 };
2296
2297 if require_non_empty && start == end {
2298 return Err(ScanError::from_kind(*mark, ErrorKind::MissingTagUri));
2299 }
2300
2301 if let Some(slice) = self.try_borrow_slice(start, end) {
2302 Ok(Cow::Borrowed(slice))
2303 } else {
2304 let slice = self
2305 .input
2306 .slice_bytes(start, end)
2307 .ok_or_else(|| ScanError::from_kind(*mark, ErrorKind::InputSlicingUnavailable))?;
2308 Ok(Cow::Owned(slice.to_owned()))
2309 }
2310 }
2311
2312 fn scan_tag_handle(&mut self, directive: bool, mark: &Marker) -> Result<String, ScanError> {
2313 let mut string = String::new();
2314 if self.input.look_ch() != '!' {
2315 return Err(ScanError::from_kind(*mark, ErrorKind::ExpectedTagBang));
2316 }
2317
2318 string.push(self.input.peek());
2319 self.skip_non_blank();
2320
2321 let n_chars = self.input.fetch_while_is_alpha(&mut string);
2322 self.mark.offsets.chars += n_chars;
2323 self.mark.col += n_chars;
2324 self.mark.offsets.bytes = self.input.byte_offset();
2325
2326 if self.input.peek() == '!' {
2328 string.push(self.input.peek());
2329 self.skip_non_blank();
2330 } else if directive && string != "!" {
2331 return Err(ScanError::from_kind(
2335 *mark,
2336 ErrorKind::ExpectedTagDirectiveBang,
2337 ));
2338 }
2339 Ok(string)
2340 }
2341
2342 fn scan_tag_prefix(&mut self, start_mark: &Marker) -> Result<String, ScanError> {
2348 let mut string = String::new();
2349
2350 if self.input.look_ch() == '!' {
2351 string.push(self.input.peek());
2353 self.skip_non_blank();
2354 } else if !is_tag_char(self.input.peek()) {
2355 return Err(ScanError::from_kind(
2357 *start_mark,
2358 ErrorKind::InvalidGlobalTagCharacter,
2359 ));
2360 } else if self.input.peek() == '%' {
2361 string.push(self.scan_uri_escapes(start_mark)?);
2363 } else {
2364 string.push(self.input.peek());
2366 self.skip_non_blank();
2367 }
2368
2369 while is_uri_char(self.input.look_ch()) {
2370 if self.input.peek() == '%' {
2371 string.push(self.scan_uri_escapes(start_mark)?);
2372 } else {
2373 string.push(self.input.peek());
2374 self.skip_non_blank();
2375 }
2376 }
2377
2378 Ok(string)
2379 }
2380
2381 fn scan_verbatim_tag(&mut self, start_mark: &Marker) -> Result<String, ScanError> {
2385 self.skip_non_blank();
2387 self.skip_non_blank();
2388
2389 let mut string = String::new();
2390 while is_uri_char(self.input.look_ch()) {
2391 if self.input.peek() == '%' {
2392 string.push(self.scan_uri_escapes(start_mark)?);
2393 } else {
2394 string.push(self.input.peek());
2395 self.skip_non_blank();
2396 }
2397 }
2398
2399 if string.is_empty() {
2400 return Err(ScanError::from_kind(*start_mark, ErrorKind::MissingTagUri));
2401 }
2402
2403 if self.input.peek() != '>' {
2404 return Err(ScanError::from_kind(
2405 *start_mark,
2406 ErrorKind::UnclosedVerbatimTag,
2407 ));
2408 }
2409 self.skip_non_blank();
2410
2411 Ok(string)
2412 }
2413
2414 fn scan_tag_shorthand_suffix(
2415 &mut self,
2416 _directive: bool,
2417 _is_secondary: bool,
2418 head: &str,
2419 mark: &Marker,
2420 ) -> Result<String, ScanError> {
2421 let mut length = head.len();
2422 let mut string = String::new();
2423
2424 if length > 1 {
2427 string.extend(head.chars().skip(1));
2428 }
2429
2430 while is_tag_char(self.input.look_ch()) {
2431 if self.input.peek() == '%' {
2433 string.push(self.scan_uri_escapes(mark)?);
2434 } else {
2435 string.push(self.input.peek());
2436 self.skip_non_blank();
2437 }
2438
2439 length += 1;
2440 }
2441
2442 if length == 0 {
2443 return Err(ScanError::from_kind(*mark, ErrorKind::MissingTagUri));
2444 }
2445
2446 Ok(string)
2447 }
2448
2449 fn scan_uri_escapes(&mut self, mark: &Marker) -> Result<char, ScanError> {
2450 let mut width = 0usize;
2451 let mut bytes = [0u8; 4];
2452 let mut bytes_len = 0usize;
2453 loop {
2454 self.input.lookahead(3);
2455
2456 let c = self.input.peek_nth(1);
2457 let nc = self.input.peek_nth(2);
2458
2459 if !(self.input.peek() == '%' && is_hex(c) && is_hex(nc)) {
2460 return Err(ScanError::from_kind(*mark, ErrorKind::InvalidTagEscape));
2461 }
2462
2463 let byte = u8::try_from((as_hex(c) << 4) + as_hex(nc))
2464 .expect("two hex nibbles always fit in a byte");
2465 if width == 0 {
2466 width = match byte {
2467 _ if byte & 0x80 == 0x00 => 1,
2468 _ if byte & 0xE0 == 0xC0 => 2,
2469 _ if byte & 0xF0 == 0xE0 => 3,
2470 _ if byte & 0xF8 == 0xF0 => 4,
2471 _ => {
2472 return Err(ScanError::from_kind(
2473 *mark,
2474 ErrorKind::InvalidTagUtf8LeadingByte,
2475 ));
2476 }
2477 };
2478 } else if byte & 0xc0 != 0x80 {
2479 return Err(ScanError::from_kind(
2480 *mark,
2481 ErrorKind::InvalidTagUtf8TrailingByte,
2482 ));
2483 }
2484
2485 bytes[bytes_len] = byte;
2486 bytes_len += 1;
2487
2488 self.skip_n_non_blank(3);
2489
2490 width -= 1;
2491 if width == 0 {
2492 break;
2493 }
2494 }
2495
2496 let s = core::str::from_utf8(&bytes[..bytes_len])
2497 .map_err(|_| ScanError::from_kind(*mark, ErrorKind::InvalidTagUtf8))?;
2498
2499 let Some(ch) = s.chars().next() else {
2500 unreachable!("a validated URI escape cannot decode to an empty string")
2501 };
2502 Ok(ch)
2503 }
2504
2505 fn fetch_anchor(&mut self, alias: bool) -> ScanResult {
2506 self.save_simple_key();
2507 self.disallow_simple_key();
2508
2509 let tok = self.scan_anchor(alias)?;
2510
2511 self.tokens.push_back(tok.into());
2512
2513 Ok(())
2514 }
2515
2516 fn scan_anchor(&mut self, alias: bool) -> Result<Token<'input>, ScanError> {
2517 let start_mark = self.mark;
2518
2519 self.skip_non_blank();
2521
2522 if let Some(start) = self.input.byte_offset() {
2524 while is_anchor_char(self.input.look_ch()) {
2525 self.skip_non_blank();
2526 }
2527
2528 let end = self
2529 .input
2530 .byte_offset()
2531 .expect("byte_offset() must remain available once enabled");
2532
2533 if start == end {
2534 return Err(ScanError::from_kind(
2535 start_mark,
2536 ErrorKind::MissingAnchorOrAliasName,
2537 ));
2538 }
2539
2540 let cow = if let Some(slice) = self.try_borrow_slice(start, end) {
2541 Cow::Borrowed(slice)
2542 } else if let Some(slice) = self.input.slice_bytes(start, end) {
2543 Cow::Owned(slice.to_owned())
2544 } else {
2545 return Err(ScanError::from_kind(
2546 start_mark,
2547 ErrorKind::InputSlicingUnavailable,
2548 ));
2549 };
2550
2551 let tok = if alias {
2552 TokenType::Alias(cow)
2553 } else {
2554 TokenType::Anchor(cow)
2555 };
2556 return Ok(Token(Span::new(start_mark, self.mark), tok));
2557 }
2558
2559 let mut string = String::new();
2560 while is_anchor_char(self.input.look_ch()) {
2561 string.push(self.input.peek());
2562 self.skip_non_blank();
2563 }
2564
2565 if string.is_empty() {
2566 return Err(ScanError::from_kind(
2567 start_mark,
2568 ErrorKind::MissingAnchorOrAliasName,
2569 ));
2570 }
2571
2572 let tok = if alias {
2573 TokenType::Alias(string.into())
2574 } else {
2575 TokenType::Anchor(string.into())
2576 };
2577 Ok(Token(Span::new(start_mark, self.mark), tok))
2578 }
2579
2580 fn fetch_flow_collection_start(&mut self, tok: TokenType<'input>) -> ScanResult {
2581 self.save_simple_key();
2583
2584 let start_mark = self.mark;
2585 let indicator = self.input.peek();
2586 self.flow_markers.push((start_mark, indicator));
2587
2588 self.roll_one_col_indent();
2589 self.increase_flow_level()?;
2590
2591 self.allow_simple_key();
2592
2593 self.skip_non_blank();
2594 let end_mark = self.mark;
2595
2596 if tok == TokenType::FlowMappingStart {
2597 self.flow_mapping_started.push(true);
2598 } else {
2599 self.flow_mapping_started.push(false);
2600 self.implicit_flow_mapping_states
2601 .push(ImplicitMappingState::Possible);
2602 }
2603
2604 let token_index = self.tokens.len();
2605 self.skip_ws_to_eol(SkipTabs::Yes)?;
2606
2607 self.insert_token(token_index, Token(Span::new(start_mark, end_mark), tok));
2608 Ok(())
2609 }
2610
2611 fn fetch_flow_collection_end(
2612 &mut self,
2613 tok: TokenType<'input>,
2614 expected_open: char,
2615 actual_close: char,
2616 ) -> ScanResult {
2617 if self.flow_level == 0 {
2619 return Err(self.scan_error(ErrorKind::MisplacedFlowCollectionEnd));
2620 }
2621
2622 let Some((open_mark, open_ch)) = self.flow_markers.pop() else {
2623 return Err(self.scan_error(ErrorKind::MisplacedFlowCollectionEnd));
2624 };
2625
2626 if open_ch != expected_open {
2627 return Err(ScanError::from_kind(
2628 open_mark,
2629 ErrorKind::MismatchedFlowCollectionEnd {
2630 open: open_ch,
2631 close: actual_close,
2632 },
2633 ));
2634 }
2635
2636 let flow_level = self.flow_level;
2637
2638 self.remove_simple_key()?;
2639
2640 if matches!(tok, TokenType::FlowSequenceEnd) {
2641 self.end_implicit_mapping(self.mark, flow_level);
2642 self.implicit_flow_mapping_states.pop();
2644 }
2645 self.flow_mapping_started.pop();
2646
2647 self.decrease_flow_level();
2648
2649 self.disallow_simple_key();
2650
2651 let start_mark = self.mark;
2652 self.skip_non_blank();
2653 let end_mark = self.mark;
2654 let token_index = self.tokens.len();
2655 self.skip_ws_to_eol(SkipTabs::Yes)?;
2656
2657 if self.flow_level > 0 {
2663 self.adjacent_value_allowed_at = self.mark.index();
2664 }
2665
2666 self.insert_token(token_index, Token(Span::new(start_mark, end_mark), tok));
2667 Ok(())
2668 }
2669
2670 fn fetch_flow_entry(&mut self) -> ScanResult {
2672 self.remove_simple_key()?;
2673 self.allow_simple_key();
2674
2675 self.end_implicit_mapping(self.mark, self.flow_level);
2676 if self.current_flow_collection_is_sequence() {
2677 self.set_current_flow_mapping_started(false);
2678 }
2679
2680 let start_mark = self.mark;
2681 self.skip_non_blank();
2682 let end_mark = self.mark;
2683 let token_index = self.tokens.len();
2684 self.skip_ws_to_eol(SkipTabs::Yes)?;
2685
2686 self.insert_token(
2687 token_index,
2688 Token(Span::new(start_mark, end_mark), TokenType::FlowEntry),
2689 );
2690 Ok(())
2691 }
2692
2693 fn increase_flow_level(&mut self) -> ScanResult {
2694 self.simple_keys.push(SimpleKey::new(Marker::new(0, 0, 0)));
2695 self.flow_level = self
2696 .flow_level
2697 .checked_add(1)
2698 .ok_or_else(|| self.scan_error(ErrorKind::RecursionLimitExceeded))?;
2699 Ok(())
2700 }
2701
2702 fn decrease_flow_level(&mut self) {
2703 if self.flow_level > 0 {
2704 self.flow_level -= 1;
2705 self.simple_keys.pop().unwrap();
2706 }
2707 }
2708
2709 fn fetch_block_entry(&mut self) -> ScanResult {
2715 if self.flow_level > 0 {
2716 return Err(self.scan_error(ErrorKind::BlockEntryInFlowCollection));
2718 }
2719 if !self.simple_key_allowed {
2721 return Err(self.scan_error(ErrorKind::BlockSequenceEntryNotAllowed));
2722 }
2723
2724 let mark = self.mark;
2726 self.skip_non_blank();
2727
2728 self.roll_indent(mark.col, None, TokenType::BlockSequenceStart, mark);
2730 let token_index = self.tokens.len();
2731 let found_tabs = self.skip_ws_to_eol(SkipTabs::Yes)?.found_tabs();
2732 self.input.lookahead(2);
2733 if found_tabs && self.input.next_char_is('-') && is_blank_or_breakz(self.input.peek_nth(1))
2734 {
2735 return Err(self.scan_error(ErrorKind::InvalidBlockEntryWhitespace));
2736 }
2737
2738 self.skip_ws_to_eol(SkipTabs::No)?;
2739 self.input.lookahead(1);
2740 if self.input.next_is_break() || self.input.next_is_flow() {
2741 self.roll_one_col_indent();
2742 }
2743
2744 self.remove_simple_key()?;
2745 self.allow_simple_key();
2746
2747 self.insert_token(
2748 token_index,
2749 Token(Span::empty(self.mark), TokenType::BlockEntry),
2750 );
2751
2752 Ok(())
2753 }
2754
2755 fn fetch_document_indicator(&mut self, t: TokenType<'input>) -> ScanResult {
2756 if let Some((mark, bracket)) = self.flow_markers.pop() {
2757 return Err(ScanError::from_kind(
2758 mark,
2759 ErrorKind::UnclosedFlowCollection { open: bracket },
2760 ));
2761 }
2762
2763 self.unroll_indent(-1);
2764 self.remove_simple_key()?;
2765 self.disallow_simple_key();
2766
2767 let mark = self.mark;
2768
2769 self.skip_n_non_blank(3);
2770
2771 self.document_prefix_allowed = matches!(t, TokenType::DocumentEnd);
2772 self.tokens
2773 .push_back(Token(Span::new(mark, self.mark), t).into());
2774 Ok(())
2775 }
2776
2777 fn fetch_block_scalar(&mut self, literal: bool) -> ScanResult {
2778 self.save_simple_key();
2779 self.allow_simple_key();
2780 let tok = self.scan_block_scalar(literal)?;
2781
2782 self.tokens.push_back(tok.into());
2783 Ok(())
2784 }
2785
2786 #[allow(clippy::too_many_lines)]
2787 fn scan_block_scalar(&mut self, literal: bool) -> Result<Token<'input>, ScanError> {
2788 let start_mark = self.mark;
2789 let mut chomping = Chomping::Clip;
2790 let mut increment: usize = 0;
2791 let mut indent: usize = 0;
2792 let mut trailing_blank: bool;
2793 let mut leading_blank: bool = false;
2794 let style = if literal {
2795 ScalarStyle::Literal
2796 } else {
2797 ScalarStyle::Folded
2798 };
2799
2800 let mut string = String::new();
2801 let mut leading_break = String::new();
2802 let mut trailing_breaks = String::new();
2803 let mut chomping_break = String::new();
2804
2805 self.skip_non_blank();
2807 self.unroll_non_block_indents();
2808
2809 if self.input.look_ch() == '+' || self.input.peek() == '-' {
2810 if self.input.peek() == '+' {
2811 chomping = Chomping::Keep;
2812 } else {
2813 chomping = Chomping::Strip;
2814 }
2815 self.skip_non_blank();
2816 self.input.lookahead(1);
2817 if self.input.next_is_digit() {
2818 if self.input.peek() == '0' {
2819 return Err(ScanError::from_kind(
2820 start_mark,
2821 ErrorKind::ZeroBlockScalarIndent,
2822 ));
2823 }
2824 increment = (self.input.peek() as usize) - ('0' as usize);
2825 self.skip_non_blank();
2826 }
2827 } else if self.input.next_is_digit() {
2828 if self.input.peek() == '0' {
2829 return Err(ScanError::from_kind(
2830 start_mark,
2831 ErrorKind::ZeroBlockScalarIndent,
2832 ));
2833 }
2834
2835 increment = (self.input.peek() as usize) - ('0' as usize);
2836 self.skip_non_blank();
2837 self.input.lookahead(1);
2838 if self.input.peek() == '+' || self.input.peek() == '-' {
2839 if self.input.peek() == '+' {
2840 chomping = Chomping::Keep;
2841 } else {
2842 chomping = Chomping::Strip;
2843 }
2844 self.skip_non_blank();
2845 }
2846 }
2847
2848 self.skip_ws_to_eol(SkipTabs::Yes)?;
2849
2850 self.input.lookahead(1);
2852 self.ensure_current_char_is_printable()?;
2853 if !self.input.next_is_breakz() {
2854 return Err(ScanError::from_kind(
2855 start_mark,
2856 ErrorKind::InvalidBlockScalarHeader,
2857 ));
2858 }
2859
2860 if self.input.next_is_break() {
2861 self.input.lookahead(2);
2862 self.read_break(&mut chomping_break);
2863 }
2864
2865 self.ensure_current_char_is_printable()?;
2866
2867 if self.input.look_ch() == '\t' {
2868 return Err(ScanError::from_kind(
2869 start_mark,
2870 ErrorKind::TabAtBlockScalarStart,
2871 ));
2872 }
2873
2874 if increment > 0 {
2875 indent = if self.indent >= 0 {
2876 (self.indent + increment as isize) as usize
2877 } else {
2878 increment
2879 }
2880 }
2881
2882 if indent == 0 {
2884 self.skip_block_scalar_first_line_indent(&mut indent, &mut trailing_breaks);
2885 } else {
2886 self.skip_block_scalar_indent(indent, &mut trailing_breaks);
2887 }
2888
2889 self.ensure_current_char_is_printable()?;
2890
2891 if self.input.next_is_z() {
2896 let contents = match chomping {
2897 Chomping::Strip => String::new(),
2899 _ if self.mark.line == start_mark.line() => String::new(),
2901 Chomping::Clip => String::new(),
2903 Chomping::Keep if trailing_breaks.is_empty() && self.mark.col > 0 => chomping_break,
2905 Chomping::Keep => trailing_breaks,
2907 };
2908
2909 return Ok(Token(
2910 Span::new(start_mark, self.mark),
2911 TokenType::Scalar(style, contents.into()),
2912 ));
2913 }
2914
2915 if self.mark.col < indent && (self.mark.col as isize) > self.indent {
2916 if self.indent < 0 && self.mark.col == 0 {
2917 self.input.lookahead(4);
2918 if self.input.next_is_document_start()
2919 || self.input.next_is_document_end()
2920 || self.input.peek() == '#'
2921 {
2922 } else {
2926 return Err(self.scan_error(ErrorKind::InvalidBlockScalarIndent));
2927 }
2928 } else {
2929 return Err(self.scan_error(ErrorKind::InvalidBlockScalarIndent));
2930 }
2931 }
2932
2933 let mut line_buffer = String::with_capacity(100);
2934 let start_mark = self.mark;
2935 while self.mark.col == indent && !self.input.next_is_z() {
2936 self.ensure_current_char_is_printable()?;
2937
2938 if indent == 0 {
2939 self.input.lookahead(4);
2940 if self.input.next_is_document_end() {
2941 break;
2942 }
2943 }
2944
2945 trailing_blank = self.input.next_is_blank();
2947 if !literal && !leading_break.is_empty() && !leading_blank && !trailing_blank {
2948 string.push_str(&trailing_breaks);
2949 if trailing_breaks.is_empty() {
2950 string.push(' ');
2951 }
2952 } else {
2953 string.push_str(&leading_break);
2954 string.push_str(&trailing_breaks);
2955 }
2956
2957 leading_break.clear();
2958 trailing_breaks.clear();
2959
2960 leading_blank = self.input.next_is_blank();
2961
2962 self.scan_block_scalar_content_line(&mut string, &mut line_buffer);
2963
2964 self.input.lookahead(2);
2966 if self.input.next_is_z() {
2967 break;
2968 }
2969
2970 self.ensure_current_char_is_printable()?;
2971
2972 self.read_break(&mut leading_break);
2973
2974 self.skip_block_scalar_indent(indent, &mut trailing_breaks);
2976 }
2977
2978 self.ensure_current_char_is_printable()?;
2979
2980 if chomping != Chomping::Strip {
2982 string.push_str(&leading_break);
2983 if self.input.next_is_z() && self.mark.col >= indent.max(1) {
2987 string.push('\n');
2988 }
2989 }
2990
2991 if chomping == Chomping::Keep {
2992 string.push_str(&trailing_breaks);
2993 }
2994
2995 if let Some(character) = find_non_printable(&string) {
2996 return Err(ScanError::from_kind(
2997 start_mark,
2998 ErrorKind::UnexpectedCharacter { character },
2999 ));
3000 }
3001
3002 let span = if string.trim().is_empty() {
3003 Span::new(start_mark, self.mark)
3004 } else {
3005 Span::new(start_mark, self.mark).with_indent(Some(indent))
3006 };
3007
3008 Ok(Token(span, TokenType::Scalar(style, string.into())))
3009 }
3010
3011 fn scan_block_scalar_content_line(&mut self, string: &mut String, line_buffer: &mut String) {
3021 while !self.input.buf_is_empty() && !self.input.next_is_breakz() {
3023 string.push(self.input.peek());
3024 self.skip_blank();
3030 }
3031
3032 if self.input.buf_is_empty() {
3035 let mut n_chars = 0;
3043 debug_assert!(line_buffer.is_empty());
3044 while let Some(c) = self.input.raw_read_non_breakz_ch() {
3045 line_buffer.push(c);
3046 n_chars += 1;
3047 }
3048
3049 self.mark.col += n_chars;
3051 self.mark.offsets.chars += n_chars;
3052 self.mark.offsets.bytes = self.input.byte_offset();
3053
3054 string.reserve(line_buffer.len());
3056 string.push_str(line_buffer);
3057 line_buffer.clear();
3059 }
3060 }
3061
3062 fn skip_block_scalar_indent(&mut self, indent: usize, breaks: &mut String) {
3064 loop {
3065 if indent < self.input.bufmaxlen().saturating_sub(2) {
3067 self.input.lookahead(self.input.bufmaxlen());
3068 while self.mark.col < indent && self.input.peek() == ' ' {
3069 self.skip_blank();
3070 }
3071 } else {
3072 loop {
3073 self.input.lookahead(self.input.bufmaxlen());
3074 while !self.input.buf_is_empty()
3075 && self.mark.col < indent
3076 && self.input.peek() == ' '
3077 {
3078 self.skip_blank();
3079 }
3080 if self.mark.col == indent
3084 || (!self.input.buf_is_empty() && self.input.peek() != ' ')
3085 {
3086 break;
3087 }
3088 }
3089 self.input.lookahead(2);
3090 }
3091
3092 if self.input.next_is_break() {
3094 self.read_break(breaks);
3095 } else {
3096 break;
3098 }
3099 }
3100 }
3101
3102 fn skip_block_scalar_first_line_indent(&mut self, indent: &mut usize, breaks: &mut String) {
3107 let mut max_indent = 0;
3108 loop {
3109 while self.input.look_ch() == ' ' {
3111 self.skip_blank();
3112 }
3113
3114 if self.mark.col > max_indent {
3115 max_indent = self.mark.col;
3116 }
3117
3118 if self.input.next_is_break() {
3119 self.input.lookahead(2);
3121 self.read_break(breaks);
3122 } else {
3123 break;
3125 }
3126 }
3127
3128 *indent = max_indent.max((self.indent + 1) as usize);
3137 }
3138
3139 fn fetch_flow_scalar(&mut self, single: bool) -> ScanResult {
3140 self.save_simple_key();
3141 self.disallow_simple_key();
3142
3143 let token_index = self.tokens.len();
3144 let tok = self.scan_flow_scalar(single)?;
3145
3146 if self.skip_to_next_token(true)? {
3149 self.adjacent_value_allowed_at = usize::MAX;
3150 } else {
3151 self.adjacent_value_allowed_at = self.mark.index();
3152 }
3153
3154 self.insert_token(token_index, tok);
3155 Ok(())
3156 }
3157
3158 #[allow(clippy::too_many_lines)]
3159 fn scan_flow_scalar(&mut self, single: bool) -> Result<Token<'input>, ScanError> {
3160 let start_mark = self.mark;
3161
3162 let mut buf = match self.input.byte_offset() {
3164 Some(off) => FlowScalarBuf::new_borrowed(off + self.input.peek().len_utf8()),
3165 None => FlowScalarBuf::new_owned(),
3166 };
3167
3168 let mut break_scratch = String::new();
3171
3172 self.skip_non_blank();
3174
3175 loop {
3176 self.input.lookahead(4);
3178
3179 if self.mark.col == 0 && self.input.next_is_document_indicator() {
3180 return Err(ScanError::from_kind(
3181 start_mark,
3182 ErrorKind::DocumentIndicatorInQuotedScalar,
3183 ));
3184 }
3185
3186 if self.input.next_is_z() {
3187 return Err(ScanError::from_kind(
3188 start_mark,
3189 ErrorKind::UnclosedQuotedScalar,
3190 ));
3191 }
3192
3193 self.ensure_current_char_is_printable()?;
3194
3195 let mut leading_blanks = false;
3198 self.consume_flow_scalar_non_whitespace_chars(
3199 single,
3200 &mut buf,
3201 &mut leading_blanks,
3202 &start_mark,
3203 )?;
3204
3205 match self.input.look_ch() {
3206 '\'' if single => break,
3207 '"' if !single => break,
3208 _ => {}
3209 }
3210
3211 let mut trailing_ws_start: Option<usize> = None;
3227 let mut has_leading_break = false;
3228 let mut has_trailing_breaks = false;
3229
3230 let mut pending_ws_start: Option<usize> = None;
3232
3233 while self.input.next_is_blank() || self.input.next_is_break() {
3235 if self.input.next_is_blank() {
3236 if leading_blanks {
3238 if self.input.peek() == '\t' && (self.mark.col as isize) < self.indent {
3239 return Err(self.scan_error(ErrorKind::TabInIndentation));
3240 }
3241 self.skip_blank();
3242 } else {
3243 match buf {
3245 FlowScalarBuf::Owned(ref mut string) => {
3246 if trailing_ws_start.is_none() {
3247 trailing_ws_start = Some(string.len());
3248 }
3249 string.push(self.input.peek());
3250 }
3251 FlowScalarBuf::Borrowed { .. } => {
3252 if pending_ws_start.is_none() {
3253 pending_ws_start = self.input.byte_offset();
3254 }
3255 }
3256 }
3257 self.skip_blank();
3258
3259 if let (FlowScalarBuf::Borrowed { .. }, Some(ws_start), Some(ws_end)) =
3260 (&mut buf, pending_ws_start, self.input.byte_offset())
3261 {
3262 buf.note_pending_ws(ws_start, ws_end);
3263 }
3264 }
3265 } else {
3266 self.input.lookahead(2);
3267
3268 if leading_blanks {
3270 match buf {
3272 FlowScalarBuf::Owned(ref mut string) => self.read_break(string),
3273 FlowScalarBuf::Borrowed { .. } => {
3274 self.promote_flow_scalar_buf_to_owned(&start_mark, &mut buf)?;
3275 let Some(string) = buf.as_owned_mut() else {
3276 unreachable!()
3277 };
3278 self.read_break(string);
3279 }
3280 }
3281 has_trailing_breaks = true;
3282 } else {
3283 if let Some(pos) = trailing_ws_start.take() {
3285 if let FlowScalarBuf::Owned(ref mut string) = buf {
3286 string.truncate(pos);
3287 }
3288 }
3289
3290 if pending_ws_start.take().is_some() {
3291 if matches!(buf, FlowScalarBuf::Borrowed { .. }) {
3293 self.promote_flow_scalar_buf_to_owned(&start_mark, &mut buf)?;
3294 }
3295 buf.discard_pending_ws();
3296 } else {
3297 buf.commit_pending_ws();
3298 }
3299
3300 break_scratch.clear();
3301 self.read_break(&mut break_scratch);
3302 has_leading_break = true;
3305 leading_blanks = true;
3306 }
3307 }
3308
3309 self.input.lookahead(1);
3310 }
3311
3312 if leading_blanks && has_leading_break && self.flow_level == 0 {
3315 let next_ch = self.input.peek();
3316 let is_closing_quote = (single && next_ch == '\'') || (!single && next_ch == '"');
3317 if !is_closing_quote && (self.mark.col as isize) <= self.indent {
3318 return Err(self.scan_error(ErrorKind::InvalidQuotedScalarIndent));
3319 }
3320 }
3321
3322 if leading_blanks {
3324 if has_leading_break && !has_trailing_breaks {
3329 match buf {
3330 FlowScalarBuf::Owned(ref mut string) => string.push(' '),
3331 FlowScalarBuf::Borrowed { .. } => {
3332 self.promote_flow_scalar_buf_to_owned(&start_mark, &mut buf)?;
3333 let Some(string) = buf.as_owned_mut() else {
3334 unreachable!()
3335 };
3336 string.push(' ');
3337 }
3338 }
3339 }
3340 }
3341 } self.skip_non_blank();
3346 let end_mark = self.mark;
3347
3348 self.skip_ws_to_eol(SkipTabs::Yes)?;
3350 self.ensure_current_char_is_printable()?;
3351 match self.input.peek() {
3352 ',' | '}' | ']' if self.flow_level > 0 => {}
3354 c if is_breakz(c) => {}
3356 ':' if self.flow_level == 0 && start_mark.line == self.mark.line => {}
3359 ':' if self.flow_level > 0 => {}
3361 _ => {
3362 let kind = if single {
3363 ErrorKind::InvalidTrailingSingleQuotedScalar
3364 } else {
3365 ErrorKind::InvalidTrailingDoubleQuotedScalar
3366 };
3367 return Err(self.scan_error(kind));
3368 }
3369 }
3370
3371 let style = if single {
3372 ScalarStyle::SingleQuoted
3373 } else {
3374 ScalarStyle::DoubleQuoted
3375 };
3376
3377 let contents = match buf {
3378 FlowScalarBuf::Owned(string) => Cow::Owned(string),
3379 FlowScalarBuf::Borrowed {
3380 start,
3381 mut end,
3382 pending_ws_start,
3383 pending_ws_end,
3384 } => {
3385 if pending_ws_start.is_some() {
3387 end = pending_ws_end;
3388 }
3389 if let Some(slice) = self.try_borrow_slice(start, end) {
3390 Cow::Borrowed(slice)
3391 } else {
3392 let slice = self.input.slice_bytes(start, end).ok_or_else(|| {
3393 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
3394 })?;
3395 Cow::Owned(slice.to_owned())
3396 }
3397 }
3398 };
3399
3400 Ok(Token(
3401 Span::new(start_mark, end_mark),
3402 TokenType::Scalar(style, contents),
3403 ))
3404 }
3405
3406 fn consume_flow_scalar_non_whitespace_chars(
3415 &mut self,
3416 single: bool,
3417 buf: &mut FlowScalarBuf,
3418 leading_blanks: &mut bool,
3419 start_mark: &Marker,
3420 ) -> Result<(), ScanError> {
3421 self.input.lookahead(2);
3422 while !is_blank_or_breakz(self.input.peek()) && is_printable(self.input.peek()) {
3423 match self.input.peek() {
3424 '\'' if self.input.peek_nth(1) == '\'' && single => {
3426 if matches!(buf, FlowScalarBuf::Borrowed { .. }) {
3427 buf.commit_pending_ws();
3428 self.promote_flow_scalar_buf_to_owned(start_mark, buf)?;
3429 }
3430 let Some(string) = buf.as_owned_mut() else {
3431 unreachable!()
3432 };
3433 string.push('\'');
3434 self.skip_n_non_blank(2);
3435 }
3436 '\'' if single => break,
3438 '"' if !single => break,
3439 '\\' if !single && is_break(self.input.peek_nth(1)) => {
3441 self.input.lookahead(3);
3442 if matches!(buf, FlowScalarBuf::Borrowed { .. }) {
3443 buf.commit_pending_ws();
3444 self.promote_flow_scalar_buf_to_owned(start_mark, buf)?;
3445 }
3446 self.skip_non_blank();
3447 self.skip_linebreak();
3448 *leading_blanks = true;
3449 break;
3450 }
3451 '\\' if !single => {
3453 if matches!(buf, FlowScalarBuf::Borrowed { .. }) {
3454 buf.commit_pending_ws();
3455 self.promote_flow_scalar_buf_to_owned(start_mark, buf)?;
3456 }
3457 let Some(string) = buf.as_owned_mut() else {
3458 unreachable!()
3459 };
3460 string.push(self.resolve_flow_scalar_escape_sequence(start_mark)?);
3461 }
3462 c => {
3463 match buf {
3464 FlowScalarBuf::Owned(ref mut string) => {
3465 string.push(c);
3466 }
3467 FlowScalarBuf::Borrowed { .. } => {
3468 buf.commit_pending_ws();
3469 }
3470 }
3471 self.skip_non_blank();
3472
3473 if let Some(new_end) = self.input.byte_offset() {
3474 if let FlowScalarBuf::Borrowed { end, .. } = buf {
3475 *end = new_end;
3476 }
3477 }
3478 }
3479 }
3480 self.input.lookahead(2);
3481 }
3482 Ok(())
3483 }
3484
3485 fn resolve_flow_scalar_escape_sequence(
3492 &mut self,
3493 start_mark: &Marker,
3494 ) -> Result<char, ScanError> {
3495 let mut code_length = 0usize;
3496 let mut ret = '\0';
3497
3498 match self.input.peek_nth(1) {
3499 '0' => ret = '\0',
3500 'a' => ret = '\x07',
3501 'b' => ret = '\x08',
3502 't' | '\t' => ret = '\t',
3503 'n' => ret = '\n',
3504 'v' => ret = '\x0b',
3505 'f' => ret = '\x0c',
3506 'r' => ret = '\x0d',
3507 'e' => ret = '\x1b',
3508 ' ' => ret = '\x20',
3509 '"' => ret = '"',
3510 '/' => ret = '/',
3511 '\\' => ret = '\\',
3512 'N' => ret = char::from_u32(0x85).unwrap(),
3514 '_' => ret = char::from_u32(0xA0).unwrap(),
3516 'L' => ret = char::from_u32(0x2028).unwrap(),
3518 'P' => ret = char::from_u32(0x2029).unwrap(),
3520 'x' => code_length = 2,
3521 'u' => code_length = 4,
3522 'U' => code_length = 8,
3523 _ => {
3524 return Err(ScanError::from_kind(
3525 *start_mark,
3526 ErrorKind::UnknownQuotedScalarEscape,
3527 ))
3528 }
3529 }
3530 self.skip_n_non_blank(2);
3531
3532 if code_length > 0 {
3534 self.input.lookahead(code_length);
3535 let mut value = 0u32;
3536 for i in 0..code_length {
3537 let c = self.input.peek_nth(i);
3538 if !is_hex(c) {
3539 return Err(ScanError::from_kind(
3540 *start_mark,
3541 ErrorKind::InvalidQuotedScalarHexEscape,
3542 ));
3543 }
3544 value = (value << 4) + as_hex(c);
3545 }
3546
3547 self.skip_n_non_blank(code_length);
3548
3549 if code_length == 4 && (0xD800..=0xDBFF).contains(&value) {
3551 self.input.lookahead(2);
3552 if self.input.peek() == '\\' && self.input.peek_nth(1) == 'u' {
3553 self.skip_n_non_blank(2);
3554 self.input.lookahead(4);
3555 let mut low_value = 0u32;
3556 for i in 0..4 {
3557 let c = self.input.peek_nth(i);
3558 if !is_hex(c) {
3559 return Err(ScanError::from_kind(
3560 *start_mark,
3561 ErrorKind::InvalidLowSurrogateHexEscape,
3562 ));
3563 }
3564 low_value = (low_value << 4) + as_hex(c);
3565 }
3566 if (0xDC00..=0xDFFF).contains(&low_value) {
3567 value = 0x10000 + (((value - 0xD800) << 10) | (low_value - 0xDC00));
3568 self.skip_n_non_blank(4);
3569 } else {
3570 return Err(ScanError::from_kind(
3571 *start_mark,
3572 ErrorKind::InvalidLowSurrogate,
3573 ));
3574 }
3575 } else {
3576 return Err(ScanError::from_kind(
3577 *start_mark,
3578 ErrorKind::MissingLowSurrogate,
3579 ));
3580 }
3581 } else if code_length == 4 && (0xDC00..=0xDFFF).contains(&value) {
3582 return Err(ScanError::from_kind(
3583 *start_mark,
3584 ErrorKind::UnpairedLowSurrogate,
3585 ));
3586 }
3587
3588 let Some(ch) = char::from_u32(value) else {
3589 return Err(ScanError::from_kind(
3590 *start_mark,
3591 ErrorKind::InvalidUnicodeEscape,
3592 ));
3593 };
3594 ret = ch;
3595 }
3596 Ok(ret)
3597 }
3598
3599 fn fetch_plain_scalar(&mut self) -> ScanResult {
3600 self.save_simple_key();
3601 self.disallow_simple_key();
3602
3603 let token_index = self.tokens.len();
3604 let tok = self.scan_plain_scalar()?;
3605
3606 self.insert_token(token_index, tok);
3607 Ok(())
3608 }
3609
3610 #[allow(clippy::too_many_lines)]
3615 fn scan_plain_scalar(&mut self) -> Result<Token<'input>, ScanError> {
3616 self.unroll_non_block_indents();
3617 let indent = self.indent + 1;
3618 let start_mark = self.mark;
3619
3620 if self.flow_level > 0 && (start_mark.col as isize) < indent {
3621 return Err(ScanError::from_kind(
3622 start_mark,
3623 ErrorKind::InvalidFlowScalarIndent,
3624 ));
3625 }
3626
3627 let borrow_start = start_mark
3628 .byte_offset()
3629 .filter(|start| self.try_borrow_slice(*start, *start).is_some());
3630 let mut string = borrow_start.is_none().then(|| String::with_capacity(32));
3631 let mut has_content = false;
3632 self.buf_whitespaces.clear();
3633 self.buf_leading_break.clear();
3634 self.buf_trailing_breaks.clear();
3635 let mut end_mark = self.mark;
3636
3637 loop {
3638 self.input.lookahead(4);
3639 if (self.mark.col == 0 && self.input.next_is_document_indicator())
3640 || self.input.peek() == '#'
3641 {
3642 if self.input.peek() == '#'
3647 && has_content
3648 && !self.buf_whitespaces.is_empty()
3649 && self.flow_level == 0
3650 {
3651 self.interrupted_plain_by_comment = Some(self.mark);
3652 }
3653 break;
3654 }
3655
3656 if self.flow_level > 0 && self.input.peek() == '-' && is_flow(self.input.peek_nth(1)) {
3657 return Err(self.scan_error(ErrorKind::PlainScalarStartsWithDashFlowIndicator));
3658 }
3659
3660 if !self.input.next_is_blank_or_breakz()
3661 && self.input.next_can_be_plain_scalar(self.flow_level > 0)
3662 {
3663 if self.leading_whitespace {
3664 if has_content && string.is_none() {
3665 let start = borrow_start.expect("borrowed scalar has a start offset");
3666 let end = end_mark.byte_offset().ok_or_else(|| {
3667 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
3668 })?;
3669 let prefix = self.try_borrow_slice(start, end).ok_or_else(|| {
3670 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
3671 })?;
3672 string = Some(prefix.to_owned());
3673 }
3674 if self.buf_leading_break.is_empty() {
3675 if let Some(output) = string.as_mut() {
3676 output.push_str(&self.buf_leading_break);
3677 output.push_str(&self.buf_trailing_breaks);
3678 }
3679 self.buf_trailing_breaks.clear();
3680 self.buf_leading_break.clear();
3681 } else {
3682 if self.buf_trailing_breaks.is_empty() {
3683 if let Some(output) = string.as_mut() {
3684 output.push(' ');
3685 }
3686 } else {
3687 if let Some(output) = string.as_mut() {
3688 output.push_str(&self.buf_trailing_breaks);
3689 }
3690 self.buf_trailing_breaks.clear();
3691 }
3692 self.buf_leading_break.clear();
3693 }
3694 self.leading_whitespace = false;
3695 } else if !self.buf_whitespaces.is_empty() {
3696 if let Some(output) = string.as_mut() {
3697 output.push_str(&self.buf_whitespaces);
3698 }
3699 self.buf_whitespaces.clear();
3700 }
3701
3702 has_content = true;
3704 if let Some(output) = string.as_mut() {
3705 output.push(self.input.peek());
3706 }
3707 self.skip_non_blank();
3708 if let Some(output) = string.as_mut() {
3709 output.reserve(self.input.bufmaxlen());
3710 }
3711
3712 let mut end = false;
3714 while !end {
3715 self.input.lookahead(self.input.bufmaxlen());
3720 let chunk_len = self.input.bufmaxlen().saturating_sub(1).max(1);
3721 let (stop, chars_consumed) = if let Some(output) = string.as_mut() {
3722 self.input
3723 .fetch_plain_scalar_chunk(output, chunk_len, self.flow_level > 0)
3724 } else {
3725 self.input
3726 .skip_plain_scalar_chunk(chunk_len, self.flow_level > 0)
3727 };
3728 end = stop;
3729 self.mark.offsets.chars += chars_consumed;
3730 self.mark.col += chars_consumed;
3731 self.mark.offsets.bytes = self.input.byte_offset();
3732 }
3733 end_mark = self.mark;
3734 }
3735
3736 if !(self.input.next_is_blank() || self.input.next_is_break()) {
3741 break;
3742 }
3743
3744 self.input.lookahead(2);
3746 while self.input.next_is_blank_or_break() {
3747 if self.input.next_is_blank() {
3748 if !self.leading_whitespace {
3749 self.buf_whitespaces.push(self.input.peek());
3750 self.skip_blank();
3751 } else if (self.mark.col as isize) < indent && self.input.peek() == '\t' {
3752 self.skip_ws_to_eol(SkipTabs::Yes)?;
3755 if !self.input.next_is_breakz() {
3756 return Err(ScanError::from_kind(
3757 start_mark,
3758 ErrorKind::TabInPlainScalar,
3759 ));
3760 }
3761 } else {
3762 self.skip_blank();
3763 }
3764 } else {
3765 if self.leading_whitespace {
3767 self.skip_break();
3768 self.buf_trailing_breaks.push('\n');
3769 } else {
3770 self.buf_whitespaces.clear();
3771 self.skip_break();
3772 self.buf_leading_break.push('\n');
3773 self.leading_whitespace = true;
3774 }
3775 }
3776 self.input.lookahead(2);
3777 }
3778
3779 if self.flow_level == 0 && (self.mark.col as isize) < indent {
3781 break;
3782 }
3783 }
3784
3785 if self.leading_whitespace {
3786 self.allow_simple_key();
3787 }
3788
3789 let borrowed_contents = if string.is_none() {
3790 let start = borrow_start.expect("borrowed scalar has a start offset");
3791 let end = end_mark.byte_offset().ok_or_else(|| {
3792 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
3793 })?;
3794 Some(self.try_borrow_slice(start, end).ok_or_else(|| {
3795 ScanError::from_kind(start_mark, ErrorKind::InputOffsetsWithoutSlice)
3796 })?)
3797 } else {
3798 None
3799 };
3800 let scalar_text = borrowed_contents.unwrap_or_else(|| {
3801 string
3802 .as_deref()
3803 .expect("owned plain scalar has an output buffer")
3804 });
3805 if let Some(character) = find_non_printable(scalar_text) {
3806 return Err(ScanError::from_kind(
3807 start_mark,
3808 ErrorKind::UnexpectedCharacter { character },
3809 ));
3810 }
3811 self.ensure_current_char_is_printable()?;
3812
3813 if has_content {
3814 let contents = if let Some(slice) = borrowed_contents {
3815 Cow::Borrowed(slice)
3816 } else {
3817 Cow::Owned(string.expect("owned plain scalar has an output buffer"))
3818 };
3819
3820 Ok(Token(
3821 Span::new(start_mark, end_mark),
3822 TokenType::Scalar(ScalarStyle::Plain, contents),
3823 ))
3824 } else {
3825 Err(ScanError::from_kind(
3829 start_mark,
3830 ErrorKind::UnexpectedEndOfPlainScalar,
3831 ))
3832 }
3833 }
3834
3835 fn fetch_key(&mut self) -> ScanResult {
3836 let start_mark = self.mark;
3837 if self.flow_level == 0 {
3838 if !self.simple_key_allowed {
3840 return Err(self.scan_error(ErrorKind::MappingKeyNotAllowed));
3841 }
3842 self.roll_indent(
3843 start_mark.col,
3844 None,
3845 TokenType::BlockMappingStart,
3846 start_mark,
3847 );
3848 } else {
3849 self.set_current_flow_mapping_started(true);
3851 }
3852
3853 self.remove_simple_key()?;
3854
3855 if self.flow_level == 0 {
3856 self.allow_simple_key();
3857 } else {
3858 self.disallow_simple_key();
3859 }
3860
3861 self.skip_non_blank();
3862 let end_mark = self.mark;
3863 let token_index = self.tokens.len();
3864 self.explicit_key_tab_check_pending = false;
3865 let stopped_after_comment = self.skip_yaml_whitespace(true)?;
3866 if self.input.peek() == '\t' {
3867 return Err(self.scan_error(ErrorKind::TabNotAllowed));
3868 }
3869 self.explicit_key_tab_check_pending = stopped_after_comment;
3870 self.insert_token(
3871 token_index,
3872 Token(Span::new(start_mark, end_mark), TokenType::Key),
3873 );
3874 Ok(())
3875 }
3876
3877 fn fetch_flow_value(&mut self) -> ScanResult {
3885 let nc = self.input.peek_nth(1);
3886
3887 if self.mark.index() != self.adjacent_value_allowed_at && (nc == '[' || nc == '{') {
3899 return Err(self.scan_error(ErrorKind::FlowMappingValueAdjacentCollection));
3900 }
3901
3902 self.fetch_value()
3903 }
3904
3905 fn fetch_value(&mut self) -> ScanResult {
3907 let sk = *self.simple_keys.last().unwrap();
3908 let start_mark = self.mark;
3909 let is_implicit_flow_mapping = self.current_flow_collection_is_sequence()
3910 && !self.current_flow_mapping_started()
3911 && !self.implicit_flow_mapping_states.is_empty();
3912 if is_implicit_flow_mapping {
3913 *self.implicit_flow_mapping_states.last_mut().unwrap() =
3914 ImplicitMappingState::Inside(self.flow_level);
3915 }
3916
3917 self.skip_non_blank();
3919 let mut trailing_tokens = VecDeque::new();
3926 if self.input.look_ch() == '\t' {
3927 let trailing_token_index = self.tokens.len();
3928 let whitespace = self.skip_ws_to_eol(SkipTabs::Yes)?;
3929 trailing_tokens = self.tokens.split_off(trailing_token_index);
3930
3931 if !whitespace.has_valid_yaml_ws()
3932 && (self.input.peek() == '-' || self.input.next_is_alpha())
3933 {
3934 return Err(self.scan_error(ErrorKind::InvalidMappingValueWhitespace));
3935 }
3936 }
3937
3938 if sk.possible {
3939 let token_index = self.simple_key_token_index(&sk, start_mark)?;
3940 let tok = Token(Span::empty(sk.mark), TokenType::Key);
3942 self.insert_token(token_index, tok);
3943 if is_implicit_flow_mapping {
3944 if sk.mark.line < start_mark.line {
3945 return Err(ScanError::from_kind(
3946 start_mark,
3947 ErrorKind::InvalidColonPlacement,
3948 ));
3949 }
3950 self.insert_token(
3951 token_index,
3952 Token(Span::empty(sk.mark), TokenType::FlowMappingStart),
3953 );
3954 }
3955
3956 self.roll_indent(
3958 sk.mark.col,
3959 Some(sk.token_number),
3960 TokenType::BlockMappingStart,
3961 sk.mark,
3962 );
3963 self.roll_one_col_indent();
3964
3965 self.simple_keys.last_mut().unwrap().possible = false;
3966 self.disallow_simple_key();
3967 } else {
3968 if is_implicit_flow_mapping {
3969 self.tokens
3970 .push_back(Token(Span::empty(start_mark), TokenType::FlowMappingStart).into());
3971 }
3972 if self.flow_level == 0 {
3974 if !self.simple_key_allowed {
3975 return Err(ScanError::from_kind(
3976 start_mark,
3977 ErrorKind::MappingValueNotAllowed,
3978 ));
3979 }
3980
3981 self.roll_indent(
3982 start_mark.col,
3983 None,
3984 TokenType::BlockMappingStart,
3985 start_mark,
3986 );
3987 }
3988 self.roll_one_col_indent();
3989
3990 if self.flow_level == 0 {
3991 self.allow_simple_key();
3992 } else {
3993 self.disallow_simple_key();
3994 }
3995 }
3996 self.tokens
3997 .push_back(Token(Span::empty(start_mark), TokenType::Value).into());
3998 self.tokens.append(&mut trailing_tokens);
3999
4000 Ok(())
4001 }
4002
4003 fn roll_indent(
4009 &mut self,
4010 col: usize,
4011 number: Option<usize>,
4012 tok: TokenType<'input>,
4013 mark: Marker,
4014 ) {
4015 if self.flow_level > 0 {
4016 return;
4017 }
4018
4019 if self.indent <= col as isize {
4023 if let Some(indent) = self.indents.last() {
4024 if !indent.needs_block_end {
4025 self.indent = indent.indent;
4026 self.indents.pop();
4027 }
4028 }
4029 }
4030
4031 if self.indent < col as isize {
4032 self.indents.push(Indent {
4033 indent: self.indent,
4034 needs_block_end: true,
4035 });
4036 self.indent = col as isize;
4037 let tokens_parsed = self.tokens_parsed;
4038 match number {
4039 Some(n) => self.insert_token(n - tokens_parsed, Token(Span::empty(mark), tok)),
4040 None => self.tokens.push_back(Token(Span::empty(mark), tok).into()),
4041 }
4042 }
4043 }
4044
4045 fn unroll_indent(&mut self, col: isize) {
4051 if self.flow_level > 0 {
4052 return;
4053 }
4054 while self.indent > col {
4055 let indent = self.indents.pop().unwrap();
4056 self.indent = indent.indent;
4057 if indent.needs_block_end {
4058 self.tokens
4059 .push_back(Token(Span::empty(self.mark), TokenType::BlockEnd).into());
4060 }
4061 }
4062 }
4063
4064 fn roll_one_col_indent(&mut self) {
4070 if self.flow_level == 0 && self.indents.last().is_some_and(|x| x.needs_block_end) {
4071 self.indents.push(Indent {
4072 indent: self.indent,
4073 needs_block_end: false,
4074 });
4075 self.indent += 1;
4076 }
4077 }
4078
4079 fn unroll_non_block_indents(&mut self) {
4081 while let Some(indent) = self.indents.last() {
4082 if indent.needs_block_end {
4083 break;
4084 }
4085 self.indent = indent.indent;
4086 self.indents.pop();
4087 }
4088 }
4089
4090 fn save_simple_key(&mut self) {
4092 if self.simple_key_allowed {
4093 let required = self.flow_level == 0
4094 && self.indent == (self.mark.col as isize)
4095 && self.indents.last().unwrap().needs_block_end;
4096
4097 if let Some(last) = self.simple_keys.last_mut() {
4098 *last = SimpleKey {
4099 mark: self.mark,
4100 possible: true,
4101 required,
4102 token_number: self.tokens_parsed + self.tokens.len(),
4103 };
4104 }
4105 }
4106 }
4107
4108 fn remove_simple_key(&mut self) -> ScanResult {
4109 let last = self.simple_keys.last_mut().unwrap();
4110 if last.possible && last.required {
4111 return Err(Self::simple_key_expected(last.mark));
4112 }
4113
4114 last.possible = false;
4115 Ok(())
4116 }
4117
4118 fn is_within_block(&self) -> bool {
4120 !self.indents.is_empty()
4121 }
4122
4123 fn end_implicit_mapping(&mut self, mark: Marker, flow_level: u8) {
4129 if self
4130 .implicit_flow_mapping_states
4131 .last()
4132 .is_some_and(|state| *state == ImplicitMappingState::Inside(flow_level))
4133 {
4134 *self.implicit_flow_mapping_states.last_mut().unwrap() = ImplicitMappingState::Possible;
4135 self.set_current_flow_mapping_started(false);
4136 self.tokens
4137 .push_back(Token(Span::empty(mark), TokenType::FlowMappingEnd).into());
4138 }
4139 }
4140
4141 fn current_flow_collection_is_sequence(&self) -> bool {
4142 self.flow_markers
4143 .last()
4144 .is_some_and(|(_, bracket)| *bracket == '[')
4145 }
4146
4147 fn current_flow_mapping_started(&self) -> bool {
4148 self.flow_mapping_started.last().copied().unwrap_or(false)
4149 }
4150
4151 fn set_current_flow_mapping_started(&mut self, started: bool) {
4152 if let Some(current) = self.flow_mapping_started.last_mut() {
4153 *current = started;
4154 }
4155 }
4156}
4157
4158#[derive(PartialEq, Eq)]
4162enum Chomping {
4163 Strip,
4165 Clip,
4167 Keep,
4169}
4170
4171#[cfg(test)]
4172mod test {
4173 use alloc::{
4174 borrow::{Cow, ToOwned},
4175 rc::Rc,
4176 string::String,
4177 vec,
4178 vec::Vec,
4179 };
4180 use core::cell::Cell;
4181
4182 use crate::error::{ErrorKind, ScanError};
4183 use crate::{
4184 input::{str::StrInput, BorrowedInput, BufferedInput, Input},
4185 scanner::{
4186 Comment, Marker, Placement, QueuedToken, QueuedTokenType, ScalarStyle, Scanner, Span,
4187 Token, TokenType,
4188 },
4189 };
4190
4191 struct CountingChars {
4192 chars: alloc::vec::IntoIter<char>,
4193 read: Rc<Cell<usize>>,
4194 }
4195
4196 impl Iterator for CountingChars {
4197 type Item = char;
4198
4199 fn next(&mut self) -> Option<Self::Item> {
4200 let next = self.chars.next();
4201 if next.is_some() {
4202 self.read.set(self.read.get() + 1);
4203 }
4204 next
4205 }
4206 }
4207
4208 struct SlicingOnlyInput<'input> {
4209 inner: StrInput<'input>,
4210 expose_slice: bool,
4211 }
4212
4213 impl<'input> SlicingOnlyInput<'input> {
4214 fn new(source: &'input str, expose_slice: bool) -> Self {
4215 Self {
4216 inner: StrInput::new(source),
4217 expose_slice,
4218 }
4219 }
4220 }
4221
4222 impl Input for SlicingOnlyInput<'_> {
4223 fn lookahead(&mut self, count: usize) {
4224 self.inner.lookahead(count);
4225 }
4226
4227 fn buflen(&self) -> usize {
4228 self.inner.buflen()
4229 }
4230
4231 fn bufmaxlen(&self) -> usize {
4232 self.inner.bufmaxlen()
4233 }
4234
4235 fn raw_read_ch(&mut self) -> char {
4236 self.inner.raw_read_ch()
4237 }
4238
4239 fn raw_read_non_breakz_ch(&mut self) -> Option<char> {
4240 self.inner.raw_read_non_breakz_ch()
4241 }
4242
4243 fn skip(&mut self) {
4244 self.inner.skip();
4245 }
4246
4247 fn skip_n(&mut self, count: usize) {
4248 self.inner.skip_n(count);
4249 }
4250
4251 fn peek(&self) -> char {
4252 self.inner.peek()
4253 }
4254
4255 fn peek_nth(&self, n: usize) -> char {
4256 self.inner.peek_nth(n)
4257 }
4258
4259 fn byte_offset(&self) -> Option<usize> {
4260 self.inner.byte_offset()
4261 }
4262
4263 fn slice_bytes(&self, start: usize, end: usize) -> Option<&str> {
4264 if self.expose_slice {
4265 self.inner.slice_bytes(start, end)
4266 } else {
4267 None
4268 }
4269 }
4270 }
4271
4272 impl<'input> BorrowedInput<'input> for SlicingOnlyInput<'input> {
4273 fn slice_borrowed(&self, _start: usize, _end: usize) -> Option<&'input str> {
4274 None
4275 }
4276 }
4277
4278 struct SmallReportedBufferInput<'input> {
4279 inner: StrInput<'input>,
4280 reported_bufmaxlen: usize,
4281 }
4282
4283 impl<'input> SmallReportedBufferInput<'input> {
4284 fn new(source: &'input str, reported_bufmaxlen: usize) -> Self {
4285 Self {
4286 inner: StrInput::new(source),
4287 reported_bufmaxlen,
4288 }
4289 }
4290 }
4291
4292 impl Input for SmallReportedBufferInput<'_> {
4293 fn lookahead(&mut self, count: usize) {
4294 self.inner.lookahead(count);
4295 }
4296
4297 fn buflen(&self) -> usize {
4298 self.inner.buflen()
4299 }
4300
4301 fn bufmaxlen(&self) -> usize {
4302 self.reported_bufmaxlen
4303 }
4304
4305 fn raw_read_ch(&mut self) -> char {
4306 self.inner.raw_read_ch()
4307 }
4308
4309 fn raw_read_non_breakz_ch(&mut self) -> Option<char> {
4310 self.inner.raw_read_non_breakz_ch()
4311 }
4312
4313 fn skip(&mut self) {
4314 self.inner.skip();
4315 }
4316
4317 fn skip_n(&mut self, count: usize) {
4318 self.inner.skip_n(count);
4319 }
4320
4321 fn peek(&self) -> char {
4322 self.inner.peek()
4323 }
4324
4325 fn peek_nth(&self, n: usize) -> char {
4326 self.inner.peek_nth(n)
4327 }
4328 }
4329
4330 impl<'input> BorrowedInput<'input> for SmallReportedBufferInput<'input> {
4331 fn slice_borrowed(&self, start: usize, end: usize) -> Option<&'input str> {
4332 self.inner.slice_borrowed(start, end)
4333 }
4334 }
4335
4336 #[test]
4337 fn anchor_character_set_allows_colon_and_rejects_flow_indicators() {
4338 use super::is_anchor_char;
4339
4340 assert!(is_anchor_char('x'));
4341 assert!(is_anchor_char('-'));
4342 assert!(is_anchor_char('_'));
4343 assert!(is_anchor_char(':'));
4344 assert!(is_anchor_char('#'));
4345 assert!(is_anchor_char('/'));
4346 assert!(is_anchor_char('?'));
4347
4348 for c in [',', '[', ']', '{', '}', ' ', '\t', '\n', '\r', '\0'] {
4349 assert!(
4350 !is_anchor_char(c),
4351 "character {c:?} must not be accepted in anchor/alias names"
4352 );
4353 }
4354 }
4355
4356 #[test]
4357 fn flow_simple_key_length_limit_bounds_buffering() {
4358 let mut yaml = String::from("[\n\"start\"\n");
4359 for _ in 0..600 {
4360 yaml.push_str("\"x\"\n");
4361 }
4362 let total_chars = yaml.chars().count();
4363 let read = Rc::new(Cell::new(0));
4364 let chars = yaml.chars().collect::<Vec<_>>().into_iter();
4365 let mut scanner = Scanner::new(BufferedInput::new(CountingChars {
4366 chars,
4367 read: Rc::clone(&read),
4368 }));
4369
4370 assert!(matches!(
4371 scanner.next_token().unwrap().unwrap().1,
4372 TokenType::StreamStart
4373 ));
4374
4375 let token = scanner.next_token().unwrap().unwrap();
4376 assert!(matches!(token.1, TokenType::FlowSequenceStart));
4377
4378 let token = scanner.next_token().unwrap().unwrap();
4379 assert!(matches!(
4380 token.1,
4381 TokenType::Scalar(_, ref value) if value == "start"
4382 ));
4383 assert!(
4384 read.get() < total_chars,
4385 "scanner consumed all {total_chars} chars before yielding the first flow scalar"
4386 );
4387 assert!(
4388 read.get() <= super::SIMPLE_KEY_MAX_LOOKAHEAD + 128,
4389 "scanner read {} chars before yielding the first flow scalar",
4390 read.get()
4391 );
4392 }
4393
4394 #[test]
4395 fn block_scalar_indent_tolerates_small_reported_bufmaxlen() {
4396 let mut scanner = Scanner::new(SmallReportedBufferInput::new("|\n value\n", 0));
4397
4398 let scalar = scanner
4399 .find_map(
4400 |token| match token.expect("valid YAML should scan without errors") {
4401 Token(_, TokenType::Scalar(ScalarStyle::Literal, value)) => {
4402 Some(value.into_owned())
4403 }
4404 _ => None,
4405 },
4406 )
4407 .expect("expected block scalar token");
4408
4409 assert_eq!(scalar, "value\n");
4410 }
4411
4412 #[test]
4413 fn plain_scalar_chunk_tolerates_small_reported_bufmaxlen() {
4414 let mut scanner = Scanner::new(SmallReportedBufferInput::new("plain\n", 0));
4415
4416 let scalar = scanner
4417 .find_map(
4418 |token| match token.expect("valid YAML should scan without errors") {
4419 Token(_, TokenType::Scalar(ScalarStyle::Plain, value)) => {
4420 Some(value.into_owned())
4421 }
4422 _ => None,
4423 },
4424 )
4425 .expect("expected plain scalar token");
4426
4427 assert_eq!(scalar, "plain");
4428 }
4429
4430 fn first_token_slice(
4431 yaml: &str,
4432 matches_token: impl Fn(&TokenType<'_>) -> bool,
4433 ) -> Option<String> {
4434 let mut scanner = Scanner::new(StrInput::new(yaml));
4435
4436 loop {
4437 let token = scanner
4438 .next_token()
4439 .expect("scanner should accept the test YAML")?;
4440 if matches_token(&token.1) {
4441 return token.0.slice(yaml).map(ToOwned::to_owned);
4442 }
4443 }
4444 }
4445
4446 #[test]
4447 fn flow_indicator_token_spans_cover_only_the_indicator() {
4448 assert_eq!(
4449 first_token_slice("[ # c\n a]\n", |token| matches!(
4450 token,
4451 TokenType::FlowSequenceStart
4452 ))
4453 .as_deref(),
4454 Some("[")
4455 );
4456 assert_eq!(
4457 first_token_slice("{ # c\n a: b}\n", |token| matches!(
4458 token,
4459 TokenType::FlowMappingStart
4460 ))
4461 .as_deref(),
4462 Some("{")
4463 );
4464 assert_eq!(
4465 first_token_slice("[a] # c\n", |token| matches!(
4466 token,
4467 TokenType::FlowSequenceEnd
4468 ))
4469 .as_deref(),
4470 Some("]")
4471 );
4472 assert_eq!(
4473 first_token_slice("{a: b} # c\n", |token| matches!(
4474 token,
4475 TokenType::FlowMappingEnd
4476 ))
4477 .as_deref(),
4478 Some("}")
4479 );
4480 assert_eq!(
4481 first_token_slice("[a, # c\nb]\n", |token| matches!(
4482 token,
4483 TokenType::FlowEntry
4484 ))
4485 .as_deref(),
4486 Some(",")
4487 );
4488 }
4489
4490 #[test]
4491 fn explicit_key_token_span_covers_only_the_indicator() {
4492 assert_eq!(
4493 first_token_slice("? # c\n: value\n", |token| matches!(token, TokenType::Key))
4494 .as_deref(),
4495 Some("?")
4496 );
4497 }
4498
4499 #[test]
4500 fn comment_capture_does_not_change_leading_whitespace() {
4501 let mut scanner = Scanner::new(StrInput::new("# comment\n"));
4502
4503 let token = scanner.scan_comment_token().unwrap();
4504
4505 assert!(scanner.leading_whitespace);
4506 assert!(matches!(token.1, TokenType::Comment(ref comment) if comment.text == " comment"));
4507
4508 let mut scanner = Scanner::new(BufferedInput::new("# streaming\n".chars()));
4509 scanner.input.lookahead(1);
4510
4511 let token = scanner.scan_comment_token().unwrap();
4512
4513 assert!(scanner.leading_whitespace);
4514 assert!(matches!(token.1, TokenType::Comment(ref comment) if comment.text == " streaming"));
4515 }
4516
4517 #[test]
4518 fn comment_capture_falls_back_to_owned_slice_when_borrow_unavailable() {
4519 let mut scanner = Scanner::new(SlicingOnlyInput::new("# sliced\n", true));
4520 scanner.input.lookahead(2);
4521 assert_eq!(scanner.input.peek_nth(1), ' ');
4522
4523 let token = scanner.scan_comment_token().unwrap();
4524
4525 assert!(matches!(token.1, TokenType::Comment(ref comment)
4526 if matches!(comment.text, Cow::Owned(ref text) if text == " sliced")));
4527 }
4528
4529 #[test]
4530 fn comment_capture_errors_when_offsets_have_no_slice() {
4531 let mut scanner = Scanner::new(SlicingOnlyInput::new("# broken\n", false));
4532
4533 let error = scanner.scan_comment_token().unwrap_err();
4534
4535 assert_eq!(error.kind(), &ErrorKind::InputOffsetsWithoutSlice);
4536 }
4537
4538 #[test]
4539 fn queued_token_roundtrips_public_token_variants() {
4540 let span = Span::new(Marker::new(0, 1, 0), Marker::new(7, 1, 7));
4541 let tokens = [
4542 Token(span, TokenType::StreamStart),
4543 Token(span, TokenType::StreamEnd),
4544 Token(span, TokenType::VersionDirective(1, 2)),
4545 Token(
4546 span,
4547 TokenType::TagDirective(Cow::Borrowed("!app!"), Cow::Borrowed("tag:app.example,")),
4548 ),
4549 Token(span, TokenType::DocumentStart),
4550 Token(span, TokenType::DocumentEnd),
4551 Token(span, TokenType::BlockSequenceStart),
4552 Token(span, TokenType::BlockMappingStart),
4553 Token(span, TokenType::BlockEnd),
4554 Token(span, TokenType::FlowSequenceStart),
4555 Token(span, TokenType::FlowSequenceEnd),
4556 Token(span, TokenType::FlowMappingStart),
4557 Token(span, TokenType::FlowMappingEnd),
4558 Token(span, TokenType::BlockEntry),
4559 Token(span, TokenType::FlowEntry),
4560 Token(span, TokenType::Key),
4561 Token(span, TokenType::Value),
4562 Token(span, TokenType::Alias(Cow::Borrowed("alias"))),
4563 Token(span, TokenType::Anchor(Cow::Borrowed("anchor"))),
4564 Token(
4565 span,
4566 TokenType::Tag(Cow::Borrowed("!"), Cow::Borrowed("tag")),
4567 ),
4568 Token(
4569 span,
4570 TokenType::Scalar(ScalarStyle::Literal, Cow::Borrowed("scalar")),
4571 ),
4572 Token(
4573 span,
4574 TokenType::Comment(
4575 Comment::new(Cow::Borrowed(" comment")).with_placement(Placement::Right),
4576 ),
4577 ),
4578 Token(
4579 span,
4580 TokenType::ReservedDirective(
4581 "reserved".to_owned(),
4582 vec!["one".to_owned(), "two".to_owned()],
4583 ),
4584 ),
4585 ];
4586
4587 for token in tokens {
4588 let queued: QueuedToken = token.clone().into();
4589
4590 assert_eq!(queued.into_public(), token);
4591 }
4592 }
4593
4594 #[test]
4595 fn comment_skipping_path_consumes_comment_without_tokenizing_it() {
4596 let mut scanner = Scanner::new(StrInput::new("# skipped\nnext: value\n"));
4597
4598 scanner.skip_yaml_whitespace(false).unwrap();
4599
4600 assert!(scanner.tokens.is_empty());
4601 assert_eq!(scanner.mark.line(), 2);
4602 assert_eq!(scanner.mark.col(), 0);
4603 }
4604
4605 #[test]
4606 fn yaml_whitespace_can_stop_after_queued_comment() {
4607 let mut scanner = Scanner::new(StrInput::new(" # queued\n# later\n"));
4608
4609 assert!(scanner.skip_yaml_whitespace(true).unwrap());
4610
4611 assert_eq!(scanner.tokens.len(), 1);
4612 assert!(matches!(
4613 scanner.tokens.front().unwrap().1,
4614 QueuedTokenType::Comment(ref comment) if comment.text == " queued"
4615 ));
4616 assert_eq!(scanner.mark.line(), 1);
4617 assert_eq!(scanner.mark.col(), 9);
4618 }
4619
4620 #[test]
4621 fn token_skip_can_stop_after_queued_comment() {
4622 let mut scanner = Scanner::new(StrInput::new("# first\n# second\n"));
4623
4624 assert!(scanner.skip_to_next_token(true).unwrap());
4625
4626 assert_eq!(scanner.tokens.len(), 1);
4627 assert!(matches!(
4628 scanner.tokens.front().unwrap().1,
4629 QueuedTokenType::Comment(ref comment) if comment.text == " first"
4630 ));
4631 assert_eq!(scanner.mark.line(), 2);
4632 assert_eq!(scanner.mark.col(), 0);
4633 }
4634
4635 #[test]
4636 fn scanner_emits_first_leading_comment_before_scanning_next_comment() {
4637 let mut scanner = Scanner::new(StrInput::new("# first\n# second\nkey: value\n"));
4638
4639 assert!(matches!(
4640 scanner.next_token().unwrap().unwrap().1,
4641 TokenType::StreamStart
4642 ));
4643 assert!(matches!(
4644 scanner.next_token().unwrap().unwrap().1,
4645 TokenType::Comment(ref comment) if comment.text == " first"
4646 ));
4647 assert!(scanner.tokens.is_empty());
4648 assert!(matches!(
4649 scanner.next_token().unwrap().unwrap().1,
4650 TokenType::Comment(ref comment) if comment.text == " second"
4651 ));
4652 }
4653
4654 #[test]
4655 fn scanner_emits_quoted_scalar_comment_before_scanning_following_value() {
4656 let mut scanner = Scanner::new(StrInput::new("\"key\" # quoted\n: value\n"));
4657
4658 assert!(matches!(
4659 scanner.next_token().unwrap().unwrap().1,
4660 TokenType::StreamStart
4661 ));
4662 assert!(matches!(
4663 scanner.next_token().unwrap().unwrap().1,
4664 TokenType::Scalar(ScalarStyle::DoubleQuoted, ref value) if value == "key"
4665 ));
4666 assert!(matches!(
4667 scanner.next_token().unwrap().unwrap().1,
4668 TokenType::Comment(ref comment) if comment.text == " quoted"
4669 ));
4670 }
4671
4672 #[test]
4673 fn flow_scalar_comment_disables_adjacent_value_lookahead() {
4674 let mut scanner = Scanner::new(StrInput::new("\"key\"\n# quoted\n: value\n"));
4675
4676 scanner.fetch_flow_scalar(false).unwrap();
4677
4678 assert_eq!(scanner.adjacent_value_allowed_at, usize::MAX);
4679 assert!(matches!(
4680 scanner.tokens.front().unwrap().1,
4681 QueuedTokenType::Scalar(ScalarStyle::DoubleQuoted, ref value) if value == "key"
4682 ));
4683 assert!(scanner.tokens.iter().any(|QueuedToken(_, token)| matches!(
4684 token,
4685 QueuedTokenType::Comment(comment) if comment.text == " quoted"
4686 )));
4687 }
4688
4689 #[test]
4690 fn deferred_error_waits_for_all_comment_tokens() {
4691 let mut scanner = Scanner::new(StrInput::new("# first\n# second\n@\n"));
4692
4693 assert!(matches!(
4694 scanner.next_token().unwrap().unwrap().1,
4695 TokenType::StreamStart
4696 ));
4697 assert!(matches!(
4698 scanner.next_token().unwrap().unwrap().1,
4699 TokenType::Comment(ref comment) if comment.text == " first"
4700 ));
4701 assert!(matches!(
4702 scanner.next_token().unwrap().unwrap().1,
4703 TokenType::Comment(ref comment) if comment.text == " second"
4704 ));
4705
4706 let error = scanner.next_token().unwrap_err();
4707
4708 assert_eq!(
4709 error.kind(),
4710 &ErrorKind::UnexpectedCharacter { character: '@' }
4711 );
4712 }
4713
4714 #[test]
4716 fn anchor_name_is_borrowed_for_str_input() {
4717 let mut scanner = Scanner::new(StrInput::new("&anch\n"));
4718
4719 loop {
4720 let tok = scanner
4721 .next_token()
4722 .expect("valid YAML must scan without errors")
4723 .expect("scanner must eventually produce a token");
4724 if let TokenType::Anchor(name) = tok.1 {
4725 assert!(matches!(name, Cow::Borrowed("anch")));
4726 break;
4727 }
4728 }
4729 }
4730
4731 #[test]
4732 fn anchor_name_rejects_non_printable_control_chars() {
4733 let mut scanner = Scanner::new(StrInput::new("&foo\u{0001}\n"));
4734
4735 scanner.next_token().unwrap();
4736 assert_eq!(
4737 scanner.next_token().unwrap_err().kind(),
4738 &ErrorKind::UnexpectedCharacter {
4739 character: '\u{0001}'
4740 }
4741 );
4742 }
4743
4744 #[test]
4745 fn alias_name_rejects_non_printable_control_chars() {
4746 let mut scanner = Scanner::new(StrInput::new("*foo\u{0001}\n"));
4747
4748 scanner.next_token().unwrap();
4749 assert_eq!(
4750 scanner.next_token().unwrap_err().kind(),
4751 &ErrorKind::UnexpectedCharacter {
4752 character: '\u{0001}'
4753 }
4754 );
4755 }
4756
4757 #[test]
4758 fn alias_name_is_borrowed_for_str_input() {
4759 let mut scanner = Scanner::new(StrInput::new("*anch\n"));
4760
4761 loop {
4762 let tok = scanner
4763 .next_token()
4764 .expect("valid YAML must scan without errors")
4765 .expect("scanner must eventually produce a token");
4766 if let TokenType::Alias(name) = tok.1 {
4767 assert!(matches!(name, Cow::Borrowed("anch")));
4768 break;
4769 }
4770 }
4771 }
4772
4773 #[test]
4774 fn alias_name_scans_colon_as_part_of_name() {
4775 let mut scanner = Scanner::new(StrInput::new("*foo: bar\n"));
4776
4777 loop {
4778 let tok = scanner
4779 .next_token()
4780 .expect("scanner must not fail before alias token")
4781 .expect("scanner must eventually emit an alias token");
4782
4783 if let TokenType::Alias(name) = tok.1 {
4784 assert_eq!(name.as_ref(), "foo:");
4785 break;
4786 }
4787 }
4788 }
4789
4790 #[test]
4791 fn anchor_name_scans_colon_as_part_of_name() {
4792 let mut scanner = Scanner::new(StrInput::new("&foo: bar\n"));
4793
4794 loop {
4795 let tok = scanner
4796 .next_token()
4797 .expect("scanner must not fail before anchor token")
4798 .expect("scanner must eventually emit an anchor token");
4799
4800 if let TokenType::Anchor(name) = tok.1 {
4801 assert_eq!(name.as_ref(), "foo:");
4802 break;
4803 }
4804 }
4805 }
4806
4807 #[test]
4809 fn tag_directive_parts_are_borrowed_for_str_input() {
4810 let mut scanner = Scanner::new(StrInput::new("%TAG !e! tag:example.com,2000:app/\n"));
4811
4812 loop {
4813 let tok = scanner
4814 .next_token()
4815 .expect("valid YAML must scan without errors")
4816 .expect("scanner must eventually produce a token");
4817 if let TokenType::TagDirective(handle, prefix) = tok.1 {
4818 assert!(matches!(handle, Cow::Borrowed("!e!")));
4819 assert!(matches!(prefix, Cow::Borrowed("tag:example.com,2000:app/")));
4820 break;
4821 }
4822 }
4823 }
4824
4825 #[test]
4826 fn tag_directive_parts_are_owned_for_buffered_input() {
4827 let mut scanner = Scanner::new(BufferedInput::new(
4828 "%TAG !e! tag:example.com,2000:app/\n".chars(),
4829 ));
4830
4831 loop {
4832 let tok = scanner
4833 .next_token()
4834 .expect("valid YAML must scan without errors")
4835 .expect("scanner must eventually produce a token");
4836 if let TokenType::TagDirective(handle, prefix) = tok.1 {
4837 assert!(matches!(handle, Cow::Owned(_)));
4838 assert_eq!(&*handle, "!e!");
4839 assert!(matches!(prefix, Cow::Owned(_)));
4840 assert_eq!(&*prefix, "tag:example.com,2000:app/");
4841 break;
4842 }
4843 }
4844 }
4845
4846 #[test]
4847 fn buffered_tag_directive_decodes_prefix_escape() {
4848 let mut scanner = Scanner::new(BufferedInput::new(
4849 "%TAG !e! %74ag:example.com,2000:app/\n".chars(),
4850 ));
4851
4852 loop {
4853 let tok = scanner
4854 .next_token()
4855 .expect("valid YAML must scan without errors")
4856 .expect("scanner must eventually produce a token");
4857 if let TokenType::TagDirective(handle, prefix) = tok.1 {
4858 assert_eq!(&*handle, "!e!");
4859 assert!(matches!(prefix, Cow::Owned(_)));
4860 assert_eq!(&*prefix, "tag:example.com,2000:app/");
4861 break;
4862 }
4863 }
4864 }
4865
4866 #[test]
4867 fn local_tag_combines_handle_text_with_escaped_suffix() {
4868 let mut scanner = Scanner::new(StrInput::new("!foo%20bar value\n"));
4869
4870 loop {
4871 let tok = scanner
4872 .next_token()
4873 .expect("valid YAML must scan without errors")
4874 .expect("scanner must eventually produce a token");
4875 if let TokenType::Tag(handle, suffix) = tok.1 {
4876 assert!(matches!(handle, Cow::Borrowed("!")));
4877 assert!(matches!(suffix, Cow::Owned(_)));
4878 assert_eq!(&*suffix, "foo bar");
4879 break;
4880 }
4881 }
4882 }
4883
4884 #[test]
4885 fn secondary_tag_requires_suffix_in_borrowed_and_buffered_paths() {
4886 assert_eq!(
4887 first_scanner_error_kind("!! value\n"),
4888 ErrorKind::MissingTagUri
4889 );
4890 assert_eq!(
4891 first_buffered_scanner_error_kind("!! value\n"),
4892 ErrorKind::MissingTagUri
4893 );
4894 }
4895
4896 #[test]
4897 fn plain_scalar_is_borrowed_when_whitespace_free_for_str_input() {
4898 let mut scanner = Scanner::new(StrInput::new("foo\n"));
4899
4900 loop {
4901 let tok = scanner
4902 .next_token()
4903 .expect("valid YAML must scan without errors")
4904 .expect("scanner must eventually produce a token");
4905 if let TokenType::Scalar(_, value) = tok.1 {
4906 assert!(matches!(value, Cow::Borrowed("foo")));
4907 break;
4908 }
4909 }
4910 }
4911
4912 #[test]
4913 fn plain_scalar_is_borrowed_when_whitespace_present_for_str_input() {
4914 let mut scanner = Scanner::new(StrInput::new("foo bar\n"));
4915
4916 loop {
4917 let tok = scanner
4918 .next_token()
4919 .expect("valid YAML must scan without errors")
4920 .expect("scanner must eventually produce a token");
4921 if let TokenType::Scalar(_, value) = tok.1 {
4922 assert!(matches!(value, Cow::Borrowed("foo bar")));
4923 break;
4924 }
4925 }
4926 }
4927
4928 #[test]
4929 fn single_quoted_scalar_is_borrowed_when_verbatim_for_str_input() {
4930 let mut scanner = Scanner::new(StrInput::new("'foo bar'\n"));
4931
4932 loop {
4933 let tok = scanner
4934 .next_token()
4935 .expect("valid YAML must scan without errors")
4936 .expect("scanner must eventually produce a token");
4937 if let TokenType::Scalar(_, value) = tok.1 {
4938 assert!(matches!(value, Cow::Borrowed("foo bar")));
4939 break;
4940 }
4941 }
4942 }
4943
4944 #[test]
4945 fn single_quoted_scalar_is_owned_when_quote_is_escaped_for_str_input() {
4946 let mut scanner = Scanner::new(StrInput::new("'foo''bar'\n"));
4947
4948 loop {
4949 let tok = scanner
4950 .next_token()
4951 .expect("valid YAML must scan without errors")
4952 .expect("scanner must eventually produce a token");
4953 if let TokenType::Scalar(_, value) = tok.1 {
4954 assert!(matches!(value, Cow::Owned(_)));
4955 assert_eq!(&*value, "foo'bar");
4956 break;
4957 }
4958 }
4959 }
4960
4961 #[test]
4962 fn double_quoted_scalar_is_borrowed_when_verbatim_for_str_input() {
4963 let mut scanner = Scanner::new(StrInput::new("\"foo bar\"\n"));
4964
4965 loop {
4966 let tok = scanner
4967 .next_token()
4968 .expect("valid YAML must scan without errors")
4969 .expect("scanner must eventually produce a token");
4970 if let TokenType::Scalar(_, value) = tok.1 {
4971 assert!(matches!(value, Cow::Borrowed("foo bar")));
4972 break;
4973 }
4974 }
4975 }
4976
4977 #[test]
4978 fn double_quoted_scalar_is_owned_when_escape_sequence_present_for_str_input() {
4979 let mut scanner = Scanner::new(StrInput::new("\"foo\\nbar\"\n"));
4980
4981 loop {
4982 let tok = scanner
4983 .next_token()
4984 .expect("valid YAML must scan without errors")
4985 .expect("scanner must eventually produce a token");
4986 if let TokenType::Scalar(_, value) = tok.1 {
4987 assert!(matches!(value, Cow::Owned(_)));
4988 assert_eq!(&*value, "foo\nbar");
4989 break;
4990 }
4991 }
4992 }
4993
4994 #[test]
4995 fn plain_key_is_borrowed_for_str_input() {
4996 let mut scanner = Scanner::new(StrInput::new("mykey: value\n"));
4998
4999 let mut found_key = false;
5000 let mut key_value: Option<Cow<'_, str>> = None;
5001
5002 loop {
5003 let tok = scanner
5004 .next_token()
5005 .expect("valid YAML must scan without errors");
5006 let Some(tok) = tok else { break };
5007
5008 if matches!(tok.1, TokenType::Key) {
5009 found_key = true;
5010 } else if found_key {
5011 if let TokenType::Scalar(_, value) = tok.1 {
5012 key_value = Some(value);
5013 break;
5014 }
5015 }
5016 }
5017
5018 assert!(found_key, "expected to find a Key token");
5019 let key_value = key_value.expect("expected to find a scalar after Key token");
5020 assert!(
5021 matches!(key_value, Cow::Borrowed("mykey")),
5022 "key should be borrowed, got: {key_value:?}"
5023 );
5024 }
5025
5026 #[test]
5027 fn quoted_key_is_borrowed_when_verbatim_for_str_input() {
5028 let mut scanner = Scanner::new(StrInput::new("\"mykey\": value\n"));
5029
5030 let mut found_key = false;
5031 let mut key_value: Option<Cow<'_, str>> = None;
5032
5033 loop {
5034 let tok = scanner
5035 .next_token()
5036 .expect("valid YAML must scan without errors");
5037 let Some(tok) = tok else { break };
5038
5039 if matches!(tok.1, TokenType::Key) {
5040 found_key = true;
5041 } else if found_key {
5042 if let TokenType::Scalar(_, value) = tok.1 {
5043 key_value = Some(value);
5044 break;
5045 }
5046 }
5047 }
5048
5049 assert!(found_key, "expected to find a Key token");
5050 let key_value = key_value.expect("expected to find a scalar after Key token");
5051 assert!(
5052 matches!(key_value, Cow::Borrowed("mykey")),
5053 "quoted key should be borrowed when verbatim, got: {key_value:?}"
5054 );
5055 }
5056
5057 #[test]
5058 fn tag_handle_and_suffix_are_borrowed_for_str_input() {
5059 let mut scanner = Scanner::new(StrInput::new("!!str foo\n"));
5061
5062 loop {
5063 let tok = scanner
5064 .next_token()
5065 .expect("valid YAML must scan without errors")
5066 .expect("scanner must eventually produce a token");
5067 if let TokenType::Tag(handle, suffix) = tok.1 {
5068 assert!(
5069 matches!(handle, Cow::Borrowed("!!")),
5070 "tag handle should be borrowed, got: {handle:?}"
5071 );
5072 assert!(
5073 matches!(suffix, Cow::Borrowed("str")),
5074 "tag suffix should be borrowed, got: {suffix:?}"
5075 );
5076 break;
5077 }
5078 }
5079 }
5080
5081 #[test]
5082 fn local_tag_suffix_is_borrowed_for_str_input() {
5083 let mut scanner = Scanner::new(StrInput::new("!mytag foo\n"));
5085
5086 loop {
5087 let tok = scanner
5088 .next_token()
5089 .expect("valid YAML must scan without errors")
5090 .expect("scanner must eventually produce a token");
5091 if let TokenType::Tag(handle, suffix) = tok.1 {
5092 assert!(
5093 matches!(handle, Cow::Borrowed("!")),
5094 "local tag handle should be '!', got: {handle:?}"
5095 );
5096 assert!(
5097 matches!(suffix, Cow::Borrowed("mytag")),
5098 "local tag suffix should be borrowed, got: {suffix:?}"
5099 );
5100 break;
5101 }
5102 }
5103 }
5104
5105 #[test]
5106 fn local_tag_suffix_with_punctuation_is_borrowed_for_str_input() {
5107 let mut scanner = Scanner::new(StrInput::new("!mytag/part foo\n"));
5108
5109 loop {
5110 let tok = scanner
5111 .next_token()
5112 .expect("valid YAML must scan without errors")
5113 .expect("scanner must eventually produce a token");
5114 if let TokenType::Tag(handle, suffix) = tok.1 {
5115 assert!(matches!(handle, Cow::Borrowed("!")));
5116 assert!(matches!(suffix, Cow::Borrowed("mytag/part")));
5117 break;
5118 }
5119 }
5120 }
5121
5122 #[test]
5123 fn tag_with_uri_escape_is_owned_for_str_input() {
5124 let mut scanner = Scanner::new(StrInput::new("!!my%20tag foo\n"));
5126
5127 loop {
5128 let tok = scanner
5129 .next_token()
5130 .expect("valid YAML must scan without errors")
5131 .expect("scanner must eventually produce a token");
5132 if let TokenType::Tag(handle, suffix) = tok.1 {
5133 assert!(
5134 matches!(handle, Cow::Borrowed("!!")),
5135 "tag handle should still be borrowed, got: {handle:?}"
5136 );
5137 assert!(
5138 matches!(suffix, Cow::Owned(_)),
5139 "tag suffix with URI escape should be owned, got: {suffix:?}"
5140 );
5141 assert_eq!(&*suffix, "my tag");
5142 break;
5143 }
5144 }
5145 }
5146
5147 #[test]
5148 fn flow_scalar_buffer_tracks_pending_whitespace() {
5149 let mut borrowed = super::FlowScalarBuf::new_borrowed(2);
5150
5151 borrowed.note_pending_ws(5, 8);
5152 borrowed.commit_pending_ws();
5153 assert!(matches!(
5154 borrowed,
5155 super::FlowScalarBuf::Borrowed {
5156 end: 8,
5157 pending_ws_start: None,
5158 pending_ws_end: 8,
5159 ..
5160 }
5161 ));
5162
5163 borrowed.note_pending_ws(9, 11);
5164 borrowed.discard_pending_ws();
5165 assert!(matches!(
5166 borrowed,
5167 super::FlowScalarBuf::Borrowed {
5168 end: 8,
5169 pending_ws_start: None,
5170 pending_ws_end: 8,
5171 ..
5172 }
5173 ));
5174 assert!(borrowed.as_owned_mut().is_none());
5175
5176 let mut owned = super::FlowScalarBuf::new_owned();
5177 owned.as_owned_mut().unwrap().push_str("owned");
5178 assert!(matches!(owned, super::FlowScalarBuf::Owned(ref s) if s == "owned"));
5179 }
5180
5181 fn first_scanner_error_kind(input: &str) -> ErrorKind {
5182 first_scanner_error(input).kind().clone()
5183 }
5184
5185 fn first_buffered_scanner_error_kind(input: &str) -> ErrorKind {
5186 let mut scanner = Scanner::new(BufferedInput::new(input.chars()));
5187 loop {
5188 match scanner.next_token() {
5189 Ok(Some(_)) => {}
5190 Ok(None) => panic!("expected scanner error"),
5191 Err(error) => return error.kind().clone(),
5192 }
5193 }
5194 }
5195
5196 fn first_scanner_error(input: &str) -> ScanError {
5197 let mut scanner = Scanner::new(StrInput::new(input));
5198 loop {
5199 match scanner.next_token() {
5200 Ok(Some(_)) => {}
5201 Ok(None) => panic!("expected scanner error"),
5202 Err(error) => return error,
5203 }
5204 }
5205 }
5206
5207 fn first_scalar_value(input: &str) -> String {
5208 let mut scanner = Scanner::new(StrInput::new(input));
5209 loop {
5210 match scanner.next_token().expect("scanner should not error") {
5211 Some(Token(_, TokenType::Scalar(_, value))) => return value.into_owned(),
5212 Some(_) => {}
5213 None => panic!("expected scalar token"),
5214 }
5215 }
5216 }
5217
5218 fn first_buffered_scalar_value(input: &str) -> String {
5219 let mut scanner = Scanner::new(BufferedInput::new(input.chars()));
5220 loop {
5221 match scanner.next_token().expect("scanner should not error") {
5222 Some(Token(_, TokenType::Scalar(_, value))) => return value.into_owned(),
5223 Some(_) => {}
5224 None => panic!("expected scalar token"),
5225 }
5226 }
5227 }
5228
5229 #[test]
5230 fn iterator_next_emits_error_and_then_stays_empty() {
5231 let mut scanner = Scanner::new(StrInput::new("\"unterminated"));
5232
5233 let error = scanner
5234 .by_ref()
5235 .find_map(Result::err)
5236 .expect("scanner should emit the error");
5237 assert_eq!(error.kind(), &ErrorKind::UnclosedQuotedScalar);
5238 assert!(scanner.next().is_none());
5239 }
5240
5241 #[test]
5242 fn next_token_returns_none_after_stream_end() {
5243 let mut scanner = Scanner::new(StrInput::new(""));
5244
5245 while let Some(token) = scanner.next_token().unwrap() {
5246 if matches!(token.1, TokenType::StreamEnd) {
5247 break;
5248 }
5249 }
5250
5251 assert!(scanner.stream_started());
5252 assert!(scanner.stream_ended());
5253 assert!(scanner.next_token().unwrap().is_none());
5254 }
5255
5256 #[test]
5257 fn directive_name_must_be_present() {
5258 assert_eq!(
5259 first_scanner_error_kind("%\n"),
5260 ErrorKind::MissingDirectiveName
5261 );
5262 }
5263
5264 #[test]
5265 fn yaml_directive_requires_dot_between_version_numbers() {
5266 assert_eq!(
5267 first_scanner_error_kind("%YAML 1\n"),
5268 ErrorKind::MissingYamlVersionSeparator
5269 );
5270 }
5271
5272 #[test]
5273 fn yaml_directive_requires_major_version_number() {
5274 assert_eq!(
5275 first_scanner_error_kind("%YAML .2\n"),
5276 ErrorKind::MissingYamlVersion
5277 );
5278 }
5279
5280 #[test]
5281 fn yaml_directive_rejects_extremely_long_version_number() {
5282 assert_eq!(
5283 first_scanner_error_kind("%YAML 1234567890.2\n"),
5284 ErrorKind::YamlVersionTooLong
5285 );
5286 }
5287
5288 #[test]
5289 fn tag_directive_handle_must_end_with_bang() {
5290 assert_eq!(
5291 first_scanner_error_kind("%TAG !bad tag:example.com,2024:\n"),
5292 ErrorKind::ExpectedTagDirectiveBang
5293 );
5294 }
5295
5296 #[test]
5297 fn tag_directive_handle_must_start_with_bang() {
5298 assert_eq!(
5299 first_scanner_error_kind("%TAG bad! tag:example.com,2024:\n"),
5300 ErrorKind::ExpectedTagBang
5301 );
5302 assert_eq!(
5303 first_buffered_scanner_error_kind("%TAG bad! tag:example.com,2024:\n"),
5304 ErrorKind::ExpectedTagBang
5305 );
5306 }
5307
5308 #[test]
5309 fn tag_directive_prefix_must_start_with_tag_character() {
5310 assert_eq!(
5311 first_scanner_error_kind("%TAG !e! `bad\n"),
5312 ErrorKind::InvalidGlobalTagCharacter
5313 );
5314 }
5315
5316 #[test]
5317 fn tag_directive_prefix_must_end_before_invalid_content() {
5318 assert_eq!(
5319 first_scanner_error_kind("%TAG !e! tag:example.com^suffix\n"),
5320 ErrorKind::InvalidTagDirectiveTerminator
5321 );
5322 }
5323
5324 #[test]
5325 fn tag_directive_prefix_with_uri_escape_is_owned_and_decoded() {
5326 let mut scanner =
5327 Scanner::new(StrInput::new("%TAG !e! tag:example.com,2024:some%20app/\n"));
5328
5329 loop {
5330 let token = scanner
5331 .next_token()
5332 .expect("valid directive should scan")
5333 .expect("scanner must produce a directive token");
5334 if let TokenType::TagDirective(handle, prefix) = token.1 {
5335 assert!(matches!(handle, Cow::Borrowed("!e!")));
5336 assert!(matches!(prefix, Cow::Owned(_)));
5337 assert_eq!(&*prefix, "tag:example.com,2024:some app/");
5338 break;
5339 }
5340 }
5341 }
5342
5343 #[test]
5344 fn bare_bang_tag_scans_as_non_specific_tag() {
5345 let mut scanner = Scanner::new(StrInput::new("! foo\n"));
5346
5347 loop {
5348 let token = scanner
5349 .next_token()
5350 .expect("valid tag should scan")
5351 .expect("scanner must produce a tag token");
5352 if let TokenType::Tag(handle, suffix) = token.1 {
5353 assert_eq!(&*handle, "");
5354 assert_eq!(&*suffix, "!");
5355 break;
5356 }
5357 }
5358 }
5359
5360 #[test]
5361 fn tag_requires_separation_after_suffix() {
5362 assert_eq!(
5363 first_scanner_error_kind("!foo,bar\n"),
5364 ErrorKind::InvalidTagTerminator
5365 );
5366 }
5367
5368 #[test]
5369 fn verbatim_tag_requires_uri() {
5370 assert_eq!(
5371 first_scanner_error_kind("!<> foo\n"),
5372 ErrorKind::MissingTagUri
5373 );
5374 }
5375
5376 #[test]
5377 fn verbatim_tag_requires_closing_angle_bracket() {
5378 assert_eq!(
5379 first_scanner_error_kind("!<tag:yaml.org,2002:str foo\n"),
5380 ErrorKind::UnclosedVerbatimTag
5381 );
5382 }
5383
5384 #[test]
5385 fn tag_uri_escape_requires_hex_digits() {
5386 assert_eq!(
5387 first_scanner_error_kind("!!bad%zz foo\n"),
5388 ErrorKind::InvalidTagEscape
5389 );
5390 }
5391
5392 #[test]
5393 fn tag_uri_escape_rejects_bad_leading_utf8_byte() {
5394 assert_eq!(
5395 first_scanner_error_kind("!!bad%80 foo\n"),
5396 ErrorKind::InvalidTagUtf8LeadingByte
5397 );
5398 }
5399
5400 #[test]
5401 fn tag_uri_escape_rejects_bad_trailing_utf8_byte() {
5402 assert_eq!(
5403 first_scanner_error_kind("!!bad%C2%41 foo\n"),
5404 ErrorKind::InvalidTagUtf8TrailingByte
5405 );
5406 }
5407
5408 #[test]
5409 fn tag_uri_escape_rejects_invalid_utf8_codepoint() {
5410 assert_eq!(
5411 first_scanner_error_kind("!!bad%F4%90%80%80 foo\n"),
5412 ErrorKind::InvalidTagUtf8
5413 );
5414 }
5415
5416 #[test]
5417 fn anchors_and_aliases_require_names() {
5418 assert_eq!(
5419 first_scanner_error_kind("& \n"),
5420 ErrorKind::MissingAnchorOrAliasName
5421 );
5422 assert_eq!(
5423 first_scanner_error_kind("* \n"),
5424 ErrorKind::MissingAnchorOrAliasName
5425 );
5426 }
5427
5428 #[test]
5429 fn document_end_marker_rejects_trailing_content() {
5430 assert_eq!(
5431 first_scanner_error_kind("... trailing\n"),
5432 ErrorKind::InvalidDocumentEnd
5433 );
5434 }
5435
5436 #[test]
5437 fn reserved_indicators_are_rejected_outside_directives() {
5438 let error = first_scanner_error(" @\n");
5439
5440 assert_eq!(
5441 error.kind(),
5442 &ErrorKind::UnexpectedCharacter { character: '@' }
5443 );
5444 }
5445
5446 #[test]
5447 fn flow_block_entry_indicator_is_rejected() {
5448 assert_eq!(
5449 first_scanner_error_kind("[- ]\n"),
5450 ErrorKind::BlockEntryInFlowCollection
5451 );
5452 }
5453
5454 #[test]
5455 fn block_entry_after_tabbed_separator_reports_specific_error() {
5456 assert_eq!(
5457 first_scanner_error_kind("-\t- value\n"),
5458 ErrorKind::InvalidBlockEntryWhitespace
5459 );
5460 }
5461
5462 #[test]
5463 fn document_indicator_reports_unclosed_flow_collection() {
5464 let error = first_scanner_error("[\n---\n");
5465
5466 assert_eq!(
5467 error.kind(),
5468 &ErrorKind::UnclosedFlowCollection { open: '[' }
5469 );
5470 }
5471
5472 #[test]
5473 fn block_scalar_header_rejects_trailing_content() {
5474 assert_eq!(
5475 first_scanner_error_kind("|+ trailing\n"),
5476 ErrorKind::InvalidBlockScalarHeader
5477 );
5478 }
5479
5480 #[test]
5481 fn block_scalar_rejects_zero_indent_indicator() {
5482 assert_eq!(
5483 first_scanner_error_kind("|0\n"),
5484 ErrorKind::ZeroBlockScalarIndent
5485 );
5486 assert_eq!(
5487 first_scanner_error_kind("|+0\n"),
5488 ErrorKind::ZeroBlockScalarIndent
5489 );
5490 }
5491
5492 #[test]
5493 fn empty_block_scalar_at_eof_honors_chomping() {
5494 assert_eq!(first_scalar_value("|\n"), "");
5495 assert_eq!(first_scalar_value("|-\n"), "");
5496 assert_eq!(first_scalar_value("|+\n"), "");
5497 assert_eq!(first_scalar_value("|+\n\n"), "\n");
5498 assert_eq!(first_scalar_value("|+\n "), "\n");
5499 }
5500
5501 #[test]
5502 fn buffered_block_scalar_reads_content_past_lookahead_window() {
5503 assert_eq!(
5504 first_buffered_scalar_value("|\n abcdefghijklmnopqrstuvwxyz\n"),
5505 "abcdefghijklmnopqrstuvwxyz\n"
5506 );
5507 }
5508
5509 #[test]
5510 fn explicit_indent_block_scalar_can_end_at_document_marker() {
5511 assert_eq!(first_scalar_value("|1\n...\n"), "");
5512 }
5513
5514 #[test]
5515 fn root_explicit_indent_block_scalar_rejects_underindented_content() {
5516 assert_eq!(
5517 first_scanner_error_kind("|2\nx\n"),
5518 ErrorKind::InvalidBlockScalarIndent
5519 );
5520 }
5521
5522 #[test]
5523 fn quoted_scalar_rejects_document_indicator_at_line_start() {
5524 assert_eq!(
5525 first_scanner_error_kind("\"one\n---\ntwo\"\n"),
5526 ErrorKind::DocumentIndicatorInQuotedScalar
5527 );
5528 }
5529
5530 #[test]
5531 fn quoted_scalar_rejects_tab_indentation_after_line_break() {
5532 assert_eq!(
5533 first_scanner_error_kind("a: \"one\n\tbad\"\n"),
5534 ErrorKind::TabInIndentation
5535 );
5536 }
5537
5538 #[test]
5539 fn quoted_scalar_rejects_underindented_continuation() {
5540 assert_eq!(
5541 first_scanner_error_kind("a: \"one\nbad\"\n"),
5542 ErrorKind::InvalidQuotedScalarIndent
5543 );
5544 }
5545
5546 #[test]
5547 fn quoted_scalar_trailing_content_error_names_quote_style() {
5548 assert_eq!(
5549 first_scanner_error_kind("'foo' trailing\n"),
5550 ErrorKind::InvalidTrailingSingleQuotedScalar
5551 );
5552 assert_eq!(
5553 first_scanner_error_kind("\"foo\" trailing\n"),
5554 ErrorKind::InvalidTrailingDoubleQuotedScalar
5555 );
5556 }
5557
5558 #[test]
5559 fn quoted_scalar_escape_errors_cover_hex_and_surrogate_edges() {
5560 assert_eq!(
5561 first_scanner_error_kind("\"\\xG0\"\n"),
5562 ErrorKind::InvalidQuotedScalarHexEscape
5563 );
5564 assert_eq!(
5565 first_scanner_error_kind("\"\\uD800\\uGGGG\"\n"),
5566 ErrorKind::InvalidLowSurrogateHexEscape
5567 );
5568 assert_eq!(
5569 first_scanner_error_kind("\"\\uD800\\u0041\"\n"),
5570 ErrorKind::InvalidLowSurrogate
5571 );
5572 assert_eq!(
5573 first_scanner_error_kind("\"\\U00110000\"\n"),
5574 ErrorKind::InvalidUnicodeEscape
5575 );
5576 }
5577
5578 #[test]
5579 fn indented_flow_scalar_reports_invalid_indentation() {
5580 assert_eq!(
5581 first_scanner_error_kind("a:\n [\nfoo]\n"),
5582 ErrorKind::InvalidIndentation
5583 );
5584 }
5585
5586 #[test]
5587 fn required_simple_key_requires_value_at_stream_end() {
5588 let error = first_scanner_error("a:\n&b\n- c\n");
5589
5590 assert_eq!(error.kind(), &ErrorKind::SimpleKeyExpected);
5591 assert_eq!(error.marker().index(), 3);
5592 assert_eq!(error.marker().line(), 2);
5593 assert_eq!(error.marker().col(), 0);
5594 }
5595
5596 #[test]
5597 fn plain_scalar_rejects_dash_before_flow_indicator() {
5598 assert_eq!(
5599 first_scanner_error_kind("[-]\n"),
5600 ErrorKind::PlainScalarStartsWithDashFlowIndicator
5601 );
5602 }
5603
5604 #[test]
5605 fn explicit_key_rejects_tab_after_indicator() {
5606 assert_eq!(
5607 first_scanner_error_kind("? \tfoo\n"),
5608 ErrorKind::TabNotAllowed
5609 );
5610 }
5611
5612 #[test]
5613 fn flow_mapping_rejects_adjacent_collection_value_after_plain_key() {
5614 assert_eq!(
5615 first_scanner_error_kind("[a:[]]\n"),
5616 ErrorKind::FlowMappingValueAdjacentCollection
5617 );
5618 }
5619
5620 #[test]
5621 fn implicit_flow_mapping_colon_cannot_move_to_next_line() {
5622 assert_eq!(
5623 first_scanner_error_kind("[foo\n: bar]\n"),
5624 ErrorKind::InvalidColonPlacement
5625 );
5626 }
5627
5628 #[test]
5629 fn invalid_simple_key_token_positions_are_scan_errors() {
5630 for (tokens_parsed, token_number) in [(1, 0), (0, 1)] {
5631 let mut scanner = Scanner::new(StrInput::new(": value\n"));
5632 scanner.fetch_stream_start();
5633 scanner.tokens.clear();
5634 scanner.tokens_parsed = tokens_parsed;
5635
5636 let simple_key = scanner
5637 .simple_keys
5638 .last_mut()
5639 .expect("stream start should create a simple key slot");
5640 simple_key.possible = true;
5641 simple_key.token_number = token_number;
5642
5643 let error = scanner
5644 .fetch_value()
5645 .expect_err("invalid simple key position should be reported as a scan error");
5646 assert_eq!(error.kind(), &ErrorKind::InvalidSimpleKey);
5647 assert_eq!(error.marker(), &Marker::new(0, 1, 0));
5648 }
5649 }
5650
5651 #[test]
5652 fn issue14_alias_scanner_consumes_colon_as_name_character() {
5653 let mut scanner = Scanner::new(StrInput::new("*foo: bar\n"));
5654
5655 assert!(matches!(
5656 scanner.next_token().unwrap().unwrap().1,
5657 TokenType::StreamStart
5658 ));
5659
5660 let token = scanner.next_token().unwrap().unwrap();
5661
5662 assert!(
5663 matches!(token.1, TokenType::Alias(ref name) if name.as_ref() == "foo:"),
5664 "expected `*foo: bar` to start with Alias(\"foo:\"), got {token:?}"
5665 );
5666 }
5667
5668 #[test]
5669 fn issue14_anchor_scanner_consumes_colon_as_name_character() {
5670 let mut scanner = Scanner::new(StrInput::new("&foo: bar\n"));
5671
5672 assert!(matches!(
5673 scanner.next_token().unwrap().unwrap().1,
5674 TokenType::StreamStart
5675 ));
5676
5677 let token = scanner.next_token().unwrap().unwrap();
5678
5679 assert!(
5680 matches!(token.1, TokenType::Anchor(ref name) if name.as_ref() == "foo:"),
5681 "expected `&foo: bar` to start with Anchor(\"foo:\"), got {token:?}"
5682 );
5683 }
5684}