@@ 1,11 1,13 @@
use std::{collections::BTreeMap, fs::read_to_string};
-use bare_schema_parser::{parse_string, AnyType, PrimativeType};
-use proc_macro2::TokenStream;
+use bare_schema_parser::{parse_string, AnyType, PrimativeType, StructField};
use proc_macro;
+use proc_macro2::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ExprLit};
+type DefRegistry = Vec<TokenStream>;
+
#[proc_macro]
pub fn bare_schema(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
// TODO: support Into<PathBuf>
@@ 16,120 18,289 @@ pub fn bare_schema(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
};
let file = read_to_string(path.value()).unwrap();
let user_type_registry: BTreeMap<String, AnyType> = parse_string(&file);
- let user_types = user_type_registry
- .iter()
- .map(|(k, v)| gen_anytype_tokenstream(k.clone(), v.clone()));
+ let mut user_type_syntax = DefRegistry::new();
+ for (name, user_type) in user_type_registry {
+ user_type_syntax = gen_user_type(user_type_syntax, &name, &user_type);
+ }
quote! {
use std::io::{Error as IOError, Read, Write};
- use bare_parser;
+ use serde::{Serialize, Deserialize};
- trait FromBytes {
- fn from_bytes(&[u8]) -> Result<Self, IOError>;
- fn read_from(reader: impl Read) -> Result<Self, IOError>;
- }
+ #(#user_type_syntax)*
+ }
+ .into()
+}
- trait ToBytes {
- fn to_bytes(self) -> Vec<u8>;
- fn write(self, writer: impl Write) -> Result<(), IOError>;
+/// `gen_user_type` is responsible for generating the token streams of a single user type at a top
+/// level. Rust does not support anonymous structs/enums/etc., so we must recursively parse any
+/// anonymous definitions and generate top-level definitions. As such, this function may generate
+/// multiple types.
+fn gen_user_type(mut registry: DefRegistry, name: &String, t: &AnyType) -> DefRegistry {
+ let mut def = TokenStream::new();
+ use AnyType::*;
+ (registry, def) = match t {
+ Primative(p) => {
+ def = gen_primative_type_def(p);
+ (
+ registry,
+ quote! {
+ #name = #def;
+ },
+ )
+ }
+ List { inner, length } => {
+ (registry, def) = gen_list(registry, name, inner.as_ref(), length);
+ (
+ registry,
+ quote! {
+ type #name = #def;
+ },
+ )
+ }
+ Struct(fields) => {
+ (registry, _) = gen_struct(registry, name, fields);
+ // `gen_struct` only has side-effects on the registry, so we return nothing
+ (registry, TokenStream::new())
+ }
+ Map { key, value } => {
+ let (registry, map_def) = gen_map(registry, name, key.as_ref(), value.as_ref());
+ (
+ registry,
+ quote! {
+ type #name = #map_def;
+ },
+ )
+ }
+ Optional(inner) => {
+ let (registry, inner_def) = dispatch_type(registry, name, inner);
+ (
+ registry,
+ quote! {
+ type #name = #inner_def;
+ },
+ )
+ }
+ TypeReference(_) => (registry, quote! { type #name = #def; }),
+ Enum(members) => {
+ (registry, _) = gen_enum(registry, name, members);
+ // `gen_enum` only has side-effects on the registry, so we return nothing
+ (registry, TokenStream::new())
+ }
+ Union(members) => {
+ (registry, _) = gen_union(registry, name, members);
+ // `gen_union` only has side-effects on the registry, so we return nothing
+ (registry, TokenStream::new())
}
+ };
+ registry.push(def);
+ registry
+}
- #(#user_types)*
+fn dispatch_type(
+ registry: DefRegistry,
+ name: &String,
+ any_type: &AnyType,
+) -> (DefRegistry, TokenStream) {
+ match any_type {
+ AnyType::Primative(p) => (registry, gen_primative_type_def(p)),
+ AnyType::List { inner, length } => gen_list(registry, name, inner.as_ref(), length),
+ AnyType::Struct(fields) => gen_struct(registry, name, fields),
+ AnyType::Enum(members) => gen_enum(registry, name, members),
+ AnyType::Map { key, value } => gen_map(registry, name, key.as_ref(), value.as_ref()),
+ AnyType::Union(members) => gen_union(registry, name, members),
+ AnyType::Optional(inner) => gen_option(registry, name, inner),
+ AnyType::TypeReference(ident) => (registry, quote! { #ident }),
}
- .into()
}
-fn gen_anytype_tokenstream(name: String, t: AnyType) -> TokenStream {
- match t {
- AnyType::Struct(s) => {
- let fields: Vec<proc_macro2::TokenStream> = s
- .iter()
- .map(|f| {
- let field_name = f.name.clone();
- let field_type = gen_anytype_tokenstream(f.name.clone(), f.type_r.clone());
- quote! {
- #field_name: #field_type
- }
- })
- .collect();
- let parsers: Vec<proc_macro2::TokenStream> = s
- .iter()
- .map(|f| {
- let field_name = f.name.clone();
- let field_type = gen_anytype_tokenstream(f.name.clone(), f.type_r.clone());
- quote! {
-
- }
- })
- .collect();
+fn gen_map(
+ registry: DefRegistry,
+ name: &String,
+ key: &AnyType,
+ value: &AnyType,
+) -> (DefRegistry, TokenStream) {
+ let (registry, key_def) = dispatch_type(registry, name, key);
+ let (registry, val_def) = dispatch_type(registry, name, value);
+ (
+ registry,
+ quote! {
+ HashMap<#key_def, #val_def>
+ },
+ )
+}
+
+fn gen_list(
+ registry: DefRegistry,
+ name: &String,
+ inner_type: &AnyType,
+ size: &Option<usize>,
+) -> (DefRegistry, TokenStream) {
+ let (registry, inner_def) = dispatch_type(registry, name, inner_type);
+ (
+ registry,
+ if let Some(size) = size {
+ // If we know the size, it's an array
quote! {
- struct #name {
- #(#fields)*
- }
+ [#inner_def; #size]
+ }
+ } else {
+ // otherwise we don't know and must allocate
+ quote! {
+ Vec<#inner_def>
+ }
+ },
+ )
+}
+
+fn gen_struct(
+ mut registry: DefRegistry,
+ name: &String,
+ fields: &Vec<StructField>,
+) -> (DefRegistry, TokenStream) {
+ // clone so we can safely drain this
+ let fields_clone = fields.clone();
+ let mut fields_gen: Vec<TokenStream> = Vec::new();
+ (registry, fields_gen) = gen_struct_field(registry, name, fields_clone, fields_gen);
+ gen_anonymous(registry, name, |name| {
+ quote! {
+ struct #name {
+ #(#fields_gen),*
}
}
- AnyType::Enum(e) => todo!(),
- AnyType::Union(u) => todo!(),
- // These are all type aliases and should be treated as such.
- AnyType::Map { key, value } => todo!(),
- AnyType::Optional(o) => todo!(),
- AnyType::TypeReference(u) => todo!(),
- AnyType::Primative(p) => {
- panic!("It is invalid to define a primative as a top-level BARE type.")
- }
- AnyType::List { inner, length } => {
- panic!("It is invalid to define a list as a top-level BARE type.")
+ })
+}
+
+fn gen_union(
+ mut registry: DefRegistry,
+ name: &String,
+ members: &Vec<AnyType>,
+) -> (DefRegistry, TokenStream) {
+ let mut members_def: Vec<TokenStream> = Vec::with_capacity(members.len());
+ for (i, member) in members.iter().enumerate() {
+ // This is to allow the `registry` binding to not shadow the function arg, but instead
+ // rebind it as it's used in the subsequent `gen_anonymous` call. We'll get move errors if
+ // we don't do it this way.
+ #[allow(unused_assignments)]
+ let mut member_def = TokenStream::new();
+ (registry, member_def) = match member {
+ AnyType::Struct(fields) => {
+ let mut fields_defs: Vec<TokenStream> = Vec::new();
+ (registry, fields_defs) =
+ gen_struct_field(registry, name, fields.clone(), fields_defs);
+ (
+ registry,
+ quote! {
+ {
+ #(#fields_defs),*
+ }
+ },
+ )
+ }
+ _ => dispatch_type(registry, &format!("{name}Member{i}"), member),
+ };
+ members_def.push(member_def);
+ }
+ gen_anonymous(registry, name, |name| {
+ quote! {
+ enum #name {
+ #(#members_def)*,
+ }
}
+ })
+}
+
+fn gen_option(registry: DefRegistry, name: &String, inner: &AnyType) -> (DefRegistry, TokenStream) {
+ let (registry, inner_def) = dispatch_type(registry, name, inner);
+ (
+ registry,
+ quote! {
+ Option<#inner_def>
+ },
+ )
+}
+
+fn gen_struct_field(
+ registry: DefRegistry,
+ struct_name: &String,
+ mut fields: Vec<StructField>,
+ mut fields_gen: Vec<TokenStream>,
+) -> (DefRegistry, Vec<TokenStream>) {
+ if let Some(StructField { name, type_r }) = fields.pop() {
+ let (registry, field_gen) =
+ dispatch_type(registry, &format!("{struct_name}{name}"), &type_r);
+ fields_gen.push(field_gen);
+ gen_struct_field(registry, struct_name, fields, fields_gen)
+ } else {
+ (registry, fields_gen)
}
- .into()
}
-fn gen_primative_parser(p: &PrimativeType, input: TokenStream) -> TokenStream {
+fn gen_enum(
+ registry: DefRegistry,
+ name: &String,
+ members: &Vec<(String, Option<usize>)>,
+) -> (DefRegistry, TokenStream) {
+ let member_defs = members.iter().map(|(name, val)| {
+ if let Some(val) = val {
+ quote! {
+ #name = #val
+ }
+ } else {
+ quote! {
+ #name,
+ }
+ }
+ });
+ gen_anonymous(registry, name, |name| {
+ quote! {
+ enum #name {
+ #(#member_defs),*
+ }
+ }
+ })
+}
+
+fn gen_anonymous(
+ mut registry: Vec<TokenStream>,
+ name: &String,
+ inner: impl FnOnce(&String) -> TokenStream,
+) -> (Vec<TokenStream>, TokenStream) {
+ registry.push(inner(&name));
+ (
+ registry,
+ quote! {
+ #name
+ },
+ )
+}
+
+fn gen_primative_type_def(p: &PrimativeType) -> TokenStream {
+ use PrimativeType::*;
match p {
- PrimativeType::UInt => TokenStream::new(),
- PrimativeType::U64 => TokenStream::new(),
- PrimativeType::U32 => TokenStream::new(),
- PrimativeType::U16 => TokenStream::new(),
- PrimativeType::U8 => TokenStream::new(),
- PrimativeType::Int => TokenStream::new(),
- PrimativeType::I64 => TokenStream::new(),
- PrimativeType::I32 => TokenStream::new(),
- PrimativeType::I16 => TokenStream::new(),
- PrimativeType::I8 => TokenStream::new(),
- PrimativeType::F64 => TokenStream::new(),
- PrimativeType::F32 => TokenStream::new(),
- PrimativeType::Str => TokenStream::new(),
- PrimativeType::Data(_) => TokenStream::new(),
- PrimativeType::Void => TokenStream::new(),
- PrimativeType::Bool => TokenStream::new(),
+ UInt | U64 => quote! { u64 },
+ U32 => quote! { u32 },
+ U16 => quote! { u16 },
+ U8 => quote! { u8 },
+ Int | I64 => quote! { i64 },
+ I32 => quote! { i32 },
+ I16 => quote! { i16 },
+ I8 => quote! { i8 },
+ F64 => quote! { f64 },
+ F32 => quote! { f32 },
+ Str => quote! { String },
+ Data(s) => match s {
+ Some(size) => quote! { [u8; #size] },
+ None => quote! { Vec<u8> },
+ },
+ Void => quote! { () },
+ Bool => quote! { bool },
}
}
-fn parser_for_type(t: AnyType) -> TokenStream {
- match t {
- AnyType::Primative(p) => match p {
- PrimativeType::UInt => quote! { bare_parser::uint },
- PrimativeType::U64 => todo!(),
- PrimativeType::U32 => todo!(),
- PrimativeType::U16 => todo!(),
- PrimativeType::U8 => todo!(),
- PrimativeType::Int => todo!(),
- PrimativeType::I64 => todo!(),
- PrimativeType::I32 => todo!(),
- PrimativeType::I16 => todo!(),
- PrimativeType::I8 => todo!(),
- PrimativeType::F64 => todo!(),
- PrimativeType::F32 => todo!(),
- PrimativeType::Str => todo!(),
- PrimativeType::Data(_) => todo!(),
- PrimativeType::Void => todo!(),
- PrimativeType::Bool => todo!(),
- },
- AnyType::List { inner, length } => todo!(),
- AnyType::Struct(_) => todo!(),
- AnyType::Enum(_) => todo!(),
- AnyType::Map { key, value } => todo!(),
- AnyType::Union(_) => todo!(),
- AnyType::Optional(_) => todo!(),
- AnyType::TypeReference(_) => todo!(),
+#[cfg(test)]
+mod test {
+ #[test]
+ fn test_basic() {
+ todo!();
}
}