use crate::error::{SourcegenError, SourcegenErrorKind};
use anyhow::Context;
use std::io::Write;
use std::path::Path;
use std::process::{Command, Output, Stdio};
pub struct Formatter {
rustfmt: String,
}
impl Formatter {
pub fn new(root: &Path) -> Result<Self, SourcegenError> {
let basedir = dunce::canonicalize(root).context(SourcegenErrorKind::WhichRustFmtFailed)?;
let output = Command::new("rustup")
.current_dir(basedir)
.arg("which")
.arg("rustfmt")
.stderr(Stdio::null())
.output()
.context(SourcegenErrorKind::WhichRustFmtFailed)?;
if !output.status.success() {
return Err(SourcegenErrorKind::NoRustFmt.into());
}
let rustfmt = String::from_utf8(output.stdout)
.context(SourcegenErrorKind::WhichRustFmtFailed)?
.trim()
.to_owned();
Ok(Self { rustfmt })
}
pub fn format(
&self,
basefile: &Path,
content: impl std::fmt::Display,
) -> Result<String, SourcegenError> {
let basedir = dunce::canonicalize(basefile.parent().unwrap())
.context(SourcegenErrorKind::RustFmtFailed)?;
let mut rustfmt = Command::new(&self.rustfmt)
.current_dir(basedir)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.stdin(Stdio::piped())
.spawn()
.context(SourcegenErrorKind::RustFmtFailed)?;
let write = rustfmt.stdin.as_mut().unwrap();
write!(write, "{}", content).context(SourcegenErrorKind::RustFmtFailed)?;
let output = rustfmt
.wait_with_output()
.context(SourcegenErrorKind::RustFmtFailed)?;
rustfmt_output(output)
}
}
fn rustfmt_output(output: Output) -> Result<String, SourcegenError> {
if output.status.success() {
let result = String::from_utf8(output.stdout).context(SourcegenErrorKind::RustFmtFailed)?;
Ok(result)
} else {
let err = String::from_utf8(output.stderr).context(SourcegenErrorKind::RustFmtFailed)?;
Err(SourcegenErrorKind::RustFmtError(err).into())
}
}