pub mod helpers;
mod attr;
mod expr;
mod function;
mod impls;
mod item;
mod other;
mod structs;
mod types;
use super::ast::PureFile;
pub fn format_files_with_rustfmt<P: AsRef<std::path::Path>>(files: &[P]) -> Result<(), String> {
use std::process::Command;
if files.is_empty() {
return Ok(());
}
let output = Command::new("rustfmt")
.args(["--edition", "2021"])
.args(files.iter().map(|p| p.as_ref()))
.output()
.map_err(|e| format!("Failed to spawn rustfmt: {}", e))?;
if output.status.success() {
Ok(())
} else {
Err(String::from_utf8_lossy(&output.stderr).to_string())
}
}
pub trait ToSyn {
type Output;
fn to_syn(&self) -> Result<Self::Output, ToSynError>;
}
#[derive(Debug, Clone)]
pub enum ToSynError {
ParsePath {
input: String,
message: String,
},
ParseExpr {
input: String,
message: String,
},
ParsePattern {
input: String,
message: String,
},
ParseType {
input: String,
message: String,
},
MissingValue {
context: String,
},
Other {
message: String,
},
}
impl std::fmt::Display for ToSynError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ParsePath { input, message } => {
write!(f, "Failed to parse path '{}': {}", input, message)
}
Self::ParseExpr { input, message } => {
write!(f, "Failed to parse expression '{}': {}", input, message)
}
Self::ParsePattern { input, message } => {
write!(f, "Failed to parse pattern '{}': {}", input, message)
}
Self::ParseType { input, message } => {
write!(f, "Failed to parse type '{}': {}", input, message)
}
Self::MissingValue { context } => {
write!(f, "Missing required value: {}", context)
}
Self::Other { message } => write!(f, "Conversion error: {}", message),
}
}
}
impl std::error::Error for ToSynError {}
impl PureFile {
pub fn to_syn_file(&self) -> Result<syn::File, ToSynError> {
self.to_syn()
}
pub fn to_source(&self) -> Result<String, ToSynError> {
let file = self.to_syn()?;
Ok(
catch_unwind_silent(|| prettyplease::unparse(&file)).unwrap_or_else(|_| {
use quote::ToTokens;
file.to_token_stream().to_string()
}),
)
}
}
fn catch_unwind_silent<F, R>(f: F) -> std::thread::Result<R>
where
F: FnOnce() -> R + std::panic::UnwindSafe,
{
let prev_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(|_| {}));
let result = std::panic::catch_unwind(f);
std::panic::set_hook(prev_hook);
result
}
impl ToSyn for PureFile {
type Output = syn::File;
fn to_syn(&self) -> Result<syn::File, ToSynError> {
Ok(syn::File {
shebang: None,
attrs: self
.attrs
.iter()
.map(|a| a.to_syn())
.collect::<Result<Vec<_>, _>>()?,
items: self
.items
.iter()
.map(|i| i.to_syn())
.collect::<Result<Vec<_>, _>>()?,
})
}
}
#[cfg(test)]
mod tests;