Init
This commit is contained in:
commit
bf08cdc5dc
6 changed files with 187 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/target
|
46
Cargo.lock
generated
Normal file
46
Cargo.lock
generated
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "none-guard"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.39"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
15
Cargo.toml
Normal file
15
Cargo.toml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
[package]
|
||||||
|
name = "none-guard"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["Dominic Grimm <dominic@dergrimm.net>"]
|
||||||
|
repository = "https://git.dergrimm.net/dergrimm/none-guard"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
quote = "1.0.33"
|
||||||
|
syn = "2.0.39"
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 Dominic Grimm <dominic@dergrimm.net>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
31
examples/example.rs
Normal file
31
examples/example.rs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
use none_guard::NoneGuard;
|
||||||
|
|
||||||
|
#[derive(NoneGuard)]
|
||||||
|
struct Test {
|
||||||
|
x: Option<i32>,
|
||||||
|
y: Option<String>,
|
||||||
|
z: Option<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x = Test {
|
||||||
|
x: Some(1),
|
||||||
|
y: None,
|
||||||
|
z: None,
|
||||||
|
};
|
||||||
|
dbg!(x.is_all_none());
|
||||||
|
|
||||||
|
let y = Test {
|
||||||
|
x: None,
|
||||||
|
y: Some("dd".to_string()),
|
||||||
|
z: Some(2.0),
|
||||||
|
};
|
||||||
|
dbg!(y.is_all_none());
|
||||||
|
|
||||||
|
let z = Test {
|
||||||
|
x: None,
|
||||||
|
y: None,
|
||||||
|
z: None,
|
||||||
|
};
|
||||||
|
dbg!(z.is_all_none());
|
||||||
|
}
|
73
src/lib.rs
Normal file
73
src/lib.rs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
extern crate proc_macro;
|
||||||
|
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
use syn::{parse_macro_input, DeriveInput};
|
||||||
|
|
||||||
|
#[proc_macro_derive(NoneGuard)]
|
||||||
|
pub fn none_guard(input: TokenStream) -> TokenStream {
|
||||||
|
let ast = parse_macro_input!(input as DeriveInput);
|
||||||
|
let struct_name = &ast.ident;
|
||||||
|
|
||||||
|
if let syn::Data::Struct(s) = &ast.data {
|
||||||
|
let field_checks = s.fields.iter().map(|field| {
|
||||||
|
let name = &field.ident;
|
||||||
|
if let syn::Type::Path(type_path) = &field.ty {
|
||||||
|
if type_path.path.segments.len() == 1 {
|
||||||
|
let segment = &type_path.path.segments.iter().next().unwrap();
|
||||||
|
if segment.ident != "Option" {
|
||||||
|
return quote! {
|
||||||
|
compile_error!("All fields must be of type Option!");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return quote! {
|
||||||
|
compile_error!("All fields must be of type Option!");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return quote! {
|
||||||
|
compile_error!("All fields must be of type Option!");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
quote! { self.#name.is_some() }
|
||||||
|
});
|
||||||
|
|
||||||
|
let is_some_checks = field_checks
|
||||||
|
.clone()
|
||||||
|
.reduce(|acc, e| {
|
||||||
|
dbg!(&acc, &e);
|
||||||
|
|
||||||
|
quote! { #acc && #e }
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let is_none_checks = field_checks
|
||||||
|
.map(|tok| quote! { !(#tok) })
|
||||||
|
.reduce(|acc, e| {
|
||||||
|
dbg!(&acc, &e);
|
||||||
|
|
||||||
|
quote! { #acc && #e }
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
impl #struct_name {
|
||||||
|
pub fn is_all_some(&self) -> bool {
|
||||||
|
#is_some_checks
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_all_none(&self) -> bool {
|
||||||
|
#is_none_checks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
compile_error!("CheckAllNone macro can only be used with structs!");
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue