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