1use indexmap::{IndexMap, IndexSet};
8use serde::Deserialize;
9
10use crate::common::custom_error;
11
12#[derive(Deserialize, Debug)]
14#[serde(rename_all = "kebab-case")]
15pub struct Dependabot {
16 pub version: u64,
18 #[serde(default)]
19 pub enable_beta_ecosystems: bool,
20 #[serde(default)]
21 pub registries: IndexMap<String, Registry>,
22 pub updates: Vec<Update>,
23}
24
25#[derive(Deserialize, Debug)]
27#[serde(rename_all = "kebab-case", tag = "type")]
28pub enum Registry {
29 ComposerRepository {
30 url: String,
31 username: Option<String>,
32 password: Option<String>,
33 },
34 DockerRegistry {
35 url: String,
36 username: Option<String>,
37 password: Option<String>,
38 #[serde(default)]
39 replaces_base: bool,
40 },
41 Git {
42 url: String,
43 username: Option<String>,
44 password: Option<String>,
45 },
46 HexOrganization {
47 organization: String,
48 key: Option<String>,
49 },
50 HexRepository {
51 repo: Option<String>,
52 url: String,
53 auth_key: Option<String>,
54 public_key_fingerprint: Option<String>,
55 },
56 MavenRepository {
57 url: String,
58 username: Option<String>,
59 password: Option<String>,
60 },
61 NpmRegistry {
62 url: String,
63 username: Option<String>,
64 password: Option<String>,
65 #[serde(default)]
66 replaces_base: bool,
67 },
68 NugetFeed {
69 url: String,
70 username: Option<String>,
71 password: Option<String>,
72 },
73 PythonIndex {
74 url: String,
75 username: Option<String>,
76 password: Option<String>,
77 #[serde(default)]
78 replaces_base: bool,
79 },
80 RubygemsServer {
81 url: String,
82 username: Option<String>,
83 password: Option<String>,
84 #[serde(default)]
85 replaces_base: bool,
86 },
87 TerraformRegistry {
88 url: String,
89 token: Option<String>,
90 },
91}
92
93#[derive(Deserialize, Debug)]
95#[serde(rename_all = "kebab-case")]
96pub struct Cooldown {
97 pub default_days: Option<u64>,
98 pub semver_major_days: Option<u64>,
99 pub semver_minor_days: Option<u64>,
100 pub semver_patch_days: Option<u64>,
101 #[serde(default)]
102 pub include: Vec<String>,
103 #[serde(default)]
104 pub exclude: Vec<String>,
105}
106
107#[derive(Deserialize, Debug, PartialEq)]
109#[serde(rename_all = "kebab-case")]
110pub enum Directories {
111 Directory(String),
112 Directories(Vec<String>),
113}
114
115#[derive(Deserialize, Debug)]
117#[serde(rename_all = "kebab-case")]
118pub struct Update {
119 #[serde(default)]
120 pub allow: Vec<Allow>,
121 #[serde(default)]
122 pub assignees: IndexSet<String>,
123 pub commit_message: Option<CommitMessage>,
124 pub cooldown: Option<Cooldown>,
125 #[serde(flatten)]
126 pub directories: Directories,
127 #[serde(default)]
128 pub groups: IndexMap<String, Group>,
129 #[serde(default)]
130 pub ignore: Vec<Ignore>,
131 #[serde(default)]
132 pub insecure_external_code_execution: AllowDeny,
133 #[serde(default = "default_labels")]
137 pub labels: IndexSet<String>,
138 pub milestone: Option<u64>,
139 #[serde(default = "default_open_pull_requests_limit")]
144 pub open_pull_requests_limit: u64,
145 pub package_ecosystem: PackageEcosystem,
146 #[serde(default)]
148 pub rebase_strategy: RebaseStrategy,
149 #[serde(default, deserialize_with = "crate::common::scalar_or_vector")]
150 pub registries: Vec<String>,
151 #[serde(default)]
152 pub reviewers: IndexSet<String>,
153 pub schedule: Schedule,
154 pub target_branch: Option<String>,
155 #[serde(default)]
156 pub vendor: bool,
157 pub versioning_strategy: Option<VersioningStrategy>,
158}
159
160#[inline]
161fn default_labels() -> IndexSet<String> {
162 IndexSet::from(["dependencies".to_string()])
163}
164
165#[inline]
166fn default_open_pull_requests_limit() -> u64 {
167 5
169}
170
171#[derive(Deserialize, Debug)]
173#[serde(rename_all = "kebab-case")]
174pub struct Allow {
175 pub dependency_name: Option<String>,
176 pub dependency_type: Option<DependencyType>,
177}
178
179#[derive(Deserialize, Debug)]
181#[serde(rename_all = "kebab-case")]
182pub enum DependencyType {
183 Direct,
184 Indirect,
185 All,
186 Production,
187 Development,
188}
189
190#[derive(Deserialize, Debug)]
192#[serde(rename_all = "kebab-case")]
193pub struct CommitMessage {
194 pub prefix: Option<String>,
195 pub prefix_development: Option<String>,
196 pub include: Option<String>,
198}
199
200#[derive(Deserialize, Debug)]
202#[serde(rename_all = "kebab-case")]
203pub struct Group {
204 pub dependency_type: Option<DependencyType>,
207 #[serde(default)]
208 pub patterns: IndexSet<String>,
209 #[serde(default)]
210 pub exclude_patterns: IndexSet<String>,
211 #[serde(default)]
212 pub update_types: IndexSet<UpdateType>,
213}
214
215#[derive(Deserialize, Debug, Hash, Eq, PartialEq)]
217#[serde(rename_all = "kebab-case")]
218pub enum UpdateType {
219 Major,
220 Minor,
221 Patch,
222}
223
224#[derive(Deserialize, Debug)]
226#[serde(rename_all = "kebab-case")]
227pub struct Ignore {
228 pub dependency_name: Option<String>,
229 #[serde(default)]
232 pub update_types: IndexSet<String>,
233 #[serde(default)]
234 pub versions: IndexSet<String>,
235}
236
237#[derive(Deserialize, Debug, Default)]
239#[serde(rename_all = "kebab-case")]
240pub enum AllowDeny {
241 Allow,
242 #[default]
243 Deny,
244}
245
246#[derive(Deserialize, Debug, PartialEq)]
248#[serde(rename_all = "kebab-case")]
249pub enum PackageEcosystem {
250 Bun,
252 Bundler,
254 Cargo,
256 Composer,
258 Conda,
260 Devcontainers,
262 Docker,
264 DockerCompose,
266 DotnetSdk,
268 Helm,
270 Elm,
272 Gitsubmodule,
274 GithubActions,
276 Gomod,
278 Gradle,
280 Maven,
282 Mix,
284 Npm,
286 Nuget,
288 Pip,
290 Pub,
292 RustToolchain,
294 Swift,
296 Terraform,
298 Uv,
300 Vcpkg,
302}
303
304#[derive(Deserialize, Debug, Default, PartialEq)]
306#[serde(rename_all = "kebab-case")]
307pub enum RebaseStrategy {
308 #[default]
309 Auto,
310 Disabled,
311}
312
313#[derive(Deserialize, Debug)]
315#[serde(rename_all = "kebab-case", remote = "Self")]
316pub struct Schedule {
317 pub interval: Interval,
318 pub day: Option<Day>,
319 pub time: Option<String>,
320 pub timezone: Option<String>,
321 pub cronjob: Option<String>,
322}
323
324impl<'de> Deserialize<'de> for Schedule {
325 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
326 where
327 D: serde::Deserializer<'de>,
328 {
329 let schedule = Self::deserialize(deserializer)?;
330
331 if schedule.interval == Interval::Cron && schedule.cronjob.is_none() {
332 return Err(custom_error::<D>(
333 "`schedule.cronjob` must be set when `schedule.interval` is `cron`",
334 ));
335 }
336
337 if schedule.interval != Interval::Cron && schedule.cronjob.is_some() {
338 return Err(custom_error::<D>(
339 "`schedule.cronjob` may only be set when `schedule.interval` is `cron`",
340 ));
341 }
342
343 if schedule.interval != Interval::Weekly && schedule.day.is_some() {
344 return Err(custom_error::<D>(
345 "`schedule.day` is only valid when `schedule.interval` is `weekly`",
346 ));
347 }
348
349 Ok(Self {
350 interval: schedule.interval,
351 day: schedule.day,
352 time: schedule.time,
353 timezone: schedule.timezone,
354 cronjob: schedule.cronjob,
355 })
356 }
357}
358
359#[derive(Deserialize, Debug, PartialEq)]
361#[serde(rename_all = "kebab-case")]
362pub enum Interval {
363 Daily,
364 Weekly,
365 Monthly,
366 Quarterly,
367 Semiannually,
368 Yearly,
369 Cron,
370}
371
372#[derive(Deserialize, Debug, PartialEq)]
374#[serde(rename_all = "kebab-case")]
375pub enum Day {
376 Monday,
377 Tuesday,
378 Wednesday,
379 Thursday,
380 Friday,
381 Saturday,
382 Sunday,
383}
384
385#[derive(Deserialize, Debug, PartialEq)]
387#[serde(rename_all = "kebab-case")]
388pub enum VersioningStrategy {
389 Auto,
390 Increase,
391 IncreaseIfNecessary,
392 LockfileOnly,
393 Widen,
394}