grid_tariffs/
links.rs

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    /// Website page containing info about the fees that the company charges
9    fee_info: Link,
10    /// Website page containing info about feed-in revenue
11    feed_in_revenue_info: Option<Link>,
12    /// Link to public Eltariff-API endpoint (https://github.com/RI-SE/Eltariff-API)
13    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 const fn builder() -> LinksBuilder {
22        LinksBuilder::new()
23    }
24}
25
26pub struct LinksBuilder {
27    fee_info: Option<Link>,
28    feed_in_revenue_info: Option<Link>,
29    eltariff_api: Option<&'static str>,
30}
31
32impl Default for LinksBuilder {
33    fn default() -> Self {
34        Self::new()
35    }
36}
37
38impl LinksBuilder {
39    pub const fn new() -> Self {
40        Self {
41            fee_info: None,
42            feed_in_revenue_info: None,
43            eltariff_api: None,
44        }
45    }
46
47    pub const fn fee_info(mut self, link: &'static str, css_selector: &'static str) -> Self {
48        self.fee_info = Some(
49            Link::builder(link)
50                .plain_content_locator(css_selector)
51                .build(),
52        );
53        self
54    }
55
56    /// Fee info that uses the default content locator
57    pub const fn fee_info_default(mut self, link: &'static str) -> Self {
58        self.fee_info = Some(Link::builder(link).content_locator_default().build());
59        self
60    }
61
62    pub const fn fee_info_complex(
63        mut self,
64        link: &'static str,
65        content_locator: ContentLocator,
66    ) -> Self {
67        self.fee_info = Some(Link::builder(link).content_locator(content_locator).build());
68        self
69    }
70
71    pub 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 const fn feed_in_revenue_info_default(mut self, link: &'static str) -> Self {
85        self.feed_in_revenue_info = Some(Link::builder(link).content_locator_default().build());
86        self
87    }
88
89    pub const fn eltariff_api(mut self, link: &'static str) -> Self {
90        self.eltariff_api = Some(link);
91        self
92    }
93
94    pub const fn build(self) -> Links {
95        Links {
96            fee_info: self.fee_info.expect("`fee_info` not specified"),
97            feed_in_revenue_info: self.feed_in_revenue_info,
98            eltariff_api: self.eltariff_api,
99        }
100    }
101}
102
103#[derive(Debug, Clone, Copy, Serialize)]
104#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
105pub enum TargetContainer {
106    Current,
107    Parent,
108    Ancestor(usize),
109}
110
111/// What content to checksum witihin the elements found
112#[derive(Debug, Clone, Serialize)]
113#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
114pub enum ContentTarget {
115    TextWithLinks,
116    /// Attribute which contains the relevant content
117    Attribute(&'static str),
118}
119
120#[derive(Debug, Clone, Serialize)]
121#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
122pub struct ContentLocator {
123    method: LocatorMethod,
124    content: ContentTarget,
125    uses_default_locator: bool,
126}
127
128impl ContentLocator {
129    pub(crate) const fn new(method: LocatorMethod, content: ContentTarget) -> Self {
130        Self {
131            method,
132            content,
133            uses_default_locator: false,
134        }
135    }
136
137    pub const fn method(&self) -> &LocatorMethod {
138        &self.method
139    }
140
141    pub const fn uses_default_locator(&self) -> bool {
142        self.uses_default_locator
143    }
144
145    pub const fn content_target(&self) -> &ContentTarget {
146        &self.content
147    }
148
149    pub(crate) const fn new_starts_with(
150        needle: &'static str,
151        target_container: TargetContainer,
152        content: ContentTarget,
153    ) -> ContentLocator {
154        Self::new(
155            LocatorMethod::TextStartsWith {
156                needle,
157                target_container,
158            },
159            content,
160        )
161    }
162}
163
164#[derive(Debug, Clone, Serialize)]
165#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
166pub enum LocatorMethod {
167    CssSelector(&'static str),
168    TextStartsWith {
169        needle: &'static str,
170        target_container: TargetContainer,
171    },
172}
173
174impl LocatorMethod {
175    pub fn target_container(&self) -> TargetContainer {
176        match self {
177            Self::CssSelector(_) => TargetContainer::Current,
178            Self::TextStartsWith {
179                target_container, ..
180            } => *target_container,
181        }
182    }
183}
184
185#[derive(Debug, Clone, Serialize)]
186#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
187pub struct Link {
188    link: &'static str,
189    content_locator: ContentLocator,
190}
191
192impl Link {
193    pub const fn link(&self) -> &str {
194        self.link
195    }
196
197    pub const fn content_locator(&self) -> &ContentLocator {
198        &self.content_locator
199    }
200
201    pub(crate) const fn builder(link: &'static str) -> LinkBuilder {
202        LinkBuilder::new(link)
203    }
204}
205
206pub struct LinkBuilder {
207    link: &'static str,
208    content_locator: Option<ContentLocator>,
209}
210
211impl LinkBuilder {
212    pub(crate) const fn new(link: &'static str) -> Self {
213        Self {
214            link,
215            content_locator: None,
216        }
217    }
218
219    pub(crate) const fn content_locator(mut self, locator: ContentLocator) -> Self {
220        self.content_locator = Some(locator);
221        self
222    }
223
224    pub(crate) const fn plain_content_locator(mut self, css_selector: &'static str) -> Self {
225        self.content_locator = Some(ContentLocator::new(
226            LocatorMethod::CssSelector(css_selector),
227            ContentTarget::TextWithLinks,
228        ));
229        self
230    }
231
232    pub(crate) const fn content_locator_default(mut self) -> Self {
233        self.content_locator = Some(ContentLocator {
234            method: LocatorMethod::CssSelector(DEFAULT_CSS_SELECTOR),
235            content: ContentTarget::TextWithLinks,
236            uses_default_locator: true,
237        });
238        self
239    }
240
241    pub(crate) const fn build(self) -> Link {
242        Link {
243            link: self.link,
244            content_locator: self.content_locator.expect("`locator` missing"),
245        }
246    }
247}