sl_types/
key.rs

1//! Second Life key (UUID) related types
2
3use uuid::{uuid, Uuid};
4
5#[cfg(feature = "chumsky")]
6use chumsky::{
7    prelude::{just, one_of, Simple},
8    Parser,
9};
10
11/// parse a UUID
12///
13/// # Errors
14///
15/// returns an error if the string could not be parsed
16#[cfg(feature = "chumsky")]
17#[must_use]
18pub fn uuid_parser() -> impl Parser<char, uuid::Uuid, Error = Simple<char>> {
19    one_of("0123456789abcdef")
20        .repeated()
21        .exactly(8)
22        .collect::<String>()
23        .then_ignore(just('-'))
24        .then(
25            one_of("0123456789abcdef")
26                .repeated()
27                .exactly(4)
28                .collect::<String>(),
29        )
30        .then_ignore(just('-'))
31        .then(
32            one_of("0123456789abcdef")
33                .repeated()
34                .exactly(4)
35                .collect::<String>(),
36        )
37        .then_ignore(just('-'))
38        .then(
39            one_of("0123456789abcdef")
40                .repeated()
41                .exactly(4)
42                .collect::<String>(),
43        )
44        .then_ignore(just('-'))
45        .then(
46            one_of("0123456789abcdef")
47                .repeated()
48                .exactly(12)
49                .collect::<String>(),
50        )
51        .try_map(|((((a, b), c), d), e), span: std::ops::Range<usize>| {
52            uuid::Uuid::parse_str(&format!("{}-{}-{}-{}-{}", a, b, c, d, e))
53                .map_err(|e| Simple::custom(span.clone(), format!("{:?}", e)))
54        })
55}
56
57/// represents a general Second Life key without any knowledge about the type
58/// of entity this represents
59#[derive(Debug, Clone, PartialEq, Eq)]
60pub struct Key(pub Uuid);
61
62impl std::fmt::Display for Key {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        write!(f, "{}", self.0)
65    }
66}
67
68/// parse a Key
69///
70/// # Errors
71///
72/// returns an error if the string could not be parsed
73#[cfg(feature = "chumsky")]
74#[must_use]
75pub fn key_parser() -> impl Parser<char, Key, Error = Simple<char>> {
76    uuid_parser().map(Key)
77}
78
79/// the null key used by Second Life in many places to represent the absence
80/// of a key value
81pub const NULL_KEY: Key = Key(uuid!("00000000-0000-0000-0000-000000000000"));
82
83/// the key used by the Second Life system to send combat logs to the COMBAT_CHANNEL
84pub const COMBAT_LOG_ID: Key = Key(uuid!("45e0fcfa-2268-4490-a51c-3e51bdfe80d1"));
85
86/// represents a Second Life key for an agent (avatar)
87#[derive(Debug, Clone, PartialEq, Eq)]
88pub struct AgentKey(pub Key);
89
90impl std::fmt::Display for AgentKey {
91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92        write!(f, "{}", self.0)
93    }
94}
95
96impl From<AgentKey> for Key {
97    fn from(val: AgentKey) -> Self {
98        val.0
99    }
100}
101
102/// parse an AgentKey
103///
104/// # Errors
105///
106/// returns an error if the string could not be parsed
107#[cfg(feature = "chumsky")]
108#[must_use]
109pub fn agent_key_parser() -> impl Parser<char, AgentKey, Error = Simple<char>> {
110    key_parser().map(AgentKey)
111}
112
113/// parse a viewer URI that is either an /about or /inspect URL for an avatar
114/// and return the AgentKey
115///
116/// # Errors
117///
118/// returns an error if the string could not be parsed
119#[cfg(feature = "chumsky")]
120#[must_use]
121pub fn app_agent_uri_as_agent_key_parser() -> impl Parser<char, AgentKey, Error = Simple<char>> {
122    crate::viewer_uri::viewer_app_agent_uri_parser().try_map(|uri, span| match uri {
123        crate::viewer_uri::ViewerUri::AgentAbout(agent_key)
124        | crate::viewer_uri::ViewerUri::AgentInspect(agent_key) => Ok(agent_key),
125        _ => Err(Simple::custom(span, "Unexpected type of Agent viewer URI")),
126    })
127}
128
129/// represents a Second Life key for a classified ad
130#[derive(Debug, Clone, PartialEq, Eq)]
131pub struct ClassifiedKey(pub Key);
132
133impl std::fmt::Display for ClassifiedKey {
134    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135        write!(f, "{}", self.0)
136    }
137}
138
139impl From<ClassifiedKey> for Key {
140    fn from(val: ClassifiedKey) -> Self {
141        val.0
142    }
143}
144
145/// parse a ClassifiedKey
146///
147/// # Errors
148///
149/// returns an error if the string could not be parsed
150#[cfg(feature = "chumsky")]
151#[must_use]
152pub fn classified_key_parser() -> impl Parser<char, ClassifiedKey, Error = Simple<char>> {
153    key_parser().map(ClassifiedKey)
154}
155
156/// represents a Second Life key for an event
157#[derive(Debug, Clone, PartialEq, Eq)]
158pub struct EventKey(pub Key);
159
160impl std::fmt::Display for EventKey {
161    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162        write!(f, "{}", self.0)
163    }
164}
165
166impl From<EventKey> for Key {
167    fn from(val: EventKey) -> Self {
168        val.0
169    }
170}
171
172/// parse an EventKey
173///
174/// # Errors
175///
176/// returns an error if the string could not be parsed
177#[cfg(feature = "chumsky")]
178#[must_use]
179pub fn event_key_parser() -> impl Parser<char, EventKey, Error = Simple<char>> {
180    key_parser().map(EventKey)
181}
182
183/// represents a Second Life key for an experience
184#[derive(Debug, Clone, PartialEq, Eq)]
185pub struct ExperienceKey(pub Key);
186
187impl std::fmt::Display for ExperienceKey {
188    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189        write!(f, "{}", self.0)
190    }
191}
192
193impl From<ExperienceKey> for Key {
194    fn from(val: ExperienceKey) -> Self {
195        val.0
196    }
197}
198
199/// parse an ExperienceKey
200///
201/// # Errors
202///
203/// returns an error if the string could not be parsed
204#[cfg(feature = "chumsky")]
205#[must_use]
206pub fn experience_key_parser() -> impl Parser<char, ExperienceKey, Error = Simple<char>> {
207    key_parser().map(ExperienceKey)
208}
209
210/// represents a Second Life key for an agent who is a friend
211#[derive(Debug, Clone, PartialEq, Eq)]
212pub struct FriendKey(pub Key);
213
214impl std::fmt::Display for FriendKey {
215    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216        write!(f, "{}", self.0)
217    }
218}
219
220impl From<FriendKey> for Key {
221    fn from(val: FriendKey) -> Self {
222        val.0
223    }
224}
225
226impl From<FriendKey> for AgentKey {
227    fn from(val: FriendKey) -> Self {
228        AgentKey(val.0)
229    }
230}
231
232/// parse a FriendKey
233///
234/// # Errors
235///
236/// returns an error if the string could not be parsed
237#[cfg(feature = "chumsky")]
238#[must_use]
239pub fn friend_key_parser() -> impl Parser<char, FriendKey, Error = Simple<char>> {
240    key_parser().map(FriendKey)
241}
242
243/// represents a Second Life key for a group
244#[derive(Debug, Clone, PartialEq, Eq)]
245pub struct GroupKey(pub Key);
246
247impl std::fmt::Display for GroupKey {
248    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
249        write!(f, "{}", self.0)
250    }
251}
252
253impl From<GroupKey> for Key {
254    fn from(val: GroupKey) -> Self {
255        val.0
256    }
257}
258
259/// parse a GroupKey
260///
261/// # Errors
262///
263/// returns an error if the string could not be parsed
264#[cfg(feature = "chumsky")]
265#[must_use]
266pub fn group_key_parser() -> impl Parser<char, GroupKey, Error = Simple<char>> {
267    key_parser().map(GroupKey)
268}
269
270/// parse a viewer URI that is either an /about or /inspect URL for a group
271/// and return the GroupKey
272///
273/// # Errors
274///
275/// returns an error if the string could not be parsed
276#[cfg(feature = "chumsky")]
277#[must_use]
278pub fn app_group_uri_as_group_key_parser() -> impl Parser<char, GroupKey, Error = Simple<char>> {
279    crate::viewer_uri::viewer_app_group_uri_parser().try_map(|uri, span| match uri {
280        crate::viewer_uri::ViewerUri::GroupAbout(group_key)
281        | crate::viewer_uri::ViewerUri::GroupInspect(group_key) => Ok(group_key),
282        _ => Err(Simple::custom(span, "Unexpected type of group viewer URI")),
283    })
284}
285
286/// represents a Second Life key for an inventory item
287#[derive(Debug, Clone, PartialEq, Eq)]
288pub struct InventoryKey(pub Key);
289
290impl std::fmt::Display for InventoryKey {
291    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
292        write!(f, "{}", self.0)
293    }
294}
295
296impl From<InventoryKey> for Key {
297    fn from(val: InventoryKey) -> Self {
298        val.0
299    }
300}
301
302/// parse an InventoryKey
303///
304/// # Errors
305///
306/// returns an error if the string could not be parsed
307#[cfg(feature = "chumsky")]
308#[must_use]
309pub fn inventory_key_parser() -> impl Parser<char, InventoryKey, Error = Simple<char>> {
310    key_parser().map(InventoryKey)
311}
312
313/// represents a Second Life key for an object
314#[derive(Debug, Clone, PartialEq, Eq)]
315pub struct ObjectKey(pub Key);
316
317impl std::fmt::Display for ObjectKey {
318    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
319        write!(f, "{}", self.0)
320    }
321}
322
323impl From<ObjectKey> for Key {
324    fn from(val: ObjectKey) -> Self {
325        val.0
326    }
327}
328
329/// parse an ObjectKey
330///
331/// # Errors
332///
333/// returns an error if the string could not be parsed
334#[cfg(feature = "chumsky")]
335#[must_use]
336pub fn object_key_parser() -> impl Parser<char, ObjectKey, Error = Simple<char>> {
337    key_parser().map(ObjectKey)
338}
339
340/// represents a Second Life key for a parcel
341#[derive(Debug, Clone, PartialEq, Eq)]
342pub struct ParcelKey(pub Key);
343
344impl std::fmt::Display for ParcelKey {
345    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
346        write!(f, "{}", self.0)
347    }
348}
349
350impl From<ParcelKey> for Key {
351    fn from(val: ParcelKey) -> Self {
352        val.0
353    }
354}
355
356/// parse a ParcelKey
357///
358/// # Errors
359///
360/// returns an error if the string could not be parsed
361#[cfg(feature = "chumsky")]
362#[must_use]
363pub fn parcel_key_parser() -> impl Parser<char, ParcelKey, Error = Simple<char>> {
364    key_parser().map(ParcelKey)
365}
366
367/// represents a Second Life key for a texture
368#[derive(Debug, Clone, PartialEq, Eq)]
369pub struct TextureKey(pub Key);
370
371impl std::fmt::Display for TextureKey {
372    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
373        write!(f, "{}", self.0)
374    }
375}
376
377impl From<TextureKey> for Key {
378    fn from(val: TextureKey) -> Self {
379        val.0
380    }
381}
382
383/// parse a TextureKey
384///
385/// # Errors
386///
387/// returns an error if the string could not be parsed
388#[cfg(feature = "chumsky")]
389#[must_use]
390pub fn texture_key_parser() -> impl Parser<char, TextureKey, Error = Simple<char>> {
391    key_parser().map(TextureKey)
392}
393
394/// represents a Second Life key for an inventory folder
395#[derive(Debug, Clone, PartialEq, Eq)]
396pub struct InventoryFolderKey(pub Key);
397
398impl std::fmt::Display for InventoryFolderKey {
399    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
400        write!(f, "{}", self.0)
401    }
402}
403
404impl From<InventoryFolderKey> for Key {
405    fn from(val: InventoryFolderKey) -> Self {
406        val.0
407    }
408}
409
410/// parse an InventoryFolderKey
411///
412/// # Errors
413///
414/// returns an error if the string could not be parsed
415#[cfg(feature = "chumsky")]
416#[must_use]
417pub fn inventory_folder_key_parser() -> impl Parser<char, InventoryFolderKey, Error = Simple<char>>
418{
419    key_parser().map(InventoryFolderKey)
420}
421
422/// represents s Second Life key for an owner (e.g. of an object)
423#[derive(Debug, Clone, PartialEq, Eq, strum::EnumIs)]
424pub enum OwnerKey {
425    /// the owner is an agent
426    Agent(AgentKey),
427    /// the owner is a group
428    Group(GroupKey),
429}
430
431impl std::fmt::Display for OwnerKey {
432    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
433        match self {
434            OwnerKey::Agent(agent_key) => write!(f, "{}", agent_key),
435            OwnerKey::Group(group_key) => write!(f, "{}", group_key),
436        }
437    }
438}
439
440/// error when the owner is a group while trying to convert an OwnerKey to an AgentKey
441#[derive(Debug, Clone)]
442pub struct OwnerIsGroupError(GroupKey);
443
444impl std::fmt::Display for OwnerIsGroupError {
445    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
446        write!(f, "The owner is not an agent but the group {}", self.0)
447    }
448}
449
450impl TryInto<AgentKey> for OwnerKey {
451    type Error = OwnerIsGroupError;
452
453    fn try_into(self) -> Result<AgentKey, Self::Error> {
454        match self {
455            OwnerKey::Agent(agent_key) => Ok(agent_key),
456            OwnerKey::Group(group_key) => Err(OwnerIsGroupError(group_key)),
457        }
458    }
459}
460
461/// error when the owner is an agent while trying to convert an OwnerKey to a GroupKey
462#[derive(Debug, Clone)]
463pub struct OwnerIsAgentError(AgentKey);
464
465impl std::fmt::Display for OwnerIsAgentError {
466    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
467        write!(f, "The owner is not a group but the agent {}", self.0)
468    }
469}
470
471impl TryInto<GroupKey> for OwnerKey {
472    type Error = OwnerIsAgentError;
473
474    fn try_into(self) -> Result<GroupKey, Self::Error> {
475        match self {
476            OwnerKey::Agent(agent_key) => Err(OwnerIsAgentError(agent_key)),
477            OwnerKey::Group(group_key) => Ok(group_key),
478        }
479    }
480}
481
482impl From<OwnerKey> for Key {
483    fn from(val: OwnerKey) -> Self {
484        match val {
485            OwnerKey::Agent(agent_key) => agent_key.into(),
486            OwnerKey::Group(group_key) => group_key.into(),
487        }
488    }
489}
490
491/// parse a viewer URI that is either an /about or /inspect URL for an agent group
492/// or for a group and return the OwnerKey
493///
494/// # Errors
495///
496/// returns an error if the string could not be parsed
497#[cfg(feature = "chumsky")]
498#[must_use]
499pub fn app_agent_or_group_uri_as_owner_key_parser(
500) -> impl Parser<char, OwnerKey, Error = Simple<char>> {
501    app_agent_uri_as_agent_key_parser()
502        .map(OwnerKey::Agent)
503        .or(app_group_uri_as_group_key_parser().map(OwnerKey::Group))
504}