1use serde::Serialize;
2
3#[derive(Debug, Clone, Serialize)]
4#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5pub struct Links {
6 fee_info: Link,
8 feed_in_revenue_info: Option<Link>,
10 eltariff_api: Option<&'static str>,
12}
13
14impl Links {
15 pub const fn fee_info(&self) -> &Link {
16 &self.fee_info
17 }
18
19 pub(crate) const fn new(fee_info: Link) -> Self {
20 Self::builder().fee_info(fee_info).build()
21 }
22
23 pub(crate) const fn builder() -> LinksBuilder {
24 LinksBuilder::new()
25 }
26}
27
28pub(crate) struct LinksBuilder {
29 fee_info: Option<Link>,
30 feed_in_revenue_info: Option<Link>,
31 eltariff_api: Option<&'static str>,
32}
33
34impl LinksBuilder {
35 pub(crate) const fn new() -> Self {
36 Self {
37 fee_info: None,
38 feed_in_revenue_info: None,
39 eltariff_api: None,
40 }
41 }
42
43 pub(crate) const fn fee_info(mut self, link: Link) -> Self {
44 self.fee_info = Some(link);
45 self
46 }
47
48 pub(crate) const fn new_fee_info(self, link: &'static str, css_selector: &'static str) -> Self {
49 self.fee_info(
50 Link::builder(link)
51 .plain_content_locator(css_selector)
52 .build(),
53 )
54 }
55
56 pub(crate) const fn feed_in_revenue_info(mut self, link: Link) -> Self {
57 self.feed_in_revenue_info = Some(link);
58 self
59 }
60
61 pub(crate) const fn new_feed_in_revenue_info(
62 self,
63 link: &'static str,
64 css_selector: &'static str,
65 ) -> Self {
66 self.feed_in_revenue_info(
67 Link::builder(link)
68 .plain_content_locator(css_selector)
69 .build(),
70 )
71 }
72
73 pub(crate) const fn eltariff_api(mut self, link: &'static str) -> Self {
74 self.eltariff_api = Some(link);
75 self
76 }
77
78 pub(crate) const fn build(self) -> Links {
79 Links {
80 fee_info: self.fee_info.expect("`fee_info` not specified"),
81 feed_in_revenue_info: self.feed_in_revenue_info,
82 eltariff_api: self.eltariff_api,
83 }
84 }
85}
86
87#[derive(Debug, Clone, Copy, Serialize)]
88#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
89pub enum TargetContainer {
90 Current,
91 Parent,
92 Ancestor(usize),
93}
94
95#[derive(Debug, Clone, Serialize)]
97#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
98pub enum ContentTarget {
99 TextWithLinks,
100 Attribute(&'static str),
102}
103
104#[derive(Debug, Clone, Serialize)]
105#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
106pub struct ContentLocator {
107 method: LocatorMethod,
108 content: ContentTarget,
109 uses_default_locator: bool,
110}
111
112impl ContentLocator {
113 pub(crate) const fn new(method: LocatorMethod, content: ContentTarget) -> Self {
114 Self {
115 method,
116 content,
117 uses_default_locator: false,
118 }
119 }
120
121 pub const fn method(&self) -> &LocatorMethod {
122 &self.method
123 }
124
125 pub const fn uses_default_locator(&self) -> bool {
126 self.uses_default_locator
127 }
128
129 pub const fn content_target(&self) -> &ContentTarget {
130 &self.content
131 }
132
133 pub(crate) const fn new_starts_with(
134 needle: &'static str,
135 target_container: TargetContainer,
136 content: ContentTarget,
137 ) -> ContentLocator {
138 Self::new(
139 LocatorMethod::TextStartsWith {
140 needle,
141 target_container,
142 },
143 content,
144 )
145 }
146}
147
148#[derive(Debug, Clone, Serialize)]
149#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
150pub enum LocatorMethod {
151 CssSelector(&'static str),
152 TextStartsWith {
153 needle: &'static str,
154 target_container: TargetContainer,
155 },
156}
157
158impl LocatorMethod {
159 pub fn target_container(&self) -> TargetContainer {
160 match self {
161 Self::CssSelector(_) => TargetContainer::Current,
162 Self::TextStartsWith {
163 target_container, ..
164 } => *target_container,
165 }
166 }
167}
168
169#[derive(Debug, Clone, Serialize)]
170#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
171pub struct Link {
172 link: &'static str,
173 content_locator: ContentLocator,
174}
175
176impl Link {
177 pub const fn link(&self) -> &str {
178 &self.link
179 }
180
181 pub const fn content_locator(&self) -> &ContentLocator {
182 &self.content_locator
183 }
184
185 pub(crate) const fn builder(link: &'static str) -> LinkBuilder {
186 LinkBuilder::new(link)
187 }
188}
189
190pub struct LinkBuilder {
191 link: &'static str,
192 content_locator: Option<ContentLocator>,
193}
194
195impl LinkBuilder {
196 pub(crate) const fn new(link: &'static str) -> Self {
197 Self {
198 link,
199 content_locator: None,
200 }
201 }
202
203 pub(crate) const fn content_locator(mut self, locator: ContentLocator) -> Self {
204 self.content_locator = Some(locator);
205 self
206 }
207
208 pub(crate) const fn plain_content_locator(mut self, css_selector: &'static str) -> Self {
209 self.content_locator = Some(ContentLocator::new(
210 LocatorMethod::CssSelector(css_selector),
211 ContentTarget::TextWithLinks,
212 ));
213 self
214 }
215
216 pub(crate) const fn content_locator_default(mut self) -> Self {
217 self.content_locator = Some(ContentLocator {
218 method: LocatorMethod::CssSelector("main"),
219 content: ContentTarget::TextWithLinks,
220 uses_default_locator: true,
221 });
222 self
223 }
224
225 pub(crate) const fn build(self) -> Link {
226 Link {
227 link: self.link,
228 content_locator: self.content_locator.expect("`locator` missing"),
229 }
230 }
231}