use std::collections::BTreeMap;
use std::fmt;
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::ConfigValueSource;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum PatchSourceKind {
Path,
}
impl PatchSourceKind {
pub const fn as_key(self) -> &'static str {
match self {
PatchSourceKind::Path => "path",
}
}
}
impl fmt::Display for PatchSourceKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_key())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "kebab-case")]
pub enum PatchSource {
Path { path: PathBuf },
}
impl PatchSource {
pub fn kind(&self) -> PatchSourceKind {
match self {
PatchSource::Path { .. } => PatchSourceKind::Path,
}
}
pub fn from_path_field(
package: &str,
raw_path: Option<String>,
) -> Result<PatchSource, PatchValidationError> {
match raw_path {
Some(path) if !path.trim().is_empty() => Ok(PatchSource::Path {
path: PathBuf::from(path.trim()),
}),
_ => Err(PatchValidationError::MissingSource {
package: package.to_owned(),
}),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum PatchProvenance {
Manifest,
Config(ConfigValueSource),
}
impl PatchProvenance {
pub fn as_key(self) -> String {
match self {
PatchProvenance::Manifest => "manifest".to_owned(),
PatchProvenance::Config(source) => source.as_key().to_owned(),
}
}
}
impl fmt::Display for PatchProvenance {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.as_key())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DeclaredPatch {
pub source: PatchSource,
pub declared_in: PathBuf,
pub provenance: PatchProvenance,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct PatchManifestSettings {
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub entries: BTreeMap<crate::PackageName, PatchSource>,
}
impl PatchManifestSettings {
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
#[derive(Debug, Error, Clone, PartialEq, Eq)]
pub enum PatchValidationError {
#[error("patch for package `{package}` is missing a source; expected `path = \"...\"`")]
MissingSource { package: String },
#[error(
"patch for package `{package}` points to `{path}`, but that path does not contain a cabin.toml"
)]
MissingManifest { package: String, path: String },
#[error(
"patch for package `{package}` points to `{path}`, but its cabin.toml declares no `[package]`"
)]
ManifestHasNoPackage { package: String, path: String },
#[error(
"patch for package `{package}` points to package `{actual}`; patch package name must match `{package}`"
)]
PackageNameMismatch { package: String, actual: String },
#[error(
"patch package `{package}` has version `{version}`, which does not satisfy dependency requirement `{requirement}`"
)]
VersionMismatch {
package: String,
version: String,
requirement: String,
},
#[error(
"multiple patches for package `{package}` are active at the same precedence level; remove one patch declaration"
)]
DuplicateAtSameLevel { package: String },
}