feat(serde): make serde optional default

This commit is contained in:
Kristofers Solo 2025-07-15 14:40:36 +03:00
parent 337491b37f
commit db1dab2aa1
Signed by: kristoferssolo
GPG Key ID: 8687F2D3EEE6F0ED
5 changed files with 26 additions and 46 deletions

View File

@ -4,7 +4,7 @@ members = ["filecaster", "filecaster-derive"]
[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }
merge = "0.2.0"
merge = "0.2"
# dev-dependencies
claims = "0.8"
serde_json = "1.0"

View File

@ -7,7 +7,8 @@ edition = "2024"
proc-macro = true
[features]
default = []
default = ["serde"]
serde = ["dep:serde"]
merge = ["dep:merge"]
[dependencies]
@ -15,7 +16,7 @@ proc-macro2 = "1.0"
quote = "1.0"
proc-macro-error2 = "2.0"
syn = { version = "2.0", features = ["extra-traits", "parsing"] }
serde.workspace = true
serde = { workspace = true, optional = true }
merge = { workspace = true, optional = true }
[dev-dependencies]

View File

@ -1,5 +1,3 @@
use std::path::PathBuf;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{
@ -72,23 +70,6 @@ fn extract_named_fields(input: &DeriveInput) -> Result<&FieldsNamed> {
}
}
/// Nested-struct detection
fn is_from_file_struct(ty: &Type) -> bool {
if let Type::Path(TypePath { qself: None, path }) = ty {
return path.segments.len() == 1;
}
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
@ -159,14 +140,10 @@ fn build_where_clause(
/// Derive clause for the shadow struct
fn build_derive_clause() -> TokenStream {
if WITH_MERGE {
return quote! {
#[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize, merge::Merge)]
};
}
quote! {
#[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize)]
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "merge", derive(merge::Merge))]
}
}
@ -332,21 +309,6 @@ mod tests {
assert_eq!(preds, vec!["Z : Eq".to_string()]);
}
#[test]
fn build_derive_clause_defaults() {
let derive_ts = build_derive_clause();
let s = derive_ts.to_string();
if WITH_MERGE {
assert!(s.contains(
"derive (Debug , Clone , Default , serde :: Deserialize , serde :: Serialize , merge :: Merge)"
));
} else {
assert!(s.contains(
"derive (Debug , Clone , Default , serde :: Deserialize , serde :: Serialize)"
));
}
}
#[test]
fn add_trait_bouds_appends_default() {
let gens: Generics = parse_quote!(<T, U>);

View File

@ -14,13 +14,14 @@ exclude = ["/.github", "/.gitignore", "/tests", "*.png", "*.md"]
readme = "README.md"
[features]
default = ["derive"]
default = ["serde", "derive"]
derive = ["dep:filecaster-derive"]
serde = ["dep:serde", "filecaster-derive/serde"]
merge = ["dep:merge", "filecaster-derive/merge"]
[dependencies]
filecaster-derive = { path = "../filecaster-derive", optional = true }
serde.workspace = true
serde = { workspace = true, optional = true }
merge = { workspace = true, optional = true }
[dev-dependencies]

View File

@ -1,4 +1,5 @@
pub use filecaster_derive::FromFile;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// Marker for types that can be built from an `Option<Shadow>` produced by the macro.
@ -6,9 +7,13 @@ pub trait FromFile: Sized {
fn from_file(file: Option<Self::Shadow>) -> Self;
/// Associated shadow type generated by the macro.
#[cfg(feature = "serde")]
type Shadow: Default + Serialize + for<'de> Deserialize<'de>;
#[cfg(not(feature = "serde"))]
type Shadow: Default;
}
#[cfg(feature = "serde")]
impl<T> FromFile for T
where
T: Default + Serialize + for<'de> Deserialize<'de>,
@ -18,3 +23,14 @@ where
file.unwrap_or_default()
}
}
#[cfg(not(feature = "serde"))]
impl<T> FromFile for T
where
T: Default,
{
type Shadow = T;
fn from_file(file: Option<Self::Shadow>) -> Self {
file.unwrap_or_default()
}
}