rama_http/protocols/rss/atom/
builder.rs1use std::marker::PhantomData;
2
3use jiff::Timestamp;
4use rama_net::uri::Uri;
5use rama_utils::macros::generate_set_and_with;
6
7use super::types::{
8 AtomCategory, AtomEntry, AtomFeed, AtomGenerator, AtomLink, AtomPerson, AtomText,
9};
10use crate::protocols::rss::feed_ext::FeedExtensions;
11use crate::protocols::rss::rss2::{Missing, Present};
12
13pub struct AtomFeedBuilder<I, T, U> {
19 pub(super) id: Option<Uri>,
20 pub(super) title: AtomText,
21 pub(super) updated: Option<Timestamp>,
22 pub(super) authors: Vec<AtomPerson>,
23 pub(super) links: Vec<AtomLink>,
24 pub(super) categories: Vec<AtomCategory>,
25 pub(super) contributors: Vec<AtomPerson>,
26 pub(super) generator: Option<AtomGenerator>,
27 pub(super) icon: Option<Uri>,
28 pub(super) logo: Option<Uri>,
29 pub(super) rights: Option<AtomText>,
30 pub(super) subtitle: Option<AtomText>,
31 pub(super) entries: Vec<AtomEntry>,
32 pub(super) extensions: FeedExtensions,
33 pub(super) _pd: PhantomData<(I, T, U)>,
34}
35
36impl AtomFeedBuilder<Missing, Missing, Missing> {
37 pub(super) fn new() -> Self {
38 Self {
39 id: None,
40 title: AtomText::text(""),
41 updated: None,
42 authors: Vec::new(),
43 links: Vec::new(),
44 categories: Vec::new(),
45 contributors: Vec::new(),
46 generator: None,
47 icon: None,
48 logo: None,
49 rights: None,
50 subtitle: None,
51 entries: Vec::new(),
52 extensions: FeedExtensions::default(),
53 _pd: PhantomData,
54 }
55 }
56}
57
58impl<T, U> AtomFeedBuilder<Missing, T, U> {
59 #[must_use]
60 pub fn id(self, id: Uri) -> AtomFeedBuilder<Present, T, U> {
61 AtomFeedBuilder {
62 id: Some(id),
63 title: self.title,
64 updated: self.updated,
65 authors: self.authors,
66 links: self.links,
67 categories: self.categories,
68 contributors: self.contributors,
69 generator: self.generator,
70 icon: self.icon,
71 logo: self.logo,
72 rights: self.rights,
73 subtitle: self.subtitle,
74 entries: self.entries,
75 extensions: self.extensions,
76 _pd: PhantomData,
77 }
78 }
79}
80
81impl<I, U> AtomFeedBuilder<I, Missing, U> {
82 #[must_use]
83 pub fn title(self, title: impl Into<AtomText>) -> AtomFeedBuilder<I, Present, U> {
84 AtomFeedBuilder {
85 id: self.id,
86 title: title.into(),
87 updated: self.updated,
88 authors: self.authors,
89 links: self.links,
90 categories: self.categories,
91 contributors: self.contributors,
92 generator: self.generator,
93 icon: self.icon,
94 logo: self.logo,
95 rights: self.rights,
96 subtitle: self.subtitle,
97 entries: self.entries,
98 extensions: self.extensions,
99 _pd: PhantomData,
100 }
101 }
102}
103
104impl<I, T> AtomFeedBuilder<I, T, Missing> {
105 #[must_use]
106 pub fn updated(self, ts: Timestamp) -> AtomFeedBuilder<I, T, Present> {
107 AtomFeedBuilder {
108 id: self.id,
109 title: self.title,
110 updated: Some(ts),
111 authors: self.authors,
112 links: self.links,
113 categories: self.categories,
114 contributors: self.contributors,
115 generator: self.generator,
116 icon: self.icon,
117 logo: self.logo,
118 rights: self.rights,
119 subtitle: self.subtitle,
120 entries: self.entries,
121 extensions: self.extensions,
122 _pd: PhantomData,
123 }
124 }
125}
126
127impl<I, T, U> AtomFeedBuilder<I, T, U> {
128 generate_set_and_with! {
129 pub fn author(mut self, author: AtomPerson) -> Self {
131 self.authors.push(author);
132 self
133 }
134 }
135
136 generate_set_and_with! {
137 pub fn link(mut self, link: AtomLink) -> Self {
139 self.links.push(link);
140 self
141 }
142 }
143
144 generate_set_and_with! {
145 pub fn category(mut self, cat: AtomCategory) -> Self {
147 self.categories.push(cat);
148 self
149 }
150 }
151
152 generate_set_and_with! {
153 pub fn contributor(mut self, c: AtomPerson) -> Self {
155 self.contributors.push(c);
156 self
157 }
158 }
159
160 generate_set_and_with! {
161 pub fn generator(mut self, generator: AtomGenerator) -> Self {
162 self.generator = Some(generator);
163 self
164 }
165 }
166
167 generate_set_and_with! {
168 pub fn icon(mut self, icon: Uri) -> Self {
169 self.icon = Some(icon);
170 self
171 }
172 }
173
174 generate_set_and_with! {
175 pub fn logo(mut self, logo: Uri) -> Self {
176 self.logo = Some(logo);
177 self
178 }
179 }
180
181 generate_set_and_with! {
182 pub fn rights(mut self, rights: impl Into<AtomText>) -> Self {
183 self.rights = Some(rights.into());
184 self
185 }
186 }
187
188 generate_set_and_with! {
189 pub fn subtitle(mut self, subtitle: impl Into<AtomText>) -> Self {
190 self.subtitle = Some(subtitle.into());
191 self
192 }
193 }
194
195 generate_set_and_with! {
196 pub fn entry(mut self, entry: AtomEntry) -> Self {
198 self.entries.push(entry);
199 self
200 }
201 }
202
203 generate_set_and_with! {
204 pub fn entries(mut self, entries: impl IntoIterator<Item = AtomEntry>) -> Self {
205 self.entries.extend(entries);
206 self
207 }
208 }
209
210 generate_set_and_with! {
211 pub fn feed_extensions(mut self, ext: FeedExtensions) -> Self {
212 self.extensions = ext;
213 self
214 }
215 }
216}
217
218impl AtomFeedBuilder<Present, Present, Present> {
219 #[must_use]
220 pub fn build(self) -> AtomFeed {
221 AtomFeed {
222 #[expect(
223 clippy::expect_used,
224 reason = "type-state guarantees `id` is Present once build() is callable"
225 )]
226 id: self.id.expect("id is Present"),
227 title: self.title,
228 #[expect(
229 clippy::expect_used,
230 reason = "type-state guarantees `updated` is Present once build() is callable"
231 )]
232 updated: self.updated.expect("updated is Present"),
233 authors: self.authors,
234 links: self.links,
235 categories: self.categories,
236 contributors: self.contributors,
237 generator: self.generator,
238 icon: self.icon,
239 logo: self.logo,
240 rights: self.rights,
241 subtitle: self.subtitle,
242 entries: self.entries,
243 extensions: self.extensions,
244 }
245 }
246}