orchestra_toolkit/
entity.rs1use std::str::FromStr;
14
15use serde::{Deserialize, Serialize};
16use thiserror::Error;
17
18#[derive(Default, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
19#[serde(
20 try_from = "String",
21 into = "String",
22 expecting = "a string like <1000|6|100042>"
23)]
24pub struct Entity {
25 pub pid: u32,
26 pub hid: u32,
27 pub uid: u64,
28}
29
30impl std::fmt::Debug for Entity {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 write!(f, "{}", self)
33 }
34}
35
36impl std::fmt::Display for Entity {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 write!(f, "<{}|{}|{}>", self.pid, self.hid, self.uid)
39 }
40}
41
42#[derive(Error, Debug)]
43#[error("Cannot parse entity from string: {0}")]
44pub struct EntityParseError(String);
45
46impl Entity {
47 pub const NULL: Self = Self::new(0, 0, 0);
48
49 pub const fn new(pid: u32, hid: u32, uid: u64) -> Self {
50 Self { pid, hid, uid }
51 }
52
53 pub fn is_null(&self) -> bool {
54 *self == Self::NULL
55 }
56
57 pub fn from_str_lenient(s: &str) -> Result<Self, EntityParseError> {
59 if !s.starts_with('<') || !s.ends_with('>') {
60 return Err(EntityParseError(s.to_string()));
61 }
62 let ids: Vec<_> = s.split('|').collect();
63 match ids.as_slice() {
64 [uid] => {
65 let uid = u64::from_str(uid).map_err(|_| EntityParseError(s.to_string()))?;
66 Ok(Entity {
67 pid: 0,
68 hid: 0,
69 uid,
70 })
71 }
72 [pid, hid, uid] => {
73 let pid = u32::from_str(pid).map_err(|_| EntityParseError(s.to_string()))?;
74 let hid = u32::from_str(hid).map_err(|_| EntityParseError(s.to_string()))?;
75 let uid = u64::from_str(uid).map_err(|_| EntityParseError(s.to_string()))?;
76 Ok(Entity { pid, hid, uid })
77 }
78 _ => Err(EntityParseError(s.to_string())),
79 }
80 }
81
82 pub fn from_str_strict(s: &str) -> Result<Self, EntityParseError> {
84 if !s.starts_with('<') || !s.ends_with('>') {
85 return Err(EntityParseError(s.to_string()));
86 }
87 let ids: Vec<_> = s[1..s.len() - 1].split('|').collect();
88 match ids.as_slice() {
89 [pid, hid, uid] => {
90 let pid = u32::from_str(pid).map_err(|_| EntityParseError(s.to_string()))?;
91 let hid = u32::from_str(hid).map_err(|_| EntityParseError(s.to_string()))?;
92 let uid = u64::from_str(uid).map_err(|_| EntityParseError(s.to_string()))?;
93 Ok(Entity { pid, hid, uid })
94 }
95 _ => Err(EntityParseError(s.to_string())),
96 }
97 }
98}
99
100impl FromStr for Entity {
101 type Err = EntityParseError;
102
103 fn from_str(s: &str) -> Result<Self, Self::Err> {
104 Self::from_str_strict(s)
105 }
106}
107
108impl TryFrom<String> for Entity {
110 type Error = EntityParseError;
111
112 fn try_from(s: String) -> Result<Self, Self::Error> {
113 Self::from_str_strict(&s)
114 }
115}
116
117impl From<Entity> for String {
119 fn from(val: Entity) -> Self {
120 val.to_string()
121 }
122}
123
124#[cfg(test)]
125mod test {
126 use serde_json::json;
127
128 use super::*;
129
130 #[test]
131 fn parse_string() {
132 let e: Entity = "<1000|6|100384>".parse().unwrap();
133 assert_eq!(e, Entity::new(1000, 6, 100384));
134 }
135
136 #[test]
137 fn parse_json() {
138 let json = json!("<1002|27|13>");
139
140 let e: Entity = serde_json::from_value(json).unwrap();
141 assert_eq!(e, Entity::new(1002, 27, 13));
142 }
143}