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, is_anchor_char, is_blank_or_breakz, is_bom, is_break, is_breakz, is_flow, is_hex,
23 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) = string.chars().find(|&character| !is_printable(character)) {
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) = scalar_text
3806 .chars()
3807 .find(|&character| !is_printable(character))
3808 {
3809 return Err(ScanError::from_kind(
3810 start_mark,
3811 ErrorKind::UnexpectedCharacter { character },
3812 ));
3813 }
3814 self.ensure_current_char_is_printable()?;
3815
3816 if has_content {
3817 let contents = if let Some(slice) = borrowed_contents {
3818 Cow::Borrowed(slice)
3819 } else {
3820 Cow::Owned(string.expect("owned plain scalar has an output buffer"))
3821 };
3822
3823 Ok(Token(
3824 Span::new(start_mark, end_mark),
3825 TokenType::Scalar(ScalarStyle::Plain, contents),
3826 ))
3827 } else {
3828 Err(ScanError::from_kind(
3832 start_mark,
3833 ErrorKind::UnexpectedEndOfPlainScalar,
3834 ))
3835 }
3836 }
3837
3838 fn fetch_key(&mut self) -> ScanResult {
3839 let start_mark = self.mark;
3840 if self.flow_level == 0 {
3841 if !self.simple_key_allowed {
3843 return Err(self.scan_error(ErrorKind::MappingKeyNotAllowed));
3844 }
3845 self.roll_indent(
3846 start_mark.col,
3847 None,
3848 TokenType::BlockMappingStart,
3849 start_mark,
3850 );
3851 } else {
3852 self.set_current_flow_mapping_started(true);
3854 }
3855
3856 self.remove_simple_key()?;
3857
3858 if self.flow_level == 0 {
3859 self.allow_simple_key();
3860 } else {
3861 self.disallow_simple_key();
3862 }
3863
3864 self.skip_non_blank();
3865 let end_mark = self.mark;
3866 let token_index = self.tokens.len();
3867 self.explicit_key_tab_check_pending = false;
3868 let stopped_after_comment = self.skip_yaml_whitespace(true)?;
3869 if self.input.peek() == '\t' {
3870 return Err(self.scan_error(ErrorKind::TabNotAllowed));
3871 }
3872 self.explicit_key_tab_check_pending = stopped_after_comment;
3873 self.insert_token(
3874 token_index,
3875 Token(Span::new(start_mark, end_mark), TokenType::Key),
3876 );
3877 Ok(())
3878 }
3879
3880 fn fetch_flow_value(&mut self) -> ScanResult {
3888 let nc = self.input.peek_nth(1);
3889
3890 if self.mark.index() != self.adjacent_value_allowed_at && (nc == '[' || nc == '{') {
3902 return Err(self.scan_error(ErrorKind::FlowMappingValueAdjacentCollection));
3903 }
3904
3905 self.fetch_value()
3906 }
3907
3908 fn fetch_value(&mut self) -> ScanResult {
3910 let sk = *self.simple_keys.last().unwrap();
3911 let start_mark = self.mark;
3912 let is_implicit_flow_mapping = self.current_flow_collection_is_sequence()
3913 && !self.current_flow_mapping_started()
3914 && !self.implicit_flow_mapping_states.is_empty();
3915 if is_implicit_flow_mapping {
3916 *self.implicit_flow_mapping_states.last_mut().unwrap() =
3917 ImplicitMappingState::Inside(self.flow_level);
3918 }
3919
3920 self.skip_non_blank();
3922 let mut trailing_tokens = VecDeque::new();
3929 if self.input.look_ch() == '\t' {
3930 let trailing_token_index = self.tokens.len();
3931 let whitespace = self.skip_ws_to_eol(SkipTabs::Yes)?;
3932 trailing_tokens = self.tokens.split_off(trailing_token_index);
3933
3934 if !whitespace.has_valid_yaml_ws()
3935 && (self.input.peek() == '-' || self.input.next_is_alpha())
3936 {
3937 return Err(self.scan_error(ErrorKind::InvalidMappingValueWhitespace));
3938 }
3939 }
3940
3941 if sk.possible {
3942 let token_index = self.simple_key_token_index(&sk, start_mark)?;
3943 let tok = Token(Span::empty(sk.mark), TokenType::Key);
3945 self.insert_token(token_index, tok);
3946 if is_implicit_flow_mapping {
3947 if sk.mark.line < start_mark.line {
3948 return Err(ScanError::from_kind(
3949 start_mark,
3950 ErrorKind::InvalidColonPlacement,
3951 ));
3952 }
3953 self.insert_token(
3954 token_index,
3955 Token(Span::empty(sk.mark), TokenType::FlowMappingStart),
3956 );
3957 }
3958
3959 self.roll_indent(
3961 sk.mark.col,
3962 Some(sk.token_number),
3963 TokenType::BlockMappingStart,
3964 sk.mark,
3965 );
3966 self.roll_one_col_indent();
3967
3968 self.simple_keys.last_mut().unwrap().possible = false;
3969 self.disallow_simple_key();
3970 } else {
3971 if is_implicit_flow_mapping {
3972 self.tokens
3973 .push_back(Token(Span::empty(start_mark), TokenType::FlowMappingStart).into());
3974 }
3975 if self.flow_level == 0 {
3977 if !self.simple_key_allowed {
3978 return Err(ScanError::from_kind(
3979 start_mark,
3980 ErrorKind::MappingValueNotAllowed,
3981 ));
3982 }
3983
3984 self.roll_indent(
3985 start_mark.col,
3986 None,
3987 TokenType::BlockMappingStart,
3988 start_mark,
3989 );
3990 }
3991 self.roll_one_col_indent();
3992
3993 if self.flow_level == 0 {
3994 self.allow_simple_key();
3995 } else {
3996 self.disallow_simple_key();
3997 }
3998 }
3999 self.tokens
4000 .push_back(Token(Span::empty(start_mark), TokenType::Value).into());
4001 self.tokens.append(&mut trailing_tokens);
4002
4003 Ok(())
4004 }
4005
4006 fn roll_indent(
4012 &mut self,
4013 col: usize,
4014 number: Option<usize>,
4015 tok: TokenType<'input>,
4016 mark: Marker,
4017 ) {
4018 if self.flow_level > 0 {
4019 return;
4020 }
4021
4022 if self.indent <= col as isize {
4026 if let Some(indent) = self.indents.last() {
4027 if !indent.needs_block_end {
4028 self.indent = indent.indent;
4029 self.indents.pop();
4030 }
4031 }
4032 }
4033
4034 if self.indent < col as isize {
4035 self.indents.push(Indent {
4036 indent: self.indent,
4037 needs_block_end: true,
4038 });
4039 self.indent = col as isize;
4040 let tokens_parsed = self.tokens_parsed;
4041 match number {
4042 Some(n) => self.insert_token(n - tokens_parsed, Token(Span::empty(mark), tok)),
4043 None => self.tokens.push_back(Token(Span::empty(mark), tok).into()),
4044 }
4045 }
4046 }
4047
4048 fn unroll_indent(&mut self, col: isize) {
4054 if self.flow_level > 0 {
4055 return;
4056 }
4057 while self.indent > col {
4058 let indent = self.indents.pop().unwrap();
4059 self.indent = indent.indent;
4060 if indent.needs_block_end {
4061 self.tokens
4062 .push_back(Token(Span::empty(self.mark), TokenType::BlockEnd).into());
4063 }
4064 }
4065 }
4066
4067 fn roll_one_col_indent(&mut self) {
4073 if self.flow_level == 0 && self.indents.last().is_some_and(|x| x.needs_block_end) {
4074 self.indents.push(Indent {
4075 indent: self.indent,
4076 needs_block_end: false,
4077 });
4078 self.indent += 1;
4079 }
4080 }
4081
4082 fn unroll_non_block_indents(&mut self) {
4084 while let Some(indent) = self.indents.last() {
4085 if indent.needs_block_end {
4086 break;
4087 }
4088 self.indent = indent.indent;
4089 self.indents.pop();
4090 }
4091 }
4092
4093 fn save_simple_key(&mut self) {
4095 if self.simple_key_allowed {
4096 let required = self.flow_level == 0
4097 && self.indent == (self.mark.col as isize)
4098 && self.indents.last().unwrap().needs_block_end;
4099
4100 if let Some(last) = self.simple_keys.last_mut() {
4101 *last = SimpleKey {
4102 mark: self.mark,
4103 possible: true,
4104 required,
4105 token_number: self.tokens_parsed + self.tokens.len(),
4106 };
4107 }
4108 }
4109 }
4110
4111 fn remove_simple_key(&mut self) -> ScanResult {
4112 let last = self.simple_keys.last_mut().unwrap();
4113 if last.possible && last.required {
4114 return Err(Self::simple_key_expected(last.mark));
4115 }
4116
4117 last.possible = false;
4118 Ok(())
4119 }
4120
4121 fn is_within_block(&self) -> bool {
4123 !self.indents.is_empty()
4124 }
4125
4126 fn end_implicit_mapping(&mut self, mark: Marker, flow_level: u8) {
4132 if self
4133 .implicit_flow_mapping_states
4134 .last()
4135 .is_some_and(|state| *state == ImplicitMappingState::Inside(flow_level))
4136 {
4137 *self.implicit_flow_mapping_states.last_mut().unwrap() = ImplicitMappingState::Possible;
4138 self.set_current_flow_mapping_started(false);
4139 self.tokens
4140 .push_back(Token(Span::empty(mark), TokenType::FlowMappingEnd).into());
4141 }
4142 }
4143
4144 fn current_flow_collection_is_sequence(&self) -> bool {
4145 self.flow_markers
4146 .last()
4147 .is_some_and(|(_, bracket)| *bracket == '[')
4148 }
4149
4150 fn current_flow_mapping_started(&self) -> bool {
4151 self.flow_mapping_started.last().copied().unwrap_or(false)
4152 }
4153
4154 fn set_current_flow_mapping_started(&mut self, started: bool) {
4155 if let Some(current) = self.flow_mapping_started.last_mut() {
4156 *current = started;
4157 }
4158 }
4159}
4160
4161#[derive(PartialEq, Eq)]
4165enum Chomping {
4166 Strip,
4168 Clip,
4170 Keep,
4172}
4173
4174#[cfg(test)]
4175mod test {
4176 use alloc::{
4177 borrow::{Cow, ToOwned},
4178 rc::Rc,
4179 string::String,
4180 vec,
4181 vec::Vec,
4182 };
4183 use core::cell::Cell;
4184
4185 use crate::error::{ErrorKind, ScanError};
4186 use crate::{
4187 input::{str::StrInput, BorrowedInput, BufferedInput, Input},
4188 scanner::{
4189 Comment, Marker, Placement, QueuedToken, QueuedTokenType, ScalarStyle, Scanner, Span,
4190 Token, TokenType,
4191 },
4192 };
4193
4194 struct CountingChars {
4195 chars: alloc::vec::IntoIter<char>,
4196 read: Rc<Cell<usize>>,
4197 }
4198
4199 impl Iterator for CountingChars {
4200 type Item = char;
4201
4202 fn next(&mut self) -> Option<Self::Item> {
4203 let next = self.chars.next();
4204 if next.is_some() {
4205 self.read.set(self.read.get() + 1);
4206 }
4207 next
4208 }
4209 }
4210
4211 struct SlicingOnlyInput<'input> {
4212 inner: StrInput<'input>,
4213 expose_slice: bool,
4214 }
4215
4216 impl<'input> SlicingOnlyInput<'input> {
4217 fn new(source: &'input str, expose_slice: bool) -> Self {
4218 Self {
4219 inner: StrInput::new(source),
4220 expose_slice,
4221 }
4222 }
4223 }
4224
4225 impl Input for SlicingOnlyInput<'_> {
4226 fn lookahead(&mut self, count: usize) {
4227 self.inner.lookahead(count);
4228 }
4229
4230 fn buflen(&self) -> usize {
4231 self.inner.buflen()
4232 }
4233
4234 fn bufmaxlen(&self) -> usize {
4235 self.inner.bufmaxlen()
4236 }
4237
4238 fn raw_read_ch(&mut self) -> char {
4239 self.inner.raw_read_ch()
4240 }
4241
4242 fn raw_read_non_breakz_ch(&mut self) -> Option<char> {
4243 self.inner.raw_read_non_breakz_ch()
4244 }
4245
4246 fn skip(&mut self) {
4247 self.inner.skip();
4248 }
4249
4250 fn skip_n(&mut self, count: usize) {
4251 self.inner.skip_n(count);
4252 }
4253
4254 fn peek(&self) -> char {
4255 self.inner.peek()
4256 }
4257
4258 fn peek_nth(&self, n: usize) -> char {
4259 self.inner.peek_nth(n)
4260 }
4261
4262 fn byte_offset(&self) -> Option<usize> {
4263 self.inner.byte_offset()
4264 }
4265
4266 fn slice_bytes(&self, start: usize, end: usize) -> Option<&str> {
4267 if self.expose_slice {
4268 self.inner.slice_bytes(start, end)
4269 } else {
4270 None
4271 }
4272 }
4273 }
4274
4275 impl<'input> BorrowedInput<'input> for SlicingOnlyInput<'input> {
4276 fn slice_borrowed(&self, _start: usize, _end: usize) -> Option<&'input str> {
4277 None
4278 }
4279 }
4280
4281 struct SmallReportedBufferInput<'input> {
4282 inner: StrInput<'input>,
4283 reported_bufmaxlen: usize,
4284 }
4285
4286 impl<'input> SmallReportedBufferInput<'input> {
4287 fn new(source: &'input str, reported_bufmaxlen: usize) -> Self {
4288 Self {
4289 inner: StrInput::new(source),
4290 reported_bufmaxlen,
4291 }
4292 }
4293 }
4294
4295 impl Input for SmallReportedBufferInput<'_> {
4296 fn lookahead(&mut self, count: usize) {
4297 self.inner.lookahead(count);
4298 }
4299
4300 fn buflen(&self) -> usize {
4301 self.inner.buflen()
4302 }
4303
4304 fn bufmaxlen(&self) -> usize {
4305 self.reported_bufmaxlen
4306 }
4307
4308 fn raw_read_ch(&mut self) -> char {
4309 self.inner.raw_read_ch()
4310 }
4311
4312 fn raw_read_non_breakz_ch(&mut self) -> Option<char> {
4313 self.inner.raw_read_non_breakz_ch()
4314 }
4315
4316 fn skip(&mut self) {
4317 self.inner.skip();
4318 }
4319
4320 fn skip_n(&mut self, count: usize) {
4321 self.inner.skip_n(count);
4322 }
4323
4324 fn peek(&self) -> char {
4325 self.inner.peek()
4326 }
4327
4328 fn peek_nth(&self, n: usize) -> char {
4329 self.inner.peek_nth(n)
4330 }
4331 }
4332
4333 impl<'input> BorrowedInput<'input> for SmallReportedBufferInput<'input> {
4334 fn slice_borrowed(&self, start: usize, end: usize) -> Option<&'input str> {
4335 self.inner.slice_borrowed(start, end)
4336 }
4337 }
4338
4339 #[test]
4340 fn anchor_character_set_allows_colon_and_rejects_flow_indicators() {
4341 use super::is_anchor_char;
4342
4343 assert!(is_anchor_char('x'));
4344 assert!(is_anchor_char('-'));
4345 assert!(is_anchor_char('_'));
4346 assert!(is_anchor_char(':'));
4347 assert!(is_anchor_char('#'));
4348 assert!(is_anchor_char('/'));
4349 assert!(is_anchor_char('?'));
4350
4351 for c in [',', '[', ']', '{', '}', ' ', '\t', '\n', '\r', '\0'] {
4352 assert!(
4353 !is_anchor_char(c),
4354 "character {c:?} must not be accepted in anchor/alias names"
4355 );
4356 }
4357 }
4358
4359 #[test]
4360 fn flow_simple_key_length_limit_bounds_buffering() {
4361 let mut yaml = String::from("[\n\"start\"\n");
4362 for _ in 0..600 {
4363 yaml.push_str("\"x\"\n");
4364 }
4365 let total_chars = yaml.chars().count();
4366 let read = Rc::new(Cell::new(0));
4367 let chars = yaml.chars().collect::<Vec<_>>().into_iter();
4368 let mut scanner = Scanner::new(BufferedInput::new(CountingChars {
4369 chars,
4370 read: Rc::clone(&read),
4371 }));
4372
4373 assert!(matches!(
4374 scanner.next_token().unwrap().unwrap().1,
4375 TokenType::StreamStart
4376 ));
4377
4378 let token = scanner.next_token().unwrap().unwrap();
4379 assert!(matches!(token.1, TokenType::FlowSequenceStart));
4380
4381 let token = scanner.next_token().unwrap().unwrap();
4382 assert!(matches!(
4383 token.1,
4384 TokenType::Scalar(_, ref value) if value == "start"
4385 ));
4386 assert!(
4387 read.get() < total_chars,
4388 "scanner consumed all {total_chars} chars before yielding the first flow scalar"
4389 );
4390 assert!(
4391 read.get() <= super::SIMPLE_KEY_MAX_LOOKAHEAD + 128,
4392 "scanner read {} chars before yielding the first flow scalar",
4393 read.get()
4394 );
4395 }
4396
4397 #[test]
4398 fn block_scalar_indent_tolerates_small_reported_bufmaxlen() {
4399 let mut scanner = Scanner::new(SmallReportedBufferInput::new("|\n value\n", 0));
4400
4401 let scalar = scanner
4402 .find_map(
4403 |token| match token.expect("valid YAML should scan without errors") {
4404 Token(_, TokenType::Scalar(ScalarStyle::Literal, value)) => {
4405 Some(value.into_owned())
4406 }
4407 _ => None,
4408 },
4409 )
4410 .expect("expected block scalar token");
4411
4412 assert_eq!(scalar, "value\n");
4413 }
4414
4415 #[test]
4416 fn plain_scalar_chunk_tolerates_small_reported_bufmaxlen() {
4417 let mut scanner = Scanner::new(SmallReportedBufferInput::new("plain\n", 0));
4418
4419 let scalar = scanner
4420 .find_map(
4421 |token| match token.expect("valid YAML should scan without errors") {
4422 Token(_, TokenType::Scalar(ScalarStyle::Plain, value)) => {
4423 Some(value.into_owned())
4424 }
4425 _ => None,
4426 },
4427 )
4428 .expect("expected plain scalar token");
4429
4430 assert_eq!(scalar, "plain");
4431 }
4432
4433 fn first_token_slice(
4434 yaml: &str,
4435 matches_token: impl Fn(&TokenType<'_>) -> bool,
4436 ) -> Option<String> {
4437 let mut scanner = Scanner::new(StrInput::new(yaml));
4438
4439 loop {
4440 let token = scanner
4441 .next_token()
4442 .expect("scanner should accept the test YAML")?;
4443 if matches_token(&token.1) {
4444 return token.0.slice(yaml).map(ToOwned::to_owned);
4445 }
4446 }
4447 }
4448
4449 #[test]
4450 fn flow_indicator_token_spans_cover_only_the_indicator() {
4451 assert_eq!(
4452 first_token_slice("[ # c\n a]\n", |token| matches!(
4453 token,
4454 TokenType::FlowSequenceStart
4455 ))
4456 .as_deref(),
4457 Some("[")
4458 );
4459 assert_eq!(
4460 first_token_slice("{ # c\n a: b}\n", |token| matches!(
4461 token,
4462 TokenType::FlowMappingStart
4463 ))
4464 .as_deref(),
4465 Some("{")
4466 );
4467 assert_eq!(
4468 first_token_slice("[a] # c\n", |token| matches!(
4469 token,
4470 TokenType::FlowSequenceEnd
4471 ))
4472 .as_deref(),
4473 Some("]")
4474 );
4475 assert_eq!(
4476 first_token_slice("{a: b} # c\n", |token| matches!(
4477 token,
4478 TokenType::FlowMappingEnd
4479 ))
4480 .as_deref(),
4481 Some("}")
4482 );
4483 assert_eq!(
4484 first_token_slice("[a, # c\nb]\n", |token| matches!(
4485 token,
4486 TokenType::FlowEntry
4487 ))
4488 .as_deref(),
4489 Some(",")
4490 );
4491 }
4492
4493 #[test]
4494 fn explicit_key_token_span_covers_only_the_indicator() {
4495 assert_eq!(
4496 first_token_slice("? # c\n: value\n", |token| matches!(token, TokenType::Key))
4497 .as_deref(),
4498 Some("?")
4499 );
4500 }
4501
4502 #[test]
4503 fn comment_capture_does_not_change_leading_whitespace() {
4504 let mut scanner = Scanner::new(StrInput::new("# comment\n"));
4505
4506 let token = scanner.scan_comment_token().unwrap();
4507
4508 assert!(scanner.leading_whitespace);
4509 assert!(matches!(token.1, TokenType::Comment(ref comment) if comment.text == " comment"));
4510
4511 let mut scanner = Scanner::new(BufferedInput::new("# streaming\n".chars()));
4512 scanner.input.lookahead(1);
4513
4514 let token = scanner.scan_comment_token().unwrap();
4515
4516 assert!(scanner.leading_whitespace);
4517 assert!(matches!(token.1, TokenType::Comment(ref comment) if comment.text == " streaming"));
4518 }
4519
4520 #[test]
4521 fn comment_capture_falls_back_to_owned_slice_when_borrow_unavailable() {
4522 let mut scanner = Scanner::new(SlicingOnlyInput::new("# sliced\n", true));
4523 scanner.input.lookahead(2);
4524 assert_eq!(scanner.input.peek_nth(1), ' ');
4525
4526 let token = scanner.scan_comment_token().unwrap();
4527
4528 assert!(matches!(token.1, TokenType::Comment(ref comment)
4529 if matches!(comment.text, Cow::Owned(ref text) if text == " sliced")));
4530 }
4531
4532 #[test]
4533 fn comment_capture_errors_when_offsets_have_no_slice() {
4534 let mut scanner = Scanner::new(SlicingOnlyInput::new("# broken\n", false));
4535
4536 let error = scanner.scan_comment_token().unwrap_err();
4537
4538 assert_eq!(error.kind(), &ErrorKind::InputOffsetsWithoutSlice);
4539 }
4540
4541 #[test]
4542 fn queued_token_roundtrips_public_token_variants() {
4543 let span = Span::new(Marker::new(0, 1, 0), Marker::new(7, 1, 7));
4544 let tokens = [
4545 Token(span, TokenType::StreamStart),
4546 Token(span, TokenType::StreamEnd),
4547 Token(span, TokenType::VersionDirective(1, 2)),
4548 Token(
4549 span,
4550 TokenType::TagDirective(Cow::Borrowed("!app!"), Cow::Borrowed("tag:app.example,")),
4551 ),
4552 Token(span, TokenType::DocumentStart),
4553 Token(span, TokenType::DocumentEnd),
4554 Token(span, TokenType::BlockSequenceStart),
4555 Token(span, TokenType::BlockMappingStart),
4556 Token(span, TokenType::BlockEnd),
4557 Token(span, TokenType::FlowSequenceStart),
4558 Token(span, TokenType::FlowSequenceEnd),
4559 Token(span, TokenType::FlowMappingStart),
4560 Token(span, TokenType::FlowMappingEnd),
4561 Token(span, TokenType::BlockEntry),
4562 Token(span, TokenType::FlowEntry),
4563 Token(span, TokenType::Key),
4564 Token(span, TokenType::Value),
4565 Token(span, TokenType::Alias(Cow::Borrowed("alias"))),
4566 Token(span, TokenType::Anchor(Cow::Borrowed("anchor"))),
4567 Token(
4568 span,
4569 TokenType::Tag(Cow::Borrowed("!"), Cow::Borrowed("tag")),
4570 ),
4571 Token(
4572 span,
4573 TokenType::Scalar(ScalarStyle::Literal, Cow::Borrowed("scalar")),
4574 ),
4575 Token(
4576 span,
4577 TokenType::Comment(
4578 Comment::new(Cow::Borrowed(" comment")).with_placement(Placement::Right),
4579 ),
4580 ),
4581 Token(
4582 span,
4583 TokenType::ReservedDirective(
4584 "reserved".to_owned(),
4585 vec!["one".to_owned(), "two".to_owned()],
4586 ),
4587 ),
4588 ];
4589
4590 for token in tokens {
4591 let queued: QueuedToken = token.clone().into();
4592
4593 assert_eq!(queued.into_public(), token);
4594 }
4595 }
4596
4597 #[test]
4598 fn comment_skipping_path_consumes_comment_without_tokenizing_it() {
4599 let mut scanner = Scanner::new(StrInput::new("# skipped\nnext: value\n"));
4600
4601 scanner.skip_yaml_whitespace(false).unwrap();
4602
4603 assert!(scanner.tokens.is_empty());
4604 assert_eq!(scanner.mark.line(), 2);
4605 assert_eq!(scanner.mark.col(), 0);
4606 }
4607
4608 #[test]
4609 fn yaml_whitespace_can_stop_after_queued_comment() {
4610 let mut scanner = Scanner::new(StrInput::new(" # queued\n# later\n"));
4611
4612 assert!(scanner.skip_yaml_whitespace(true).unwrap());
4613
4614 assert_eq!(scanner.tokens.len(), 1);
4615 assert!(matches!(
4616 scanner.tokens.front().unwrap().1,
4617 QueuedTokenType::Comment(ref comment) if comment.text == " queued"
4618 ));
4619 assert_eq!(scanner.mark.line(), 1);
4620 assert_eq!(scanner.mark.col(), 9);
4621 }
4622
4623 #[test]
4624 fn token_skip_can_stop_after_queued_comment() {
4625 let mut scanner = Scanner::new(StrInput::new("# first\n# second\n"));
4626
4627 assert!(scanner.skip_to_next_token(true).unwrap());
4628
4629 assert_eq!(scanner.tokens.len(), 1);
4630 assert!(matches!(
4631 scanner.tokens.front().unwrap().1,
4632 QueuedTokenType::Comment(ref comment) if comment.text == " first"
4633 ));
4634 assert_eq!(scanner.mark.line(), 2);
4635 assert_eq!(scanner.mark.col(), 0);
4636 }
4637
4638 #[test]
4639 fn scanner_emits_first_leading_comment_before_scanning_next_comment() {
4640 let mut scanner = Scanner::new(StrInput::new("# first\n# second\nkey: value\n"));
4641
4642 assert!(matches!(
4643 scanner.next_token().unwrap().unwrap().1,
4644 TokenType::StreamStart
4645 ));
4646 assert!(matches!(
4647 scanner.next_token().unwrap().unwrap().1,
4648 TokenType::Comment(ref comment) if comment.text == " first"
4649 ));
4650 assert!(scanner.tokens.is_empty());
4651 assert!(matches!(
4652 scanner.next_token().unwrap().unwrap().1,
4653 TokenType::Comment(ref comment) if comment.text == " second"
4654 ));
4655 }
4656
4657 #[test]
4658 fn scanner_emits_quoted_scalar_comment_before_scanning_following_value() {
4659 let mut scanner = Scanner::new(StrInput::new("\"key\" # quoted\n: value\n"));
4660
4661 assert!(matches!(
4662 scanner.next_token().unwrap().unwrap().1,
4663 TokenType::StreamStart
4664 ));
4665 assert!(matches!(
4666 scanner.next_token().unwrap().unwrap().1,
4667 TokenType::Scalar(ScalarStyle::DoubleQuoted, ref value) if value == "key"
4668 ));
4669 assert!(matches!(
4670 scanner.next_token().unwrap().unwrap().1,
4671 TokenType::Comment(ref comment) if comment.text == " quoted"
4672 ));
4673 }
4674
4675 #[test]
4676 fn flow_scalar_comment_disables_adjacent_value_lookahead() {
4677 let mut scanner = Scanner::new(StrInput::new("\"key\"\n# quoted\n: value\n"));
4678
4679 scanner.fetch_flow_scalar(false).unwrap();
4680
4681 assert_eq!(scanner.adjacent_value_allowed_at, usize::MAX);
4682 assert!(matches!(
4683 scanner.tokens.front().unwrap().1,
4684 QueuedTokenType::Scalar(ScalarStyle::DoubleQuoted, ref value) if value == "key"
4685 ));
4686 assert!(scanner.tokens.iter().any(|QueuedToken(_, token)| matches!(
4687 token,
4688 QueuedTokenType::Comment(comment) if comment.text == " quoted"
4689 )));
4690 }
4691
4692 #[test]
4693 fn deferred_error_waits_for_all_comment_tokens() {
4694 let mut scanner = Scanner::new(StrInput::new("# first\n# second\n@\n"));
4695
4696 assert!(matches!(
4697 scanner.next_token().unwrap().unwrap().1,
4698 TokenType::StreamStart
4699 ));
4700 assert!(matches!(
4701 scanner.next_token().unwrap().unwrap().1,
4702 TokenType::Comment(ref comment) if comment.text == " first"
4703 ));
4704 assert!(matches!(
4705 scanner.next_token().unwrap().unwrap().1,
4706 TokenType::Comment(ref comment) if comment.text == " second"
4707 ));
4708
4709 let error = scanner.next_token().unwrap_err();
4710
4711 assert_eq!(
4712 error.kind(),
4713 &ErrorKind::UnexpectedCharacter { character: '@' }
4714 );
4715 }
4716
4717 #[test]
4719 fn anchor_name_is_borrowed_for_str_input() {
4720 let mut scanner = Scanner::new(StrInput::new("&anch\n"));
4721
4722 loop {
4723 let tok = scanner
4724 .next_token()
4725 .expect("valid YAML must scan without errors")
4726 .expect("scanner must eventually produce a token");
4727 if let TokenType::Anchor(name) = tok.1 {
4728 assert!(matches!(name, Cow::Borrowed("anch")));
4729 break;
4730 }
4731 }
4732 }
4733
4734 #[test]
4735 fn anchor_name_rejects_non_printable_control_chars() {
4736 let mut scanner = Scanner::new(StrInput::new("&foo\u{0001}\n"));
4737
4738 scanner.next_token().unwrap();
4739 assert_eq!(
4740 scanner.next_token().unwrap_err().kind(),
4741 &ErrorKind::UnexpectedCharacter {
4742 character: '\u{0001}'
4743 }
4744 );
4745 }
4746
4747 #[test]
4748 fn alias_name_rejects_non_printable_control_chars() {
4749 let mut scanner = Scanner::new(StrInput::new("*foo\u{0001}\n"));
4750
4751 scanner.next_token().unwrap();
4752 assert_eq!(
4753 scanner.next_token().unwrap_err().kind(),
4754 &ErrorKind::UnexpectedCharacter {
4755 character: '\u{0001}'
4756 }
4757 );
4758 }
4759
4760 #[test]
4761 fn alias_name_is_borrowed_for_str_input() {
4762 let mut scanner = Scanner::new(StrInput::new("*anch\n"));
4763
4764 loop {
4765 let tok = scanner
4766 .next_token()
4767 .expect("valid YAML must scan without errors")
4768 .expect("scanner must eventually produce a token");
4769 if let TokenType::Alias(name) = tok.1 {
4770 assert!(matches!(name, Cow::Borrowed("anch")));
4771 break;
4772 }
4773 }
4774 }
4775
4776 #[test]
4777 fn alias_name_scans_colon_as_part_of_name() {
4778 let mut scanner = Scanner::new(StrInput::new("*foo: bar\n"));
4779
4780 loop {
4781 let tok = scanner
4782 .next_token()
4783 .expect("scanner must not fail before alias token")
4784 .expect("scanner must eventually emit an alias token");
4785
4786 if let TokenType::Alias(name) = tok.1 {
4787 assert_eq!(name.as_ref(), "foo:");
4788 break;
4789 }
4790 }
4791 }
4792
4793 #[test]
4794 fn anchor_name_scans_colon_as_part_of_name() {
4795 let mut scanner = Scanner::new(StrInput::new("&foo: bar\n"));
4796
4797 loop {
4798 let tok = scanner
4799 .next_token()
4800 .expect("scanner must not fail before anchor token")
4801 .expect("scanner must eventually emit an anchor token");
4802
4803 if let TokenType::Anchor(name) = tok.1 {
4804 assert_eq!(name.as_ref(), "foo:");
4805 break;
4806 }
4807 }
4808 }
4809
4810 #[test]
4812 fn tag_directive_parts_are_borrowed_for_str_input() {
4813 let mut scanner = Scanner::new(StrInput::new("%TAG !e! tag:example.com,2000:app/\n"));
4814
4815 loop {
4816 let tok = scanner
4817 .next_token()
4818 .expect("valid YAML must scan without errors")
4819 .expect("scanner must eventually produce a token");
4820 if let TokenType::TagDirective(handle, prefix) = tok.1 {
4821 assert!(matches!(handle, Cow::Borrowed("!e!")));
4822 assert!(matches!(prefix, Cow::Borrowed("tag:example.com,2000:app/")));
4823 break;
4824 }
4825 }
4826 }
4827
4828 #[test]
4829 fn tag_directive_parts_are_owned_for_buffered_input() {
4830 let mut scanner = Scanner::new(BufferedInput::new(
4831 "%TAG !e! tag:example.com,2000:app/\n".chars(),
4832 ));
4833
4834 loop {
4835 let tok = scanner
4836 .next_token()
4837 .expect("valid YAML must scan without errors")
4838 .expect("scanner must eventually produce a token");
4839 if let TokenType::TagDirective(handle, prefix) = tok.1 {
4840 assert!(matches!(handle, Cow::Owned(_)));
4841 assert_eq!(&*handle, "!e!");
4842 assert!(matches!(prefix, Cow::Owned(_)));
4843 assert_eq!(&*prefix, "tag:example.com,2000:app/");
4844 break;
4845 }
4846 }
4847 }
4848
4849 #[test]
4850 fn buffered_tag_directive_decodes_prefix_escape() {
4851 let mut scanner = Scanner::new(BufferedInput::new(
4852 "%TAG !e! %74ag:example.com,2000:app/\n".chars(),
4853 ));
4854
4855 loop {
4856 let tok = scanner
4857 .next_token()
4858 .expect("valid YAML must scan without errors")
4859 .expect("scanner must eventually produce a token");
4860 if let TokenType::TagDirective(handle, prefix) = tok.1 {
4861 assert_eq!(&*handle, "!e!");
4862 assert!(matches!(prefix, Cow::Owned(_)));
4863 assert_eq!(&*prefix, "tag:example.com,2000:app/");
4864 break;
4865 }
4866 }
4867 }
4868
4869 #[test]
4870 fn local_tag_combines_handle_text_with_escaped_suffix() {
4871 let mut scanner = Scanner::new(StrInput::new("!foo%20bar value\n"));
4872
4873 loop {
4874 let tok = scanner
4875 .next_token()
4876 .expect("valid YAML must scan without errors")
4877 .expect("scanner must eventually produce a token");
4878 if let TokenType::Tag(handle, suffix) = tok.1 {
4879 assert!(matches!(handle, Cow::Borrowed("!")));
4880 assert!(matches!(suffix, Cow::Owned(_)));
4881 assert_eq!(&*suffix, "foo bar");
4882 break;
4883 }
4884 }
4885 }
4886
4887 #[test]
4888 fn secondary_tag_requires_suffix_in_borrowed_and_buffered_paths() {
4889 assert_eq!(
4890 first_scanner_error_kind("!! value\n"),
4891 ErrorKind::MissingTagUri
4892 );
4893 assert_eq!(
4894 first_buffered_scanner_error_kind("!! value\n"),
4895 ErrorKind::MissingTagUri
4896 );
4897 }
4898
4899 #[test]
4900 fn plain_scalar_is_borrowed_when_whitespace_free_for_str_input() {
4901 let mut scanner = Scanner::new(StrInput::new("foo\n"));
4902
4903 loop {
4904 let tok = scanner
4905 .next_token()
4906 .expect("valid YAML must scan without errors")
4907 .expect("scanner must eventually produce a token");
4908 if let TokenType::Scalar(_, value) = tok.1 {
4909 assert!(matches!(value, Cow::Borrowed("foo")));
4910 break;
4911 }
4912 }
4913 }
4914
4915 #[test]
4916 fn plain_scalar_is_borrowed_when_whitespace_present_for_str_input() {
4917 let mut scanner = Scanner::new(StrInput::new("foo bar\n"));
4918
4919 loop {
4920 let tok = scanner
4921 .next_token()
4922 .expect("valid YAML must scan without errors")
4923 .expect("scanner must eventually produce a token");
4924 if let TokenType::Scalar(_, value) = tok.1 {
4925 assert!(matches!(value, Cow::Borrowed("foo bar")));
4926 break;
4927 }
4928 }
4929 }
4930
4931 #[test]
4932 fn single_quoted_scalar_is_borrowed_when_verbatim_for_str_input() {
4933 let mut scanner = Scanner::new(StrInput::new("'foo bar'\n"));
4934
4935 loop {
4936 let tok = scanner
4937 .next_token()
4938 .expect("valid YAML must scan without errors")
4939 .expect("scanner must eventually produce a token");
4940 if let TokenType::Scalar(_, value) = tok.1 {
4941 assert!(matches!(value, Cow::Borrowed("foo bar")));
4942 break;
4943 }
4944 }
4945 }
4946
4947 #[test]
4948 fn single_quoted_scalar_is_owned_when_quote_is_escaped_for_str_input() {
4949 let mut scanner = Scanner::new(StrInput::new("'foo''bar'\n"));
4950
4951 loop {
4952 let tok = scanner
4953 .next_token()
4954 .expect("valid YAML must scan without errors")
4955 .expect("scanner must eventually produce a token");
4956 if let TokenType::Scalar(_, value) = tok.1 {
4957 assert!(matches!(value, Cow::Owned(_)));
4958 assert_eq!(&*value, "foo'bar");
4959 break;
4960 }
4961 }
4962 }
4963
4964 #[test]
4965 fn double_quoted_scalar_is_borrowed_when_verbatim_for_str_input() {
4966 let mut scanner = Scanner::new(StrInput::new("\"foo bar\"\n"));
4967
4968 loop {
4969 let tok = scanner
4970 .next_token()
4971 .expect("valid YAML must scan without errors")
4972 .expect("scanner must eventually produce a token");
4973 if let TokenType::Scalar(_, value) = tok.1 {
4974 assert!(matches!(value, Cow::Borrowed("foo bar")));
4975 break;
4976 }
4977 }
4978 }
4979
4980 #[test]
4981 fn double_quoted_scalar_is_owned_when_escape_sequence_present_for_str_input() {
4982 let mut scanner = Scanner::new(StrInput::new("\"foo\\nbar\"\n"));
4983
4984 loop {
4985 let tok = scanner
4986 .next_token()
4987 .expect("valid YAML must scan without errors")
4988 .expect("scanner must eventually produce a token");
4989 if let TokenType::Scalar(_, value) = tok.1 {
4990 assert!(matches!(value, Cow::Owned(_)));
4991 assert_eq!(&*value, "foo\nbar");
4992 break;
4993 }
4994 }
4995 }
4996
4997 #[test]
4998 fn plain_key_is_borrowed_for_str_input() {
4999 let mut scanner = Scanner::new(StrInput::new("mykey: value\n"));
5001
5002 let mut found_key = false;
5003 let mut key_value: Option<Cow<'_, str>> = None;
5004
5005 loop {
5006 let tok = scanner
5007 .next_token()
5008 .expect("valid YAML must scan without errors");
5009 let Some(tok) = tok else { break };
5010
5011 if matches!(tok.1, TokenType::Key) {
5012 found_key = true;
5013 } else if found_key {
5014 if let TokenType::Scalar(_, value) = tok.1 {
5015 key_value = Some(value);
5016 break;
5017 }
5018 }
5019 }
5020
5021 assert!(found_key, "expected to find a Key token");
5022 let key_value = key_value.expect("expected to find a scalar after Key token");
5023 assert!(
5024 matches!(key_value, Cow::Borrowed("mykey")),
5025 "key should be borrowed, got: {key_value:?}"
5026 );
5027 }
5028
5029 #[test]
5030 fn quoted_key_is_borrowed_when_verbatim_for_str_input() {
5031 let mut scanner = Scanner::new(StrInput::new("\"mykey\": value\n"));
5032
5033 let mut found_key = false;
5034 let mut key_value: Option<Cow<'_, str>> = None;
5035
5036 loop {
5037 let tok = scanner
5038 .next_token()
5039 .expect("valid YAML must scan without errors");
5040 let Some(tok) = tok else { break };
5041
5042 if matches!(tok.1, TokenType::Key) {
5043 found_key = true;
5044 } else if found_key {
5045 if let TokenType::Scalar(_, value) = tok.1 {
5046 key_value = Some(value);
5047 break;
5048 }
5049 }
5050 }
5051
5052 assert!(found_key, "expected to find a Key token");
5053 let key_value = key_value.expect("expected to find a scalar after Key token");
5054 assert!(
5055 matches!(key_value, Cow::Borrowed("mykey")),
5056 "quoted key should be borrowed when verbatim, got: {key_value:?}"
5057 );
5058 }
5059
5060 #[test]
5061 fn tag_handle_and_suffix_are_borrowed_for_str_input() {
5062 let mut scanner = Scanner::new(StrInput::new("!!str foo\n"));
5064
5065 loop {
5066 let tok = scanner
5067 .next_token()
5068 .expect("valid YAML must scan without errors")
5069 .expect("scanner must eventually produce a token");
5070 if let TokenType::Tag(handle, suffix) = tok.1 {
5071 assert!(
5072 matches!(handle, Cow::Borrowed("!!")),
5073 "tag handle should be borrowed, got: {handle:?}"
5074 );
5075 assert!(
5076 matches!(suffix, Cow::Borrowed("str")),
5077 "tag suffix should be borrowed, got: {suffix:?}"
5078 );
5079 break;
5080 }
5081 }
5082 }
5083
5084 #[test]
5085 fn local_tag_suffix_is_borrowed_for_str_input() {
5086 let mut scanner = Scanner::new(StrInput::new("!mytag foo\n"));
5088
5089 loop {
5090 let tok = scanner
5091 .next_token()
5092 .expect("valid YAML must scan without errors")
5093 .expect("scanner must eventually produce a token");
5094 if let TokenType::Tag(handle, suffix) = tok.1 {
5095 assert!(
5096 matches!(handle, Cow::Borrowed("!")),
5097 "local tag handle should be '!', got: {handle:?}"
5098 );
5099 assert!(
5100 matches!(suffix, Cow::Borrowed("mytag")),
5101 "local tag suffix should be borrowed, got: {suffix:?}"
5102 );
5103 break;
5104 }
5105 }
5106 }
5107
5108 #[test]
5109 fn local_tag_suffix_with_punctuation_is_borrowed_for_str_input() {
5110 let mut scanner = Scanner::new(StrInput::new("!mytag/part foo\n"));
5111
5112 loop {
5113 let tok = scanner
5114 .next_token()
5115 .expect("valid YAML must scan without errors")
5116 .expect("scanner must eventually produce a token");
5117 if let TokenType::Tag(handle, suffix) = tok.1 {
5118 assert!(matches!(handle, Cow::Borrowed("!")));
5119 assert!(matches!(suffix, Cow::Borrowed("mytag/part")));
5120 break;
5121 }
5122 }
5123 }
5124
5125 #[test]
5126 fn tag_with_uri_escape_is_owned_for_str_input() {
5127 let mut scanner = Scanner::new(StrInput::new("!!my%20tag foo\n"));
5129
5130 loop {
5131 let tok = scanner
5132 .next_token()
5133 .expect("valid YAML must scan without errors")
5134 .expect("scanner must eventually produce a token");
5135 if let TokenType::Tag(handle, suffix) = tok.1 {
5136 assert!(
5137 matches!(handle, Cow::Borrowed("!!")),
5138 "tag handle should still be borrowed, got: {handle:?}"
5139 );
5140 assert!(
5141 matches!(suffix, Cow::Owned(_)),
5142 "tag suffix with URI escape should be owned, got: {suffix:?}"
5143 );
5144 assert_eq!(&*suffix, "my tag");
5145 break;
5146 }
5147 }
5148 }
5149
5150 #[test]
5151 fn flow_scalar_buffer_tracks_pending_whitespace() {
5152 let mut borrowed = super::FlowScalarBuf::new_borrowed(2);
5153
5154 borrowed.note_pending_ws(5, 8);
5155 borrowed.commit_pending_ws();
5156 assert!(matches!(
5157 borrowed,
5158 super::FlowScalarBuf::Borrowed {
5159 end: 8,
5160 pending_ws_start: None,
5161 pending_ws_end: 8,
5162 ..
5163 }
5164 ));
5165
5166 borrowed.note_pending_ws(9, 11);
5167 borrowed.discard_pending_ws();
5168 assert!(matches!(
5169 borrowed,
5170 super::FlowScalarBuf::Borrowed {
5171 end: 8,
5172 pending_ws_start: None,
5173 pending_ws_end: 8,
5174 ..
5175 }
5176 ));
5177 assert!(borrowed.as_owned_mut().is_none());
5178
5179 let mut owned = super::FlowScalarBuf::new_owned();
5180 owned.as_owned_mut().unwrap().push_str("owned");
5181 assert!(matches!(owned, super::FlowScalarBuf::Owned(ref s) if s == "owned"));
5182 }
5183
5184 fn first_scanner_error_kind(input: &str) -> ErrorKind {
5185 first_scanner_error(input).kind().clone()
5186 }
5187
5188 fn first_buffered_scanner_error_kind(input: &str) -> ErrorKind {
5189 let mut scanner = Scanner::new(BufferedInput::new(input.chars()));
5190 loop {
5191 match scanner.next_token() {
5192 Ok(Some(_)) => {}
5193 Ok(None) => panic!("expected scanner error"),
5194 Err(error) => return error.kind().clone(),
5195 }
5196 }
5197 }
5198
5199 fn first_scanner_error(input: &str) -> ScanError {
5200 let mut scanner = Scanner::new(StrInput::new(input));
5201 loop {
5202 match scanner.next_token() {
5203 Ok(Some(_)) => {}
5204 Ok(None) => panic!("expected scanner error"),
5205 Err(error) => return error,
5206 }
5207 }
5208 }
5209
5210 fn first_scalar_value(input: &str) -> String {
5211 let mut scanner = Scanner::new(StrInput::new(input));
5212 loop {
5213 match scanner.next_token().expect("scanner should not error") {
5214 Some(Token(_, TokenType::Scalar(_, value))) => return value.into_owned(),
5215 Some(_) => {}
5216 None => panic!("expected scalar token"),
5217 }
5218 }
5219 }
5220
5221 fn first_buffered_scalar_value(input: &str) -> String {
5222 let mut scanner = Scanner::new(BufferedInput::new(input.chars()));
5223 loop {
5224 match scanner.next_token().expect("scanner should not error") {
5225 Some(Token(_, TokenType::Scalar(_, value))) => return value.into_owned(),
5226 Some(_) => {}
5227 None => panic!("expected scalar token"),
5228 }
5229 }
5230 }
5231
5232 #[test]
5233 fn iterator_next_emits_error_and_then_stays_empty() {
5234 let mut scanner = Scanner::new(StrInput::new("\"unterminated"));
5235
5236 let error = scanner
5237 .by_ref()
5238 .find_map(Result::err)
5239 .expect("scanner should emit the error");
5240 assert_eq!(error.kind(), &ErrorKind::UnclosedQuotedScalar);
5241 assert!(scanner.next().is_none());
5242 }
5243
5244 #[test]
5245 fn next_token_returns_none_after_stream_end() {
5246 let mut scanner = Scanner::new(StrInput::new(""));
5247
5248 while let Some(token) = scanner.next_token().unwrap() {
5249 if matches!(token.1, TokenType::StreamEnd) {
5250 break;
5251 }
5252 }
5253
5254 assert!(scanner.stream_started());
5255 assert!(scanner.stream_ended());
5256 assert!(scanner.next_token().unwrap().is_none());
5257 }
5258
5259 #[test]
5260 fn directive_name_must_be_present() {
5261 assert_eq!(
5262 first_scanner_error_kind("%\n"),
5263 ErrorKind::MissingDirectiveName
5264 );
5265 }
5266
5267 #[test]
5268 fn yaml_directive_requires_dot_between_version_numbers() {
5269 assert_eq!(
5270 first_scanner_error_kind("%YAML 1\n"),
5271 ErrorKind::MissingYamlVersionSeparator
5272 );
5273 }
5274
5275 #[test]
5276 fn yaml_directive_requires_major_version_number() {
5277 assert_eq!(
5278 first_scanner_error_kind("%YAML .2\n"),
5279 ErrorKind::MissingYamlVersion
5280 );
5281 }
5282
5283 #[test]
5284 fn yaml_directive_rejects_extremely_long_version_number() {
5285 assert_eq!(
5286 first_scanner_error_kind("%YAML 1234567890.2\n"),
5287 ErrorKind::YamlVersionTooLong
5288 );
5289 }
5290
5291 #[test]
5292 fn tag_directive_handle_must_end_with_bang() {
5293 assert_eq!(
5294 first_scanner_error_kind("%TAG !bad tag:example.com,2024:\n"),
5295 ErrorKind::ExpectedTagDirectiveBang
5296 );
5297 }
5298
5299 #[test]
5300 fn tag_directive_handle_must_start_with_bang() {
5301 assert_eq!(
5302 first_scanner_error_kind("%TAG bad! tag:example.com,2024:\n"),
5303 ErrorKind::ExpectedTagBang
5304 );
5305 assert_eq!(
5306 first_buffered_scanner_error_kind("%TAG bad! tag:example.com,2024:\n"),
5307 ErrorKind::ExpectedTagBang
5308 );
5309 }
5310
5311 #[test]
5312 fn tag_directive_prefix_must_start_with_tag_character() {
5313 assert_eq!(
5314 first_scanner_error_kind("%TAG !e! `bad\n"),
5315 ErrorKind::InvalidGlobalTagCharacter
5316 );
5317 }
5318
5319 #[test]
5320 fn tag_directive_prefix_must_end_before_invalid_content() {
5321 assert_eq!(
5322 first_scanner_error_kind("%TAG !e! tag:example.com^suffix\n"),
5323 ErrorKind::InvalidTagDirectiveTerminator
5324 );
5325 }
5326
5327 #[test]
5328 fn tag_directive_prefix_with_uri_escape_is_owned_and_decoded() {
5329 let mut scanner =
5330 Scanner::new(StrInput::new("%TAG !e! tag:example.com,2024:some%20app/\n"));
5331
5332 loop {
5333 let token = scanner
5334 .next_token()
5335 .expect("valid directive should scan")
5336 .expect("scanner must produce a directive token");
5337 if let TokenType::TagDirective(handle, prefix) = token.1 {
5338 assert!(matches!(handle, Cow::Borrowed("!e!")));
5339 assert!(matches!(prefix, Cow::Owned(_)));
5340 assert_eq!(&*prefix, "tag:example.com,2024:some app/");
5341 break;
5342 }
5343 }
5344 }
5345
5346 #[test]
5347 fn bare_bang_tag_scans_as_non_specific_tag() {
5348 let mut scanner = Scanner::new(StrInput::new("! foo\n"));
5349
5350 loop {
5351 let token = scanner
5352 .next_token()
5353 .expect("valid tag should scan")
5354 .expect("scanner must produce a tag token");
5355 if let TokenType::Tag(handle, suffix) = token.1 {
5356 assert_eq!(&*handle, "");
5357 assert_eq!(&*suffix, "!");
5358 break;
5359 }
5360 }
5361 }
5362
5363 #[test]
5364 fn tag_requires_separation_after_suffix() {
5365 assert_eq!(
5366 first_scanner_error_kind("!foo,bar\n"),
5367 ErrorKind::InvalidTagTerminator
5368 );
5369 }
5370
5371 #[test]
5372 fn verbatim_tag_requires_uri() {
5373 assert_eq!(
5374 first_scanner_error_kind("!<> foo\n"),
5375 ErrorKind::MissingTagUri
5376 );
5377 }
5378
5379 #[test]
5380 fn verbatim_tag_requires_closing_angle_bracket() {
5381 assert_eq!(
5382 first_scanner_error_kind("!<tag:yaml.org,2002:str foo\n"),
5383 ErrorKind::UnclosedVerbatimTag
5384 );
5385 }
5386
5387 #[test]
5388 fn tag_uri_escape_requires_hex_digits() {
5389 assert_eq!(
5390 first_scanner_error_kind("!!bad%zz foo\n"),
5391 ErrorKind::InvalidTagEscape
5392 );
5393 }
5394
5395 #[test]
5396 fn tag_uri_escape_rejects_bad_leading_utf8_byte() {
5397 assert_eq!(
5398 first_scanner_error_kind("!!bad%80 foo\n"),
5399 ErrorKind::InvalidTagUtf8LeadingByte
5400 );
5401 }
5402
5403 #[test]
5404 fn tag_uri_escape_rejects_bad_trailing_utf8_byte() {
5405 assert_eq!(
5406 first_scanner_error_kind("!!bad%C2%41 foo\n"),
5407 ErrorKind::InvalidTagUtf8TrailingByte
5408 );
5409 }
5410
5411 #[test]
5412 fn tag_uri_escape_rejects_invalid_utf8_codepoint() {
5413 assert_eq!(
5414 first_scanner_error_kind("!!bad%F4%90%80%80 foo\n"),
5415 ErrorKind::InvalidTagUtf8
5416 );
5417 }
5418
5419 #[test]
5420 fn anchors_and_aliases_require_names() {
5421 assert_eq!(
5422 first_scanner_error_kind("& \n"),
5423 ErrorKind::MissingAnchorOrAliasName
5424 );
5425 assert_eq!(
5426 first_scanner_error_kind("* \n"),
5427 ErrorKind::MissingAnchorOrAliasName
5428 );
5429 }
5430
5431 #[test]
5432 fn document_end_marker_rejects_trailing_content() {
5433 assert_eq!(
5434 first_scanner_error_kind("... trailing\n"),
5435 ErrorKind::InvalidDocumentEnd
5436 );
5437 }
5438
5439 #[test]
5440 fn reserved_indicators_are_rejected_outside_directives() {
5441 let error = first_scanner_error(" @\n");
5442
5443 assert_eq!(
5444 error.kind(),
5445 &ErrorKind::UnexpectedCharacter { character: '@' }
5446 );
5447 }
5448
5449 #[test]
5450 fn flow_block_entry_indicator_is_rejected() {
5451 assert_eq!(
5452 first_scanner_error_kind("[- ]\n"),
5453 ErrorKind::BlockEntryInFlowCollection
5454 );
5455 }
5456
5457 #[test]
5458 fn block_entry_after_tabbed_separator_reports_specific_error() {
5459 assert_eq!(
5460 first_scanner_error_kind("-\t- value\n"),
5461 ErrorKind::InvalidBlockEntryWhitespace
5462 );
5463 }
5464
5465 #[test]
5466 fn document_indicator_reports_unclosed_flow_collection() {
5467 let error = first_scanner_error("[\n---\n");
5468
5469 assert_eq!(
5470 error.kind(),
5471 &ErrorKind::UnclosedFlowCollection { open: '[' }
5472 );
5473 }
5474
5475 #[test]
5476 fn block_scalar_header_rejects_trailing_content() {
5477 assert_eq!(
5478 first_scanner_error_kind("|+ trailing\n"),
5479 ErrorKind::InvalidBlockScalarHeader
5480 );
5481 }
5482
5483 #[test]
5484 fn block_scalar_rejects_zero_indent_indicator() {
5485 assert_eq!(
5486 first_scanner_error_kind("|0\n"),
5487 ErrorKind::ZeroBlockScalarIndent
5488 );
5489 assert_eq!(
5490 first_scanner_error_kind("|+0\n"),
5491 ErrorKind::ZeroBlockScalarIndent
5492 );
5493 }
5494
5495 #[test]
5496 fn empty_block_scalar_at_eof_honors_chomping() {
5497 assert_eq!(first_scalar_value("|\n"), "");
5498 assert_eq!(first_scalar_value("|-\n"), "");
5499 assert_eq!(first_scalar_value("|+\n"), "");
5500 assert_eq!(first_scalar_value("|+\n\n"), "\n");
5501 assert_eq!(first_scalar_value("|+\n "), "\n");
5502 }
5503
5504 #[test]
5505 fn buffered_block_scalar_reads_content_past_lookahead_window() {
5506 assert_eq!(
5507 first_buffered_scalar_value("|\n abcdefghijklmnopqrstuvwxyz\n"),
5508 "abcdefghijklmnopqrstuvwxyz\n"
5509 );
5510 }
5511
5512 #[test]
5513 fn explicit_indent_block_scalar_can_end_at_document_marker() {
5514 assert_eq!(first_scalar_value("|1\n...\n"), "");
5515 }
5516
5517 #[test]
5518 fn root_explicit_indent_block_scalar_rejects_underindented_content() {
5519 assert_eq!(
5520 first_scanner_error_kind("|2\nx\n"),
5521 ErrorKind::InvalidBlockScalarIndent
5522 );
5523 }
5524
5525 #[test]
5526 fn quoted_scalar_rejects_document_indicator_at_line_start() {
5527 assert_eq!(
5528 first_scanner_error_kind("\"one\n---\ntwo\"\n"),
5529 ErrorKind::DocumentIndicatorInQuotedScalar
5530 );
5531 }
5532
5533 #[test]
5534 fn quoted_scalar_rejects_tab_indentation_after_line_break() {
5535 assert_eq!(
5536 first_scanner_error_kind("a: \"one\n\tbad\"\n"),
5537 ErrorKind::TabInIndentation
5538 );
5539 }
5540
5541 #[test]
5542 fn quoted_scalar_rejects_underindented_continuation() {
5543 assert_eq!(
5544 first_scanner_error_kind("a: \"one\nbad\"\n"),
5545 ErrorKind::InvalidQuotedScalarIndent
5546 );
5547 }
5548
5549 #[test]
5550 fn quoted_scalar_trailing_content_error_names_quote_style() {
5551 assert_eq!(
5552 first_scanner_error_kind("'foo' trailing\n"),
5553 ErrorKind::InvalidTrailingSingleQuotedScalar
5554 );
5555 assert_eq!(
5556 first_scanner_error_kind("\"foo\" trailing\n"),
5557 ErrorKind::InvalidTrailingDoubleQuotedScalar
5558 );
5559 }
5560
5561 #[test]
5562 fn quoted_scalar_escape_errors_cover_hex_and_surrogate_edges() {
5563 assert_eq!(
5564 first_scanner_error_kind("\"\\xG0\"\n"),
5565 ErrorKind::InvalidQuotedScalarHexEscape
5566 );
5567 assert_eq!(
5568 first_scanner_error_kind("\"\\uD800\\uGGGG\"\n"),
5569 ErrorKind::InvalidLowSurrogateHexEscape
5570 );
5571 assert_eq!(
5572 first_scanner_error_kind("\"\\uD800\\u0041\"\n"),
5573 ErrorKind::InvalidLowSurrogate
5574 );
5575 assert_eq!(
5576 first_scanner_error_kind("\"\\U00110000\"\n"),
5577 ErrorKind::InvalidUnicodeEscape
5578 );
5579 }
5580
5581 #[test]
5582 fn indented_flow_scalar_reports_invalid_indentation() {
5583 assert_eq!(
5584 first_scanner_error_kind("a:\n [\nfoo]\n"),
5585 ErrorKind::InvalidIndentation
5586 );
5587 }
5588
5589 #[test]
5590 fn required_simple_key_requires_value_at_stream_end() {
5591 let error = first_scanner_error("a:\n&b\n- c\n");
5592
5593 assert_eq!(error.kind(), &ErrorKind::SimpleKeyExpected);
5594 assert_eq!(error.marker().index(), 3);
5595 assert_eq!(error.marker().line(), 2);
5596 assert_eq!(error.marker().col(), 0);
5597 }
5598
5599 #[test]
5600 fn plain_scalar_rejects_dash_before_flow_indicator() {
5601 assert_eq!(
5602 first_scanner_error_kind("[-]\n"),
5603 ErrorKind::PlainScalarStartsWithDashFlowIndicator
5604 );
5605 }
5606
5607 #[test]
5608 fn explicit_key_rejects_tab_after_indicator() {
5609 assert_eq!(
5610 first_scanner_error_kind("? \tfoo\n"),
5611 ErrorKind::TabNotAllowed
5612 );
5613 }
5614
5615 #[test]
5616 fn flow_mapping_rejects_adjacent_collection_value_after_plain_key() {
5617 assert_eq!(
5618 first_scanner_error_kind("[a:[]]\n"),
5619 ErrorKind::FlowMappingValueAdjacentCollection
5620 );
5621 }
5622
5623 #[test]
5624 fn implicit_flow_mapping_colon_cannot_move_to_next_line() {
5625 assert_eq!(
5626 first_scanner_error_kind("[foo\n: bar]\n"),
5627 ErrorKind::InvalidColonPlacement
5628 );
5629 }
5630
5631 #[test]
5632 fn invalid_simple_key_token_positions_are_scan_errors() {
5633 for (tokens_parsed, token_number) in [(1, 0), (0, 1)] {
5634 let mut scanner = Scanner::new(StrInput::new(": value\n"));
5635 scanner.fetch_stream_start();
5636 scanner.tokens.clear();
5637 scanner.tokens_parsed = tokens_parsed;
5638
5639 let simple_key = scanner
5640 .simple_keys
5641 .last_mut()
5642 .expect("stream start should create a simple key slot");
5643 simple_key.possible = true;
5644 simple_key.token_number = token_number;
5645
5646 let error = scanner
5647 .fetch_value()
5648 .expect_err("invalid simple key position should be reported as a scan error");
5649 assert_eq!(error.kind(), &ErrorKind::InvalidSimpleKey);
5650 assert_eq!(error.marker(), &Marker::new(0, 1, 0));
5651 }
5652 }
5653
5654 #[test]
5655 fn issue14_alias_scanner_consumes_colon_as_name_character() {
5656 let mut scanner = Scanner::new(StrInput::new("*foo: bar\n"));
5657
5658 assert!(matches!(
5659 scanner.next_token().unwrap().unwrap().1,
5660 TokenType::StreamStart
5661 ));
5662
5663 let token = scanner.next_token().unwrap().unwrap();
5664
5665 assert!(
5666 matches!(token.1, TokenType::Alias(ref name) if name.as_ref() == "foo:"),
5667 "expected `*foo: bar` to start with Alias(\"foo:\"), got {token:?}"
5668 );
5669 }
5670
5671 #[test]
5672 fn issue14_anchor_scanner_consumes_colon_as_name_character() {
5673 let mut scanner = Scanner::new(StrInput::new("&foo: bar\n"));
5674
5675 assert!(matches!(
5676 scanner.next_token().unwrap().unwrap().1,
5677 TokenType::StreamStart
5678 ));
5679
5680 let token = scanner.next_token().unwrap().unwrap();
5681
5682 assert!(
5683 matches!(token.1, TokenType::Anchor(ref name) if name.as_ref() == "foo:"),
5684 "expected `&foo: bar` to start with Anchor(\"foo:\"), got {token:?}"
5685 );
5686 }
5687}