diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-03-07 15:17:13 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-03-07 15:17:13 +0100 |
| commit | 25b5bd117529cd04bb789e1988eb3a3db8025a0e (patch) | |
| tree | 2fbb4650903123da047a1f1f11a0abda95286e12 /macros/src/util.rs | |
| parent | 6ab7760822ccd24b4ef126d4737d41f1be15fe19 (diff) | |
Fully untyped model
Diffstat (limited to 'macros/src/util.rs')
| -rw-r--r-- | macros/src/util.rs | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/macros/src/util.rs b/macros/src/util.rs new file mode 100644 index 00000000..c8c56a05 --- /dev/null +++ b/macros/src/util.rs @@ -0,0 +1,88 @@ +use super::*; + +/// Return an error at the given item. +macro_rules! bail { + (callsite, $fmt:literal $($tts:tt)*) => { + return Err(syn::Error::new( + proc_macro2::Span::call_site(), + format!(concat!("typst: ", $fmt) $($tts)*) + )) + }; + ($item:expr, $fmt:literal $($tts:tt)*) => { + return Err(syn::Error::new_spanned( + &$item, + format!(concat!("typst: ", $fmt) $($tts)*) + )) + }; +} + +/// Whether an attribute list has a specified attribute. +pub fn has_attr(attrs: &mut Vec<syn::Attribute>, target: &str) -> bool { + take_attr(attrs, target).is_some() +} + +/// Whether an attribute list has a specified attribute. +pub fn parse_attr<T: Parse>( + attrs: &mut Vec<syn::Attribute>, + target: &str, +) -> Result<Option<Option<T>>> { + take_attr(attrs, target) + .map(|attr| (!attr.tokens.is_empty()).then(|| attr.parse_args()).transpose()) + .transpose() +} + +/// Whether an attribute list has a specified attribute. +pub fn take_attr( + attrs: &mut Vec<syn::Attribute>, + target: &str, +) -> Option<syn::Attribute> { + attrs + .iter() + .position(|attr| attr.path.is_ident(target)) + .map(|i| attrs.remove(i)) +} + +/// Ensure that no unrecognized attributes remain. +pub fn validate_attrs(attrs: &[syn::Attribute]) -> Result<()> { + for attr in attrs { + if !attr.path.is_ident("doc") { + let ident = attr.path.get_ident().unwrap(); + bail!(ident, "unrecognized attribute: {:?}", ident.to_string()); + } + } + Ok(()) +} + +/// Convert an identifier to a kebab-case string. +pub fn kebab_case(name: &Ident) -> String { + name.to_string().to_lowercase().replace('_', "-") +} + +/// Dedent documentation text. +pub fn dedent(text: &str) -> String { + text.lines() + .map(|s| s.strip_prefix(" ").unwrap_or(s)) + .collect::<Vec<_>>() + .join("\n") +} + +/// Extract documentation comments from an attribute list. +pub fn documentation(attrs: &[syn::Attribute]) -> String { + let mut doc = String::new(); + + // Parse doc comments. + for attr in attrs { + if let Ok(syn::Meta::NameValue(meta)) = attr.parse_meta() { + if meta.path.is_ident("doc") { + if let syn::Lit::Str(string) = &meta.lit { + let full = string.value(); + let line = full.strip_prefix(' ').unwrap_or(&full); + doc.push_str(line); + doc.push('\n'); + } + } + } + } + + doc.trim().into() +} |
