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    pub(crate) const fn feed_in_revenue_info(
66        mut self,
67        link: &'static str,
68        css_selector: &'static str,
69    ) -> Self {
70        self.feed_in_revenue_info = Some(
71            Link::builder(link)
72                .plain_content_locator(css_selector)
73                .build(),
74        );
75        self
76    }
77
78    pub(crate) const fn eltariff_api(mut self, link: &'static str) -> Self {
79        self.eltariff_api = Some(link);
80        self
81    }
82
83    pub(crate) const fn build(self) -> Links {
84        Links {
85            fee_info: self.fee_info.expect("`fee_info` not specified"),
86            feed_in_revenue_info: self.feed_in_revenue_info,
87            eltariff_api: self.eltariff_api,
88        }
89    }
90}
91
92#[derive(Debug, Clone, Copy, Serialize)]
93#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
94pub enum TargetContainer {
95    Current,
96    Parent,
97    Ancestor(usize),
98}
99
100/// What content to checksum witihin the elements found
101#[derive(Debug, Clone, Serialize)]
102#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
103pub enum ContentTarget {
104    TextWithLinks,
105    /// Attribute which contains the relevant content
106    Attribute(&'static str),
107}
108
109#[derive(Debug, Clone, Serialize)]
110#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
111pub struct ContentLocator {
112    method: LocatorMethod,
113    content: ContentTarget,
114    uses_default_locator: bool,
115}
116
117impl ContentLocator {
118    pub(crate) const fn new(method: LocatorMethod, content: ContentTarget) -> Self {
119        Self {
120            method,
121            content,
122            uses_default_locator: false,
123        }
124    }
125
126    pub const fn method(&self) -> &LocatorMethod {
127        &self.method
128    }
129
130    pub const fn uses_default_locator(&self) -> bool {
131        self.uses_default_locator
132    }
133
134    pub const fn content_target(&self) -> &ContentTarget {
135        &self.content
136    }
137
138    pub(crate) const fn new_starts_with(
139        needle: &'static str,
140        target_container: TargetContainer,
141        content: ContentTarget,
142    ) -> ContentLocator {
143        Self::new(
144            LocatorMethod::TextStartsWith {
145                needle,
146                target_container,
147            },
148            content,
149        )
150    }
151}
152
153#[derive(Debug, Clone, Serialize)]
154#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
155pub enum LocatorMethod {
156    CssSelector(&'static str),
157    TextStartsWith {
158        needle: &'static str,
159        target_container: TargetContainer,
160    },
161}
162
163impl LocatorMethod {
164    pub fn target_container(&self) -> TargetContainer {
165        match self {
166            Self::CssSelector(_) => TargetContainer::Current,
167            Self::TextStartsWith {
168                target_container, ..
169            } => *target_container,
170        }
171    }
172}
173
174#[derive(Debug, Clone, Serialize)]
175#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
176pub struct Link {
177    link: &'static str,
178    content_locator: ContentLocator,
179}
180
181impl Link {
182    pub const fn link(&self) -> &str {
183        self.link
184    }
185
186    pub const fn content_locator(&self) -> &ContentLocator {
187        &self.content_locator
188    }
189
190    pub(crate) const fn builder(link: &'static str) -> LinkBuilder {
191        LinkBuilder::new(link)
192    }
193}
194
195pub struct LinkBuilder {
196    link: &'static str,
197    content_locator: Option<ContentLocator>,
198}
199
200impl LinkBuilder {
201    pub(crate) const fn new(link: &'static str) -> Self {
202        Self {
203            link,
204            content_locator: None,
205        }
206    }
207
208    pub(crate) const fn content_locator(mut self, locator: ContentLocator) -> Self {
209        self.content_locator = Some(locator);
210        self
211    }
212
213    pub(crate) const fn plain_content_locator(mut self, css_selector: &'static str) -> Self {
214        self.content_locator = Some(ContentLocator::new(
215            LocatorMethod::CssSelector(css_selector),
216            ContentTarget::TextWithLinks,
217        ));
218        self
219    }
220
221    pub(crate) const fn content_locator_default(mut self) -> Self {
222        self.content_locator = Some(ContentLocator {
223            method: LocatorMethod::CssSelector(DEFAULT_CSS_SELECTOR),
224            content: ContentTarget::TextWithLinks,
225            uses_default_locator: true,
226        });
227        self
228    }
229
230    pub(crate) const fn build(self) -> Link {
231        Link {
232            link: self.link,
233            content_locator: self.content_locator.expect("`locator` missing"),
234        }
235    }
236}