-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
408 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
//! Struct Field Info | ||
|
||
use crate::{ | ||
errors::Errors, | ||
parse_attrs::{FieldAttrs, FieldKind}, | ||
}; | ||
use quote::format_ident; | ||
use syn::Ident; | ||
|
||
/// A field of a `#![derive(VennDB)]` struct with attributes and some other | ||
/// notable metadata appended. | ||
pub struct StructField<'a> { | ||
/// The original parsed field | ||
field: &'a syn::Field, | ||
/// The parsed attributes of the field | ||
attrs: FieldAttrs, | ||
/// The field name. This is contained optionally inside `field`, | ||
/// but is duplicated non-optionally here to indicate that all field that | ||
/// have reached this point must have a field name, and it no longer | ||
/// needs to be unwrapped. | ||
name: &'a syn::Ident, | ||
} | ||
|
||
pub enum FieldInfo<'a> { | ||
Key(KeyField<'a>), | ||
} | ||
|
||
pub struct KeyField<'a> { | ||
pub name: &'a Ident, | ||
pub ty: &'a syn::Type, | ||
} | ||
|
||
impl<'a> KeyField<'a> { | ||
pub fn name(&'a self) -> &'a Ident { | ||
self.name | ||
} | ||
|
||
pub fn ty(&'a self) -> &'a syn::Type { | ||
self.ty | ||
} | ||
|
||
pub fn method_name(&self) -> Ident { | ||
format_ident!("get_by_{}", self.name) | ||
} | ||
|
||
pub fn map_name(&self) -> Ident { | ||
format_ident!("map_{}", self.name) | ||
} | ||
} | ||
|
||
impl<'a> StructField<'a> { | ||
/// Attempts to parse a field of a `#[derive(VennDB)]` struct, pulling out the | ||
/// fields required for code generation. | ||
pub fn new(_errors: &Errors, field: &'a syn::Field, attrs: FieldAttrs) -> Option<Self> { | ||
let name = field.ident.as_ref().expect("missing ident for named field"); | ||
Some(StructField { field, attrs, name }) | ||
} | ||
|
||
/// Return the method name for this struct field. | ||
pub fn info(&self) -> Option<FieldInfo> { | ||
self.attrs.kind.as_ref().map(|kind| match kind { | ||
FieldKind::Key => FieldInfo::Key(KeyField { | ||
name: self.name, | ||
ty: &self.field.ty, | ||
}), | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,263 @@ | ||
use crate::field::{FieldInfo, StructField}; | ||
use proc_macro2::TokenStream; | ||
use quote::quote; | ||
use syn::Ident; | ||
|
||
/// Generate the venndb logic | ||
pub fn generate_db( | ||
name: &Ident, | ||
name_db: &Ident, | ||
vis: &syn::Visibility, | ||
fields: &[StructField], | ||
) -> TokenStream { | ||
let fields: Vec<_> = fields.iter().filter_map(StructField::info).collect(); | ||
|
||
let db_struct = generate_db_struct(name, name_db, vis, &fields[..]); | ||
let db_struct_methods = generate_db_struct_methods(name, name_db, vis, &fields[..]); | ||
|
||
let db_query = generate_query_struct(name, name_db, vis, &fields[..]); | ||
let db_query_methods = generate_query_struct_methods(name, name_db, vis, &fields[..]); | ||
|
||
quote! { | ||
#db_struct | ||
|
||
#db_struct_methods | ||
|
||
#db_query | ||
|
||
#db_query_methods | ||
} | ||
} | ||
|
||
fn generate_db_struct( | ||
name: &Ident, | ||
name_db: &Ident, | ||
vis: &syn::Visibility, | ||
fields: &[FieldInfo], | ||
) -> TokenStream { | ||
let db_fields: Vec<_> = fields | ||
.iter() | ||
.map(|info| match info { | ||
FieldInfo::Key(field) => { | ||
let field_name = field.map_name(); | ||
let ty: &syn::Type = field.ty(); | ||
quote! { | ||
#field_name: std::collections::HashMap<#ty, usize>, | ||
} | ||
} | ||
}) | ||
.collect(); | ||
|
||
let db_doc = format!( | ||
"An in-memory database for storing instances of [`{}`], generated by `#[derive(VennDB)]`.", | ||
name | ||
); | ||
quote! { | ||
#[doc=#db_doc] | ||
#vis struct #name_db { | ||
rows: Vec<#name>, | ||
#(#db_fields)* | ||
} | ||
} | ||
} | ||
|
||
fn generate_db_struct_methods( | ||
name: &Ident, | ||
name_db: &Ident, | ||
vis: &syn::Visibility, | ||
fields: &[FieldInfo], | ||
) -> TokenStream { | ||
let method_new = generate_db_struct_method_new(name, name_db, vis, fields); | ||
let method_with_capacity = generate_db_struct_method_with_capacity(name, name_db, vis, fields); | ||
let key_methods = generate_db_struct_methods_key(name, name_db, vis, fields); | ||
let method_append = generate_db_struct_method_append(name, name_db, vis, fields); | ||
|
||
quote! { | ||
impl #name_db { | ||
#method_new | ||
|
||
#method_with_capacity | ||
|
||
/// Return the number of rows in the database. | ||
#vis fn len(&self) -> usize { | ||
self.rows.len() | ||
} | ||
|
||
/// Return the capacity of the database, | ||
/// which is automatically grown as needed. | ||
#vis fn capacity(&self) -> usize { | ||
self.rows.capacity() | ||
} | ||
|
||
/// Return `true` if the database is empty. | ||
#vis fn is_empty(&self) -> bool { | ||
self.rows.is_empty() | ||
} | ||
|
||
#key_methods | ||
|
||
#method_append | ||
} | ||
|
||
impl Default for #name_db { | ||
fn default() -> Self { | ||
Self::new() | ||
} | ||
} | ||
} | ||
} | ||
|
||
pub fn generate_db_struct_method_new( | ||
name: &Ident, | ||
_name_db: &Ident, | ||
vis: &syn::Visibility, | ||
fields: &[FieldInfo], | ||
) -> TokenStream { | ||
let method_doc = format!( | ||
"Construct a new empty database for storing instances of [`{}`].", | ||
name | ||
); | ||
|
||
let db_fields_initialisers: Vec<_> = fields | ||
.iter() | ||
.map(|info| match info { | ||
FieldInfo::Key(field) => { | ||
let name = field.map_name(); | ||
quote! { | ||
#name: std::collections::HashMap::new(), | ||
} | ||
} | ||
}) | ||
.collect(); | ||
|
||
quote! { | ||
#[doc=#method_doc] | ||
#vis fn new() -> Self { | ||
Self { | ||
rows: Vec::new(), | ||
#(#db_fields_initialisers)* | ||
} | ||
} | ||
} | ||
} | ||
|
||
pub fn generate_db_struct_method_append( | ||
name: &Ident, | ||
_name_db: &Ident, | ||
vis: &syn::Visibility, | ||
fields: &[FieldInfo], | ||
) -> TokenStream { | ||
let method_doc = format!("Append a new instance of [`{}`] to the database.", name); | ||
|
||
let db_field_inserts: Vec<_> = fields | ||
.iter() | ||
.map(|info| match info { | ||
FieldInfo::Key(field) => { | ||
let map_name = field.map_name(); | ||
let field_name = field.name(); | ||
|
||
quote! { | ||
self.#map_name.insert(data.#field_name.clone(), index); | ||
} | ||
} | ||
}) | ||
.collect(); | ||
|
||
quote! { | ||
#[doc=#method_doc] | ||
#vis fn append(&mut self, data: #name) { | ||
let index = self.rows.len(); | ||
|
||
if index == self.rows.capacity() { | ||
// TODO: double the bitvecs in size... | ||
} | ||
|
||
#(#db_field_inserts)* | ||
|
||
self.rows.push(data); | ||
} | ||
} | ||
} | ||
|
||
pub fn generate_db_struct_method_with_capacity( | ||
name: &Ident, | ||
_name_db: &Ident, | ||
vis: &syn::Visibility, | ||
fields: &[FieldInfo], | ||
) -> TokenStream { | ||
let method_doc = format!( | ||
"Construct a new empty database for storing instances of [`{}`] with a given capacity.", | ||
name | ||
); | ||
|
||
let db_fields_initialisers_with_capacity: Vec<_> = fields | ||
.iter() | ||
.map(|info| match info { | ||
FieldInfo::Key(field) => { | ||
let name = field.map_name(); | ||
quote! { | ||
#name: std::collections::HashMap::with_capacity(capacity), | ||
} | ||
} | ||
}) | ||
.collect(); | ||
|
||
quote! { | ||
#[doc=#method_doc] | ||
#vis fn with_capacity(capacity: usize) -> Self { | ||
Self { | ||
rows: Vec::new(), | ||
#(#db_fields_initialisers_with_capacity)* | ||
} | ||
} | ||
} | ||
} | ||
|
||
pub fn generate_db_struct_methods_key( | ||
name: &Ident, | ||
_name_db: &Ident, | ||
vis: &syn::Visibility, | ||
fields: &[FieldInfo], | ||
) -> TokenStream { | ||
let db_key_methods: Vec<_> = fields | ||
.iter() | ||
.map(|info| match info { | ||
FieldInfo::Key(field) => { | ||
let map_name = field.map_name(); | ||
let ty = field.ty(); | ||
let method_name = field.method_name(); | ||
quote! { | ||
#vis fn #method_name<Q>(&self, key: &Q) -> std::option::Option<&#name> | ||
where | ||
#ty: std::borrow::Borrow<Q>, | ||
Q: std::hash::Hash + std::cmp::Eq + ?std::marker::Sized, | ||
{ | ||
self.#map_name.get(key).and_then(|index| self.rows.get(*index)) | ||
} | ||
} | ||
} | ||
}) | ||
.collect(); | ||
|
||
quote! { | ||
#(#db_key_methods)* | ||
} | ||
} | ||
|
||
fn generate_query_struct( | ||
_name: &Ident, | ||
_name_db: &Ident, | ||
_vis: &syn::Visibility, | ||
_fields: &[FieldInfo], | ||
) -> TokenStream { | ||
TokenStream::new() // TOOD | ||
} | ||
|
||
fn generate_query_struct_methods( | ||
_name: &Ident, | ||
_name_db: &Ident, | ||
_vis: &syn::Visibility, | ||
_fields: &[FieldInfo], | ||
) -> TokenStream { | ||
TokenStream::new() // TOOD | ||
} |
Oops, something went wrong.