Skip to content

Commit

Permalink
Cleanup (#20)
Browse files Browse the repository at this point in the history
* Fix spelling
* Fix Clippy warnings
* Fix github action
* Add comments and split large file
  • Loading branch information
siefkenj authored Apr 11, 2024
1 parent 90fb74d commit 01de96e
Show file tree
Hide file tree
Showing 16 changed files with 1,036 additions and 926 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/on-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,8 @@ jobs:
with:
command: fmt
args: --all -- --check

- name: Cargo clippy
uses: actions-rs/cargo@v1
with:
command: clippy
29 changes: 20 additions & 9 deletions tsify-next-macros/src/attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,43 @@ use serde_derive_internals::ast::Field;

use crate::comments::extract_doc_comments;

/// Attributes that can be applied to a type decorated with `#[derive(Tsify)]`.
/// E.g., through `#[tsify(into_wasm_abi)]`.
#[derive(Debug, Default)]
pub struct TsifyContainerAttrs {
/// Implement `IntoWasmAbi` for the type.
pub into_wasm_abi: bool,
/// Implement `FromWasmAbi` for the type.
pub from_wasm_abi: bool,
/// Whether the type should be wrapped in a Typescript namespace.
pub namespace: bool,
/// Information about how the type should be serialized.
pub ty_config: TypeGenerationConfig,
/// Comments associated with the type. These will be written out to the generated Typescript.
pub comments: Vec<String>,
}

/// Configuration affecting how Typescript types are generated.
#[derive(Debug, Default)]
pub struct TypeGenerationConfig {
/// Universal prefix for generated types
pub type_prefix: Option<String>,
/// Universal suffix for generated types
pub type_suffix: Option<String>,
/// Whether missing fields should be represented as null in Typescript
pub missing_as_null: bool,
/// Whether a hashmap should be represented as an object in Typescript
pub hashmap_as_object: bool,
/// Whether large number types should be represented as BigInts in Typescript
pub large_number_types_as_bigints: bool,
}

impl TypeGenerationConfig {
pub fn format_name(&self, mut name: String) -> String {
if let Some(ref prefix) = self.type_prefix {
name.insert_str(0, prefix);
}
if let Some(ref suffix) = self.type_suffix {
name.push_str(suffix);
}
name
/// Format a type `name` adding a prefix and suffix if they are set.
pub fn format_name(&self, name: String) -> String {
let prefix = self.type_prefix.as_ref().map_or("", String::as_str);
let suffix = self.type_suffix.as_ref().map_or("", String::as_str);
format!("{}{}{}", prefix, name, suffix)
}
}

Expand Down Expand Up @@ -131,7 +142,7 @@ impl TsifyContainerAttrs {
return Ok(());
}

Err(meta.error("unsupported tsify attribute, expected one of `into_wasm_abi`, `from_wasm_abi`, `namespace`, 'type_prefix', 'type_suffix', 'missing_as_null', 'hashmap_as_object', 'large_number_types_as_bigints'"))
Err(meta.error("unsupported tsify attribute, expected one of `into_wasm_abi`, `from_wasm_abi`, `namespace`, `type_prefix`, `type_suffix`, `missing_as_null`, `hashmap_as_object`, `large_number_types_as_bigints`"))
})?;
}

