Skip to main content

cup_cli/config/
mod.rs

1mod build;
2mod config;
3mod dependency;
4pub mod languages;
5mod package;
6mod program;
7pub mod utils;
8
9use std::{
10    collections::HashMap,
11    env::{current_dir, set_current_dir},
12    path::{Path, PathBuf},
13};
14
15pub use build::{Build, BuildConfig};
16pub use config::{ModeTable, ModeTableConfig, Table, TableConfig};
17pub use dependency::{Dependency, DependencyConfig, DependencySource};
18pub use languages::{Language, LanguageConfig};
19pub use package::{Package, PackageConfig};
20pub use program::Program;
21
22use crate::{config::program::ProgramConfig, utils::TryAll};
23
24/// 此 trait 用于提供原始配置文件数据(XXConfig)转换到最小配置数据结构(XX)的能力
25pub trait Shelling<T, P: AsRef<Path>> {
26    /// 剥壳函数,此函数实现校验逻辑
27    /// `base` 为配置文件所在的绝对路径
28    fn shelling(self, base: P) -> T;
29}
30
31/// 此 trait 用于提取缩减表示的原始展开,例如路径通配符的展开
32pub trait Expand<T, P: AsRef<Path>> {
33    fn expand(self, base: P) -> T;
34}
35
36impl<P: AsRef<Path>> Expand<anyhow::Result<Vec<PathBuf>>, P> for Vec<String> {
37    fn expand(self, base: P) -> anyhow::Result<Vec<PathBuf>> {
38        let cpath = current_dir()?;
39        set_current_dir(&base)?;
40
41        // 展开路径通配符
42        let mut result = vec![];
43        for pattern in self.iter() {
44            for entry in glob::glob(pattern)? {
45                let path = entry?;
46                let path = if path.is_relative() {
47                    base.as_ref().join(path)
48                } else {
49                    path
50                };
51                result.push(path);
52            }
53        }
54
55        set_current_dir(cpath)?;
56        Ok(result)
57    }
58}
59
60#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
61pub struct RawConfig {
62    pub package: PackageConfig,
63    pub build: Option<BuildConfig>,
64    pub dependencies: Option<HashMap<String, DependencyConfig>>,
65    #[serde(rename = "dev-dependencies")]
66    pub dev_dependencies: Option<HashMap<String, DependencyConfig>>,
67    pub executables: Option<Vec<ProgramConfig>>,
68    pub tests: Option<Vec<ProgramConfig>>,
69    pub examples: Option<Vec<ProgramConfig>>,
70    pub features: Option<HashMap<String, Vec<String>>>,
71    pub target: Option<HashMap<String, ModeTableConfig>>,
72    pub feature: Option<HashMap<String, ModeTableConfig>>,
73    pub generator: Option<HashMap<String, ModeTableConfig>>,
74}
75
76impl<P: AsRef<Path>> Shelling<anyhow::Result<Config>, P> for RawConfig {
77    fn shelling(self, base: P) -> anyhow::Result<Config> {
78        Ok(Config {
79            package: self.package.shelling(&base),
80            build: self.build.unwrap_or_default().shelling(&base)?,
81            dependencies: self
82                .dependencies
83                .unwrap_or_default()
84                .into_iter()
85                .try_all(|(k, v)| -> anyhow::Result<_> { Ok((k, v.shelling(&base)?)) })?
86                .collect(),
87            dev_dependencies: self
88                .dev_dependencies
89                .unwrap_or_default()
90                .into_iter()
91                .try_all(|(k, v)| -> anyhow::Result<_> { Ok((k, v.shelling(&base)?)) })?
92                .collect(),
93            executables: self
94                .executables
95                .unwrap_or_default()
96                .into_iter()
97                .map(|v| v.shelling(&base))
98                .collect(),
99            tests: self
100                .tests
101                .unwrap_or_default()
102                .into_iter()
103                .map(|v| v.shelling(&base))
104                .collect(),
105            examples: self
106                .examples
107                .unwrap_or_default()
108                .into_iter()
109                .map(|v| v.shelling(&base))
110                .collect(),
111            features: self.features.unwrap_or_default(),
112            target: self
113                .target
114                .unwrap_or_default()
115                .into_iter()
116                .try_all(|(k, v)| -> anyhow::Result<_> { Ok((k, v.shelling(&base)?)) })?
117                .collect(),
118            feature: self
119                .feature
120                .unwrap_or_default()
121                .into_iter()
122                .try_all(|(k, v)| -> anyhow::Result<_> { Ok((k, v.shelling(&base)?)) })?
123                .collect(),
124            generator: self
125                .generator
126                .unwrap_or_default()
127                .into_iter()
128                .try_all(|(k, v)| -> anyhow::Result<_> { Ok((k, v.shelling(&base)?)) })?
129                .collect(),
130        })
131    }
132}
133
134/// 原始项目配置
135#[derive(Debug, Clone)]
136pub struct Config {
137    pub package: Package,
138    pub build: Build,
139    pub dependencies: HashMap<String, Dependency>,
140    pub dev_dependencies: HashMap<String, Dependency>,
141    pub executables: Vec<Program>,
142    pub tests: Vec<Program>,
143    pub examples: Vec<Program>,
144    pub features: HashMap<String, Vec<String>>,
145    pub target: HashMap<String, ModeTable>,
146    pub feature: HashMap<String, ModeTable>,
147    pub generator: HashMap<String, ModeTable>,
148}
149
150impl Config {
151    /// 读取项目的配置信息
152    pub fn read<P: AsRef<Path>>(project: P) -> anyhow::Result<Config> {
153        let content = fs_err::read_to_string(project.as_ref().join("Cup.toml"))?;
154        let raw_config: RawConfig = toml::from_str(&content)?;
155        Ok(raw_config.shelling(project)?)
156    }
157}