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}