mirror of
https://github.com/kristoferssolo/filecaster.git
synced 2025-10-21 19:00:34 +00:00
refactor(workspace): make a workspace
This commit is contained in:
parent
6a973db003
commit
337491b37f
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -51,6 +51,18 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "filecaster"
|
name = "filecaster"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"filecaster-derive",
|
||||||
|
"merge",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tempfile",
|
||||||
|
"toml",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "filecaster-derive"
|
||||||
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"claims",
|
"claims",
|
||||||
"merge",
|
"merge",
|
||||||
@ -58,10 +70,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
|
||||||
"syn",
|
"syn",
|
||||||
"tempfile",
|
|
||||||
"toml",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
39
Cargo.toml
39
Cargo.toml
@ -1,40 +1,17 @@
|
|||||||
[package]
|
[workspace]
|
||||||
name = "filecaster"
|
resolver = "2"
|
||||||
version = "0.1.0"
|
members = ["filecaster", "filecaster-derive"]
|
||||||
edition = "2024"
|
|
||||||
authors = ["Kristofers Solo <dev@kristofers.xyz>"]
|
|
||||||
description = "Procedural macro to derive configuration from files, with optional merging capabilities."
|
|
||||||
repository = "https://github.com/kristoferssolo/filecaster"
|
|
||||||
documentation = "https://docs.rs/filecaster"
|
|
||||||
homepage = "https://github.com/kristoferssolo/filecaster"
|
|
||||||
license = "MIT OR Apache-2.0"
|
|
||||||
keywords = ["proc-macro", "derive", "configuration", "file-parsing"]
|
|
||||||
categories = ["rust-patterns", "parsing", "config"]
|
|
||||||
exclude = ["/.github", "/.gitignore", "/tests", "*.png", "*.md"]
|
|
||||||
readme = "README.md"
|
|
||||||
|
|
||||||
[features]
|
[workspace.dependencies]
|
||||||
default = []
|
|
||||||
merge = ["dep:merge"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
proc-macro2 = "1.0"
|
|
||||||
quote = "1.0"
|
|
||||||
proc-macro-error2 = "2.0"
|
|
||||||
syn = { version = "2.0", features = ["extra-traits", "parsing"] }
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
merge = { version = "0.2", optional = true }
|
merge = "0.2.0"
|
||||||
|
# dev-dependencies
|
||||||
[dev-dependencies]
|
|
||||||
claims = "0.8"
|
claims = "0.8"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
tempfile = "3.20"
|
tempfile = "3.10"
|
||||||
toml = "0.9"
|
toml = "0.9"
|
||||||
|
|
||||||
[lib]
|
[workspace.lints.clippy]
|
||||||
proc-macro = true
|
|
||||||
|
|
||||||
[lints.clippy]
|
|
||||||
pedantic = "warn"
|
pedantic = "warn"
|
||||||
nursery = "warn"
|
nursery = "warn"
|
||||||
unwrap_used = "warn"
|
unwrap_used = "warn"
|
||||||
|
|||||||
22
filecaster-derive/Cargo.toml
Normal file
22
filecaster-derive/Cargo.toml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
[package]
|
||||||
|
name = "filecaster-derive"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
merge = ["dep:merge"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
proc-macro2 = "1.0"
|
||||||
|
quote = "1.0"
|
||||||
|
proc-macro-error2 = "2.0"
|
||||||
|
syn = { version = "2.0", features = ["extra-traits", "parsing"] }
|
||||||
|
serde.workspace = true
|
||||||
|
merge = { workspace = true, optional = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
claims.workspace = true
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use syn::{
|
use syn::{
|
||||||
@ -78,6 +80,15 @@ fn is_from_file_struct(ty: &Type) -> bool {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extract the last identifier from a [`TypePath`].
|
||||||
|
fn last_path_ident(ty: &Type) -> Result<String> {
|
||||||
|
if let Type::Path(TypePath { qself: None, path }) = ty {
|
||||||
|
return Ok(path.segments.last().unwrap().ident.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(Error::new_spanned(ty, "expected a plain struct name"))
|
||||||
|
}
|
||||||
|
|
||||||
/// Build the shadow field + assignment for one original field
|
/// Build the shadow field + assignment for one original field
|
||||||
fn build_file_field(field: &Field) -> Result<(TokenStream, TokenStream, Option<TokenStream>)> {
|
fn build_file_field(field: &Field) -> Result<(TokenStream, TokenStream, Option<TokenStream>)> {
|
||||||
let ident = field
|
let ident = field
|
||||||
@ -87,38 +98,23 @@ fn build_file_field(field: &Field) -> Result<(TokenStream, TokenStream, Option<T
|
|||||||
let ty = &field.ty;
|
let ty = &field.ty;
|
||||||
|
|
||||||
let field_attrs = if WITH_MERGE {
|
let field_attrs = if WITH_MERGE {
|
||||||
quote! {
|
quote! { #[merge(strategy = merge::option::overwrite_none)] }
|
||||||
#[merge(strategy = merge::option::overwrite_none)]
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
quote! {}
|
quote! {}
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_from_file_struct(ty) {
|
// Nested struct -> delegate to its own `FromFile` impl
|
||||||
// Nested FromFile struct
|
|
||||||
let field_decl = quote! {
|
|
||||||
#field_attrs
|
|
||||||
pub #ident: Option<#ty>
|
|
||||||
};
|
|
||||||
let assign = quote! {
|
|
||||||
#ident: <#ty>::from_file(file.#ident)
|
|
||||||
};
|
|
||||||
return Ok((field_decl, assign, None));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Primitive / leaf field
|
|
||||||
let default_expr = parse_from_file_default_attr(&field.attrs)?;
|
|
||||||
let field_decl = quote! {
|
let field_decl = quote! {
|
||||||
#field_attrs
|
#field_attrs
|
||||||
pub #ident: Option<#ty>
|
pub #ident: Option<#ty>
|
||||||
};
|
};
|
||||||
let assign = default_expr.map_or_else(
|
let assign = quote! {
|
||||||
|| quote! { #ident: file.#ident.unwrap_or_default() },
|
#ident: <#ty as filecaster::FromFile>::from_file(file.#ident)
|
||||||
|expr| quote! { #ident: file.#ident.unwrap_or_else(|| #expr) },
|
};
|
||||||
);
|
|
||||||
let default = quote! { #ty: Default };
|
|
||||||
|
|
||||||
Ok((field_decl, assign, Some(default)))
|
let default_bound = Some(quote! { #ty: Default });
|
||||||
|
|
||||||
|
Ok((field_decl, assign, default_bound))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process all fields
|
/// Process all fields
|
||||||
@ -76,10 +76,9 @@
|
|||||||
//!
|
//!
|
||||||
//! MIT OR Apache-2.0
|
//! MIT OR Apache-2.0
|
||||||
|
|
||||||
mod derive_from_file;
|
|
||||||
mod from_file;
|
mod from_file;
|
||||||
|
|
||||||
pub(crate) use derive_from_file::impl_from_file;
|
pub(crate) use from_file::impl_from_file;
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro_error2::proc_macro_error;
|
use proc_macro_error2::proc_macro_error;
|
||||||
use syn::{DeriveInput, parse_macro_input};
|
use syn::{DeriveInput, parse_macro_input};
|
||||||
29
filecaster/Cargo.toml
Normal file
29
filecaster/Cargo.toml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
[package]
|
||||||
|
name = "filecaster"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
authors = ["Kristofers Solo <dev@kristofers.xyz>"]
|
||||||
|
description = "Procedural macro to derive configuration from files, with optional merging capabilities."
|
||||||
|
repository = "https://github.com/kristoferssolo/filecaster"
|
||||||
|
documentation = "https://docs.rs/filecaster"
|
||||||
|
homepage = "https://github.com/kristoferssolo/filecaster"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
keywords = ["proc-macro", "derive", "configuration", "file-parsing"]
|
||||||
|
categories = ["rust-patterns", "parsing", "config"]
|
||||||
|
exclude = ["/.github", "/.gitignore", "/tests", "*.png", "*.md"]
|
||||||
|
readme = "README.md"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["derive"]
|
||||||
|
derive = ["dep:filecaster-derive"]
|
||||||
|
merge = ["dep:merge", "filecaster-derive/merge"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
filecaster-derive = { path = "../filecaster-derive", optional = true }
|
||||||
|
serde.workspace = true
|
||||||
|
merge = { workspace = true, optional = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
serde_json.workspace = true
|
||||||
|
tempfile.workspace = true
|
||||||
|
toml.workspace = true
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
pub use filecaster_derive::FromFile;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Marker for types that can be built from an `Option<Shadow>` produced by the macro.
|
/// Marker for types that can be built from an `Option<Shadow>` produced by the macro.
|
||||||
14
filecaster/tests/nested_structure.rs
Normal file
14
filecaster/tests/nested_structure.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
use filecaster::FromFile;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, PartialEq, FromFile)]
|
||||||
|
pub struct Coordinates {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, FromFile)]
|
||||||
|
pub struct Parent {
|
||||||
|
#[from_file(default = "Foo")]
|
||||||
|
name: String,
|
||||||
|
coordinates: Coordinates,
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user