Skip to content

Commit

Permalink
Rebuilding the library
Browse files Browse the repository at this point in the history
  • Loading branch information
viktorlott committed Jan 13, 2023
1 parent 54ca064 commit db6299a
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 89 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ description = "Get the types of a struct"
proc-macro2 = "1.0.49"
quote = "1.0.23"
rustfmt = "0.10.0"
syn = "1.0.107"
syn = { version = "1.0.107", features = [ "visit", "full" ]}

[lib]
proc-macro = true
Expand Down
251 changes: 223 additions & 28 deletions src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,48 +1,243 @@
use proc_macro::TokenStream;
use quote::{quote};
use syn::{parse, Data, DeriveInput};
use tools::{modify, format_code, doc_struct, TypeDecl, publicify};
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote, ToTokens, TokenStreamExt};
use syn::{
self,
parse::Parse,
parse_quote, token,
visit::{visit_type, visit_type_path, Visit},
Attribute, Fields, Generics, Ident, Token, Type, Visibility,
};
use std::{collections::HashSet};

#[path = "tools.rs"]
use tools::{doc_struct, doc_type, format_code, modify};

#[path ="tools.rs"]
mod tools;

struct TypeModule {
attrs: Vec<Attribute>,
vis: Visibility,
ident: Ident,
inner: TypeModuleInner,
}

pub fn codegen(_attr: TokenStream, item: TokenStream) -> TokenStream {
let ast: DeriveInput = parse(item.clone()).unwrap();
struct TypeModuleInner {
type_decls: Vec<TypeDecl>,
struct_decl: TypeStructure,
}

let mut struct_entry = ast.clone();
struct TypeStructure {
attrs: Vec<Attribute>,
vis: Visibility,
struct_token: Token![struct],
ident: Ident,
generics: Generics,
fields: Fields,
semi_colon: Option<Token![;]>,
}

publicify(&mut struct_entry);
struct TypeDecl {
docs: Attribute,
ident: Ident,
ty: Type,
}

let Data::Struct(ref mut data_struct) = struct_entry.data else {
panic!("Cannot destruct Struct");
};
struct Source {
name: String,
code: String,
}

let parent = ast.ident.to_string();
let original = format_code(item.to_string());

let mut ty_decls: Vec<TypeDecl> = Vec::new();
struct FieldTypeGenerics(HashSet<Ident>);

for (index, field) in data_struct.fields.iter_mut().enumerate() {
let ident = modify(field, parent.as_str(), index);
ty_decls.push(TypeDecl::new(original.as_str(), ident, &field.ty));
impl FieldTypeGenerics {
fn get_idents(ty: &Type) -> Self {
let mut field_type_generics = FieldTypeGenerics(<_>::default());
visit_type(&mut field_type_generics, ty);
field_type_generics
}
}
impl<'ast> Visit<'ast> for FieldTypeGenerics {
fn visit_type_path(&mut self, i: &'ast syn::TypePath) {
if let Some(p) = i.path.segments.first() {
self.0.insert(p.ident.clone());
}
visit_type_path(self, i);
}
}


let name = ast.ident;
let docs = doc_struct(parent.as_str(), original.as_str());
impl Parse for TypeModule {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let code = format_code(input.to_string());
println!("{}", code);

let output = quote!(
#[allow(non_snake_case)]
#[doc = #docs]
pub mod #name {
#![allow(non_camel_case_types)]
let mut attrs: Vec<Attribute> = input.call(Attribute::parse_outer)?;
let vis: Visibility = input.parse()?;
let struct_token: Token![struct] = input.parse()?;
let ident: Ident = input.parse()?;
let generics: Generics = input.parse()?;
let mut semi_colon: Option<Token![;]> = None;

#(#ty_decls)*
let source = Source {
name: ident.to_string(),
code,
};

#[doc = #docs]
#struct_entry
let mut type_decls: Vec<TypeDecl> = Vec::new();
let mut fields: Fields;

if input.peek(token::Brace) {
fields = Fields::Named(input.parse()?);
type_decls = parse_type_decls(&mut fields, &generics, &source);
} else if input.peek(token::Paren) {
fields = Fields::Unnamed(input.parse()?);
} else {
fields = Fields::Unit;
}
);

let docs = doc_struct(
source.name.as_str(),
source.code.as_str()
);

attrs.push(parse_quote!(#[doc = #docs]));


if input.peek(Token![;]) {
semi_colon = input.parse().ok();
}

let struct_decl = TypeStructure {
attrs: attrs.clone(),
vis: parse_quote!(pub),
struct_token,
ident: format_ident!("ty", span = proc_macro2::Span::call_site()),
generics,
fields: fields.clone(),
semi_colon,
};


let inner = TypeModuleInner {
type_decls,
struct_decl,
};

Ok(Self {
attrs,
vis,
ident,
inner,
})
}
}


impl ToTokens for TypeModule {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let attrs = &self.attrs;
let vis = &self.vis;
let ident = &self.ident;
let module_inner = &self.inner;


let type_module = quote!(
#[allow(non_snake_case)]
#(#attrs)*
#vis mod #ident {
#![allow(non_camel_case_types)]

#module_inner

}
);

tokens.append_all(type_module);
}
}

impl ToTokens for TypeModuleInner {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let type_delcs = &self.type_decls;
let struct_decl = &self.struct_decl;

let inner_decls = quote!(
#(#type_delcs)*

#struct_decl
);

tokens.append_all(inner_decls)
}
}

