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