diff --git a/Cargo.lock b/Cargo.lock index c324ef2..ddb248c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,10 +2,17 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + [[package]] name = "bok" version = "0.1.0" dependencies = [ + "bitflags", "bok-macro", "paste", ] diff --git a/bok-macro/src/lib.rs b/bok-macro/src/lib.rs index 5ce9640..56a5af7 100644 --- a/bok-macro/src/lib.rs +++ b/bok-macro/src/lib.rs @@ -19,6 +19,10 @@ use ::proc_macro::TokenStream; use ::quote::quote; use ::syn::{parse::Parser, parse_macro_input, DeriveInput}; +/// Use as #[repository(MyBaseRepo)] +/// Will setup a `base` field that is the given base repo +/// +/// e.g.: `#[repository(::bok::RepositoryEmpty)]` #[proc_macro_attribute] pub fn repository(attrs: TokenStream, input: TokenStream) -> TokenStream { let mut ast = parse_macro_input!(input as DeriveInput); @@ -44,9 +48,9 @@ pub fn repository(attrs: TokenStream, input: TokenStream) -> TokenStream { _ => panic!("`repository` has to be used with a struct"), } } -//#[proc_macro_attribute] -//pub fn packages(attrs: TokenStream, input: TokenStream) -> TokenStream {} +/// Use as #[derive(Repository)] +/// adds extension capabilities to a repo #[proc_macro_derive(Repository)] pub fn derive_repository(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); @@ -88,12 +92,49 @@ pub fn derive_repository(input: TokenStream) -> TokenStream { TokenStream::from(expanded) } +/// Use as #[package(MyBasePackage)] +/// Will setup a `base` field that is the given base package +/// +/// e.g.: `#[package(::bok::PkgEmpty)]` +#[proc_macro_attribute] +pub fn package(attrs: TokenStream, input: TokenStream) -> TokenStream { + let mut ast = parse_macro_input!(input as DeriveInput); + match &mut ast.data { + syn::Data::Struct(ref mut struct_data) => { + match &mut struct_data.fields { + syn::Fields::Named(fields) => { + let base = proc_macro2::TokenStream::from(attrs); + fields.named.push( + syn::Field::parse_named + .parse2(quote! { base: #base }) + .unwrap(), + ); + } + _ => (), + } + + quote! { + #ast + } + .into() + } + _ => panic!("`package` has to be used with a struct"), + } +} + +/// #[derive(Package)] +/// adds: +/// * Builder pattern to a package +/// * deref for builder towards `.base` +/// * getters/setters for all package fields +/// * deref for package #[proc_macro_derive(Package)] pub fn derive_package(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let name = input.ident.clone(); let name_builder = quote::format_ident!("{name}Builder"); + let name_builder2 = name_builder.clone(); let elements = match input.data { ::syn::Data::Struct(s) => match s.fields { syn::Fields::Named(n) => n.named, @@ -101,68 +142,125 @@ pub fn derive_package(input: TokenStream) -> TokenStream { }, _ => panic!("only struct allowed"), }; - let all_fields = elements.iter().map(|field| &field.ident); - let non_opt_fields = elements.iter().filter_map(|field| match &field.ty { - syn::Type::Path(pth) => { - let first_path = pth.path.segments.first().unwrap(); - if first_path.ident == "Option" { - None - } else { - let id = &field.ident; - Some(quote! {#id}) + let all_fields = elements.iter().filter_map(|field| { + if let Some(id) = field.ident.clone() { + if id.to_string() != "base" { + return Some(&field.ident); } } - t => Some(quote! {#t}), + None }); - let opt_fields = elements.iter().filter_map(|field| match &field.ty { - syn::Type::Path(pth) => { - let first_path = pth.path.segments.first().unwrap(); - if first_path.ident == "Option" { - let id = &field.ident; - Some(quote! {#id}) - } else { - None + let all_fields2 = all_fields.clone(); + let all_fields3 = all_fields.clone(); + let all_fields_mut = elements.iter().filter_map(|field| { + if let Some(id) = field.ident.clone() { + if id.to_string() != "base" { + return Some(quote::format_ident!("{}_mut", id.to_string())); } } - _ => None, + None }); - let non_opt_types = elements.iter().filter_map(|field| match &field.ty { - syn::Type::Path(pth) => { - let first_path = pth.path.segments.first().unwrap(); - if first_path.ident == "Option" { - None - } else { - let t = &field.ty; - Some(quote! {#t}) - } - } - _ => None, - }); - let opt_types = elements.iter().filter_map(|field| match &field.ty { - syn::Type::Path(pth) => { - let first_path = pth.path.segments.first().unwrap(); - if first_path.ident == "Option" { - match &first_path.arguments { - syn::PathArguments::AngleBracketed( - syn::AngleBracketedGenericArguments { args, .. }, - ) => { - if let Some(syn::GenericArgument::Type( - syn::Type::Path(p), - )) = args.first() - { - let id = &p.path.segments.first().unwrap().ident; - Some(quote! {#id}) - } else { - None - } - } - _ => None, + let base_type = elements + .iter() + .find_map(|field| { + if let Some(id) = field.ident.clone() { + if id.to_string() == "base" { + return Some(&field.ty); } - } else { - None + } + None + }) + .expect("expected a base type"); + let all_types = elements.iter().filter_map(|field| { + if let Some(id) = field.ident.clone() { + if id.to_string() != "base" { + return Some(&field.ty); } } - _ => None, + None + }); + let all_types2 = all_types.clone(); + let non_opt_fields = elements.iter().filter_map(|field| { + if let Some(id) = field.ident.clone() { + if id.to_string() == "base" { + return None; + } + } + match &field.ty { + syn::Type::Path(pth) => { + let first_path = pth.path.segments.first().unwrap(); + if first_path.ident == "Option" { + None + } else { + let id = &field.ident; + Some(quote! {#id}) + } + } + t => Some(quote! {#t}), + } + }); + let opt_fields = elements.iter().filter_map(|field| { + if let Some(id) = field.ident.clone() { + if id.to_string() == "base" { + return None; + } + } + match &field.ty { + syn::Type::Path(pth) => { + let first_path = pth.path.segments.first().unwrap(); + if first_path.ident == "Option" { + let id = &field.ident; + Some(quote! {#id}) + } else { + None + } + } + _ => None, + } + }); + let non_opt_types = elements.iter().filter_map(|field| { + if let Some(id) = field.ident.clone() { + if id.to_string() == "base" { + return None; + } + } + match &field.ty { + syn::Type::Path(pth) => { + let first_path = pth.path.segments.first().unwrap(); + if first_path.ident == "Option" { + None + } else { + let t = &field.ty; + Some(quote! {#t}) + } + } + _ => None, + } + }); + let opt_types = elements.iter().filter_map(|field| { + if let Some(id) = field.ident.clone() { + if id.to_string() == "base" { + return None; + } + } + if let ::syn::Type::Path(pth) = &field.ty { + let first_path = pth.path.segments.first().unwrap(); + if first_path.ident == "Option" { + if let syn::PathArguments::AngleBracketed( + syn::AngleBracketedGenericArguments { args, .. }, + ) = &first_path.arguments + { + if let Some(syn::GenericArgument::Type(syn::Type::Path( + p, + ))) = args.first() + { + let id = &p.path.segments.first().unwrap().ident; + return Some(quote! {#id}); + } + } + } + } + None }); let non_opt_fields2 = non_opt_fields.clone(); let non_opt_fields3 = non_opt_fields.clone(); @@ -180,18 +278,34 @@ pub fn derive_package(input: TokenStream) -> TokenStream { #(#all_fields : ::std::option::Option::None,)* } } + #(pub fn #all_fields2(&self) -> &#all_types { + &self.#all_fields2 + })* + #(pub fn #all_fields_mut(&mut self) -> &#all_types2 { + &mut self.#all_fields3 + })* } pub struct #name_builder { #(#non_opt_fields: ::std::option::Option<#non_opt_types>,)* #(#opt_fields: ::std::option::Option<#opt_types>,)* } - impl #name_builder { + /* + impl ::std::ops::Deref for #name_builder { + type Target = #base_type; + fn deref(&self) -> &#base_type { + &self.base + } + } + */ + impl #name_builder2 { pub fn build(&mut self) -> Result<#name, ::std::boxed::Box> { #(if self.#non_opt_fields2.is_none() { return ::std::result::Result::Err("unset field".into()); })* Ok( #name { + // FIXME: user must be able to override. Trait? + base: #base_type::default(), #(#non_opt_fields3 : self.#non_opt_fields3.clone().unwrap(),)* #(#opt_fields2 : self.#opt_fields2.clone(),)* } diff --git a/bok-utils/src/pkgs/one.rs b/bok-utils/src/pkgs/one.rs index 673bd6b..1028953 100644 --- a/bok-utils/src/pkgs/one.rs +++ b/bok-utils/src/pkgs/one.rs @@ -17,6 +17,7 @@ /// Example package /// Automatically implements `.builder().my_attr(42).build()` pattern +#[::bok_macro::package(::bok::PkgEmpty)] #[derive(::bok_macro::Package)] pub struct One { pub my_attr: u32, @@ -24,6 +25,9 @@ pub struct One { impl ::std::default::Default for One { fn default() -> Self { - One { my_attr: 1 } + One { + base: ::bok::PkgEmpty::default(), + my_attr: 1, + } } } diff --git a/bok-utils/src/pkgs/three.rs b/bok-utils/src/pkgs/three.rs index 55a0aa0..bf65bee 100644 --- a/bok-utils/src/pkgs/three.rs +++ b/bok-utils/src/pkgs/three.rs @@ -17,6 +17,7 @@ /// Example package /// Automatically implements `.builder().my_attr(42).build()` pattern +#[::bok_macro::package(::bok::PkgEmpty)] #[derive(::bok_macro::Package)] pub struct Three { pub my_attr: u32, @@ -24,6 +25,9 @@ pub struct Three { impl ::std::default::Default for Three { fn default() -> Self { - Three { my_attr: 3 } + Three { + base: bok::PkgEmpty::default(), + my_attr: 3, + } } } diff --git a/bok-utils/src/pkgs/two.rs b/bok-utils/src/pkgs/two.rs index c256bdb..df6ca59 100644 --- a/bok-utils/src/pkgs/two.rs +++ b/bok-utils/src/pkgs/two.rs @@ -17,6 +17,7 @@ /// Example package /// Automatically implements `.builder().my_attr(42).build()` pattern +#[::bok_macro::package(::bok::PkgEmpty)] #[derive(::bok_macro::Package)] pub struct Two { pub my_attr: u32, @@ -24,6 +25,9 @@ pub struct Two { impl ::std::default::Default for Two { fn default() -> Self { - Two { my_attr: 2 } + Two { + base: ::bok::PkgEmpty::default(), + my_attr: 2, + } } } diff --git a/bok/Cargo.toml b/bok/Cargo.toml index b22f619..7ec74da 100644 --- a/bok/Cargo.toml +++ b/bok/Cargo.toml @@ -15,6 +15,7 @@ publish = false [dependencies] paste = "1.0" +bitflags = "2.4" [dev-dependencies] bok-macro = { path="../bok-macro" } diff --git a/bok/src/lib.rs b/bok/src/lib.rs index dde0ca5..69dbc90 100644 --- a/bok/src/lib.rs +++ b/bok/src/lib.rs @@ -15,7 +15,7 @@ * limitations under the License. */ -use ::std::any::Any; +//use ::std::any::Any; // Package stuff @@ -31,7 +31,7 @@ macro_rules! moduse { }; } // re-export `paste` crate for next macros -pub use paste; +pub use ::paste; /// Add multipla packages to a repo /// e.g.: @@ -71,6 +71,7 @@ macro_rules! packages { }; } +/// Marks your struct as a repository pub trait Repository: Default {} /// @@ -80,13 +81,69 @@ pub trait Repository: Default {} pub struct RepositoryEmpty {} impl Repository for RepositoryEmpty {} -pub trait Pkg: ::std::default::Default + Any { +/// Implement common package operations +pub trait Pkg: ::std::default::Default { + /* + fn prepare_pre(); + fn prepare(); + fn prepare_post(); + fn configure_pre(); + fn configure(); + fn configure_post(); + fn build_pre(); + fn build(); + fn build_post(); + fn check_pre(); + fn check(); + fn check_post(); + fn install_pre(); + fn install(); + fn install_post(); + */ + /* + fn build_base(&mut self) { + Self::base::default() + } + */ + /* fn as_any(&self) -> &dyn Any where Self: Sized, { self } + */ +} + +#[derive(Default)] +pub struct PkgEmpty {} + +::bitflags::bitflags! { + /// In each package, what we can build and how + #[repr(transparent)] + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct Build: u64 { + /// only the binary + const Binary = 0b1; + /// only the library + const Library = 0b10; + /// only the development headers + const Development = 0b100; + /// only the documentation + const Documentation = 0b1000; + /// Run checks during build + const Checks = 0b10000; + /// Strip binaries after build + const Strip = 0b100000; + /// Build in release mode + const Release = 0b1000000; + /// Build in debug mode + const Debug = 0b10000000; + /// Build with profiling + const Profile = 0b10000000; + /// Build optimised for size + const MinSize = 0b100000000; + } } // Conf stuff diff --git a/flake.lock b/flake.lock index 013ad06..ec650cf 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1705309234, - "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { @@ -38,11 +38,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1708702655, - "narHash": "sha256-qxT5jSLhelfLhQ07+AUxSTm1VnVH+hQxDkQSZ/m/Smo=", + "lastModified": 1710420202, + "narHash": "sha256-MvFKESbq4rUWuaf2RKPNYENaSZEw/jaCLo2gU6oREcM=", "owner": "nixos", "repo": "nixpkgs", - "rev": "c5101e457206dd437330d283d6626944e28794b3", + "rev": "878ef7d9721bee9f81f8a80819f9211ad1f993da", "type": "github" }, "original": { @@ -54,11 +54,11 @@ }, "nixpkgs-unstable": { "locked": { - "lastModified": 1708655239, - "narHash": "sha256-ZrP/yACUvDB+zbqYJsln4iwotbH6CTZiTkANJ0AgDv4=", + "lastModified": 1710451336, + "narHash": "sha256-pP86Pcfu3BrAvRO7R64x7hs+GaQrjFes+mEPowCfkxY=", "owner": "nixos", "repo": "nixpkgs", - "rev": "cbc4211f0afffe6dfd2478a62615dd5175a13f9a", + "rev": "d691274a972b3165335d261cc4671335f5c67de9", "type": "github" }, "original": { @@ -98,11 +98,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1708827164, - "narHash": "sha256-oBNS6pO04Y6gZBLThP3JDDgviex0+WTXz3bVBenyzms=", + "lastModified": 1710555016, + "narHash": "sha256-Lbdq3/TH4VrrR7A6FxIYwu5tFOcprYh8Q49Nc9s/i6c=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "e0626adabd5ea461f80b1b11390da2a6575adb30", + "rev": "42baa9e2e4713572d7481f917243b07dffdf54b8", "type": "github" }, "original": {