use crate::cli::{CountMode, DisplayMode};
use crate::counter::Count;
use crate::output::calculate_total;
pub fn format(results: &[(String, Count)], display: DisplayMode, mode: CountMode) -> String {
if results.len() == 1 || display == DisplayMode::Total {
let total = calculate_total(results);
format_single(&total, mode)
} else {
format_array(results, mode)
}
}
fn format_single(count: &Count, mode: CountMode) -> String {
match mode {
CountMode::Both => {
format!(
r#"{{"words":{},"characters":{}}}"#,
count.words, count.characters
)
}
CountMode::Words => format!(r#"{{"words":{}}}"#, count.words),
CountMode::Characters => format!(r#"{{"characters":{}}}"#, count.characters),
}
}
fn format_array(results: &[(String, Count)], mode: CountMode) -> String {
let mut output = String::from("[\n");
for (i, (name, count)) in results.iter().enumerate() {
let comma = if i < results.len() - 1 { "," } else { "" };
let entry = format_entry(name, count, mode, comma);
output.push_str(&entry);
output.push('\n');
}
output.push(']');
output
}
fn format_entry(name: &str, count: &Count, mode: CountMode, comma: &str) -> String {
match mode {
CountMode::Both => {
format!(
r#" {{"file":"{}","words":{},"characters":{}}}{}"#,
name, count.words, count.characters, comma
)
}
CountMode::Words => {
format!(
r#" {{"file":"{}","words":{}}}{}"#,
name, count.words, comma
)
}
CountMode::Characters => {
format!(
r#" {{"file":"{}","characters":{}}}{}"#,
name, count.characters, comma
)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_single_both() {
let count = Count {
words: 100,
characters: 500,
};
let output = format_single(&count, CountMode::Both);
assert_eq!(output, r#"{"words":100,"characters":500}"#);
}
#[test]
fn test_format_single_words_only() {
let count = Count {
words: 100,
characters: 500,
};
let output = format_single(&count, CountMode::Words);
assert_eq!(output, r#"{"words":100}"#);
}
#[test]
fn test_format_single_characters_only() {
let count = Count {
words: 100,
characters: 500,
};
let output = format_single(&count, CountMode::Characters);
assert_eq!(output, r#"{"characters":500}"#);
}
#[test]
fn test_format_entry_both() {
let count = Count {
words: 100,
characters: 500,
};
let entry = format_entry("test.typ", &count, CountMode::Both, ",");
assert_eq!(
entry,
r#" {"file":"test.typ","words":100,"characters":500},"#
);
}
#[test]
fn test_format_entry_no_comma() {
let count = Count {
words: 100,
characters: 500,
};
let entry = format_entry("test.typ", &count, CountMode::Both, "");
assert_eq!(
entry,
r#" {"file":"test.typ","words":100,"characters":500}"#
);
}
#[test]
fn test_format_entry_words_only() {
let count = Count {
words: 100,
characters: 500,
};
let entry = format_entry("test.typ", &count, CountMode::Words, "");
assert_eq!(entry, r#" {"file":"test.typ","words":100}"#);
}
#[test]
fn test_format_entry_characters_only() {
let count = Count {
words: 100,
characters: 500,
};
let entry = format_entry("test.typ", &count, CountMode::Characters, "");
assert_eq!(entry, r#" {"file":"test.typ","characters":500}"#);
}
#[test]
fn test_format_array() {
let results = vec![
(
"file1.typ".to_string(),
Count {
words: 100,
characters: 500,
},
),
(
"file2.typ".to_string(),
Count {
words: 200,
characters: 1000,
},
),
];
let output = format_array(&results, CountMode::Both);
assert!(output.starts_with("[\n"));
assert!(output.ends_with(']'));
assert!(output.contains(r#""file":"file1.typ""#));
assert!(output.contains(r#""file":"file2.typ""#));
assert!(output.contains(r#""words":100"#));
assert!(output.contains(r#""words":200"#));
assert!(output.contains(r#""characters":500"#));
assert!(output.contains(r#""characters":1000"#));
}
#[test]
fn test_format_single_file() {
let results = vec![(
"test.typ".to_string(),
Count {
words: 100,
characters: 500,
},
)];
let output = format(&results, DisplayMode::Auto, CountMode::Both);
assert_eq!(output, r#"{"words":100,"characters":500}"#);
}
#[test]
fn test_format_multiple_files() {
let results = vec![
(
"file1.typ".to_string(),
Count {
words: 100,
characters: 500,
},
),
(
"file2.typ".to_string(),
Count {
words: 200,
characters: 1000,
},
),
];
let output = format(&results, DisplayMode::Auto, CountMode::Both);
assert!(output.starts_with("[\n"));
assert!(output.contains(r#""file":"file1.typ""#));
assert!(output.contains(r#""file":"file2.typ""#));
}
#[test]
fn test_format_display_mode_total() {
let results = vec![
(
"file1.typ".to_string(),
Count {
words: 100,
characters: 500,
},
),
(
"file2.typ".to_string(),
Count {
words: 200,
characters: 1000,
},
),
];
let output = format(&results, DisplayMode::Total, CountMode::Both);
assert_eq!(output, r#"{"words":300,"characters":1500}"#);
}
#[test]
fn test_format_words_only() {
let results = vec![(
"test.typ".to_string(),
Count {
words: 42,
characters: 200,
},
)];
let output = format(&results, DisplayMode::Auto, CountMode::Words);
assert_eq!(output, r#"{"words":42}"#);
assert!(!output.contains("characters"));
}
#[test]
fn test_format_characters_only() {
let results = vec![(
"test.typ".to_string(),
Count {
words: 42,
characters: 200,
},
)];
let output = format(&results, DisplayMode::Auto, CountMode::Characters);
assert_eq!(output, r#"{"characters":200}"#);
assert!(!output.contains("words"));
}
}