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