1use std::fmt::{Display, Formatter};
2use std::ops::Deref;
3use std::str::{from_utf8, FromStr};
4
5use ibc::core::ics24_host::{path, validate::validate_identifier, Path as IbcPath};
6
7use crate::{Result, ServerError};
8
9macro_rules! impl_into_path_for {
10 ($($path:ty),+) => {
11 $(impl From<$path> for Path {
12 fn from(ibc_path: $path) -> Self {
13 Self::try_from(ibc_path.to_string()).unwrap() }
15 })+
16 };
17}
18
19impl_into_path_for!(
20 path::ClientTypePath,
21 path::ClientStatePath,
22 path::ClientConsensusStatePath,
23 path::ConnectionsPath,
24 path::ClientConnectionsPath,
25 path::ChannelEndsPath,
26 path::SeqSendsPath,
27 path::SeqRecvsPath,
28 path::SeqAcksPath,
29 path::CommitmentsPath,
30 path::ReceiptsPath,
31 path::AcksPath
32);
33
34#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
37pub struct Identifier(String);
38
39impl Identifier {
40 fn validate(s: impl AsRef<str>) -> Result<()> {
47 let s = s.as_ref();
48
49 validate_identifier(s, 0, s.len()).map_err(ServerError::ValidateIdentifier)
53 }
54}
55
56impl Deref for Identifier {
57 type Target = String;
58
59 fn deref(&self) -> &Self::Target {
60 &self.0
61 }
62}
63
64impl TryFrom<String> for Identifier {
65 type Error = ServerError;
66
67 fn try_from(s: String) -> Result<Self> {
68 Identifier::validate(&s).map(|_| Self(s))
69 }
70}
71
72impl Display for Identifier {
73 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
74 write!(f, "{}", self.0)
75 }
76}
77
78#[derive(Copy, Clone, Debug, PartialEq, Eq)]
79pub enum StoreHeight {
80 Latest,
81 Stable(u64),
82}
83
84#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
86pub struct Path(Vec<Identifier>);
87
88impl Path {
89 pub fn starts_with(&self, prefix: &Path) -> bool {
90 KeyPrefix::from(self)
91 .as_ref()
92 .starts_with(KeyPrefix::from(prefix).as_ref())
93 }
94}
95
96impl TryFrom<String> for Path {
97 type Error = ServerError;
98
99 fn try_from(s: String) -> Result<Self> {
100 let mut identifiers = vec![];
101 let parts = s.split('/'); for part in parts {
103 identifiers.push(Identifier::try_from(part.to_owned())?);
104 }
105 Ok(Self(identifiers))
106 }
107}
108
109impl TryFrom<&[u8]> for Path {
110 type Error = ServerError;
111
112 fn try_from(value: &[u8]) -> Result<Self> {
113 let s = from_utf8(value).map_err(ServerError::FromUtf8)?;
114 s.to_owned().try_into()
115 }
116}
117
118impl From<Identifier> for Path {
119 fn from(id: Identifier) -> Self {
120 Self(vec![id])
121 }
122}
123
124impl Display for Path {
125 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
126 write!(
127 f,
128 "{}",
129 self.0
130 .iter()
131 .map(|iden| iden.as_str().to_owned())
132 .collect::<Vec<String>>()
133 .join("/")
134 )
135 }
136}
137
138impl TryFrom<Path> for IbcPath {
139 type Error = path::PathError;
140
141 fn try_from(path: Path) -> std::result::Result<Self, Self::Error> {
142 Self::from_str(path.to_string().as_str())
143 }
144}
145
146impl From<IbcPath> for Path {
147 fn from(ibc_path: IbcPath) -> Self {
148 Self::try_from(ibc_path.to_string()).unwrap() }
150}
151
152#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)]
153pub struct KeyPrefix(String);
154
155impl From<&Path> for KeyPrefix {
156 fn from(ibc_path: &Path) -> Self {
157 let strs: Vec<String> = ibc_path.0.iter().map(|x| x.to_string()).collect();
158 KeyPrefix(strs.join("/"))
159 }
160}
161
162impl From<&KeyPrefix> for String {
163 fn from(key_prefix: &KeyPrefix) -> Self {
164 key_prefix.0.clone()
165 }
166}
167
168impl AsRef<[u8]> for KeyPrefix {
169 fn as_ref(&self) -> &[u8] {
170 self.0.as_bytes()
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use ibc::core::ics02_client::client_type::ClientType;
177 use ibc::core::ics24_host::{identifier::ClientId, path::ClientTypePath};
178
179 use super::*;
180
181 #[test]
182 fn starts_with_test_case01() {
183 let dst_path: Path = "clients/07-tendermint-1033".to_owned().try_into().unwrap();
184 let client_id = ClientId::new(ClientType::Tendermint, 103344).unwrap();
185 let path = ClientTypePath(client_id);
186 let src_path: Path = Path::from(path);
187
188 assert!(src_path.starts_with(&dst_path));
189 }
190
191 #[test]
192
193 fn starts_with_test_case02() {
194 use ibc::core::ics24_host::{identifier::ClientId, path::ClientStatePath};
195 let dst_path = "clients/07-tend".to_owned().try_into().unwrap();
196 let client_id = ClientId::new(ClientType::Tendermint, 1033).unwrap();
197 let path = ClientStatePath(client_id);
198 let src_path: Path = Path::from(path);
199
200 assert!(src_path.starts_with(&dst_path));
201 }
202
203 #[test]
204
205 fn starts_with_test_case03() {
206 use ibc::core::ics24_host::{identifier::ClientId, path::ClientStatePath};
207 let dst_path = "clients".to_owned().try_into().unwrap();
208 let client_id = ClientId::new(ClientType::Tendermint, 1033).unwrap();
209 let path = ClientStatePath(client_id);
210 let src_path: Path = Path::from(path);
211
212 assert!(src_path.starts_with(&dst_path));
213 }
214
215 #[test]
216
217 fn test_key_prefix_from_path_case01() {
218 let src = String::from("connections");
219 let src_path: Path = src
220 .clone()
221 .try_into()
222 .expect("'connections' expected to be a valid Path");
223 let prefix_key = KeyPrefix::from(&src_path);
224 assert_eq!(src, String::from(&prefix_key))
225 }
226
227 #[test]
228
229 fn test_key_prefix_from_path_case02() {
230 let src = String::from("channelEnds/ports");
231 let src_path: Path = src
232 .clone()
233 .try_into()
234 .expect("'channelEnds/ports' expected to be a valid Path");
235 let prefix_key = KeyPrefix::from(&src_path);
236 assert_eq!(src, String::from(&prefix_key))
237 }
238
239 #[test]
240
241 fn test_key_prefix_from_path_case03() {
242 let src = format!("clients/{}/consensusStates", "123client_id");
243 let src_path = src
244 .clone()
245 .try_into()
246 .expect("'channelEnds/ports' expected to be a valid Path");
247 let prefix_key = KeyPrefix::from(&src_path);
248 assert_eq!(src, String::from(&prefix_key))
249 }
250}