apt_edsp/scenario/mod.rs
1use std::collections::HashMap;
2use std::io::BufRead;
3
4use serde::{Deserialize, Serialize};
5
6pub use relations::{Dependency, DependencyParseError, Relation, VersionSet, VersionSetParseError};
7pub use version::Version;
8
9use super::Bool;
10
11mod relations;
12mod version;
13
14#[cfg(test)]
15mod tests;
16
17/// Describes an [APT EDSP scenario][scenario].
18///
19/// [scenario]: https://salsa.debian.org/apt-team/apt/-/blob/a8367745/doc/external-dependency-solver-protocol.md#scenario
20pub struct Scenario {
21 /// The [`Request`] stanza.
22 pub request: Request,
23
24 /// The [`Package`] stanzas comprising the [package universe][universe].
25 ///
26 /// [universe]: https://salsa.debian.org/apt-team/apt/-/blob/a8367745/doc/external-dependency-solver-protocol.md#package-universe
27 pub universe: Vec<Package>,
28}
29
30impl Scenario {
31 /// Reads a [`Scenario`] from the given `reader`. On error, returns an [`ScenarioReadError`].
32 pub fn read_from(mut reader: impl BufRead) -> Result<Self, ScenarioReadError> {
33 let request: Request = rfc822_like::from_reader(&mut reader)?;
34 let universe: Vec<Package> = rfc822_like::from_reader(&mut reader)?;
35 Ok(Scenario { request, universe })
36 }
37}
38
39/// The error returned when [`Scenario::read_from`] fails.
40///
41/// Though the implementation details are hidden, the struct implements [`std::error::Error`]
42/// and a human-friendly [`std::fmt::Display`] implementation.
43#[derive(Debug, thiserror::Error)]
44#[error(transparent)]
45pub struct ScenarioReadError(#[from] rfc822_like::de::Error);
46
47/// Encapsulates the _action_ fields in a [`Request`] stanza.
48#[derive(Serialize, Deserialize, Debug, Default, Eq, PartialEq)]
49#[serde(rename_all = "PascalCase")]
50pub struct Actions {
51 /// (deprecated) Set to [`Bool::YES`] in an APT `dist-upgrade` request. Defaults to
52 /// [`Bool::NO`].
53 ///
54 /// Equivalent to setting [`Actions::upgrade_all`] to [`Bool::YES`], and
55 /// [`Preferences::forbid_new_install`] and [`Preferences::forbid_remove`] to [`Bool::NO`].
56 #[serde(rename = "Dist-Upgrade")]
57 pub dist_upgrade: Bool,
58
59 /// (deprecated) Set to [`Bool::YES`] in an APT `upgrade` request. Defaults to [`Bool::NO`].
60 ///
61 /// Equivalent to setting [`Actions::upgrade_all`], [`Preferences::forbid_new_install`] and
62 /// [`Preferences::forbid_remove`] to [`Bool::YES`].
63 pub upgrade: Bool,
64
65 /// If set to [`Bool::YES`], a cleanup of unused automatically installed packages has been
66 /// requested, usually via an APT `autoremove` request. Defaults to [`Bool::NO`].
67 pub autoremove: Bool,
68
69 /// If set to [`Bool::YES`], an upgrade of all installed packages has been requested,
70 /// usually via an upgrade command like `apt full-upgrade`. Defaults to [`Bool::NO`].
71 #[serde(rename = "Upgrade-All")]
72 pub upgrade_all: Bool,
73
74 /// A space-separated list of arch-qualified package names, with no version attached, to
75 /// remove. A value of [`None`] denotes an empty list.
76 pub remove: Option<String>,
77
78 /// A space-separated list of arch-qualified package names, with no version attached, to
79 /// install. A value of [`None`] denotes an empty list.
80 pub install: Option<String>,
81}
82
83/// Encapsulates the _preference_ fields in a [`Request`] stanza.
84#[derive(Serialize, Deserialize, Debug, Default, Eq, PartialEq)]
85#[serde(rename_all = "PascalCase")]
86pub struct Preferences {
87 /// When set to [`Bool::YES`], APT pinning is _strict_, i.e. the solver must not propose to
88 /// install packages which are not APT candidates[^note]. When set to [`Bool::NO`], the solver
89 /// does only a best effort attempt to install APT candidates. Defaults to [`Bool::YES`].
90 ///
91 /// [^note]: See [`Package::pin`] and [`Package::candidate`].
92 #[serde(rename = "Strict-Pinning")]
93 pub strict_pinning: Bool<true>,
94
95 /// When set to [`Bool::YES`] the resolver is forbidden to install new packages in its
96 /// returned solution. Defaults to [`Bool::NO`].
97 #[serde(rename = "Forbid-New-Install")]
98 pub forbid_new_install: Bool,
99
100 /// When set to [`Bool::YES`] the resolver is forbidden to remove currently installed
101 /// packages in its returned solution. Defaults to [`Bool::NO`].
102 #[serde(rename = "Forbid-Remove")]
103 pub forbid_remove: Bool,
104
105 /// A purely informational string specifying the solver to which this request was initially
106 /// sent.
107 pub solver: Option<String>,
108
109 /// A solver-specific preferences string, usually coming from the `APT::Solver::Preferences`
110 /// APT configuration option.
111 pub preferences: Option<String>,
112}
113
114/// The [request stanza][req] of a [`Scenario`].
115///
116/// [req]: https://salsa.debian.org/apt-team/apt/-/blob/a8367745/doc/external-dependency-solver-protocol.md#request
117#[derive(Serialize, Deserialize, Debug, Default, Eq, PartialEq)]
118#[serde(rename_all = "PascalCase")]
119pub struct Request {
120 /// The EDSP protocol used to communicate with APT.
121 pub request: String,
122
123 /// The name of the native architecture on the user machine.
124 pub architecture: String,
125
126 /// A space separated list of all architectures known to APT.
127 pub architectures: Option<String>,
128
129 /// The action fields in a [`Request`] stanza.
130 #[serde(flatten)]
131 pub actions: Actions,
132
133 /// The preference fields in a [`Request`] stanza.
134 #[serde(flatten)]
135 pub preferences: Preferences,
136}
137
138/// Describes an installed or available package in the [package universe][universe].
139///
140/// [universe]: https://salsa.debian.org/apt-team/apt/-/blob/a8367745/doc/external-dependency-solver-protocol.md#package-universe
141#[derive(Serialize, Deserialize, Debug, Default, Eq, PartialEq)]
142#[serde(rename_all = "PascalCase")]
143pub struct Package {
144 /// The name of the package.
145 pub package: String,
146
147 /// The version of the package.
148 pub version: Version,
149
150 /// A string representing the [architecture(s)][arch] the package supports.
151 ///
152 /// [arch]: https://www.debian.org/doc/debian-policy/ch-controlfields.html#architecture
153 pub architecture: String,
154
155 /// If set to [`Bool::YES`], the package is installed in the system. Defaults to [`Bool::NO`].
156 pub installed: Bool,
157
158 /// If set to [`Bool::YES`], the package is marked as "on hold" by `dpkg`. Defaults to
159 /// [`Bool::NO`].
160 pub hold: Bool,
161
162 /// The unique package identifier, according to APT.
163 #[serde(rename = "APT-ID")]
164 pub id: String,
165
166 /// The package pin value, according to APT policy.
167 #[serde(rename = "APT-Pin", with = "super::util::serde_as_string")]
168 pub pin: u32,
169
170 /// If set to [`Bool::YES`], the package is the APT candidate for installation among all
171 /// available packages with the same name and architecture. Defaults to [`Bool::NO`].
172 #[serde(rename = "APT-Candidate")]
173 pub candidate: Bool,
174
175 /// If set to [`Bool::YES`], the package is marked by APT as automatic installed.
176 #[serde(rename = "APT-Automatic")]
177 pub automatic: Bool,
178
179 /// Specifies the absolute dependencies of the package. See the [Debian Policy Manual][man]
180 /// on the `Depends` field for more information.
181 ///
182 /// [man]: https://www.debian.org/doc/debian-policy/ch-relationships.html#binary-dependencies-depends-recommends-suggests-enhances-pre-depends
183 #[serde(default)]
184 pub depends: Vec<Dependency>,
185
186 /// Specifies packages that conflict with this package. See the [Debian Policy Manual][man]
187 /// on the `Conflicts` field for more information.
188 ///
189 /// [man]: https://www.debian.org/doc/debian-policy/ch-relationships.html#conflicting-binary-packages-conflicts
190 #[serde(default)]
191 pub conflicts: Vec<VersionSet>,
192
193 /// Contains other optional fields that can be contained in a [`Package`] stanza.
194 #[serde(flatten)]
195 pub extra: HashMap<String, String>,
196}