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(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    /// Fee info that uses the default content locator
51    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    /// Feed-in revenue info that uses the default content locator
66    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/// What content to checksum witihin the elements found
107#[derive(Debug, Clone, Serialize)]
108#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
109pub enum ContentTarget {
110    TextWithLinks,
111    /// Attribute which contains the relevant content
112    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}