1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
// SPDX-FileCopyrightText: 2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
// SPDX-FileContributor: Andrew Hayzen <andrew.hayzen@kdab.com>
// SPDX-FileContributor: Gerhard de Clercq <gerhard.declercq@kdab.com>
//
// SPDX-License-Identifier: MIT OR Apache-2.0
use std::env;
use std::io::Write;
use std::process::{Command, Stdio};
/// Describes the style to pass to clang-format
#[derive(Debug, PartialEq)]
pub enum ClangFormatStyle {
Chromium,
Default,
File,
Google,
Llvm,
Mozilla,
WebKit,
}
impl ClangFormatStyle {
/// Converts the enum ClangFormatStyle to a string that clang-format expects
fn as_str(&self) -> &'static str {
match self {
Self::Chromium => "Chromium",
// Will use clang-format default options
Self::Default => "{}",
// Will look in parent directories for a .clang-format file
Self::File => "file",
Self::Google => "Google",
Self::Llvm => "LLVM",
Self::Mozilla => "Mozilla",
Self::WebKit => "WebKit",
}
}
}
/// Describes which error spawning clang-format failed with
#[derive(Debug)]
pub enum ClangFormatError {
SpawnFailure,
StdInFailure,
StdInWriteFailure,
Utf8FormatError,
WaitFailure,
}
/// Execute clang-format with the given input, using the given style, and collect the output
///
/// # Example
///
/// ```
/// # use clang_format::{clang_format_with_style, ClangFormatStyle};
/// # fn main() {
/// 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");
/// # }
/// ```
pub fn clang_format_with_style(
input: &str,
style: &ClangFormatStyle,
) -> Result<String, ClangFormatError> {
// Create and try to spawn the command with the specified style
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()
{
// Try to take the stdin pipe
if let Some(mut stdin) = child.stdin.take() {
// Write the input to the stdin
if write!(stdin, "{}", input).is_err() {
return Err(ClangFormatError::StdInWriteFailure);
}
} else {
return Err(ClangFormatError::StdInFailure);
}
// Wait for the output
//
// Note this cannot be inside the stdin block, as stdin is only closed
// when it goes out of scope
if let Ok(output) = child.wait_with_output() {
// Parse the output into a String
//
// TODO: do we need to check stderr or exitcode?
if let Ok(stdout) = String::from_utf8(output.stdout) {
Ok(stdout)
} else {
Err(ClangFormatError::Utf8FormatError)
}
} else {
Err(ClangFormatError::WaitFailure)
}
} else {
Err(ClangFormatError::SpawnFailure)
}
}
/// Execute clang-format with the given input and collect the output
///
/// Note that this uses `ClangFormatStyle::Default` as the style.
///
/// # Example
///
/// ```
/// # use clang_format::clang_format;
/// # fn main() {
/// let input = r#"
/// struct Test {
///
/// };
/// "#;
/// let output = clang_format(input);
/// assert!(output.is_ok());
/// assert_eq!(output.unwrap(), "\nstruct Test {};\n");
/// # }
/// ```
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");
}
}