cargo_generate/
app_config.rs

1use anyhow::{Context, Result};
2use console::style;
3use serde::Deserialize;
4use std::{
5    collections::HashMap,
6    fs,
7    path::{Path, PathBuf},
8};
9
10use crate::{info, Vcs};
11
12pub const CONFIG_FILE_NAME: &str = "cargo-generate.toml";
13
14#[derive(Deserialize, Default, Debug)]
15pub struct AppConfig {
16    pub defaults: Option<DefaultsConfig>,
17    pub favorites: Option<HashMap<String, FavoriteConfig>>,
18    pub values: Option<HashMap<String, toml::Value>>,
19}
20
21impl AppConfig {
22    pub fn get_favorite_cfg(&self, favorite_name: &str) -> Option<&FavoriteConfig> {
23        self.favorites.as_ref().and_then(|f| f.get(favorite_name))
24    }
25}
26
27#[derive(Deserialize, Default, Debug)]
28pub struct FavoriteConfig {
29    pub description: Option<String>,
30    pub git: Option<String>,
31    pub branch: Option<String>,
32    pub tag: Option<String>,
33    pub revision: Option<String>,
34    pub subfolder: Option<String>,
35    pub path: Option<PathBuf>,
36    pub values: Option<HashMap<String, toml::Value>>,
37    pub vcs: Option<Vcs>,
38    pub init: Option<bool>,
39    pub overwrite: Option<bool>,
40}
41
42#[derive(Deserialize, Default, Debug)]
43pub struct DefaultsConfig {
44    /// relates to `crate::Args::ssh_identity`
45    pub ssh_identity: Option<PathBuf>,
46}
47
48impl TryFrom<&Path> for AppConfig {
49    type Error = anyhow::Error;
50
51    fn try_from(path: &Path) -> Result<Self, Self::Error> {
52        if !path.exists() {
53            return Ok(Default::default());
54        }
55
56        let cfg = fs::read_to_string(path)?;
57        Ok(if cfg.trim().is_empty() {
58            Self::default()
59        } else {
60            info!("Using application config: {}", style(path.display()).bold());
61            toml::from_str(&cfg)?
62        })
63    }
64}
65
66/// # Panics
67pub fn app_config_path(path: &Option<PathBuf>) -> Result<PathBuf> {
68    path.as_ref()
69        .map(|p| p.canonicalize().unwrap())
70        .or_else(|| {
71            home::cargo_home().map_or(None, |home| {
72                let preferred_path = home.join(CONFIG_FILE_NAME);
73                if preferred_path.exists() {
74                    Some(preferred_path)
75                } else {
76                    let without_extension = preferred_path.with_extension("");
77                    if without_extension.exists() {
78                        Some(without_extension)
79                    } else {
80                        Some(preferred_path)
81                    }
82                }
83            })
84        })
85        .with_context(|| {
86            format!(
87                r#"
88Unable to resolve path for configuration file.
89Use --config option, or place {CONFIG_FILE_NAME} in $CARGO_HOME."#
90            )
91        })
92}