use std::fmt;
use indexmap::IndexMap;
use serde::{de, Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub struct Dependabot {
#[serde(deserialize_with = "de_version")]
version: u8,
#[serde(default, skip_serializing_if = "Registries::is_empty")]
pub registries: Registries,
pub updates: Vec<Update>,
}
impl Dependabot {
pub fn new(updates: Vec<Update>) -> Self {
Self { version: 2, registries: Registries::default(), updates }
}
}
impl Default for Dependabot {
fn default() -> Self {
Self::new(vec![])
}
}
impl ToString for Dependabot {
fn to_string(&self) -> String {
serde_yaml::to_string(&self).unwrap()
}
}
#[allow(single_use_lifetimes)]
fn de_version<'de, D>(deserializer: D) -> Result<u8, D::Error>
where
D: de::Deserializer<'de>,
{
let n: u8 = Deserialize::deserialize(deserializer)?;
match n {
2 => Ok(n),
_ => Err(de::Error::custom(format!(
"The property 'version' value \"{}\" did not match: 2",
n
))),
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub struct Update {
pub package_ecosystem: PackageEcosystem,
pub directory: String,
pub schedule: Schedule,
#[serde(skip_serializing_if = "Option::is_none")]
pub allow: Option<Vec<Allow>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub assignees: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub commit_message: Option<CommitMessage>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ignore: Option<Vec<Ignore>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub insecure_external_code_execution: Option<InsecureExternalCodeExecution>,
#[serde(skip_serializing_if = "Option::is_none")]
pub labels: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub milestone: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub open_pull_requests_limit: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pull_request_branch_name: Option<PullRequestBranchName>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rebase_strategy: Option<RebaseStrategy>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reviewers: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub target_branch: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub vendor: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub versioning_strategy: Option<VersioningStrategy>,
}
impl Update {
pub fn new(
package_ecosystem: PackageEcosystem,
directory: impl Into<String>,
schedule: Schedule,
) -> Self {
Self {
package_ecosystem,
directory: directory.into(),
schedule,
allow: None,
assignees: None,
commit_message: None,
ignore: None,
insecure_external_code_execution: None,
labels: None,
milestone: None,
open_pull_requests_limit: None,
pull_request_branch_name: None,
rebase_strategy: None,
reviewers: None,
target_branch: None,
vendor: None,
versioning_strategy: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum PackageEcosystem {
Bundler,
Cargo,
Composer,
Docker,
Hex,
Elm,
Gitsubmodule,
GithubActions,
Gomod,
Gradle,
Maven,
Npm,
Nuget,
Pip,
Terraform,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub struct Schedule {
pub interval: Interval,
#[serde(skip_serializing_if = "Option::is_none")]
pub day: Option<Day>,
#[serde(skip_serializing_if = "Option::is_none")]
pub time: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timezone: Option<String>,
}
impl Schedule {
pub fn new(interval: Interval) -> Self {
Self { interval, day: None, time: None, timezone: None }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum Interval {
Daily,
Weekly,
Monthly,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum Day {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday,
}
impl Default for Day {
fn default() -> Self {
Self::Monday
}
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub struct Allow {
#[serde(skip_serializing_if = "Option::is_none")]
pub dependency_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub dependency_type: Option<DependencyType>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum DependencyType {
Direct,
Indirect,
All,
Production,
Development,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub struct CommitMessage {
#[serde(skip_serializing_if = "Option::is_none")]
pub prefix: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prefix_development: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include: Option<CommitMessageInclude>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum CommitMessageInclude {
Scope,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub struct Ignore {
pub dependency_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub versions: Option<Vec<String>>,
}
impl Ignore {
pub fn new(dependency_name: String) -> Self {
Self { dependency_name, versions: None }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum InsecureExternalCodeExecution {
Allow,
Deny,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub struct PullRequestBranchName {
pub separator: Separator,
}
#[derive(Debug, Clone, Serialize)]
#[serde(transparent)]
#[allow(missing_copy_implementations)]
pub struct Separator {
repr: char,
}
impl fmt::Display for Separator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.repr, f)
}
}
impl<'de> Deserialize<'de> for Separator {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let c: char = Deserialize::deserialize(deserializer)?;
match c {
'-' | '/' | '_' => Ok(Self { repr: c }),
_ => Err(de::Error::custom(format!(
"The property 'pull-request-branch-name/separator' value \"{}\" \
did not match one of the following values: -, /, _",
c
))),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum RebaseStrategy {
Disabled,
Auto,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum VersioningStrategy {
LockfileOnly,
Auto,
Widen,
Increase,
IncreaseIfNecessary,
}
pub type Registries = IndexMap<String, Registry>;
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub struct Registry {
#[serde(rename = "type")]
pub type_: String,
pub url: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub username: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub password: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub replaces_base: Option<bool>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum RegistryType {
ComposerRepository,
DockerRegistry,
Git,
MavenRepository,
NpmRegistry,
NugetFeed,
PythonIndex,
RubygemsServer,
}