use quote::ToTokens;
use ryo_source::pure::{
PureAttrMeta, PureAttribute, PureEnum, PureFn, PureItem, PureStruct, PureTrait, ToSyn,
ToSynError,
};
pub struct SourceFormatter;
impl SourceFormatter {
fn format_item(item: syn::Item) -> String {
let file = syn::File {
shebang: None,
attrs: vec![],
items: vec![item],
};
prettyplease::unparse(&file)
}
fn format_signature(sig: &syn::Signature) -> String {
sig.to_token_stream().to_string()
}
pub fn format_fn_full(f: &PureFn) -> Result<String, ToSynError> {
Ok(Self::format_item(syn::Item::Fn(f.to_syn()?)))
}
pub fn format_fn_signature(f: &PureFn) -> Result<String, ToSynError> {
Ok(Self::format_signature(&f.to_syn()?.sig))
}
pub fn format_struct(s: &PureStruct) -> Result<String, ToSynError> {
Ok(Self::format_item(syn::Item::Struct(s.to_syn()?)))
}
pub fn format_enum(e: &PureEnum) -> Result<String, ToSynError> {
Ok(Self::format_item(syn::Item::Enum(e.to_syn()?)))
}
pub fn format_trait(t: &PureTrait) -> Result<String, ToSynError> {
Ok(Self::format_item(syn::Item::Trait(t.to_syn()?)))
}
pub fn format_item_source(item: &PureItem) -> Result<String, ToSynError> {
Ok(Self::format_item(item.to_syn()?))
}
pub fn extract_doc(attrs: &[PureAttribute]) -> Option<String> {
let docs: Vec<String> = attrs
.iter()
.filter(|a| a.path == "doc")
.filter_map(|a| {
if let PureAttrMeta::NameValue(val) = &a.meta {
Some(val.trim_matches('"').to_string())
} else {
None
}
})
.collect();
if docs.is_empty() {
None
} else {
Some(docs.join("\n"))
}
}
pub fn extract_spec(attrs: &[PureAttribute]) -> Option<String> {
let specs: Vec<String> = attrs
.iter()
.filter(|a| a.path == "spec")
.map(|a| format!("#[spec({:?})]", a.meta))
.collect();
if specs.is_empty() {
None
} else {
Some(specs.join("\n"))
}
}
pub fn extract_doc_and_spec(attrs: &[PureAttribute]) -> Option<String> {
let mut lines = Vec::new();
if let Some(spec) = Self::extract_spec(attrs) {
lines.push(spec);
}
if let Some(doc) = Self::extract_doc(attrs) {
lines.push(doc);
}
if lines.is_empty() {
None
} else {
Some(lines.join("\n"))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use ryo_source::pure::{PureBlock, PureFields, PureGenerics, PureVis};
#[test]
fn test_format_fn_signature() {
let f = PureFn {
name: "hello".to_string(),
vis: PureVis::Public,
is_async: false,
is_const: false,
is_unsafe: false,
is_async_inferred: false,
abi: None,
generics: PureGenerics::default(),
params: vec![],
ret: None,
body: PureBlock::default(),
attrs: vec![],
};
let sig = SourceFormatter::format_fn_signature(&f).unwrap();
assert!(sig.contains("fn hello"));
}
#[test]
fn test_format_struct() {
let s = PureStruct {
name: "MyStruct".to_string(),
vis: PureVis::Public,
generics: PureGenerics::default(),
fields: PureFields::Unit,
attrs: vec![],
};
let source = SourceFormatter::format_struct(&s).unwrap();
assert!(source.contains("pub struct MyStruct"));
}
}