use std::env;
use std::io::Write;
use std::process::{Command, Stdio};
#[derive(Debug, PartialEq)]
pub enum ClangFormatStyle {
Chromium,
Default,
File,
Google,
Llvm,
Mozilla,
WebKit,
}
impl ClangFormatStyle {
fn as_str(&self) -> &'static str {
match self {
Self::Chromium => "Chromium",
Self::Default => "{}",
Self::File => "file",
Self::Google => "Google",
Self::Llvm => "LLVM",
Self::Mozilla => "Mozilla",
Self::WebKit => "WebKit",
}
}
}
#[derive(Debug)]
pub enum ClangFormatError {
SpawnFailure,
StdInFailure,
StdInWriteFailure,
Utf8FormatError,
WaitFailure,
}
pub fn clang_format_with_style(
input: &str,
style: &ClangFormatStyle,
) -> Result<String, ClangFormatError> {
let clang_binary = env::var("CLANG_FORMAT_BINARY").unwrap_or("clang-format".to_string());
if let Ok(mut child) = Command::new(clang_binary.as_str())
.arg(format!("--style={}", style.as_str()))
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
{
if let Some(mut stdin) = child.stdin.take() {
if write!(stdin, "{}", input).is_err() {
return Err(ClangFormatError::StdInWriteFailure);
}
} else {
return Err(ClangFormatError::StdInFailure);
}
if let Ok(output) = child.wait_with_output() {
if let Ok(stdout) = String::from_utf8(output.stdout) {
Ok(stdout)
} else {
Err(ClangFormatError::Utf8FormatError)
}
} else {
Err(ClangFormatError::WaitFailure)
}
} else {
Err(ClangFormatError::SpawnFailure)
}
}
pub fn clang_format(input: &str) -> Result<String, ClangFormatError> {
clang_format_with_style(input, &ClangFormatStyle::Default)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn format_default() {
let input = r#"
struct Test {
};
"#;
let output = clang_format_with_style(input, &ClangFormatStyle::Default);
assert!(output.is_ok());
assert_eq!(output.unwrap(), "\nstruct Test {};\n");
}
#[test]
fn format_mozilla() {
let input = r#"
struct Test {
};
"#;
let output = clang_format_with_style(input, &ClangFormatStyle::Mozilla);
assert!(output.is_ok());
assert_eq!(output.unwrap(), "\nstruct Test\n{};\n");
}
}