Expand Down
12 changes: 5 additions & 7 deletions tsify-next-macros/src/comments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,7 @@ pub fn extract_doc_comments(attrs: &[syn::Attribute]) -> Vec<String> {
.filter_map(|a| {
// if the path segments include an ident of "doc" we know this
// this is a doc comment
if a.path()
.segments
.iter()
.any(|s| s.ident.to_string() == "doc")
{
if a.path().segments.iter().any(|s| s.ident == "doc") {
Some(a.to_token_stream().into_iter().filter_map(|t| match t {
TokenTree::Group(group) => {
// this will return the inner tokens of the group
Expand Down Expand Up @@ -48,6 +44,7 @@ pub fn extract_doc_comments(attrs: &[syn::Attribute]) -> Vec<String> {
})
}

/// Output extracted doc comments as Typescript doc comments.
pub fn write_doc_comments(
f: &mut std::fmt::Formatter<'_>,
comments: &Vec<String>,

Check warning on line 50 in tsify-next-macros/src/comments.rs

View workflow job for this annotation

GitHub Actions / Lint

writing `&Vec` instead of `&[_]` involves a new object where a slice will do
Expand All @@ -62,10 +59,11 @@ pub fn write_doc_comments(
.collect::<Vec<_>>()
.join("");

write!(f, "{}", format!("/**\n{} */\n", comment))
write!(f, "{}", format_args!("/**\n{} */\n", comment))
}

pub fn clean_comments(typ: &mut TsType) -> () {
/// Remove all comments from a `TsType::TypeLit`
pub fn clean_comments(typ: &mut TsType) {
if let TsType::TypeLit(ref mut lit) = typ {
lit.members.iter_mut().for_each(|elem| {
elem.comments = vec![];
Expand Down
29 changes: 22 additions & 7 deletions tsify-next-macros/src/container.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
use serde_derive_internals::{ast, ast::Container as SerdeContainer, attr};

use crate::{attrs::TsifyContainerAttrs, ctxt::Ctxt};
use crate::{attrs::TsifyContainerAttrs, error_tracker::ErrorTracker};

/// Data structure storing information about a type decorated with `#[derive(Tsify)]`.
/// This structure also keeps information that was parsed via Serde's macros.
pub struct Container<'a> {
pub ctxt: Ctxt,
/// Errors that occurred during processing.
pub errors: ErrorTracker,
/// Attributes passed to the `#[derive(Tsify)]` macro.
pub attrs: TsifyContainerAttrs,
/// Information about the type as parsed by Serde.
pub serde_container: SerdeContainer<'a>,
/// The `ident` of the type as written in the Rust code.
pub ident_str: String,
/// The name type that will be serialized to Typescript.
pub name: String,
}

impl<'a> Container<'a> {
pub fn new(serde_container: SerdeContainer<'a>) -> Self {
let input = &serde_container.original;
let attrs = TsifyContainerAttrs::from_derive_input(input);
let ctxt = Ctxt::new();
let errors = ErrorTracker::new();

let attrs = match attrs {
Ok(attrs) => attrs,
Err(err) => {
ctxt.syn_error(err);
errors.syn_error(err);
Default::default()
}
};
Expand All @@ -33,7 +40,7 @@ impl<'a> Container<'a> {
.format_name(serde_container.ident.to_string());

Self {
ctxt,
errors,
attrs,
serde_container,
ident_str,
Expand All @@ -55,10 +62,12 @@ impl<'a> Container<'a> {
}
}

/// The `ident` of the type as written in the Rust code.
pub fn ident(&self) -> &syn::Ident {
&self.serde_container.ident
}

/// The `ident` of the type as written in the Rust code as a string.
pub fn ident_str(&self) -> String {
self.ident_str.clone()
}
Expand All @@ -68,27 +77,33 @@ impl<'a> Container<'a> {
&self.serde_container.attrs
}

/// Whether or not Serde has marked this type as `transparent`.
pub fn transparent(&self) -> bool {
self.serde_attrs().transparent()
}

/// The name of the type that will be serialized to Typescript.
pub fn name(&self) -> String {
self.name.clone()
}

/// Information about the generics associated with the type as parsed by Serde.
pub fn generics(&self) -> &syn::Generics {
self.serde_container.generics
}

/// Information about the data fields of the type as parsed by Serde.
pub fn serde_data(&self) -> &ast::Data {
&self.serde_container.data
}

/// Add a new error to the list of processing errors.
pub fn syn_error(&self, err: syn::Error) {
self.ctxt.syn_error(err);
self.errors.syn_error(err);
}

/// Return all accumulated errors.
pub fn check(self) -> syn::Result<()> {
self.ctxt.check()
self.errors.check()
}
}
2 changes: 1 addition & 1 deletion tsify-next-macros/src/decl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ impl TsTypeAliasDecl {
pub fn to_string_with_indent(&self, indent: usize) -> String {
let out = self.to_string();
let indent_str = " ".repeat(indent);
out.split("\n")
out.split('\n')
.map(|line| format!("{}{}", indent_str, line))
.collect::<Vec<_>>()
.join("\n")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,35 @@
use std::cell::RefCell;

pub struct Ctxt {
/// Tracks errors during macro expansion. This struct implements a panic on `Drop`
/// if there are accumulated errors that weren't checked.
///
/// By using an error tracker, you can accumulate errors inside of closures and still propagate
/// them when needed.
///
/// # Example
/// ```ignore
/// let errors = ErrorTracker::new();
/// errors.syn_error(syn::Error::new_spanned(ident, "error message"));
/// // Make sure to check the errors or else you'll get a panic.
/// errors.check()?;
/// ```
pub struct ErrorTracker {
errors: RefCell<Option<Vec<syn::Error>>>,
}

impl Ctxt {
impl ErrorTracker {
pub fn new() -> Self {
Self {
errors: RefCell::new(Some(Vec::new())),
}
}

/// Add a `syn::Error` to the list of errors.
pub fn syn_error(&self, err: syn::Error) {
self.errors.borrow_mut().as_mut().unwrap().push(err)
}

/// Return all accumulated errors. This also clears the list of errors.
pub fn check(self) -> syn::Result<()> {
let mut errors = self.errors.take().unwrap().into_iter();

Expand All @@ -31,7 +46,7 @@ impl Ctxt {
}
}

impl Drop for Ctxt {
impl Drop for ErrorTracker {
fn drop(&mut self) {
if !std::thread::panicking() && self.errors.borrow().is_some() {
panic!("forgot to check for errors");
Expand Down
6 changes: 4 additions & 2 deletions tsify-next-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
mod attrs;
mod comments;
mod container;
mod ctxt;
mod decl;
mod derive;
mod error_tracker;
mod parser;
mod type_alias;
mod typescript;
Expand All @@ -16,7 +16,7 @@ fn declare_impl(
item: syn::Item,
) -> syn::Result<proc_macro2::TokenStream> {
match item {
syn::Item::Type(item) => type_alias::expend(item),
syn::Item::Type(item) => type_alias::expand(item),
syn::Item::Enum(item) => derive::expand_by_attr(args, item.into()),
syn::Item::Struct(item) => derive::expand_by_attr(args, item.into()),
_ => Err(syn::Error::new_spanned(
Expand All @@ -26,6 +26,7 @@ fn declare_impl(
}
}

/// The `declare` macro, used in `#[declare]` annotations.
#[proc_macro_attribute]
pub fn declare(
args: proc_macro::TokenStream,
Expand All @@ -39,6 +40,7 @@ pub fn declare(
.into()
}

/// The `Tsify` derive macro, used in `#[derive(Tsify, ...)]` annotations.
#[proc_macro_derive(Tsify, attributes(tsify, serde))]
pub fn derive_tsify(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let item: DeriveInput = parse_macro_input!(input);
Expand Down
1 change: 0 additions & 1 deletion tsify-next-macros/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ impl<'a> Parser<'a> {
self.container
.generics()
.type_params()
.into_iter()
.map(|p| p.ident.to_string())
.filter(|t| type_ref_names.contains(t))
.collect()
Expand Down
11 changes: 6 additions & 5 deletions tsify-next-macros/src/type_alias.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ use proc_macro2::TokenStream;
use quote::quote;

use crate::{
attrs::TypeGenerationConfig, comments::extract_doc_comments, ctxt::Ctxt, decl::TsTypeAliasDecl,
typescript::TsType,
attrs::TypeGenerationConfig, comments::extract_doc_comments, decl::TsTypeAliasDecl,
error_tracker::ErrorTracker, typescript::TsType,
};

pub fn expend(item: syn::ItemType) -> syn::Result<TokenStream> {
let ctxt = Ctxt::new();
/// Expand a `#[declare]` macro on a Rust `type = ...` expression.
pub fn expand(item: syn::ItemType) -> syn::Result<TokenStream> {
let errors = ErrorTracker::new();

let type_ann = TsType::from_syn_type(&TypeGenerationConfig::default(), item.ty.as_ref());

Expand All @@ -34,7 +35,7 @@ pub fn expend(item: syn::ItemType) -> syn::Result<TokenStream> {
};
};

ctxt.check()?;
errors.check()?;

let tokens = quote! {
#item
Expand Down
Loading

0 comments on commit 01de96e

Please sign in to comment.