more builder
Signed-off-by: Luca Fulchir <luca.fulchir@runesauth.com>
This commit is contained in:
parent
48b4fc6113
commit
c83cf30a24
|
@ -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",
|
||||
]
|
||||
|
|
|
@ -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<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(),)*
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ publish = false
|
|||
|
||||
[dependencies]
|
||||
paste = "1.0"
|
||||
bitflags = "2.4"
|
||||
|
||||
[dev-dependencies]
|
||||
bok-macro = { path="../bok-macro" }
|
||||
|
|
|
@ -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
|
||||
|
|
24
flake.lock
24
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": {
|
||||
|
|
Loading…
Reference in New Issue