#[cfg(feature = "diff_assert")]
pub mod diff_assert;
pub trait Indentable {
fn indent(&self, amount: usize) -> Vec<String>;
fn indent_chars(amount: usize) -> String {
(0..amount).map(|_| " ").collect::<Vec<_>>().join("")
}
}
impl Indentable for String {
fn indent(&self, amount: usize) -> Vec<String> {
self.get_lines().indent(amount)
}
}
impl Indentable for Vec<String> {
fn indent(&self, amount: usize) -> Vec<String> {
let indentation = Self::indent_chars(amount);
self.iter()
.cloned()
.map(|l| {
if l.contains("\n") {
l.indent(amount)
} else {
vec![format!("{}{}", indentation, l)]
}
})
.flatten()
.collect()
}
}
pub trait HasLines {
fn get_lines(&self) -> Vec<String>;
}
impl HasLines for String {
fn get_lines(&self) -> Vec<String> {
self.split("\n").map(String::from).collect()
}
}
impl HasLines for &str {
fn get_lines(&self) -> Vec<String> {
self.split("\n").map(String::from).collect()
}
}
impl HasLines for Vec<String> {
fn get_lines(&self) -> Vec<String> {
self.clone()
}
}
impl HasLines for Code {
fn get_lines(&self) -> Vec<String> {
self.lines.clone()
}
}
impl HasLines for Vec<Code> {
fn get_lines(&self) -> Vec<String> {
let mut result = Code::new();
for code in self {
result.join(code)
}
result.get_lines()
}
}
impl<T> HasLines for Option<T> where T: HasLines {
fn get_lines(&self) -> Vec<String> {
match self {
Some(line) => line.get_lines(),
None => vec![],
}
}
}
impl<T> HasLines for &T
where
T: HasLines,
{
fn get_lines(&self) -> Vec<String> {
(*self).get_lines()
}
}
pub struct Code {
lines: Vec<String>,
}
impl Code {
pub fn new() -> Self {
Code { lines: vec![] }
}
pub fn to_string(&self) -> String {
self.lines.join("\n")
}
pub fn join(&mut self, other: impl HasLines) {
self.lines.append(&mut other.get_lines())
}
pub fn join_indent(&mut self, amount: usize, other: impl HasLines) {
self.lines.append(&mut other.get_lines().indent(amount))
}
}
#[macro_export]
macro_rules! code {
() => {
Code::new()
};
($code:ident, [$amount:expr] $lines:expr) => {
$code.join_indent($amount, $lines);
};
($code:ident, [$amount:expr] $lines:expr$(; $([$r_amount:expr] $r_lines:expr);*)?$(;)?) => {
code!{$code, [$amount] $lines};
$(code!{$code, $([$r_amount] $r_lines);*})?
};
($([$r_amount:expr] $r_lines:expr);*$(;)?) =>{
{
let mut code = Code::new();
code!{code, $([$r_amount] $r_lines);*};
code
}
}
}
#[cfg(feature = "diff_assert")]
#[cfg(test)]
mod tests {
use super::*;
use crate as nesty;
use indoc::indoc;
#[test]
fn basic_test() {
let result = code!(
[0] "fn main() {";
[1] &"println!(\"hello, world\")";
[0] "}"
);
let expected = indoc!(
r#"
fn main() {
println!("hello, world")
}"#
);
assert_same_code!(&result.to_string(), &expected)
}
#[test]
fn trailing_semis_are_allowed() {
let result = code!(
[0] "fn main() {";
[1] &"println!(\"hello, world\")";
[0] "}";
);
let expected = indoc!(
r#"
fn main() {
println!("hello, world")
}"#
);
assert_same_code!(&result.to_string(), &expected)
}
}
#[cfg(not(feature = "diff_assert"))]
#[cfg(test)]
mod test {
#[test]
fn dummy_test() {
panic!("Tests require the diff_assert feature to run")
}
}