egml_core/model/base/
id.rs1use crate::Error;
2use sha2::{Digest, Sha256};
3use std::fmt;
4use std::fmt::Write;
5use uuid::Uuid;
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
14pub struct Id(String);
15
16impl Id {
17 pub fn from_hashed_bytes(val: impl AsRef<[u8]>) -> Self {
30 Self(Self::hash_bytes_to_hex(val.as_ref()))
31 }
32
33 pub fn from_hashed_string(val: &str) -> Self {
47 Self::from_hashed_bytes(val.as_bytes())
48 }
49
50 pub fn from_hashed_u64(val: u64) -> Self {
52 Self::from_hashed_bytes(val.to_le_bytes())
53 }
54
55 pub fn generate_uuid_v4() -> Self {
66 Self(Uuid::new_v4().to_string())
67 }
68}
69
70impl Id {
71 fn hash_bytes_to_hex(val: &[u8]) -> String {
73 let mut sha256 = Sha256::new();
74 sha256.update(val);
75 let result = sha256.finalize();
76
77 let mut hash = String::with_capacity(64);
79 for byte in result {
80 write!(&mut hash, "{:02X}", byte).unwrap();
81 }
82 hash
83 }
84}
85
86impl From<Id> for String {
87 fn from(item: Id) -> Self {
88 item.0
89 }
90}
91
92impl TryFrom<&String> for Id {
93 type Error = Error;
94
95 fn try_from(item: &String) -> Result<Self, Self::Error> {
99 if item.is_empty() {
100 Err(Error::TooFewElements {
101 geometry: "gml:id",
102 minimum: 1,
103 spec: None,
104 id: None,
105 detail: Some("id string must not be empty".to_string()),
106 })
107 } else {
108 Ok(Self(item.to_string()))
109 }
110 }
111}
112
113impl TryFrom<&str> for Id {
114 type Error = Error;
115
116 fn try_from(item: &str) -> Result<Self, Self::Error> {
120 if item.is_empty() {
121 Err(Error::TooFewElements {
122 geometry: "gml:id",
123 minimum: 1,
124 spec: None,
125 id: None,
126 detail: Some("id string must not be empty".to_string()),
127 })
128 } else {
129 Ok(Self(item.to_string()))
130 }
131 }
132}
133
134impl TryFrom<String> for Id {
135 type Error = Error;
136
137 fn try_from(item: String) -> Result<Self, Self::Error> {
141 if item.is_empty() {
142 Err(Error::TooFewElements {
143 geometry: "gml:id",
144 minimum: 1,
145 spec: None,
146 id: None,
147 detail: Some("id string must not be empty".to_string()),
148 })
149 } else {
150 Ok(Self(item))
151 }
152 }
153}
154
155impl fmt::Display for Id {
162 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
163 write!(f, "{}", self.0)
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170
171 #[test]
172 fn id_from_empty_string() {
173 let result = Id::try_from("".to_string());
174
175 assert!(matches!(
176 result,
177 Err(Error::TooFewElements {
178 geometry: "gml:id",
179 minimum: 1,
180 ..
181 })
182 ));
183 }
184
185 #[test]
186 fn test() {
187 let xml_document = "<gml:Point>
188 <gml:pos srsDimension=\"3\">678000.9484065345 5403659.060043676 417.3802376791456</gml:pos>
189 </gml:Point>";
190
191 let id_a = Id::from_hashed_string(xml_document);
192 let id_b = Id::from_hashed_string(xml_document);
193
194 assert_eq!(id_a, id_b);
195 }
196}