summaryrefslogtreecommitdiffstats
path: root/vendor/mdbook/src/utils/string.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/mdbook/src/utils/string.rs')
-rw-r--r--vendor/mdbook/src/utils/string.rs255
1 files changed, 255 insertions, 0 deletions
diff --git a/vendor/mdbook/src/utils/string.rs b/vendor/mdbook/src/utils/string.rs
new file mode 100644
index 000000000..97485d7b6
--- /dev/null
+++ b/vendor/mdbook/src/utils/string.rs
@@ -0,0 +1,255 @@
+use regex::Regex;
+use std::ops::Bound::{Excluded, Included, Unbounded};
+use std::ops::RangeBounds;
+
+/// Take a range of lines from a string.
+pub fn take_lines<R: RangeBounds<usize>>(s: &str, range: R) -> String {
+ let start = match range.start_bound() {
+ Excluded(&n) => n + 1,
+ Included(&n) => n,
+ Unbounded => 0,
+ };
+ let lines = s.lines().skip(start);
+ match range.end_bound() {
+ Excluded(end) => lines
+ .take(end.saturating_sub(start))
+ .collect::<Vec<_>>()
+ .join("\n"),
+ Included(end) => lines
+ .take((end + 1).saturating_sub(start))
+ .collect::<Vec<_>>()
+ .join("\n"),
+ Unbounded => lines.collect::<Vec<_>>().join("\n"),
+ }
+}
+
+lazy_static! {
+ static ref ANCHOR_START: Regex = Regex::new(r"ANCHOR:\s*(?P<anchor_name>[\w_-]+)").unwrap();
+ static ref ANCHOR_END: Regex = Regex::new(r"ANCHOR_END:\s*(?P<anchor_name>[\w_-]+)").unwrap();
+}
+
+/// Take anchored lines from a string.
+/// Lines containing anchor are ignored.
+pub fn take_anchored_lines(s: &str, anchor: &str) -> String {
+ let mut retained = Vec::<&str>::new();
+ let mut anchor_found = false;
+
+ for l in s.lines() {
+ if anchor_found {
+ match ANCHOR_END.captures(l) {
+ Some(cap) => {
+ if &cap["anchor_name"] == anchor {
+ break;
+ }
+ }
+ None => {
+ if !ANCHOR_START.is_match(l) {
+ retained.push(l);
+ }
+ }
+ }
+ } else if let Some(cap) = ANCHOR_START.captures(l) {
+ if &cap["anchor_name"] == anchor {
+ anchor_found = true;
+ }
+ }
+ }
+
+ retained.join("\n")
+}
+
+/// Keep lines contained within the range specified as-is.
+/// For any lines not in the range, include them but use `#` at the beginning. This will hide the
+/// lines from initial display but include them when expanding the code snippet or testing with
+/// rustdoc.
+pub fn take_rustdoc_include_lines<R: RangeBounds<usize>>(s: &str, range: R) -> String {
+ let mut output = String::with_capacity(s.len());
+
+ for (index, line) in s.lines().enumerate() {
+ if !range.contains(&index) {
+ output.push_str("# ");
+ }
+ output.push_str(line);
+ output.push('\n');
+ }
+ output.pop();
+ output
+}
+
+/// Keep lines between the anchor comments specified as-is.
+/// For any lines not between the anchors, include them but use `#` at the beginning. This will
+/// hide the lines from initial display but include them when expanding the code snippet or testing
+/// with rustdoc.
+pub fn take_rustdoc_include_anchored_lines(s: &str, anchor: &str) -> String {
+ let mut output = String::with_capacity(s.len());
+ let mut within_anchored_section = false;
+
+ for l in s.lines() {
+ if within_anchored_section {
+ match ANCHOR_END.captures(l) {
+ Some(cap) => {
+ if &cap["anchor_name"] == anchor {
+ within_anchored_section = false;
+ }
+ }
+ None => {
+ if !ANCHOR_START.is_match(l) {
+ output.push_str(l);
+ output.push('\n');
+ }
+ }
+ }
+ } else if let Some(cap) = ANCHOR_START.captures(l) {
+ if &cap["anchor_name"] == anchor {
+ within_anchored_section = true;
+ }
+ } else if !ANCHOR_END.is_match(l) {
+ output.push_str("# ");
+ output.push_str(l);
+ output.push('\n');
+ }
+ }
+
+ output.pop();
+ output
+}
+
+#[cfg(test)]
+mod tests {
+ use super::{
+ take_anchored_lines, take_lines, take_rustdoc_include_anchored_lines,
+ take_rustdoc_include_lines,
+ };
+
+ #[test]
+ #[allow(clippy::reversed_empty_ranges)] // Intentionally checking that those are correctly handled
+ fn take_lines_test() {
+ let s = "Lorem\nipsum\ndolor\nsit\namet";
+ assert_eq!(take_lines(s, 1..3), "ipsum\ndolor");
+ assert_eq!(take_lines(s, 3..), "sit\namet");
+ assert_eq!(take_lines(s, ..3), "Lorem\nipsum\ndolor");
+ assert_eq!(take_lines(s, ..), s);
+ // corner cases
+ assert_eq!(take_lines(s, 4..3), "");
+ assert_eq!(take_lines(s, ..100), s);
+ }
+
+ #[test]
+ fn take_anchored_lines_test() {
+ let s = "Lorem\nipsum\ndolor\nsit\namet";
+ assert_eq!(take_anchored_lines(s, "test"), "");
+
+ let s = "Lorem\nipsum\ndolor\nANCHOR_END: test\nsit\namet";
+ assert_eq!(take_anchored_lines(s, "test"), "");
+
+ let s = "Lorem\nipsum\nANCHOR: test\ndolor\nsit\namet";
+ assert_eq!(take_anchored_lines(s, "test"), "dolor\nsit\namet");
+ assert_eq!(take_anchored_lines(s, "something"), "");
+
+ let s = "Lorem\nipsum\nANCHOR: test\ndolor\nsit\namet\nANCHOR_END: test\nlorem\nipsum";
+ assert_eq!(take_anchored_lines(s, "test"), "dolor\nsit\namet");
+ assert_eq!(take_anchored_lines(s, "something"), "");
+
+ let s = "Lorem\nANCHOR: test\nipsum\nANCHOR: test\ndolor\nsit\namet\nANCHOR_END: test\nlorem\nipsum";
+ assert_eq!(take_anchored_lines(s, "test"), "ipsum\ndolor\nsit\namet");
+ assert_eq!(take_anchored_lines(s, "something"), "");
+
+ let s = "Lorem\nANCHOR: test2\nipsum\nANCHOR: test\ndolor\nsit\namet\nANCHOR_END: test\nlorem\nANCHOR_END:test2\nipsum";
+ assert_eq!(
+ take_anchored_lines(s, "test2"),
+ "ipsum\ndolor\nsit\namet\nlorem"
+ );
+ assert_eq!(take_anchored_lines(s, "test"), "dolor\nsit\namet");
+ assert_eq!(take_anchored_lines(s, "something"), "");
+ }
+
+ #[test]
+ #[allow(clippy::reversed_empty_ranges)] // Intentionally checking that those are correctly handled
+ fn take_rustdoc_include_lines_test() {
+ let s = "Lorem\nipsum\ndolor\nsit\namet";
+ assert_eq!(
+ take_rustdoc_include_lines(s, 1..3),
+ "# Lorem\nipsum\ndolor\n# sit\n# amet"
+ );
+ assert_eq!(
+ take_rustdoc_include_lines(s, 3..),
+ "# Lorem\n# ipsum\n# dolor\nsit\namet"
+ );
+ assert_eq!(
+ take_rustdoc_include_lines(s, ..3),
+ "Lorem\nipsum\ndolor\n# sit\n# amet"
+ );
+ assert_eq!(take_rustdoc_include_lines(s, ..), s);
+ // corner cases
+ assert_eq!(
+ take_rustdoc_include_lines(s, 4..3),
+ "# Lorem\n# ipsum\n# dolor\n# sit\n# amet"
+ );
+ assert_eq!(take_rustdoc_include_lines(s, ..100), s);
+ }
+
+ #[test]
+ fn take_rustdoc_include_anchored_lines_test() {
+ let s = "Lorem\nipsum\ndolor\nsit\namet";
+ assert_eq!(
+ take_rustdoc_include_anchored_lines(s, "test"),
+ "# Lorem\n# ipsum\n# dolor\n# sit\n# amet"
+ );
+
+ let s = "Lorem\nipsum\ndolor\nANCHOR_END: test\nsit\namet";
+ assert_eq!(
+ take_rustdoc_include_anchored_lines(s, "test"),
+ "# Lorem\n# ipsum\n# dolor\n# sit\n# amet"
+ );
+
+ let s = "Lorem\nipsum\nANCHOR: test\ndolor\nsit\namet";
+ assert_eq!(
+ take_rustdoc_include_anchored_lines(s, "test"),
+ "# Lorem\n# ipsum\ndolor\nsit\namet"
+ );
+ assert_eq!(
+ take_rustdoc_include_anchored_lines(s, "something"),
+ "# Lorem\n# ipsum\n# dolor\n# sit\n# amet"
+ );
+
+ let s = "Lorem\nipsum\nANCHOR: test\ndolor\nsit\namet\nANCHOR_END: test\nlorem\nipsum";
+ assert_eq!(
+ take_rustdoc_include_anchored_lines(s, "test"),
+ "# Lorem\n# ipsum\ndolor\nsit\namet\n# lorem\n# ipsum"
+ );
+ assert_eq!(
+ take_rustdoc_include_anchored_lines(s, "something"),
+ "# Lorem\n# ipsum\n# dolor\n# sit\n# amet\n# lorem\n# ipsum"
+ );
+
+ let s = "Lorem\nANCHOR: test\nipsum\nANCHOR: test\ndolor\nsit\namet\nANCHOR_END: test\nlorem\nipsum";
+ assert_eq!(
+ take_rustdoc_include_anchored_lines(s, "test"),
+ "# Lorem\nipsum\ndolor\nsit\namet\n# lorem\n# ipsum"
+ );
+ assert_eq!(
+ take_rustdoc_include_anchored_lines(s, "something"),
+ "# Lorem\n# ipsum\n# dolor\n# sit\n# amet\n# lorem\n# ipsum"
+ );
+
+ let s = "Lorem\nANCHOR: test2\nipsum\nANCHOR: test\ndolor\nsit\namet\nANCHOR_END: test\nlorem\nANCHOR_END:test2\nipsum";
+ assert_eq!(
+ take_rustdoc_include_anchored_lines(s, "test2"),
+ "# Lorem\nipsum\ndolor\nsit\namet\nlorem\n# ipsum"
+ );
+ assert_eq!(
+ take_rustdoc_include_anchored_lines(s, "test"),
+ "# Lorem\n# ipsum\ndolor\nsit\namet\n# lorem\n# ipsum"
+ );
+ assert_eq!(
+ take_rustdoc_include_anchored_lines(s, "something"),
+ "# Lorem\n# ipsum\n# dolor\n# sit\n# amet\n# lorem\n# ipsum"
+ );
+
+ let s = "Lorem\nANCHOR: test\nipsum\nANCHOR_END: test\ndolor\nANCHOR: test\nsit\nANCHOR_END: test\namet";
+ assert_eq!(
+ take_rustdoc_include_anchored_lines(s, "test"),
+ "# Lorem\nipsum\n# dolor\nsit\n# amet"
+ );
+ }
+}