143 lines
3.2 KiB
Rust
143 lines
3.2 KiB
Rust
|
pub(crate) fn tempdir() -> tempfile::TempDir {
|
||
|
tempfile::Builder::new()
|
||
|
.prefix("just-test-tempdir")
|
||
|
.tempdir()
|
||
|
.expect("failed to create temporary directory")
|
||
|
}
|
||
|
|
||
|
#[allow(dead_code)]
|
||
|
pub(crate) fn unindent(text: &str) -> String {
|
||
|
// find line start and end indices
|
||
|
let mut lines = Vec::new();
|
||
|
let mut start = 0;
|
||
|
for (i, c) in text.char_indices() {
|
||
|
if c == '\n' {
|
||
|
let end = i + 1;
|
||
|
lines.push((start, end));
|
||
|
start = end;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if the text isn't newline-terminated, add the final line
|
||
|
if text.chars().last() != Some('\n') {
|
||
|
lines.push((start, text.len()));
|
||
|
}
|
||
|
|
||
|
// find the longest common indentation
|
||
|
let mut common_indentation = None;
|
||
|
for (start, end) in lines.iter().cloned() {
|
||
|
let line = &text[start..end];
|
||
|
|
||
|
// skip blank lines
|
||
|
if blank(line) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// calculate new common indentation
|
||
|
common_indentation = match common_indentation {
|
||
|
Some(common_indentation) => Some(common(common_indentation, indentation(line))),
|
||
|
None => Some(indentation(line)),
|
||
|
};
|
||
|
}
|
||
|
|
||
|
// if common indentation is present, process the text
|
||
|
if let Some(common_indentation) = common_indentation {
|
||
|
if common_indentation != "" {
|
||
|
let mut output = String::new();
|
||
|
|
||
|
for (i, (start, end)) in lines.iter().cloned().enumerate() {
|
||
|
let line = &text[start..end];
|
||
|
|
||
|
if blank(line) {
|
||
|
// skip intial and final blank line
|
||
|
if i != 0 && i != lines.len() - 1 {
|
||
|
output.push('\n');
|
||
|
}
|
||
|
} else {
|
||
|
// otherwise push the line without the common indentation
|
||
|
output.push_str(&line[common_indentation.len()..]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return output;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// otherwise just return the input string
|
||
|
text.to_owned()
|
||
|
}
|
||
|
|
||
|
fn indentation(line: &str) -> &str {
|
||
|
for (i, c) in line.char_indices() {
|
||
|
if c != ' ' && c != '\t' {
|
||
|
return &line[0..i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
line
|
||
|
}
|
||
|
|
||
|
fn blank(line: &str) -> bool {
|
||
|
for (i, c) in line.char_indices() {
|
||
|
if c == ' ' || c == '\t' {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if c == '\n' && i == line.len() - 1 {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
true
|
||
|
}
|
||
|
|
||
|
fn common<'s>(a: &'s str, b: &'s str) -> &'s str {
|
||
|
for ((i, ac), bc) in a.char_indices().zip(b.chars()) {
|
||
|
if ac != bc {
|
||
|
return &a[0..i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
a
|
||
|
}
|
||
|
|
||
|
#[cfg(test)]
|
||
|
mod tests {
|
||
|
use super::*;
|
||
|
|
||
|
#[test]
|
||
|
fn unindents() {
|
||
|
assert_eq!(unindent("foo"), "foo");
|
||
|
assert_eq!(unindent("foo\nbar\nbaz\n"), "foo\nbar\nbaz\n");
|
||
|
assert_eq!(unindent(""), "");
|
||
|
assert_eq!(unindent(" foo\n bar"), "foo\nbar");
|
||
|
assert_eq!(unindent(" foo\n bar\n\n"), "foo\nbar\n");
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn indentations() {
|
||
|
assert_eq!(indentation(""), "");
|
||
|
assert_eq!(indentation("foo"), "");
|
||
|
assert_eq!(indentation(" foo"), " ");
|
||
|
assert_eq!(indentation("\t\tfoo"), "\t\t");
|
||
|
assert_eq!(indentation("\t \t foo"), "\t \t ");
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn blanks() {
|
||
|
assert!(blank(" \n"));
|
||
|
assert!(!blank(" foo\n"));
|
||
|
assert!(blank("\t\t\n"));
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn commons() {
|
||
|
assert_eq!(common("foo", "foobar"), "foo");
|
||
|
assert_eq!(common("foo", "bar"), "");
|
||
|
assert_eq!(common("", ""), "");
|
||
|
assert_eq!(common("", "bar"), "");
|
||
|
}
|
||
|
}
|