oxedyne/fe2o3/fe2o3_namex/src/id.rs
6.4 KiB, 24 runs
created by r1870400018:521, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | use oxedyne_fe2o3_core::{ |
| 2 | prelude::*, |
| 3 | rand::RanDef, |
| 4 | }; |
| 5 | use oxedyne_fe2o3_jdat::{ |
| 6 | prelude::*, |
| 7 | }; |
| 8 | |
| 9 | use std::{ |
| 10 | collections::BTreeMap, |
| 11 | fmt, |
| 12 | }; |
| 13 | |
| 14 | |
| 15 | new_type!(LocalId, u8, Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd); |
| 16 | |
| 17 | impl fmt::Display for LocalId { |
| 18 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 19 | write!(f, "{}", self.0) |
| 20 | } |
| 21 | } |
| 22 | |
| 23 | impl ToDat for LocalId { |
| 24 | fn to_dat(&self) -> Outcome<Dat> { |
| 25 | Ok(dat!(self.0)) |
| 26 | } |
| 27 | } |
| 28 | |
| 29 | impl FromDat for LocalId { |
| 30 | fn from_dat(dat: Dat) -> Outcome<Self> { |
| 31 | Ok(Self(try_extract_dat!(dat, U8))) |
| 32 | } |
| 33 | } |
| 34 | |
| 35 | new_type!(NamexId, B32, Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd); |
| 36 | |
| 37 | impl From<B32> for NamexId { |
| 38 | fn from(b: B32) -> Self { |
| 39 | Self(b) |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | impl From<&B32> for NamexId { |
| 44 | fn from(b: &B32) -> Self { |
| 45 | Self(*b) |
| 46 | } |
| 47 | } |
| 48 | |
| 49 | impl TryFrom<&Dat> for NamexId { |
| 50 | type Error = Error<ErrTag>; |
| 51 | |
| 52 | fn try_from(dat: &Dat) -> std::result::Result<Self, Self::Error> { |
| 53 | Ok(match dat { |
| 54 | Dat::B32(a) => Self::from(a), |
| 55 | // The canonical textual form of a Namex id is standard base64, which is what |
| 56 | // `Display` emits and what callers pass to `TryFrom<&str>`. |
| 57 | Dat::Str(s) => res!(Self::try_from(s.as_str())), |
| 58 | _ => return Err(err!( |
| 59 | "Expected a daticle of kind {:?} or {:?}, received a {:?}.", |
| 60 | Kind::B32, Kind::Str, dat.kind(); |
| 61 | Invalid, Input, Mismatch)), |
| 62 | }) |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | impl ToDat for NamexId { |
| 67 | fn to_dat(&self) -> Outcome<Dat> { |
| 68 | Ok(Dat::B32(self.0)) |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | impl FromDat for NamexId { |
| 73 | fn from_dat(dat: Dat) -> Outcome<Self> { |
| 74 | Ok(match dat { |
| 75 | Dat::B32(a) => Self::from(a), |
| 76 | Dat::Str(ref s) => res!(Self::try_from(s.as_str())), |
| 77 | _ => return Err(err!( |
| 78 | "Expected a daticle of kind {:?} or {:?}, received a {:?}.", |
| 79 | Kind::B32, Kind::Str, dat.kind(); |
| 80 | Invalid, Input, Mismatch)), |
| 81 | }) |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | impl TryFrom<&str> for NamexId { |
| 86 | type Error = Error<ErrTag>; |
| 87 | |
| 88 | fn try_from(s: &str) -> std::result::Result<Self, Self::Error> { |
| 89 | let byts = res!(base64::decode(s)); |
| 90 | let id = res!(Self::try_from(&byts[..])); |
| 91 | Ok(id) |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | impl TryFrom<&[u8]> for NamexId { |
| 96 | type Error = Error<ErrTag>; |
| 97 | |
| 98 | fn try_from(b: &[u8]) -> std::result::Result<Self, Self::Error> { |
| 99 | let id = Self::from(res!(B32::from_bytes(&b[..])).0); |
| 100 | Ok(id) |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | impl RanDef for NamexId { |
| 105 | fn randef() -> Self { |
| 106 | let a = B32::randef(); |
| 107 | Self(a) |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | impl fmt::Display for NamexId { |
| 112 | fn fmt (&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 113 | write!(f, "{}", base64::encode_config(&self[..], base64::STANDARD)) |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | pub struct NameIndex { |
| 118 | s2i: BTreeMap<&'static str, NamexId>, |
| 119 | i2s: BTreeMap<NamexId, &'static str>, |
| 120 | } |
| 121 | |
| 122 | impl NameIndex { |
| 123 | |
| 124 | pub fn by_name(&self, name: &str) -> Option<&NamexId> { |
| 125 | self.s2i.get(name) |
| 126 | } |
| 127 | |
| 128 | pub fn contains_name(&self, name: &str) -> bool { |
| 129 | self.s2i.contains_key(name) |
| 130 | } |
| 131 | |
| 132 | pub fn names(&self) -> Vec<&'static str> { |
| 133 | self.s2i.keys().cloned().collect() |
| 134 | } |
| 135 | |
| 136 | pub fn by_id(&self, id: &NamexId) -> Option<&'static str> { |
| 137 | self.i2s.get(id).copied() |
| 138 | } |
| 139 | |
| 140 | pub fn contains_id(&self, id: &NamexId) -> bool { |
| 141 | self.i2s.contains_key(id) |
| 142 | } |
| 143 | |
| 144 | pub fn ids(&self) -> Vec<NamexId> { |
| 145 | self.i2s.keys().cloned().collect() |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | pub trait InNamex { |
| 150 | |
| 151 | /// The universal identifier, using a type so large that randomly selected ids have a very low |
| 152 | /// chance of collision. |
| 153 | fn name_id(&self) -> Outcome<NamexId>; |
| 154 | |
| 155 | /// A shorter id for local use. Use 0 for "unknown" or "none". |
| 156 | fn local_id(&self) -> LocalId { LocalId::default() } |
| 157 | |
| 158 | /// Allow the name to be associated with groups of other names, for specific purposes. This |
| 159 | /// returns a vector of Namex ids as base64 strings. |
| 160 | fn assoc_names_base64( |
| 161 | _gname: &'static str, |
| 162 | ) |
| 163 | -> Outcome<Option<Vec<( |
| 164 | &'static str, |
| 165 | &'static str, |
| 166 | )>>> |
| 167 | { |
| 168 | Ok(None) |
| 169 | } |
| 170 | |
| 171 | /// Returns binary Namex ids based on user-specified base64 ids, indexed by name. |
| 172 | fn assoc_names_index( |
| 173 | gname: &'static str, |
| 174 | ) |
| 175 | -> Outcome<Option<NameIndex>> |
| 176 | { |
| 177 | Ok(match res!(Self::assoc_names_base64(gname)) { |
| 178 | Some(ids) => { |
| 179 | let mut s2i = BTreeMap::new(); |
| 180 | let mut i2s = BTreeMap::new(); |
| 181 | for (name, idstr) in ids { |
| 182 | let id = res!(NamexId::try_from(idstr)); |
| 183 | s2i.insert(name, id.clone()); |
| 184 | i2s.insert(id, name); |
| 185 | } |
| 186 | if s2i.len() == 0 { |
| 187 | None |
| 188 | } else { |
| 189 | Some(NameIndex { s2i, i2s }) |
| 190 | } |
| 191 | }, |
| 192 | None => None, |
| 193 | }) |
| 194 | } |
| 195 | |
| 196 | ///// Provides a search-by-name functionality with scope and ordering defined by the implementor. |
| 197 | //fn by_name<'a>( |
| 198 | // &self, |
| 199 | // gname: &'static str, |
| 200 | // name: &'static str, |
| 201 | // ndb: &'a Namex<M1, M2>, |
| 202 | //) |
| 203 | // -> Outcome<Option<(B32, &'a Entity)>> |
| 204 | //{ |
| 205 | // match res!(self.assoc_as_ids(gname)) { |
| 206 | // Some(ids) => { |
| 207 | // for id in ids { |
| 208 | // match ndb.by_id(&id) { |
| 209 | // Some(entity) => { |
| 210 | // for nam in &entity.nams { |
| 211 | // if nam == name { |
| 212 | // return Ok(Some((id, entity))); |
| 213 | // } |
| 214 | // } |
| 215 | // }, |
| 216 | // None => return Err(err!( |
| 217 | // "Namex entry '{}' does not have a (nams) field, which is required.", |
| 218 | // Namex::<M1, M2>::id_to_string(&id), |
| 219 | // ), Invalid, Input, Missing)), |
| 220 | // } |
| 221 | // } |
| 222 | // }, |
| 223 | // None => (), |
| 224 | // } |
| 225 | // Ok(None) |
| 226 | //} |
| 227 | } |
| 228 | |
| 229 | impl InNamex for () { |
| 230 | fn name_id(&self) -> Outcome<NamexId> { Ok(NamexId::default()) } |
| 231 | fn assoc_names_base64( |
| 232 | _gname: &'static str, |
| 233 | ) |
| 234 | -> Outcome<Option<Vec<( |
| 235 | &'static str, |
| 236 | &'static str, |
| 237 | )>>> |
| 238 | { |
| 239 | Ok(None) |
| 240 | } |
| 241 | fn assoc_names_index( |
| 242 | _gname: &'static str, |
| 243 | ) |
| 244 | -> Outcome<Option<NameIndex>> |
| 245 | { |
| 246 | Ok(None) |
| 247 | } |
| 248 | } |