codesnip_core/
format.rs

1use rust_minify::{minify_opt, MinifyOption};
2use std::{
3    io::Write as _,
4    path::Path,
5    process::{Command, Stdio},
6    str::FromStr,
7};
8
9#[derive(Debug, Clone)]
10pub enum FormatOption {
11    Rustfmt,
12    Minify,
13}
14
15impl Default for FormatOption {
16    fn default() -> Self {
17        Self::Rustfmt
18    }
19}
20
21impl FromStr for FormatOption {
22    type Err = &'static str;
23    fn from_str(s: &str) -> Result<Self, Self::Err> {
24        match s {
25            "rustfmt" => Ok(Self::Rustfmt),
26            "minify" => Ok(Self::Minify),
27            _ => Err("expected one of [rustfmt|minify]"),
28        }
29    }
30}
31
32impl FormatOption {
33    pub const POSSIBLE_VALUES: [&'static str; 2] = ["rustfmt", "minify"];
34    pub fn format(&self, content: &str) -> Option<String> {
35        match self {
36            Self::Rustfmt => format_with_rustfmt(content),
37            Self::Minify => minify_opt(
38                content,
39                &MinifyOption {
40                    remove_skip: true,
41                    add_rustfmt_skip: true,
42                },
43            )
44            .ok(),
45        }
46    }
47}
48
49pub fn rustfmt_exits() -> bool {
50    let rustfmt = Path::new(env!("CARGO_HOME")).join("bin").join("rustfmt");
51    let output = Command::new(rustfmt).arg("--version").output();
52    output
53        .map(|output| output.status.success())
54        .unwrap_or_default()
55}
56
57pub fn format_with_rustfmt(s: &str) -> Option<String> {
58    let rustfmt = Path::new(env!("CARGO_HOME")).join("bin").join("rustfmt");
59    let mut command = Command::new(rustfmt)
60        .args([
61            "--quiet",
62            "--config",
63            "unstable_features=true,normalize_doc_attributes=true,newline_style=Unix",
64        ])
65        .stdin(Stdio::piped())
66        .stdout(Stdio::piped())
67        .stderr(Stdio::piped())
68        .spawn()
69        .ok()?;
70    command.stdin.take().unwrap().write_all(s.as_bytes()).ok()?;
71    let output = command.wait_with_output().ok()?;
72    if output.status.success() {
73        Some(unsafe { String::from_utf8_unchecked(output.stdout) })
74    } else {
75        None
76    }
77}
78
79#[test]
80fn test_format_contents() {
81    assert_eq!(
82        format_with_rustfmt("fn  main ( ) { }"),
83        Some("fn main() {}\n".to_string())
84    )
85}