microsoft/openvmm

Public

mirrored fromhttps://github.com/microsoft/openvmmAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
93af13fed5d5fc7a8a08fbf37c0ea1e155c4160a

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

flowey/schema_ado_yaml/src/lib.rs

258lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Serde defs for ADO YAML
5
6#![allow(unused)]
7
8use serde::Deserialize;
9use serde::Serialize;
10use serde::Serializer;
11use std::collections::BTreeMap;
12
13mod none {
14 use serde::Deserialize;
15 use serde::Deserializer;
16 use serde::Serializer;
17
18 pub fn serialize<S>(_: &(), ser: S) -> Result<S::Ok, S::Error>
19 where
20 S: Serializer,
21 {
22 ser.serialize_str("none")
23 }
24
25 pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<(), D::Error> {
26 let s: &str = Deserialize::deserialize(d)?;
27 if s != "none" {
28 return Err(serde::de::Error::custom("field must be 'none'"));
29 }
30 Ok(())
31 }
32}
33
34/// Valid names may only contain alphanumeric characters and '_' and may not
35/// start with a number.
36fn validate_name<S>(s: &str, ser: S) -> Result<S::Ok, S::Error>
37where
38 S: Serializer,
39{
40 if s.is_empty() {
41 return Err(serde::ser::Error::custom("name cannot be empty"));
42 }
43
44 if s.chars().next().unwrap().is_ascii_digit() {
45 return Err(serde::ser::Error::custom("name cannot start with a number"));
46 }
47
48 if !s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
49 return Err(serde::ser::Error::custom(
50 "name must be ascii alphanumeric + '_'",
51 ));
52 }
53
54 ser.serialize_str(s)
55}
56
57#[derive(Debug, Serialize, Deserialize)]
58#[serde(rename_all = "camelCase")]
59pub struct TriggerBranches {
60 #[serde(skip_serializing_if = "Vec::is_empty")]
61 pub include: Vec<String>,
62 // Wrapping this in an Option is necessary to prevent problems when deserializing and exclude isn't present
63 #[serde(skip_serializing_if = "Option::is_none")]
64 pub exclude: Option<Vec<String>>,
65}
66
67#[derive(Debug, Serialize, Deserialize)]
68#[serde(untagged)]
69#[serde(rename_all = "camelCase")]
70pub enum PrTrigger {
71 None(#[serde(with = "none")] ()),
72 #[serde(rename_all = "camelCase")]
73 Some {
74 auto_cancel: bool,
75 drafts: bool,
76 branches: TriggerBranches,
77 },
78 // serde has a bug with untagged and `with` during deserialization
79 NoneWorkaround(String),
80}
81
82#[derive(Debug, Serialize, Deserialize)]
83#[serde(untagged)]
84#[serde(rename_all = "camelCase")]
85pub enum CiTrigger {
86 None(#[serde(with = "none")] ()),
87 #[serde(rename_all = "camelCase")]
88 Some {
89 batch: bool,
90 branches: TriggerBranches,
91 },
92 // serde has a bug with untagged and `with` during deserialization
93 NoneWorkaround(String),
94}
95
96#[derive(Debug, Serialize, Deserialize)]
97#[serde(rename_all = "camelCase")]
98pub struct Schedule {
99 // FUTURE?: proper cron validation?
100 pub cron: String,
101 pub display_name: String,
102 pub branches: TriggerBranches,
103 #[serde(skip_serializing_if = "std::ops::Not::not")]
104 #[serde(default)]
105 pub batch: bool,
106}
107
108#[derive(Debug, Serialize, Deserialize)]
109#[serde(rename_all = "camelCase")]
110pub struct Variable {
111 pub name: String,
112 pub value: String,
113}
114
115#[derive(Debug, Serialize, Deserialize)]
116#[serde(rename_all = "camelCase")]
117pub struct Pipeline {
118 #[serde(skip_serializing_if = "Option::is_none")]
119 pub name: Option<String>,
120 #[serde(skip_serializing_if = "Option::is_none")]
121 pub trigger: Option<CiTrigger>,
122 #[serde(skip_serializing_if = "Option::is_none")]
123 pub pr: Option<PrTrigger>,
124 #[serde(skip_serializing_if = "Option::is_none")]
125 pub schedules: Option<Vec<Schedule>>,
126 #[serde(skip_serializing_if = "Option::is_none")]
127 pub variables: Option<Vec<Variable>>,
128 #[serde(skip_serializing_if = "Option::is_none")]
129 pub parameters: Option<Vec<Parameter>>,
130 #[serde(skip_serializing_if = "Option::is_none")]
131 pub resources: Option<Resources>,
132 #[serde(skip_serializing_if = "Option::is_none")]
133 pub stages: Option<Vec<Stage>>,
134 #[serde(skip_serializing_if = "Option::is_none")]
135 pub jobs: Option<Vec<Job>>,
136 #[serde(skip_serializing_if = "Option::is_none")]
137 pub extends: Option<Extends>,
138}
139
140#[derive(Debug, Serialize, Deserialize)]
141#[serde(rename_all = "camelCase")]
142pub struct Extends {
143 pub template: String,
144 pub parameters: BTreeMap<String, serde_yaml::Value>,
145}
146
147#[derive(Debug, Default, Serialize, Deserialize)]
148#[serde(rename_all = "camelCase")]
149pub struct Resources {
150 #[serde(skip_serializing_if = "Vec::is_empty")]
151 pub repositories: Vec<ResourcesRepository>,
152}
153
154#[derive(Debug, Serialize, Deserialize)]
155#[serde(rename_all = "camelCase")]
156pub struct ResourcesRepository {
157 // Alias for the specified repository.
158 //
159 // Acceptable values: [-_A-Za-z0-9]*.
160 pub repository: String,
161 /// ID of the service endpoint connecting to this repository
162 #[serde(skip_serializing_if = "Option::is_none")]
163 pub endpoint: Option<String>,
164 /// Repository name. Format depends on 'type'; does not accept variables
165 pub name: String,
166 /// ref name to checkout; defaults to 'refs/heads/main'.
167 #[serde(rename = "ref")]
168 pub r#ref: String,
169 #[serde(rename = "type")]
170 pub r#type: ResourcesRepositoryType,
171}
172
173#[derive(Debug, Serialize, Deserialize)]
174#[serde(rename_all = "lowercase")]
175pub enum ResourcesRepositoryType {
176 Git,
177 GitHub,
178 GitHubEnterprise,
179 Bitbucket,
180}
181
182#[derive(Debug, Serialize, Deserialize)]
183#[serde(rename_all = "camelCase")]
184pub struct Parameter {
185 pub name: String,
186 pub display_name: String,
187 #[serde(flatten)]
188 pub ty: ParameterType,
189}
190
191// ADO also has specialized types for things like steps/jobs/stages, etc... but
192// at this time, it's unclear how they'd be useful in flowey.
193#[derive(Debug, Serialize, Deserialize)]
194#[serde(tag = "type", rename_all = "camelCase")]
195pub enum ParameterType {
196 Boolean {
197 #[serde(skip_serializing_if = "Option::is_none")]
198 default: Option<bool>,
199 },
200 String {
201 #[serde(skip_serializing_if = "Option::is_none")]
202 default: Option<String>,
203 #[serde(skip_serializing_if = "Option::is_none")]
204 values: Option<Vec<String>>,
205 },
206 Number {
207 #[serde(skip_serializing_if = "Option::is_none")]
208 default: Option<i64>,
209 #[serde(skip_serializing_if = "Option::is_none")]
210 values: Option<Vec<i64>>,
211 },
212 Object {
213 #[serde(skip_serializing_if = "Option::is_none")]
214 default: Option<serde_yaml::Value>,
215 },
216}
217
218#[derive(Debug, Serialize, Deserialize)]
219#[serde(rename_all = "camelCase")]
220pub struct Stage {
221 /// Valid names may only contain alphanumeric characters and '_' and may
222 /// not start with a number.
223 #[serde(serialize_with = "validate_name")]
224 pub stage: String,
225 pub display_name: String,
226 pub depends_on: Vec<String>,
227 #[serde(skip_serializing_if = "Option::is_none")]
228 pub condition: Option<String>,
229 pub jobs: Vec<Job>,
230}
231
232#[derive(Debug, Serialize, Deserialize)]
233#[serde(untagged)]
234#[serde(rename_all = "camelCase")]
235pub enum Pool {
236 Pool(String),
237 PoolWithMetadata(BTreeMap<String, serde_yaml::Value>),
238}
239
240#[derive(Debug, Serialize, Deserialize)]
241#[serde(rename_all = "camelCase")]
242pub struct Job {
243 #[serde(serialize_with = "validate_name")]
244 pub job: String,
245 pub display_name: String,
246 pub pool: Pool,
247 pub depends_on: Vec<String>,
248 #[serde(skip_serializing_if = "Option::is_none")]
249 pub condition: Option<String>,
250 #[serde(skip_serializing_if = "Option::is_none")]
251 pub variables: Option<Vec<Variable>>,
252 // individual steps are not type-checked by the serde schema, as there are a
253 // _lot_ of different step kinds nodes might be emitting.
254 //
255 // instead, trust that the user knows what they're doing when emitting yaml
256 // snippets.
257 pub steps: Vec<serde_yaml::Value>,
258}
259