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 multi_ecosystem_groups: IndexMap<String, MultiEcosystemGroup>,
22 #[serde(default)]
23 pub registries: IndexMap<String, Registry>,
24 pub updates: Vec<Update>,
25}
26
27#[derive(Deserialize, Debug)]
29#[serde(rename_all = "kebab-case")]
30pub struct MultiEcosystemGroup {
31 pub schedule: Schedule,
32 #[serde(default = "default_labels")]
33 pub labels: IndexSet<String>,
34 pub milestone: Option<u64>,
35 #[serde(default)]
36 pub assignees: IndexSet<String>,
37 pub target_branch: Option<String>,
38 pub commit_message: Option<CommitMessage>,
39 pub pull_request_branch_name: Option<PullRequestBranchName>,
40}
41
42#[derive(Deserialize, Debug)]
44#[serde(rename_all = "kebab-case", tag = "type")]
45pub enum Registry {
46 CargoRegistry {
47 url: String,
48 registry: String,
49 token: String,
50 },
51 ComposerRepository {
52 url: String,
53 username: Option<String>,
54 password: Option<String>,
55 },
56 DockerRegistry {
57 url: String,
58 username: Option<String>,
59 password: Option<String>,
60 #[serde(default)]
61 replaces_base: bool,
62 },
63 Git {
64 url: String,
65 username: Option<String>,
66 password: Option<String>,
67 },
68 HexOrganization {
69 organization: String,
70 key: Option<String>,
71 },
72 HexRepository {
73 repo: Option<String>,
74 url: String,
75 auth_key: Option<String>,
76 public_key_fingerprint: Option<String>,
77 },
78 MavenRepository {
79 url: String,
80 username: Option<String>,
81 password: Option<String>,
82 },
83 NpmRegistry {
84 url: String,
85 username: Option<String>,
86 password: Option<String>,
87 #[serde(default)]
88 replaces_base: bool,
89 },
90 NugetFeed {
91 url: String,
92 username: Option<String>,
93 password: Option<String>,
94 },
95 PythonIndex {
96 url: String,
97 username: Option<String>,
98 password: Option<String>,
99 #[serde(default)]
100 replaces_base: bool,
101 },
102 RubygemsServer {
103 url: String,
104 username: Option<String>,
105 password: Option<String>,
106 #[serde(default)]
107 replaces_base: bool,
108 },
109 TerraformRegistry {
110 url: String,
111 token: Option<String>,
112 },
113}
114
115#[derive(Deserialize, Debug)]
117#[serde(rename_all = "kebab-case")]
118pub struct Cooldown {
119 pub default_days: Option<u64>,
120 pub semver_major_days: Option<u64>,
121 pub semver_minor_days: Option<u64>,
122 pub semver_patch_days: Option<u64>,
123 #[serde(default)]
124 pub include: Vec<String>,
125 #[serde(default)]
126 pub exclude: Vec<String>,
127}
128
129#[derive(Deserialize, Debug, PartialEq)]
131#[serde(rename_all = "kebab-case")]
132pub enum Directories {
133 Directory(String),
134 Directories(Vec<String>),
135}
136
137#[derive(Deserialize, Debug)]
139#[serde(rename_all = "kebab-case", remote = "Self")]
140pub struct Update {
141 #[serde(default)]
143 pub allow: Vec<Allow>,
144
145 #[serde(default)]
147 pub assignees: IndexSet<String>,
148
149 pub commit_message: Option<CommitMessage>,
151
152 pub cooldown: Option<Cooldown>,
154
155 #[serde(flatten)]
158 pub directories: Directories,
159
160 #[serde(default)]
162 pub groups: IndexMap<String, Group>,
163
164 #[serde(default)]
166 pub ignore: Vec<Ignore>,
167
168 #[serde(default)]
170 pub insecure_external_code_execution: AllowDeny,
171
172 #[serde(default = "default_labels")]
176 pub labels: IndexSet<String>,
177 pub milestone: Option<u64>,
178 #[serde(default = "default_open_pull_requests_limit")]
183 pub open_pull_requests_limit: u64,
184
185 pub package_ecosystem: PackageEcosystem,
187
188 #[serde(default)]
190 pub rebase_strategy: RebaseStrategy,
191 #[serde(default, deserialize_with = "crate::common::scalar_or_vector")]
192 pub registries: Vec<String>,
193 #[serde(default)]
194 pub reviewers: IndexSet<String>,
195 pub schedule: Option<Schedule>,
196 pub target_branch: Option<String>,
197 pub pull_request_branch_name: Option<PullRequestBranchName>,
198 #[serde(default)]
199 pub vendor: bool,
200 pub versioning_strategy: Option<VersioningStrategy>,
201
202 pub multi_ecosystem_group: Option<String>,
207
208 pub patterns: Option<IndexSet<String>>,
214
215 #[serde(default)]
220 pub exclude_paths: Option<IndexSet<String>>,
221}
222
223impl<'de> Deserialize<'de> for Update {
224 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
225 where
226 D: serde::Deserializer<'de>,
227 {
228 let update = Self::deserialize(deserializer)?;
229
230 if update.multi_ecosystem_group.is_some() && update.patterns.is_none() {
232 return Err(custom_error::<D>(
233 "`patterns` must be set when `multi-ecosystem-group` is set",
234 ));
235 }
236
237 if update.multi_ecosystem_group.is_some() {
241 if update.milestone.is_some() {
242 return Err(custom_error::<D>(
243 "`milestone` may not be set when `multi-ecosystem-group` is set",
244 ));
245 }
246 if update.target_branch.is_some() {
247 return Err(custom_error::<D>(
248 "`target-branch` may not be set when `multi-ecosystem-group` is set",
249 ));
250 }
251 if update.commit_message.is_some() {
252 return Err(custom_error::<D>(
253 "`commit-message` may not be set when `multi-ecosystem-group` is set",
254 ));
255 }
256 if update.pull_request_branch_name.is_some() {
257 return Err(custom_error::<D>(
258 "`pull-request-branch-name` may not be set when `multi-ecosystem-group` is set",
259 ));
260 }
261 }
262
263 Ok(update)
264 }
265}
266
267#[inline]
268fn default_labels() -> IndexSet<String> {
269 IndexSet::from(["dependencies".to_string()])
270}
271
272#[inline]
273fn default_open_pull_requests_limit() -> u64 {
274 5
276}
277
278#[derive(Deserialize, Debug)]
280#[serde(rename_all = "kebab-case")]
281pub struct Allow {
282 pub dependency_name: Option<String>,
283 pub dependency_type: Option<DependencyType>,
284}
285
286#[derive(Deserialize, Debug)]
288#[serde(rename_all = "kebab-case")]
289pub enum DependencyType {
290 Direct,
291 Indirect,
292 All,
293 Production,
294 Development,
295}
296
297#[derive(Deserialize, Debug)]
299#[serde(rename_all = "kebab-case")]
300pub struct CommitMessage {
301 pub prefix: Option<String>,
302 pub prefix_development: Option<String>,
303 pub include: Option<String>,
305}
306
307#[derive(Deserialize, Debug)]
309#[serde(rename_all = "kebab-case")]
310pub struct Group {
311 pub dependency_type: Option<DependencyType>,
314 #[serde(default)]
315 pub patterns: IndexSet<String>,
316 #[serde(default)]
317 pub exclude_patterns: IndexSet<String>,
318 #[serde(default)]
319 pub update_types: IndexSet<UpdateType>,
320}
321
322#[derive(Deserialize, Debug, Hash, Eq, PartialEq)]
324#[serde(rename_all = "kebab-case")]
325pub enum UpdateType {
326 Major,
327 Minor,
328 Patch,
329}
330
331#[derive(Deserialize, Debug)]
333#[serde(rename_all = "kebab-case")]
334pub struct Ignore {
335 pub dependency_name: Option<String>,
336 #[serde(default)]
339 pub update_types: IndexSet<String>,
340 #[serde(default)]
341 pub versions: IndexSet<String>,
342}
343
344#[derive(Deserialize, Debug, Default)]
346#[serde(rename_all = "kebab-case")]
347pub enum AllowDeny {
348 Allow,
349 #[default]
350 Deny,
351}
352
353#[derive(Deserialize, Debug, PartialEq)]
355#[serde(rename_all = "kebab-case")]
356pub enum PackageEcosystem {
357 Bazel,
359 Bun,
361 Bundler,
363 Cargo,
365 Composer,
367 Conda,
369 Deno,
371 Devcontainers,
373 Docker,
375 DockerCompose,
377 DotnetSdk,
379 Helm,
381 Julia,
383 Elm,
385 Gitsubmodule,
387 GithubActions,
389 Gomod,
391 Gradle,
393 Maven,
395 Mix,
397 Nix,
399 Npm,
401 Nuget,
403 Opentofu,
405 Pip,
407 PreCommit,
409 Pub,
411 RustToolchain,
413 Swift,
415 Terraform,
417 Uv,
419 Vcpkg,
421}
422
423#[derive(Deserialize, Debug, Default, PartialEq)]
425#[serde(rename_all = "kebab-case")]
426pub enum RebaseStrategy {
427 #[default]
428 Auto,
429 Disabled,
430}
431
432#[derive(Deserialize, Debug)]
434#[serde(rename_all = "kebab-case", remote = "Self")]
435pub struct Schedule {
436 pub interval: Interval,
437 pub day: Option<Day>,
438 pub time: Option<String>,
439 pub timezone: Option<String>,
440 pub cronjob: Option<String>,
441}
442
443impl<'de> Deserialize<'de> for Schedule {
444 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
445 where
446 D: serde::Deserializer<'de>,
447 {
448 let schedule = Self::deserialize(deserializer)?;
449
450 if schedule.interval == Interval::Cron && schedule.cronjob.is_none() {
451 return Err(custom_error::<D>(
452 "`schedule.cronjob` must be set when `schedule.interval` is `cron`",
453 ));
454 }
455
456 if schedule.interval != Interval::Cron && schedule.cronjob.is_some() {
457 return Err(custom_error::<D>(
458 "`schedule.cronjob` may only be set when `schedule.interval` is `cron`",
459 ));
460 }
461
462 Ok(schedule)
468 }
469}
470
471#[derive(Deserialize, Debug, PartialEq)]
473#[serde(rename_all = "kebab-case")]
474pub enum Interval {
475 Daily,
476 Weekly,
477 Monthly,
478 Quarterly,
479 Semiannually,
480 Yearly,
481 Cron,
482}
483
484#[derive(Deserialize, Debug, PartialEq)]
486#[serde(rename_all = "kebab-case")]
487pub enum Day {
488 Monday,
489 Tuesday,
490 Wednesday,
491 Thursday,
492 Friday,
493 Saturday,
494 Sunday,
495}
496
497#[derive(Deserialize, Debug)]
499#[serde(rename_all = "kebab-case")]
500pub struct PullRequestBranchName {
501 pub separator: Option<String>,
502}
503
504#[derive(Deserialize, Debug, PartialEq)]
506#[serde(rename_all = "kebab-case")]
507pub enum VersioningStrategy {
508 Auto,
509 Increase,
510 IncreaseIfNecessary,
511 LockfileOnly,
512 Widen,
513}