#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![warn(clippy::print_stderr)]
#![warn(clippy::print_stdout)]
use std::io::Write;
#[cfg(feature = "clap")]
use clap::Args;
#[cfg(feature = "clap")]
#[derive(Debug, Args)]
pub struct CodeGenArgs {
#[arg(short('o'), long)]
output: std::path::PathBuf,
#[arg(long)]
check: bool,
}
#[cfg(feature = "clap")]
impl CodeGenArgs {
pub fn write_str(&self, content: &str) -> Result<(), Box<dyn std::error::Error>> {
write_str(content, &self.output, self.check)
}
}
pub fn write_str(
content: &str,
output: &std::path::Path,
check: bool,
) -> Result<(), Box<dyn std::error::Error>> {
if check {
let content: String = normalize_line_endings::normalized(content.chars()).collect();
let actual = std::fs::read_to_string(output)?;
let actual: String = normalize_line_endings::normalized(actual.chars()).collect();
if content != actual {
let allocation = content.lines().count() * actual.lines().count();
if 1_000_000_000 < allocation {
return Err(Box::new(CodeGenError {
message: format!("{} out of sync (too big to diff)", output.display()),
}));
} else {
let changeset = difference::Changeset::new(&actual, &content, "\n");
assert_ne!(changeset.distance, 0);
return Err(Box::new(CodeGenError {
message: format!("{} out of sync:\n{changeset}", output.display()),
}));
}
}
} else {
let mut file = std::io::BufWriter::new(std::fs::File::create(output)?);
write!(file, "{content}")?;
}
Ok(())
}
#[cfg(feature = "clap")]
#[derive(Debug, Args)]
pub struct RustfmtArgs {
#[arg(long)]
rustfmt_config: Option<std::path::PathBuf>,
}
#[cfg(feature = "clap")]
impl RustfmtArgs {
pub fn reformat(
&self,
text: impl std::fmt::Display,
) -> Result<String, Box<dyn std::error::Error>> {
rustfmt(text, self.rustfmt_config.as_deref())
}
}
pub fn rustfmt(
text: impl std::fmt::Display,
config: Option<&std::path::Path>,
) -> Result<String, Box<dyn std::error::Error>> {
let mut rustfmt = std::process::Command::new("rustfmt");
rustfmt
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped());
if let Some(config) = config {
rustfmt.arg("--config-path").arg(config);
}
let mut rustfmt = rustfmt
.spawn()
.map_err(|err| format!("could not run `rustfmt`: {err}"))?;
write!(
rustfmt
.stdin
.take()
.expect("rustfmt was configured with stdin"),
"{text}"
)?;
let output = rustfmt.wait_with_output()?;
let stdout = String::from_utf8(output.stdout)?;
Ok(stdout)
}
#[derive(Clone, Debug)]
struct CodeGenError {
message: String,
}
impl std::error::Error for CodeGenError {}
impl std::fmt::Display for CodeGenError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.message.fmt(f)
}
}