mirror of
https://github.com/kristoferssolo/filecaster.git
synced 2025-10-21 19:00:34 +00:00
docs: update libdocs
This commit is contained in:
parent
dabacf02df
commit
9d365a9593
@ -1,39 +1,42 @@
|
|||||||
//! # filecaster
|
//! # filecaster-derive
|
||||||
//!
|
//!
|
||||||
//! `filecaster` is a small `proc-macro` crate that provides a derive‐macro
|
//! `filecaster-derive` is the procedural macro crate for `filecaster`. It provides the
|
||||||
//! `#[derive(FromFile)]` to make it trivial to load partial configurations
|
//! `#[derive(FromFile)]` macro, which automates the process of loading partial
|
||||||
//! from files, merge them with defaults, and get a fully‐populated struct.
|
//! configurations from files, merging them with default values, and constructing
|
||||||
|
//! fully-populated Rust structs.
|
||||||
|
//!
|
||||||
|
//! This crate significantly simplifies configuration management by generating
|
||||||
|
//! the necessary boilerplate code for the `FromFile` trait (defined in the
|
||||||
|
//! `filecaster` crate).
|
||||||
//!
|
//!
|
||||||
//! ## What it does
|
//! ## What it does
|
||||||
//!
|
//!
|
||||||
//! For any struct with named fields, `#[derive(FromFile)]` generates:
|
//! For any struct with named fields, `#[derive(FromFile)]` generates:
|
||||||
//!
|
//!
|
||||||
//! 1. A companion `<YourStruct>NameFile` struct in which each field is wrapped
|
//! 1. A companion "shadow" struct (e.g., `YourStructFile` for `YourStruct`)
|
||||||
//! in `Option<...>`.
|
//! where each field is wrapped in `Option<T>`. This shadow struct is
|
||||||
//! 2. A constructor `YourStruct::from_file(file: Option<YourStructFile>) -> YourStruct`
|
//! designed for deserialization from configuration files (e.g., JSON, TOML, YAML).
|
||||||
//! that takes your partially‐filled file struct, fills in `None` fields
|
//! 2. An implementation of the `FromFile` trait for your original struct. This
|
||||||
//! with either:
|
//! includes the `from_file` method, which takes an `Option<YourStructFile>`
|
||||||
//! - an expression you supply via `#[from_file(default = ...)]`, or
|
//! and constructs your final `YourStruct`. It intelligently fills in `None`
|
||||||
//! - `Default::default()` (requires `T: Default`)
|
//! fields with either:
|
||||||
//! 3. An implementation of `From<Option<YourStructFile>> for YourStruct`.
|
//! - An expression you supply via `#[from_file(default = ...)]`.
|
||||||
|
//! - `Default::default()` (if no `default` attribute is provided, requiring `T: Default`).
|
||||||
//!
|
//!
|
||||||
//! Because each field in the file‐struct is optional, you can deserialize
|
//! ## Optional per-field defaults
|
||||||
//! e.g. JSON, YAML or TOML into it via Serde, then call `.from_file(...)`
|
|
||||||
//! to get your final struct.
|
|
||||||
//!
|
|
||||||
//! ## Optional per‐field defaults
|
|
||||||
//!
|
//!
|
||||||
//! Use a `#[from_file(default = <expr>)]` attribute on any field to override
|
//! Use a `#[from_file(default = <expr>)]` attribute on any field to override
|
||||||
//! the fallback value. You may supply any expression valid in that struct’s
|
//! the fallback value. You may supply any expression valid in that struct’s
|
||||||
//! context. If you omit it, the macro will require `T: Default` and call
|
//! context. If you omit it, the macro will require the field's type to implement
|
||||||
//! `unwrap_or_default()`.
|
//! `Default` and will call `Default::default()`.
|
||||||
//!
|
//!
|
||||||
//! Example:
|
//! ## Example
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use filecaster::FromFile;
|
//! use filecaster::FromFile;
|
||||||
|
//! use serde::{Deserialize, Serialize};
|
||||||
//!
|
//!
|
||||||
//! #[derive(Debug, Clone, FromFile)]
|
//! #[derive(Debug, Clone, PartialEq, FromFile, Serialize, Deserialize)]
|
||||||
//! struct AppConfig {
|
//! struct AppConfig {
|
||||||
//! /// If the user does not specify a host, use `"127.0.0.1"`.
|
//! /// If the user does not specify a host, use `"127.0.0.1"`.
|
||||||
//! #[from_file(default = "127.0.0.1")]
|
//! #[from_file(default = "127.0.0.1")]
|
||||||
@ -43,38 +46,54 @@
|
|||||||
//! #[from_file(default = 4)]
|
//! #[from_file(default = 4)]
|
||||||
//! workers: usize,
|
//! workers: usize,
|
||||||
//!
|
//!
|
||||||
//! /// If not set, use `false`.
|
//! /// If not set, use `false`. Requires `bool: Default`.
|
||||||
//! auto_reload: bool, // requires `bool: Default`
|
//! auto_reload: bool,
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! let file_content = r#"
|
//! fn main() {
|
||||||
//! {
|
//! // Simulate file content (e.g., from a JSON file)
|
||||||
//! "host": "localhost"
|
//! let file_content = r#"{
|
||||||
//! }
|
//! "host": "localhost",
|
||||||
//! "#;
|
//! "workers": 8
|
||||||
|
//! }"#;
|
||||||
//!
|
//!
|
||||||
//! let config_from_file = serde_json::from_str::<AppConfigFile>(file_content).unwrap();
|
//! // The `AppConfigFile` struct is automatically generated by `#[derive(FromFile)]`.
|
||||||
//! // After deserializing the partial config from disk (e.g. with Serde):
|
//! // It has all fields as `Option<T>`.
|
||||||
//! let cfg = AppConfig::from_file(Some(config_from_file));
|
//! let partial_config: AppConfigFile = serde_json::from_str(file_content).unwrap();
|
||||||
//! println!("{cfg:#?}");
|
//!
|
||||||
|
//! // Use the generated `from_file` method to get the final config.
|
||||||
|
//! // Default values are applied for missing fields.
|
||||||
|
//! let config = AppConfig::from_file(Some(partial_config));
|
||||||
|
//!
|
||||||
|
//! assert_eq!(config.host, "localhost");
|
||||||
|
//! assert_eq!(config.workers, 8);
|
||||||
|
//! assert_eq!(config.auto_reload, false); // `Default::default()` for bool is `false`
|
||||||
|
//!
|
||||||
|
//! println!("Final Config: {:#?}", config);
|
||||||
|
//!
|
||||||
|
//! // Example with no file content (all defaults)
|
||||||
|
//! let default_config = AppConfig::from_file(None);
|
||||||
|
//! assert_eq!(default_config.host, "127.0.0.1");
|
||||||
|
//! assert_eq!(default_config.workers, 4);
|
||||||
|
//! assert_eq!(default_config.auto_reload, false);
|
||||||
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ## Feature flags
|
//! ## Feature flags
|
||||||
//!
|
//!
|
||||||
//! - `merge`
|
//! - `serde`: Enables `serde` serialization/deserialization support for the
|
||||||
//! If you enable the `merge` feature, the generated `<Name>File` struct will
|
//! generated shadow structs. This is typically required to deserialize
|
||||||
//! also derive `merge::Merge`, and you can layer multiple partial files
|
//! your configuration from file formats like JSON, TOML, or YAML.
|
||||||
//! together before calling `.from_file(...)`. Any field‐level merge strategy
|
//! - `merge`: If enabled, the generated shadow struct will also derive
|
||||||
//! annotations (`#[merge(...)]`) are applied automatically.
|
//! `merge::Merge`. This allows you to layer multiple partial configuration
|
||||||
|
//! files together before calling `.from_file(...)`. Any field-level
|
||||||
|
//! `#[merge(...)]` attributes will be respected.
|
||||||
//!
|
//!
|
||||||
//! ## Limitations
|
//! ## Limitations
|
||||||
//!
|
//!
|
||||||
//! - Only works on structs with _named_ fields (no tuple‐structs or enums).
|
//! - Only works on structs with _named_ fields (no tuple structs or enums).
|
||||||
//! - All fields without a `#[from_file(default = ...)]` must implement `Default`.
|
//! - All fields without a `#[from_file(default = ...)]` attribute must
|
||||||
//!
|
//! implement the `Default` trait.
|
||||||
//! ## License
|
|
||||||
//!
|
|
||||||
//! MIT OR Apache-2.0
|
|
||||||
|
|
||||||
mod from_file;
|
mod from_file;
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,124 @@
|
|||||||
|
//! # filecaster
|
||||||
|
//!
|
||||||
|
//! `filecaster` provides the core `FromFile` trait, which is used in conjunction with the
|
||||||
|
//! `filecaster-derive` crate to enable automatic deserialization and merging of
|
||||||
|
//! configuration from various file formats into Rust structs.
|
||||||
|
//!
|
||||||
|
//! This crate defines the fundamental interface for types that can be constructed
|
||||||
|
//! from an optional "shadow" representation, typically deserialized from a file.
|
||||||
|
//! The `filecaster-derive` crate provides a procedural macro to automatically
|
||||||
|
//! implement this trait for your structs, handling default values and merging logic.
|
||||||
|
//!
|
||||||
|
//! ## How it works
|
||||||
|
//!
|
||||||
|
//! The `FromFile` trait defines how a final configuration struct (`Self`) can be
|
||||||
|
//! constructed from an optional intermediate "shadow" struct (`Self::Shadow`).
|
||||||
|
//! The `filecaster-derive` macro generates this `Shadow` struct and the
|
||||||
|
//! `from_file` implementation for your configuration types.
|
||||||
|
//!
|
||||||
|
//! When you derive `FromFile` for a struct, `filecaster-derive` creates a
|
||||||
|
//! corresponding `YourStructFile` (the `Shadow` type) where all fields are
|
||||||
|
//! wrapped in `Option<T>`. This `YourStructFile` can then be deserialized
|
||||||
|
//! from a file (e.g., JSON, TOML, YAML) using `serde`.
|
||||||
|
//!
|
||||||
|
//! The `from_file` method then takes this `Option<YourStructFile>` and
|
||||||
|
//! constructs your final `YourStruct`, applying default values for any fields
|
||||||
|
//! that were `None` in the `YourStructFile`.
|
||||||
|
//!
|
||||||
|
//! ## Example
|
||||||
|
//!
|
||||||
|
//! While the `FromFile` trait is implemented via the `filecaster-derive` macro,
|
||||||
|
//! here's a conceptual example of how it's used:
|
||||||
|
//!
|
||||||
|
//! ```rust,ignore
|
||||||
|
//! use filecaster::FromFile;
|
||||||
|
//! use serde::{Deserialize, Serialize};
|
||||||
|
//!
|
||||||
|
//! // This struct would typically have `#[derive(FromFile)]`
|
||||||
|
//! // from the `filecaster-derive` crate.
|
||||||
|
//! #[derive(Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||||
|
//! struct AppConfig {
|
||||||
|
//! host: String,
|
||||||
|
//! port: u16,
|
||||||
|
//! enabled: bool,
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // The `Shadow` type is automatically generated by `filecaster-derive`
|
||||||
|
//! // and would look something like this:
|
||||||
|
//! #[derive(Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||||
|
//! struct AppConfigFile {
|
||||||
|
//! host: Option<String>,
|
||||||
|
//! port: Option<u16>,
|
||||||
|
//! enabled: Option<bool>,
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // The `FromFile` implementation is also automatically generated.
|
||||||
|
//! // For demonstration, here's a simplified manual implementation:
|
||||||
|
//! impl FromFile for AppConfig {
|
||||||
|
//! type Shadow = AppConfigFile;
|
||||||
|
//!
|
||||||
|
//! fn from_file(file: Option<Self::Shadow>) -> Self {
|
||||||
|
//! let file = file.unwrap_or_default();
|
||||||
|
//! AppConfig {
|
||||||
|
//! host: file.host.unwrap_or_else(|| "127.0.0.1".to_string()),
|
||||||
|
//! port: file.port.unwrap_or(8080),
|
||||||
|
//! enabled: file.enabled.unwrap_or(true),
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! fn main() {
|
||||||
|
//! // Simulate deserializing from a file
|
||||||
|
//! let file_content = r#"{ "host": "localhost", "port": 3000 }"#;
|
||||||
|
//! let partial_config: AppConfigFile = serde_json::from_str(file_content).unwrap();
|
||||||
|
//!
|
||||||
|
//! // Construct the final config using the FromFile trait
|
||||||
|
//! let config = AppConfig::from_file(Some(partial_config));
|
||||||
|
//!
|
||||||
|
//! assert_eq!(config.host, "localhost");
|
||||||
|
//! assert_eq!(config.port, 3000);
|
||||||
|
//! assert_eq!(config.enabled, true); // Default value applied
|
||||||
|
//!
|
||||||
|
//! println!("Final Config: {:?}", config);
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ## Feature flags
|
||||||
|
//!
|
||||||
|
//! - `derive`: Enables the `filecaster-derive` crate, allowing you to use `#[derive(FromFile)]`.
|
||||||
|
//! - `serde`: Enables `serde` serialization/deserialization support for the `FromFile` trait.
|
||||||
|
//! - `merge`: Enables `merge` crate support, allowing for merging multiple partial configurations.
|
||||||
|
|
||||||
pub use filecaster_derive::FromFile;
|
pub use filecaster_derive::FromFile;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
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.
|
||||||
|
///
|
||||||
|
/// The `FromFile` trait is the core interface for `filecaster`. It defines how a
|
||||||
|
/// final configuration struct (`Self`) can be constructed from an optional
|
||||||
|
/// intermediate "shadow" struct (`Self::Shadow`).
|
||||||
|
///
|
||||||
|
/// The `Self::Shadow` associated type represents the intermediate structure
|
||||||
|
/// that is typically deserialized from a configuration file. All fields in
|
||||||
|
/// `Self::Shadow` are usually `Option<T>`, allowing for partial configurations.
|
||||||
|
///
|
||||||
|
/// The `from_file` method takes an `Option<Self::Shadow>` and is responsible
|
||||||
|
/// for producing a fully-populated `Self` instance. This involves applying
|
||||||
|
/// default values for any fields that were `None` in the `Shadow` instance.
|
||||||
|
///
|
||||||
|
/// This trait is primarily designed to be implemented automatically via the
|
||||||
|
/// `#[derive(FromFile)]` procedural macro provided by the `filecaster-derive` crate.
|
||||||
pub trait FromFile: Sized {
|
pub trait FromFile: Sized {
|
||||||
|
/// The intermediate "shadow" type that is typically deserialized from a file.
|
||||||
|
///
|
||||||
|
/// This type usually mirrors the main struct but with all fields wrapped in `Option<T>`.
|
||||||
type Shadow: Default;
|
type Shadow: Default;
|
||||||
|
/// Constructs the final struct from an optional shadow representation.
|
||||||
|
///
|
||||||
|
/// If `file` is `None`, a default `Shadow` instance should be used.
|
||||||
|
/// The implementation should then populate `Self` by taking values from
|
||||||
|
/// `file` where present, and applying defaults otherwise.
|
||||||
fn from_file(file: Option<Self::Shadow>) -> Self;
|
fn from_file(file: Option<Self::Shadow>) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user