isoloom_spec/syntax/step/
software.rs1use std::fmt;
5
6use schemars::JsonSchema;
7use serde::de::{self, MapAccess, Visitor};
8use serde::{Deserialize, Deserializer};
9
10use super::super::mistake::Mistake;
11use super::super::problem::Code;
12use super::words::{AptWord, HttpsUrl, PackageName, PackageVersion, RepositoryName, Sha256};
13
14#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize)]
17#[serde(from = "PackageInput")]
18pub struct Package {
19 pub name: PackageName,
21 pub version: Option<PackageVersion>,
23}
24
25impl fmt::Display for Package {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 match &self.version {
28 Some(v) => write!(f, "{} {v}", self.name),
29 None => write!(f, "{}", self.name),
30 }
31 }
32}
33
34#[derive(Deserialize, JsonSchema)]
36#[serde(deny_unknown_fields)]
37struct PackageFull {
38 name: PackageName,
40 #[serde(default)]
43 version: Option<PackageVersion>,
44}
45
46#[derive(JsonSchema)]
49#[serde(untagged)]
50enum PackageInput {
51 Name(PackageName),
53 Pinned(PackageFull),
55}
56
57schema_as!(Package, PackageInput);
58
59impl From<PackageInput> for Package {
60 fn from(written: PackageInput) -> Package {
61 match written {
62 PackageInput::Name(name) => Package { name, version: None },
63 PackageInput::Pinned(full) => Package {
64 name: full.name,
65 version: full.version,
66 },
67 }
68 }
69}
70
71impl<'de> Deserialize<'de> for PackageInput {
72 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<PackageInput, D::Error> {
73 d.deserialize_any(PackageVisitor)
74 }
75}
76
77struct PackageVisitor;
79
80impl<'de> Visitor<'de> for PackageVisitor {
81 type Value = PackageInput;
82
83 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
84 f.write_str("a package's name, or `{ name, version }`")
85 }
86
87 fn visit_str<E: de::Error>(self, v: &str) -> Result<PackageInput, E> {
88 if let Some((name, version)) = v.split_once('=') {
89 return Err(E::custom(
90 Mistake::new(Code::InvalidValue, format!("`{v}`: a package's version goes in `version`"))
91 .fix(format!("`{{ name: {name}, version: \"{version}\" }}`")),
92 ));
93 }
94 v.parse().map(PackageInput::Name).map_err(E::custom)
95 }
96
97 fn visit_map<A: MapAccess<'de>>(self, map: A) -> Result<PackageInput, A::Error> {
98 PackageFull::deserialize(de::value::MapAccessDeserializer::new(map)).map(PackageInput::Pinned)
99 }
100}
101
102#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, JsonSchema)]
105#[serde(deny_unknown_fields)]
106pub struct Repository {
107 pub name: RepositoryName,
109 pub url: HttpsUrl,
111 pub key: RepositoryKey,
113 #[serde(default)]
115 pub suite: Option<AptWord>,
116 #[serde(default)]
118 pub components: Option<Vec<AptWord>>,
119 #[serde(default)]
121 pub priority: Option<i32>,
122}
123
124#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, JsonSchema)]
126#[serde(deny_unknown_fields)]
127pub struct RepositoryKey {
128 pub url: HttpsUrl,
130 pub sha256: Sha256,
132}