clap_adapters/
lib.rs

1//! Adapter types for declaratively loading configurations via [`clap`]
2//!
3//! Types which implement [`FromStr`] may be used in a [`clap`] derive
4//! struct. This crate provides adapters which accept [`Path`]-like inputs
5//! as arguments from the user and automatically loads and parses the file
6//! at the path.
7//!
8//! The base adapter is `PathTo<T>`, which takes a path from the user and
9//! attempts to load the contents of the file into an instance of `T`, which
10//! must implement [`FromReader`]. This crate implements `FromReader` for
11//! the following types:
12//!
13//! - `Vec<u8>`
14//! - `String`
15//! - `JsonOf<T>` (where `T: serde::DeserializeOwned`)
16//! - `TomlOf<T>` (where `T: serde::DeserializeOwned`)
17//! - `YamlOf<T>` (where `T: serde::DeserializeOwned`)
18//!
19//! Additionally, `PathTo` may be wrapped in either `Periodic<T>` or `Reloading<T>`
20//! to gain the ability to automatically _reload_ the file at the user-given path
21//! at a regular interval or when the file is updated, respectively.
22//!
23//! # Example
24//!
25//! ```
26//! # fn main() -> anyhow::Result<()> {
27//! use clap::Parser;
28//! use clap_adapters::prelude::*;
29//!
30//! #[derive(Debug, Parser)]
31//! struct Cli {
32//!     /// Path to a config file of arbitrary Json
33//!     #[clap(long)]
34//!     config: PathTo<JsonOf<serde_json::Value>>,
35//! }
36//!
37//! // Create a config file in a temporary directory
38//! let config_dir = tempfile::tempdir()?;
39//! let config_path = config_dir.path().join("config.json");
40//! let config_path_string = config_path.display().to_string();
41//!
42//! // Write a test config of {"hello":"world"} to the config file
43//! let config = serde_json::json!({"hello": "world"});
44//! let config_string = serde_json::to_string(&config)?;
45//! std::fs::write(&config_path, &config_string)?;
46//!
47//! // Parse our CLI, passing our config file path to --config
48//! let cli = Cli::parse_from(["app", "--config", &config_path_string]);
49//! let data = cli.config.data();
50//!
51//! // We should expect the value we get to match what we wrote to the config
52//! assert_eq!(data, &serde_json::json!({"hello":"world"}));
53//! # Ok(())
54//! # }
55//! ```
56//!
57//! [`Path`]: std::path
58//! [`FromStr`]: std::str::FromStr
59//! [`FromReader`]: crate::traits::FromReader
60
61#![warn(missing_docs)]
62
63/// Adapters for reading file contents from CLI paths
64mod fs;
65
66/// Adapters for parsing JSON documents
67mod json;
68
69#[cfg(any(doc, feature = "periodic"))]
70mod periodic;
71
72/// Adapter for auto-reloading file contents on change
73#[cfg(any(doc, feature = "reloading"))]
74mod reloading;
75
76/// Adapters for parsing TOML documents
77mod toml;
78
79/// Traits for glueing adapters together
80pub mod traits;
81
82/// Adapters for parsing YAML documents
83mod yaml;
84
85pub use {fs::PathTo, json::JsonOf, toml::TomlOf, yaml::YamlOf};
86
87#[cfg(any(doc, feature = "periodic"))]
88pub use periodic::Periodic;
89
90#[cfg(any(doc, feature = "reloading"))]
91pub use reloading::Reloading;
92
93/// Convenience import for clap adapter building blocks
94pub mod prelude {
95    pub use crate::fs::*;
96    pub use crate::json::*;
97    #[cfg(any(doc, feature = "periodic"))]
98    pub use crate::periodic::*;
99    #[cfg(any(doc, feature = "reloading"))]
100    pub use crate::reloading::*;
101    pub use crate::toml::*;
102    pub use crate::traits::*;
103    pub use crate::yaml::*;
104}