1use std::{convert::TryFrom, marker::PhantomData, ops::Deref};
2
3use alloy_primitives::Address;
4
5use crate::{AssetId, Denomination, FeedConfig, FeedId, OracleError, StalenessPolicy};
6
7#[derive(Clone, Debug, PartialEq, Eq)]
9pub struct FeedReady;
10
11#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct FeedBuilderState;
14
15pub type FeedBuilder = Feed<FeedBuilderState>;
17
18#[derive(Clone, Debug, PartialEq, Eq)]
20pub struct Feed<State = FeedReady> {
21 access: FeedAccess,
22 _state: PhantomData<State>,
23}
24
25#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct FeedAccess {
28 inner: FeedInner,
29}
30
31#[derive(Clone, Debug, PartialEq, Eq)]
32struct FeedInner {
33 kind: FeedKind,
34 id: Option<FeedId>,
35 label: Option<String>,
36 base: Option<AssetId>,
37 quote: Option<Denomination>,
38 staleness: StalenessPolicy,
39}
40
41#[derive(Clone, Debug, PartialEq, Eq)]
42enum FeedKind {
43 Proxy { proxy: Address },
44}
45
46impl Feed<FeedBuilderState> {
47 pub fn proxy(proxy: Address) -> Self {
49 Self {
50 access: FeedAccess {
51 inner: FeedInner {
52 kind: FeedKind::Proxy { proxy },
53 id: None,
54 label: None,
55 base: None,
56 quote: None,
57 staleness: StalenessPolicy::default(),
58 },
59 },
60 _state: PhantomData,
61 }
62 }
63
64 pub fn id(mut self, id: impl Into<String>) -> Self {
66 self.access.inner.id = Some(FeedId::new(id));
67 self
68 }
69
70 pub fn feed_id(mut self, id: FeedId) -> Self {
72 self.access.inner.id = Some(id);
73 self
74 }
75
76 pub fn label(mut self, label: impl Into<String>) -> Self {
78 self.access.inner.label = Some(label.into());
79 self
80 }
81
82 pub fn base(mut self, base: AssetId) -> Self {
84 self.access.inner.base = Some(base);
85 self
86 }
87
88 pub fn quote(mut self, quote: Denomination) -> Self {
90 self.access.inner.quote = Some(quote);
91 self
92 }
93
94 pub fn max_age_secs(mut self, max_age_secs: u64) -> Feed {
96 self.access.inner.staleness = StalenessPolicy::max_age(max_age_secs);
97 self.build()
98 }
99
100 pub fn staleness(mut self, staleness: StalenessPolicy) -> Feed {
102 self.access.inner.staleness = staleness;
103 self.build()
104 }
105
106 pub fn build(self) -> Feed {
108 Feed {
109 access: self.access,
110 _state: PhantomData,
111 }
112 }
113}
114
115impl Deref for Feed {
116 type Target = FeedAccess;
117
118 fn deref(&self) -> &Self::Target {
119 &self.access
120 }
121}
122
123impl FeedAccess {
124 pub fn proxy(&self) -> Option<Address> {
126 match self.inner.kind {
127 FeedKind::Proxy { proxy } => Some(proxy),
128 }
129 }
130
131 pub fn id(&self) -> Option<FeedId> {
133 self.inner.id.clone()
134 }
135
136 pub fn base(&self) -> Option<&AssetId> {
138 self.inner.base.as_ref()
139 }
140
141 pub fn quote(&self) -> Option<&Denomination> {
143 self.inner.quote.as_ref()
144 }
145
146 pub fn label(&self) -> Option<&str> {
148 self.inner.label.as_deref()
149 }
150}
151
152impl TryFrom<Feed> for FeedConfig {
153 type Error = OracleError;
154
155 fn try_from(feed: Feed) -> Result<Self, Self::Error> {
156 feed_config_from_inner(feed.access.inner)
157 }
158}
159
160impl TryFrom<FeedBuilder> for FeedConfig {
161 type Error = OracleError;
162
163 fn try_from(feed: FeedBuilder) -> Result<Self, Self::Error> {
164 feed_config_from_inner(feed.access.inner)
165 }
166}
167
168fn feed_config_from_inner(feed: FeedInner) -> Result<FeedConfig, OracleError> {
169 let FeedKind::Proxy { proxy } = feed.kind;
170 Ok(FeedConfig {
171 proxy,
172 id: feed.id,
173 label: feed.label,
174 base: feed.base.map(String::from),
175 quote: feed.quote.map(String::from),
176 staleness: feed.staleness,
177 })
178}