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]]
|
||||
name = "filecaster"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"filecaster-derive",
|
||||
"merge",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filecaster-derive"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"claims",
|
||||
"merge",
|
||||
@ -58,10 +70,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn",
|
||||
"tempfile",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
39
Cargo.toml
39
Cargo.toml
@ -1,40 +1,17 @@
|
||||
[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"
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["filecaster", "filecaster-derive"]
|
||||
|
||||
[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"] }
|
||||
[workspace.dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
merge = { version = "0.2", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
merge = "0.2.0"
|
||||
# dev-dependencies
|
||||
claims = "0.8"
|
||||
serde_json = "1.0"
|
||||
tempfile = "3.20"
|
||||
tempfile = "3.10"
|
||||
toml = "0.9"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[lints.clippy]
|
||||
[workspace.lints.clippy]
|
||||
pedantic = "warn"
|
||||
nursery = "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 quote::{format_ident, quote};
|
||||
use syn::{
|
||||
@ -78,6 +80,15 @@ fn is_from_file_struct(ty: &Type) -> bool {
|
||||
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
|
||||
fn build_file_field(field: &Field) -> Result<(TokenStream, TokenStream, Option<TokenStream>)> {
|
||||
let ident = field
|
||||
@ -87,38 +98,23 @@ fn build_file_field(field: &Field) -> Result<(TokenStream, TokenStream, Option<T
|
||||
let ty = &field.ty;
|
||||
|
||||
let field_attrs = if WITH_MERGE {
|
||||
quote! {
|
||||
#[merge(strategy = merge::option::overwrite_none)]
|
||||
}
|
||||
quote! { #[merge(strategy = merge::option::overwrite_none)] }
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
if is_from_file_struct(ty) {
|
||||
// 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)?;
|
||||
// Nested struct -> delegate to its own `FromFile` impl
|
||||
let field_decl = quote! {
|
||||
#field_attrs
|
||||
pub #ident: Option<#ty>
|
||||
};
|
||||
let assign = default_expr.map_or_else(
|
||||
|| quote! { #ident: file.#ident.unwrap_or_default() },
|
||||
|expr| quote! { #ident: file.#ident.unwrap_or_else(|| #expr) },
|
||||
);
|
||||
let default = quote! { #ty: Default };
|
||||
let assign = quote! {
|
||||
#ident: <#ty as filecaster::FromFile>::from_file(file.#ident)
|
||||
};
|
||||
|
||||
Ok((field_decl, assign, Some(default)))
|
||||
let default_bound = Some(quote! { #ty: Default });
|
||||
|
||||
Ok((field_decl, assign, default_bound))
|
||||
}
|
||||
|
||||
/// Process all fields
|
||||
@ -76,10 +76,9 @@
|
||||
//!
|
||||
//! MIT OR Apache-2.0
|
||||
|
||||
mod derive_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_error2::proc_macro_error;
|
||||
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};
|
||||
|
||||
/// 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