use std::fmt;
use schemars::JsonSchema;
use serde::de::{self, MapAccess, Visitor};
use serde::{Deserialize, Deserializer};
use super::super::mistake::Mistake;
use super::super::problem::Code;
use super::words::{AptWord, HttpsUrl, PackageName, PackageVersion, RepositoryName, Sha256};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize)]
#[serde(from = "PackageInput")]
pub struct Package {
pub name: PackageName,
pub version: Option<PackageVersion>,
}
impl fmt::Display for Package {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.version {
Some(v) => write!(f, "{} {v}", self.name),
None => write!(f, "{}", self.name),
}
}
}
#[derive(Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
struct PackageFull {
name: PackageName,
#[serde(default)]
version: Option<PackageVersion>,
}
#[derive(JsonSchema)]
#[serde(untagged)]
enum PackageInput {
Name(PackageName),
Pinned(PackageFull),
}
schema_as!(Package, PackageInput);
impl From<PackageInput> for Package {
fn from(written: PackageInput) -> Package {
match written {
PackageInput::Name(name) => Package { name, version: None },
PackageInput::Pinned(full) => Package {
name: full.name,
version: full.version,
},
}
}
}
impl<'de> Deserialize<'de> for PackageInput {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<PackageInput, D::Error> {
d.deserialize_any(PackageVisitor)
}
}
struct PackageVisitor;
impl<'de> Visitor<'de> for PackageVisitor {
type Value = PackageInput;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("a package's name, or `{ name, version }`")
}
fn visit_str<E: de::Error>(self, v: &str) -> Result<PackageInput, E> {
if let Some((name, version)) = v.split_once('=') {
return Err(E::custom(
Mistake::new(Code::InvalidValue, format!("`{v}`: a package's version goes in `version`"))
.fix(format!("`{{ name: {name}, version: \"{version}\" }}`")),
));
}
v.parse().map(PackageInput::Name).map_err(E::custom)
}
fn visit_map<A: MapAccess<'de>>(self, map: A) -> Result<PackageInput, A::Error> {
PackageFull::deserialize(de::value::MapAccessDeserializer::new(map)).map(PackageInput::Pinned)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct Repository {
pub name: RepositoryName,
pub url: HttpsUrl,
pub key: RepositoryKey,
#[serde(default)]
pub suite: Option<AptWord>,
#[serde(default)]
pub components: Option<Vec<AptWord>>,
#[serde(default)]
pub priority: Option<i32>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct RepositoryKey {
pub url: HttpsUrl,
pub sha256: Sha256,
}