pub const AUTOGENERATION_MARKER: &str = "// AUTOGENERATED CODE -- DO NOT EDIT";
const SPACES_IN_TAB: usize = 4;
pub fn count_indent(line: &str) -> (usize, Option<char>) {
let mut count = 0;
let mut non_whitespace: Option<char> = None;
for c in line.chars() {
match c {
' ' => count += 1,
'\t' => count += SPACES_IN_TAB,
other => {
non_whitespace = Some(other);
break;
}
}
}
(count, non_whitespace)
}
pub fn add_indent(indent_size: usize, line: &str) -> String {
if line.is_empty() {
line.to_owned() } else {
" ".repeat(indent_size) + line
}
}
fn add_comment(line: &str, comment: &str, previous_was_comment: bool) -> (String, bool) {
let (indent_size, first_char) = count_indent(line);
let is_comment = first_char == Some('/');
if previous_was_comment {
return (line.to_string(), is_comment);
}
let code_line = match first_char {
Some('}') | Some(')') | Some(']') => {
line.to_string()
}
Some('/') => line.to_string(), Some('#') => {
line.to_string()
}
Some(_) => {
let comment_line = add_indent(indent_size, comment);
format!("{}\n{}", comment_line, line)
}
None => line.to_string(), };
(code_line, is_comment)
}
pub fn add_autogeneration_comments(code: &str) -> String {
if code.is_empty() {
return code.to_string(); }
let mut result = String::new();
let mut was_comment = false;
for line in code.split('\n') {
let (commented_line, is_comment) = add_comment(line, AUTOGENERATION_MARKER, was_comment);
was_comment = is_comment;
result.push_str(commented_line.as_str());
result.push('\n');
}
if result.ends_with("\n\n") {
result.pop();
}
result
}
#[cfg(test)]
mod tests {
use super::*;
use indoc::indoc;
#[test]
fn count_empty_str() {
assert_eq!(count_indent(""), (0, None));
}
#[test]
fn count_no_indent() {
assert_eq!(count_indent("fn main() {"), (0, Some('f')));
}
#[test]
fn count_space_indent() {
assert_eq!(count_indent(" fn main() {"), (3, Some('f')));
}
#[test]
fn count_tab_indent() {
assert_eq!(count_indent("\tfn main() {"), (4, Some('f')));
}
#[test]
fn count_whitespace_only() {
assert_eq!(count_indent("\t \t"), (11, None));
}
#[test]
fn count_mixed_indent() {
assert_eq!(count_indent(" \t fn main() {"), (9, Some('f')));
}
#[test]
fn test_add_no_indent() {
assert_eq!(add_indent(0, "hello"), "hello".to_owned());
}
#[test]
fn test_add_indent() {
assert_eq!(add_indent(4, "hello"), " hello".to_owned());
}
#[test]
fn test_add_indent_empty() {
assert_eq!(add_indent(4, ""), "".to_owned());
}
#[test]
fn test_comment_empty_str() {
assert_eq!(add_autogeneration_comments(""), "");
}
#[test]
fn test_comment_newline_str() {
assert_eq!(add_autogeneration_comments("\n"), "\n");
}
#[test]
fn test_comment_statement() {
assert_eq!(
add_autogeneration_comments(indoc! {"
return code.to_string();
"}),
indoc! {"
// AUTOGENERATED CODE -- DO NOT EDIT
return code.to_string();
"}
);
}
#[test]
fn test_comment_indent_and_bracket() {
assert_eq!(
add_autogeneration_comments(indoc! {"
if code.is_empty() {
return code.to_string(); // no point in adding an extra newline to an empty string
}
"}),
indoc! {"
// AUTOGENERATED CODE -- DO NOT EDIT
if code.is_empty() {
// AUTOGENERATED CODE -- DO NOT EDIT
return code.to_string(); // no point in adding an extra newline to an empty string
}
"}
);
}
#[test]
fn test_comment_closing_parentheses() {
assert_eq!(
add_autogeneration_comments(indoc! {"
assert_eq!(
Target::archetype().internal_name(),
Some(Rc::new(Target::TYPE_NAME.to_string()))
);
"}),
indoc! {"
// AUTOGENERATED CODE -- DO NOT EDIT
assert_eq!(
// AUTOGENERATED CODE -- DO NOT EDIT
Target::archetype().internal_name(),
// AUTOGENERATED CODE -- DO NOT EDIT
Some(Rc::new(Target::TYPE_NAME.to_string()))
);
"}
);
}
#[test]
fn test_comment_comment() {
assert_eq!(
add_autogeneration_comments(indoc! {"
if code.is_empty() {
// no point in adding an extra newline to an empty string
return code.to_string();
}
"}),
indoc! {"
// AUTOGENERATED CODE -- DO NOT EDIT
if code.is_empty() {
// no point in adding an extra newline to an empty string
return code.to_string();
}
"}
);
}
#[test]
fn test_comment_preamble() {
assert_eq!(
add_autogeneration_comments(indoc! {"
#![allow(dead_code)]
use something::unused;
"}),
indoc! {"
#![allow(dead_code)]
// AUTOGENERATED CODE -- DO NOT EDIT
use something::unused;
"}
);
}
#[test]
fn test_comment_whitespace() {
assert_eq!(
add_autogeneration_comments(indoc! {"
let b = c;
let a = b;
"}),
indoc! {"
// AUTOGENERATED CODE -- DO NOT EDIT
let b = c;
// AUTOGENERATED CODE -- DO NOT EDIT
let a = b;
"}
);
}
}