more builder

Signed-off-by: Luca Fulchir <luca.fulchir@runesauth.com>
This commit is contained in:
Luca Fulchir 2024-03-17 20:35:28 +01:00
parent 48b4fc6113
commit c83cf30a24
Signed by: luca.fulchir
GPG Key ID: 8F6440603D13A78E
8 changed files with 265 additions and 74 deletions

7
Cargo.lock generated
View File

@ -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",
]

View File

@ -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,8 +142,51 @@ 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 {
let all_fields = elements.iter().filter_map(|field| {
if let Some(id) = field.ident.clone() {
if id.to_string() != "base" {
return Some(&field.ident);
}
}
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
});
let base_type = elements
.iter()
.find_map(|field| {
if let Some(id) = field.ident.clone() {
if id.to_string() == "base" {
return Some(&field.ty);
}
}
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
});
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" {
@ -113,8 +197,15 @@ pub fn derive_package(input: TokenStream) -> TokenStream {
}
}
t => Some(quote! {#t}),
}
});
let opt_fields = elements.iter().filter_map(|field| match &field.ty {
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" {
@ -125,8 +216,15 @@ pub fn derive_package(input: TokenStream) -> TokenStream {
}
}
_ => None,
}
});
let non_opt_types = elements.iter().filter_map(|field| match &field.ty {
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" {
@ -137,32 +235,32 @@ pub fn derive_package(input: TokenStream) -> TokenStream {
}
}
_ => None,
}
});
let opt_types = elements.iter().filter_map(|field| match &field.ty {
syn::Type::Path(pth) => {
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" {
match &first_path.arguments {
syn::PathArguments::AngleBracketed(
if let syn::PathArguments::AngleBracketed(
syn::AngleBracketedGenericArguments { args, .. },
) => {
if let Some(syn::GenericArgument::Type(
syn::Type::Path(p),
)) = args.first()
) = &first_path.arguments
{
if let Some(syn::GenericArgument::Type(syn::Type::Path(
p,
))) = args.first()
{
let id = &p.path.segments.first().unwrap().ident;
Some(quote! {#id})
} else {
return Some(quote! {#id});
}
}
}
}
None
}
}
_ => None,
}
} else {
None
}
}
_ => 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<dyn ::std::error::Error>> {
#(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(),)*
}

View File

@ -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,
}
}
}

View File

@ -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,
}
}
}

View File

@ -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,
}
}
}

View File

@ -15,6 +15,7 @@ publish = false
[dependencies]
paste = "1.0"
bitflags = "2.4"
[dev-dependencies]
bok-macro = { path="../bok-macro" }

View File

@ -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

View File

@ -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": {