usecrate::{
de::{Position, Span},
error::SpannedResult,
}; use alloc::string::String;
impl Position { /// Given a Position and a string, return the 0-indexed grapheme index into the /// string at that position, or [None] if the Position is out of bounds of the string. #[must_use] pubfn grapheme_index(&self, s: &str) -> Option<usize> { use unicode_segmentation::UnicodeSegmentation; letmut line_no = 1; letmut col_no = 1;
if (self.line, self.col) == (1, 1) { return Some(0);
}
letmut i = 0;
// Slightly non-intuitive arithmetic: a zero-length string at line 1, col 1 -> 0
if (line_no, col_no) == (self.line, self.col) { return Some(i);
}
for ch in s.graphemes(true) { if (line_no, col_no) == (self.line, self.col) { return Some(i);
}
// "\n" and "\r\n" each come through the iterator as a single grapheme if matches!(ch, "\n" | "\r\n") {
line_no += 1;
col_no = 1;
} else {
col_no += 1;
}
i += 1;
}
// ...and a string of length 7 at line 1, col 8 -> 7 if (line_no, col_no) == (self.line, self.col) { return Some(i);
}
None
}
}
impl Span { /// Given a `Span` and a string, form the resulting string selected exclusively (as in `[start..end`]) by the `Span` /// or [`None`] if the span is out of bounds of the string at either end. #[must_use] pubfn substring_exclusive(&self, s: &str) -> Option<String> { use alloc::vec::Vec; use unicode_segmentation::UnicodeSegmentation;
/// Given a `Span` and a string, form the resulting string selected inclusively (as in `[start..=end]`) by the `Span` /// or [`None`] if the span is out of bounds of the string at either end. #[must_use] pubfn substring_inclusive(&self, s: &str) -> Option<String> { use alloc::vec::Vec; use unicode_segmentation::UnicodeSegmentation;
/// Given a string `ron`, a [`SpannedResult`], and a substring, verify that trying to parse `ron` results in an error /// equal to the [`SpannedResult`] with a Span that exclusively (as in `[start..end]`) selects that substring. /// Note that there are two versions of this helper, inclusive and exclusive. This is because while the parser cursor /// arithmetic that computes span positions always produces exclusive spans (as in `[start..end]`), /// when doing validation against a target substring, the inclusive check including the final grapheme that triggered /// the error is often a more intuitive target to check against. /// Meanwhile, if the parser threw an EOF, for example, there is no final grapheme to check, and so /// only the exclusive check would produce a meaningful result. #[allow(clippy::unwrap_used)] #[allow(clippy::missing_panics_doc)] pubfn check_error_span_exclusive<T: serde::de::DeserializeOwned + PartialEq + core::fmt::Debug>(
ron: &str,
check: SpannedResult<T>,
substr: &str,
) { let res_str = crate::de::from_str::<T>(ron);
assert_eq!(res_str, check);
let res_bytes = crate::de::from_bytes::<T>(ron.as_bytes());
assert_eq!(res_bytes, check);
/// Given a string `ron`, a [`SpannedResult`], and a substring, verify that trying to parse `ron` results in an error /// equal to the [`SpannedResult`] with a Span that inclusively (as in `[start..=end`]) selects that substring. /// See [`check_error_span_exclusive`] for the rationale behind both versions of this helper. #[allow(clippy::unwrap_used)] #[allow(clippy::missing_panics_doc)] pubfn check_error_span_inclusive<T: serde::de::DeserializeOwned + PartialEq + core::fmt::Debug>(
ron: &str,
check: SpannedResult<T>,
substr: &str,
) { let res_str = crate::de::from_str::<T>(ron);
assert_eq!(res_str, check);
let res_bytes = crate::de::from_bytes::<T>(ron.as_bytes());
assert_eq!(res_bytes, check);
#[test] fn oob_and_error_paths() { let text = "short";
// line past EOF
assert_eq!(pos(2, 1).grapheme_index(text), None);
// column past EOL
assert_eq!(pos(1, 10).grapheme_index(text), None);
// span with either endpoint oob → None
assert_eq!(span(pos(1, 1), pos(2, 1)).substring_exclusive(text), None);
}
#[test] fn whole_text_span() { let text = "αβγ\nδεζ"; let all = span(pos(1, 1), pos(2, 4));
assert_eq!(&all.substring_exclusive(text).unwrap(), text);
}
#[test] fn span_substring_helper() {
assert_eq!(
Span {
start: Position { line: 1, col: 1 },
end: Position { line: 2, col: 1 },
}
.substring_exclusive( "In the first place, there are two sorts of bets, or toh.11 There is the
single axial bet in the center between the principals (toh ketengah), and
there is the cloud of peripheral ones around the ring between members
of the audience (toh kesasi). ",
)
.unwrap(), "In the first place, there are two sorts of bets, or toh.11 There is the\n"
);
assert_eq!(
Span {
start: Position { line: 2, col: 1 },
end: Position { line: 3, col: 1 },
}
.substring_exclusive( "In the first place, there are two sorts of bets, or toh.11 There is the
single axial bet in the center between the principals (toh ketengah), and
there is the cloud of peripheral ones around the ring between members
of the audience (toh kesasi). ",
)
.unwrap(), "single axial bet in the center between the principals (toh ketengah), and\n"
);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.