Skip to main content

dnf_repofile/
builder.rs

1//! Builder-pattern API for constructing [`Repo`] values.
2//!
3//! [`RepoBuilder`] provides a fluent interface for programmatic creation of
4//! repository configurations without manually setting each field on a
5//! [`Repo`] struct.
6//!
7//! # Examples
8//!
9//! ```
10//! use dnf_repofile::{RepoBuilder, RepoId, RepoName, DnfBool, Priority};
11//!
12//! let repo = RepoBuilder::new(RepoId::try_new("custom").unwrap())
13//!     .name(RepoName::try_new("Custom Repository").unwrap())
14//!     .baseurl("https://example.com/".parse().unwrap())
15//!     .enabled(DnfBool::yes())
16//!     .gpgcheck(DnfBool::yes())
17//!     .priority(Priority::try_new(50).unwrap())
18//!     .build();
19//!
20//! assert_eq!(repo.name.as_ref().unwrap().as_ref(), "Custom Repository");
21//! ```
22
23use crate::repo::Repo;
24use crate::types::*;
25use url::Url;
26
27/// Builder-pattern API for constructing a [`Repo`] with a fluent interface.
28///
29/// Create a new builder with [`RepoBuilder::new`] or clone an existing repo
30/// with [`RepoBuilder::from`].
31///
32/// # Examples
33///
34/// ```
35/// use dnf_repofile::{RepoBuilder, RepoId, DnfBool};
36///
37/// let repo = RepoBuilder::new(RepoId::try_new("myrepo").unwrap())
38///     .enabled(DnfBool::yes())
39///     .build();
40/// ```
41#[derive(Debug, Clone)]
42pub struct RepoBuilder {
43    repo: Repo,
44}
45
46impl RepoBuilder {
47    /// Create a new builder with the given repository ID.
48    ///
49    /// All other fields are initialized to `None` or empty.
50    ///
51    /// # Examples
52    ///
53    /// ```
54    /// use dnf_repofile::{RepoBuilder, RepoId};
55    ///
56    /// let builder = RepoBuilder::new(RepoId::try_new("test").unwrap());
57    /// ```
58    pub fn new(id: RepoId) -> Self {
59        RepoBuilder {
60            repo: Repo::new(id),
61        }
62    }
63
64    /// Create a builder pre-populated from an existing [`Repo`].
65    ///
66    /// Useful for making a modified copy of an existing configuration.
67    ///
68    /// # Examples
69    ///
70    /// ```
71    /// use dnf_repofile::{RepoBuilder, Repo, RepoId};
72    ///
73    /// let original = Repo::new(RepoId::try_new("test").unwrap());
74    /// let builder = RepoBuilder::from(&original);
75    /// ```
76    pub fn from(existing: &Repo) -> Self {
77        RepoBuilder {
78            repo: existing.clone(),
79        }
80    }
81
82    /// Consume the builder and produce the final [`Repo`].
83    ///
84    /// # Examples
85    ///
86    /// ```
87    /// use dnf_repofile::{RepoBuilder, RepoId};
88    ///
89    /// let repo = RepoBuilder::new(RepoId::try_new("test").unwrap()).build();
90    /// ```
91    #[must_use]
92    pub fn build(self) -> Repo {
93        self.repo
94    }
95
96    /// Set the human-readable repository name.
97    pub fn name(mut self, v: RepoName) -> Self {
98        self.repo.name = Some(v);
99        self
100    }
101
102    /// Add a base URL to the repository.
103    ///
104    /// Multiple URLs can be added by chaining this method.
105    pub fn baseurl(mut self, v: Url) -> Self {
106        self.repo.baseurl.push(v);
107        self
108    }
109
110    /// Set all base URLs at once, replacing any existing URLs.
111    pub fn baseurls(mut self, v: Vec<Url>) -> Self {
112        self.repo.baseurl = v;
113        self
114    }
115
116    /// Set the mirror list URL.
117    pub fn mirrorlist(mut self, v: Url) -> Self {
118        self.repo.mirrorlist = Some(v);
119        self
120    }
121
122    /// Set the metalink URL.
123    pub fn metalink(mut self, v: Url) -> Self {
124        self.repo.metalink = Some(v);
125        self
126    }
127
128    /// Add a GPG key URL.
129    ///
130    /// Multiple keys can be added by chaining this method.
131    pub fn gpgkey(mut self, v: &str) -> Self {
132        self.repo.gpgkey.push(v.to_string());
133        self
134    }
135
136    /// Set all GPG key URLs at once, replacing any existing keys.
137    pub fn gpgkeys(mut self, v: Vec<String>) -> Self {
138        self.repo.gpgkey = v;
139        self
140    }
141
142    /// Set whether the repository is enabled.
143    pub fn enabled(mut self, v: DnfBool) -> Self {
144        self.repo.enabled = Some(v);
145        self
146    }
147
148    /// Set whether to GPG-check packages from this repo.
149    pub fn gpgcheck(mut self, v: DnfBool) -> Self {
150        self.repo.gpgcheck = Some(v);
151        self
152    }
153
154    /// Set whether to GPG-check repository metadata.
155    pub fn repo_gpgcheck(mut self, v: DnfBool) -> Self {
156        self.repo.repo_gpgcheck = Some(v);
157        self
158    }
159
160    /// Set the repository priority (1–99, lower = higher priority).
161    pub fn priority(mut self, v: Priority) -> Self {
162        self.repo.priority = Some(v);
163        self
164    }
165
166    /// Set the repository cost (higher = less preferred).
167    pub fn cost(mut self, v: Cost) -> Self {
168        self.repo.cost = Some(v);
169        self
170    }
171
172    /// Mark the repository as a module hotfix repository.
173    pub fn module_hotfixes(mut self, v: DnfBool) -> Self {
174        self.repo.module_hotfixes = Some(v);
175        self
176    }
177
178    /// Set the repository metadata type (e.g., `rpm-md`).
179    pub fn metadata_type(mut self, v: RepoMetadataType) -> Self {
180        self.repo.metadata_type = Some(v);
181        self
182    }
183
184    /// Set the media ID for DVD/media-based repositories.
185    pub fn mediaid(mut self, v: &str) -> Self {
186        self.repo.mediaid = Some(v.to_string());
187        self
188    }
189
190    /// Add a package name glob to the exclude list.
191    pub fn excludepkgs(mut self, v: &str) -> Self {
192        self.repo.excludepkgs.push(v.to_string());
193        self
194    }
195
196    /// Add a package name glob to the include list.
197    pub fn includepkgs(mut self, v: &str) -> Self {
198        self.repo.includepkgs.push(v.to_string());
199        self
200    }
201
202    /// Set whether to skip the repository if it is unavailable.
203    pub fn skip_if_unavailable(mut self, v: DnfBool) -> Self {
204        self.repo.skip_if_unavailable = Some(v);
205        self
206    }
207
208    /// Set the number of retries for network operations.
209    pub fn retries(mut self, v: Retries) -> Self {
210        self.repo.retries = Some(v);
211        self
212    }
213
214    /// Set the network timeout in seconds.
215    pub fn timeout(mut self, v: TimeoutSeconds) -> Self {
216        self.repo.timeout = Some(v);
217        self
218    }
219
220    /// Set the maximum number of parallel downloads.
221    pub fn max_parallel_downloads(mut self, v: MaxParallelDownloads) -> Self {
222        self.repo.max_parallel_downloads = Some(v);
223        self
224    }
225
226    /// Set the proxy configuration.
227    pub fn proxy(mut self, v: ProxySetting) -> Self {
228        self.repo.proxy = v;
229        self
230    }
231
232    /// Set the username for repository authentication.
233    pub fn username(mut self, v: Username) -> Self {
234        self.repo.username = Some(v);
235        self
236    }
237
238    /// Set the password for repository authentication.
239    pub fn password(mut self, v: Password) -> Self {
240        self.repo.password = Some(v);
241        self
242    }
243
244    /// Add an extra (unknown) key-value pair to the repository configuration.
245    ///
246    /// Unknown keys are preserved for round-trip fidelity when rendering.
247    /// If the same key is added multiple times, the values are appended.
248    pub fn extra(mut self, key: &str, value: &str) -> Self {
249        self.repo
250            .extras
251            .entry(key.to_string())
252            .or_default()
253            .push(value.to_string());
254        self
255    }
256}