quanttide_devops/contract/
scope.rs1use serde::de::{Deserializer, MapAccess, Visitor};
2use serde::{Deserialize, Serialize};
3use std::fmt;
4
5use super::platform::Registry;
6use super::stage::StageRelease;
7
8#[derive(Debug, Clone, Serialize)]
14#[serde(rename_all = "snake_case")]
15pub struct Scope {
16 pub name: String,
17 pub dir: String,
18 #[serde(default)]
19 pub language: Language,
20 #[serde(default)]
21 pub framework: String,
22 #[serde(default)]
23 pub build_tool: BuildTool,
24 #[serde(default)]
25 pub registry: Registry,
26 #[serde(default)]
27 pub release: StageRelease,
28 #[serde(default)]
29 pub test_threshold: Option<f64>,
30 #[serde(default)]
31 pub ci_workflow: Option<String>,
32}
33
34#[derive(Debug, Clone, PartialEq, Serialize)]
36#[serde(rename_all = "snake_case")]
37pub enum Language {
38 Rust,
39 Python,
40 Go,
41 Dart,
42 #[serde(rename = "typescript")]
43 TypeScript,
44 Unknown(String),
45}
46
47impl Default for Language {
48 fn default() -> Self {
49 Self::Unknown("auto".into())
50 }
51}
52
53impl Language {
54 pub fn as_str(&self) -> &str {
56 match self {
57 Self::Rust => "rust",
58 Self::Python => "python",
59 Self::Go => "go",
60 Self::Dart => "dart",
61 Self::TypeScript => "typescript",
62 Self::Unknown(s) => s,
63 }
64 }
65}
66
67#[derive(Debug, Clone, PartialEq, Serialize)]
69#[serde(rename_all = "snake_case")]
70pub enum BuildTool {
71 Cargo,
72 Uv,
73 Go,
74 Flutter,
75 Npm,
76 Unknown(String),
77}
78
79impl Default for BuildTool {
80 fn default() -> Self {
81 Self::Unknown("auto".into())
82 }
83}
84
85impl BuildTool {
86 pub fn as_str(&self) -> &str {
88 match self {
89 Self::Cargo => "cargo",
90 Self::Uv => "uv",
91 Self::Go => "go",
92 Self::Flutter => "flutter",
93 Self::Npm => "npm",
94 Self::Unknown(s) => s,
95 }
96 }
97}
98
99impl<'de> Deserialize<'de> for Language {
102 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
103 where
104 D: Deserializer<'de>,
105 {
106 let s = String::deserialize(deserializer)?;
107 Ok(match s.as_str() {
108 "rust" => Language::Rust,
109 "python" => Language::Python,
110 "go" => Language::Go,
111 "dart" => Language::Dart,
112 "typescript" | "ts" | "node" => Language::TypeScript,
113 other => Language::Unknown(other.to_string()),
114 })
115 }
116}
117
118impl<'de> Deserialize<'de> for BuildTool {
119 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
120 where
121 D: Deserializer<'de>,
122 {
123 let s = String::deserialize(deserializer)?;
124 Ok(match s.as_str() {
125 "cargo" => BuildTool::Cargo,
126 "uv" | "poetry" | "pdm" => BuildTool::Uv,
127 "go" => BuildTool::Go,
128 "flutter" => BuildTool::Flutter,
129 "npm" | "pnpm" | "yarn" | "bun" => BuildTool::Npm,
130 other => BuildTool::Unknown(other.to_string()),
131 })
132 }
133}
134
135#[derive(Debug, Deserialize)]
139#[serde(rename_all = "snake_case")]
140struct ScopeConfig {
141 dir: String,
142 #[serde(default)]
143 language: Option<Language>,
144 #[serde(default)]
145 framework: Option<String>,
146 #[serde(default)]
147 build_tool: Option<BuildTool>,
148 #[serde(default)]
149 registry: Option<Registry>,
150 #[serde(default)]
151 release: Option<StageRelease>,
152 #[serde(default)]
153 test_threshold: Option<f64>,
154 #[serde(default)]
155 ci_workflow: Option<String>,
156}
157
158pub fn deserialize_scopes<'de, D>(deserializer: D) -> Result<Vec<Scope>, D::Error>
159where
160 D: Deserializer<'de>,
161{
162 struct ScopesVisitor;
164
165 impl<'de> Visitor<'de> for ScopesVisitor {
166 type Value = Vec<Scope>;
167
168 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
169 f.write_str("作用域映射")
170 }
171
172 fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
173 where
174 M: MapAccess<'de>,
175 {
176 let mut scopes = Vec::new();
177 while let Some((name, config)) = access.next_entry::<String, ScopeConfig>()? {
178 scopes.push(Scope {
179 name,
180 dir: config.dir,
181 language: config.language.unwrap_or(Language::Unknown("auto".into())),
182 framework: config.framework.unwrap_or_default(),
183 build_tool: config
184 .build_tool
185 .unwrap_or(BuildTool::Unknown("auto".into())),
186 registry: config.registry.unwrap_or(Registry::None),
187 release: config.release.unwrap_or_default(),
188 test_threshold: config.test_threshold,
189 ci_workflow: config.ci_workflow,
190 });
191 }
192 Ok(scopes)
193 }
194 }
195
196 deserializer.deserialize_map(ScopesVisitor)
197}