plantuml_server_client_rs/
config.rs

1pub mod args;
2pub mod file;
3
4use crate::{Args, FileConfig, Format, MetadataVersion, Method, URL_PREFIX};
5use anyhow::Context as _;
6use std::path::PathBuf;
7
8/// A configuration of `plantuml-server-client`.
9#[derive(Debug)]
10pub struct Config {
11    /// The HTTP Request Method ([`Method::Get`] or [`Method::Post`])
12    pub method: Method,
13
14    /// The URL prefix of the PlantUML Server (e.g. `http://localhost:8080/`)
15    pub url_prefix: String,
16
17    /// The output format ([`Format::Svg`] or [`Format::Png`] or [`Format::Ascii`] (`txt`))
18    pub format: Format,
19
20    /// The whether to output a `combined` diagram.
21    pub combined: bool,
22
23    /// The output path for metadata
24    pub metadata: Option<PathBuf>,
25
26    /// The version of metadata
27    pub metadata_version: MetadataVersion,
28}
29
30trait ConfigTrait {
31    /// Returns HTTP Request Method ([`Method::Get`] or [`Method::Post`])
32    fn method(&self) -> Option<Method>;
33
34    /// Returns URL prefix of the PlantUML Server (e.g. `http://localhost:8080/`)
35    fn url_prefix(&self) -> Option<String>;
36
37    /// Returns output format ([`Format::Svg`] or [`Format::Png`] or [`Format::Ascii`] (`txt`))
38    fn format(&self) -> Option<Format>;
39
40    /// Returns whether to output a `combined` diagram.
41    fn combined(&self) -> Option<bool>;
42
43    /// Returns the version of metadata.
44    fn metadata_version(&self) -> Option<MetadataVersion>;
45}
46
47/// Treats priority (default configuration < file configuration < args configuration)
48macro_rules! overwrite_item {
49    ($config:expr_2021, $args:expr_2021, $from_file:expr_2021, $member:ident) => {
50        let x = $args
51            .$member()
52            .or_else(|| $from_file.as_ref().and_then(|x| x.$member()));
53        if let Some(x) = x {
54            $config.$member = x;
55        }
56    };
57}
58
59impl Config {
60    /// Creates a [`Config`] from default value -> file -> args with to read file.
61    pub async fn load(args: &Args) -> anyhow::Result<Self> {
62        let from_file = match &args.config {
63            Some(filepath) => {
64                let config = FileConfig::read_from_file(filepath)
65                    .await
66                    .with_context(|| {
67                        format!("failed to read `{filepath:?}` that specified by args.")
68                    })?;
69                Some(config)
70            }
71            None => FileConfig::read_from_default_path().await,
72        };
73
74        Self::merge_from(args, from_file)
75    }
76
77    /// Creates a [`Config`] from default value -> file -> args
78    ///
79    /// * `args` - A configuration from command-line arguments.
80    /// * `from_file` - A configuration from the file.
81    fn merge_from(args: &Args, from_file: Option<FileConfig>) -> anyhow::Result<Self> {
82        let mut config = Self::default();
83
84        overwrite_item!(config, args, from_file, method);
85        overwrite_item!(config, args, from_file, url_prefix);
86        overwrite_item!(config, args, from_file, format);
87        overwrite_item!(config, args, from_file, combined);
88        config.metadata = args.metadata.clone();
89        overwrite_item!(config, args, from_file, metadata_version);
90
91        if config.metadata.is_some() && config.metadata_version.is_v1() {
92            tracing::warn!(
93                "The `v1` metadata format is deprecated. Please specify `--metadata-version v2` and change post process."
94            );
95        }
96
97        Ok(config)
98    }
99}
100
101impl Default for Config {
102    fn default() -> Self {
103        Self {
104            method: Method::Get,
105            url_prefix: URL_PREFIX.into(),
106            format: Format::Svg,
107            combined: false,
108            metadata: None,
109            metadata_version: MetadataVersion::V1,
110        }
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117
118    use clap::Parser;
119
120    #[test]
121    fn test_combined() -> anyhow::Result<()> {
122        // args -> false, file -> false
123        let mut args = Args::parse();
124        args.combined = false;
125        let from_file: FileConfig = toml::from_str("[pscr]\ncombined = false")?;
126        let config = Config::merge_from(&args, Some(from_file))?;
127        assert!(!config.combined);
128
129        // args -> false, file -> true
130        let mut args = Args::parse();
131        args.combined = false;
132        let from_file: FileConfig = toml::from_str("[pscr]\ncombined = true")?;
133        let config = Config::merge_from(&args, Some(from_file))?;
134        assert!(config.combined);
135
136        // args -> false, file -> None
137        let mut args = Args::parse();
138        args.combined = false;
139        let from_file: FileConfig = toml::from_str("[pscr]")?;
140        let config = Config::merge_from(&args, Some(from_file))?;
141        assert!(!config.combined);
142
143        // args -> true, file -> false
144        let mut args = Args::parse();
145        args.combined = true;
146        let from_file: FileConfig = toml::from_str("[pscr]\ncombined = false")?;
147        let config = Config::merge_from(&args, Some(from_file))?;
148        assert!(config.combined);
149
150        // args -> true, file -> true
151        let mut args = Args::parse();
152        args.combined = true;
153        let from_file: FileConfig = toml::from_str("[pscr]\ncombined = true")?;
154        let config = Config::merge_from(&args, Some(from_file))?;
155        assert!(config.combined);
156
157        // args -> true, file -> None
158        let mut args = Args::parse();
159        args.combined = true;
160        let from_file: FileConfig = toml::from_str("[pscr]")?;
161        let config = Config::merge_from(&args, Some(from_file))?;
162        assert!(config.combined);
163
164        Ok(())
165    }
166}