impl ToTokens for TypeStructure {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let attrs = &self.attrs;
let visibility = &self.vis;
let struct_token = &self.struct_token;
let ident = &self.ident;
let generics = &self.generics;
let fields = &self.fields;
let semi_colon = &self.semi_colon;

let struct_decl = quote!(
#(#attrs)*
#visibility #struct_token #ident #generics #fields #semi_colon
);

tokens.append_all(struct_decl);
}
}

impl ToTokens for TypeDecl {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let docs = &self.docs;
let ident = &self.ident;
let ty = &self.ty;

tokens.append_all(quote!(#docs pub type #ident = #ty;));
}
}



impl TypeDecl {
pub fn new<'a>(source_code: &'a str, ident: &'a Ident, ty: &'a Type) -> Self {
let docs = doc_type(ident, ty, source_code);
let docs: Attribute = parse_quote!(#[doc = #docs]);

Self {
docs,
ident: ident.clone(),
ty: ty.clone(),
}
}
}

fn parse_type_decls(fields: &mut Fields, generics: &Generics, source: &Source) -> Vec<TypeDecl> {
let mut type_decls: Vec<TypeDecl> = Vec::new();

let param_generics = generics
.type_params()
.map(|tp| tp.ident.clone())
.collect::<HashSet<Ident>>();

for (index, field) in fields.iter_mut().enumerate() {
let field_type_generics = FieldTypeGenerics::get_idents(&field.ty);
if param_generics.intersection(&field_type_generics.0).count() == 0 {
let field_ident = modify(field, source.name.as_str(), index);
type_decls.push(TypeDecl::new(source.code.as_str(), &field_ident, &field.ty));
}
}

type_decls
}

pub fn codegen(_attr: TokenStream, item: TokenStream) -> TokenStream {
let type_module = syn::parse::<TypeModule>(item).unwrap();
let output = quote!(#type_module);
output.into()
}
2 changes: 1 addition & 1 deletion src/docs/struct.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Use `{name}::ty` as a type.
```
{original}
{source_code}
```
# Example
```
Expand Down
2 changes: 1 addition & 1 deletion src/docs/type.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ Use `{name}` as a type.
```
type {name} = {ty};
{original}
{source_code}
```
[Read more about `{ty}`][{ty}]

0 comments on commit db6299a

Please sign in to comment.