Skip to main content

core_crypto/mls/conversation/
id.rs

1use std::{
2    borrow::{Borrow, Cow},
3    ops::Deref,
4};
5
6/// A unique identifier for a group/conversation. The identifier must be unique within a client.
7#[derive(
8    core_crypto_macros::Debug,
9    derive_more::AsRef,
10    derive_more::From,
11    derive_more::Into,
12    PartialEq,
13    Eq,
14    PartialOrd,
15    Ord,
16    Hash,
17    Clone,
18)]
19#[sensitive]
20#[as_ref([u8])]
21#[from(&[u8], Vec<u8>)]
22pub struct ConversationId(Vec<u8>);
23
24impl Borrow<ConversationIdRef> for ConversationId {
25    fn borrow(&self) -> &ConversationIdRef {
26        ConversationIdRef::new(&self.0)
27    }
28}
29
30impl Deref for ConversationId {
31    type Target = ConversationIdRef;
32
33    fn deref(&self) -> &Self::Target {
34        ConversationIdRef::new(&self.0)
35    }
36}
37
38impl From<ConversationId> for Cow<'_, [u8]> {
39    fn from(value: ConversationId) -> Self {
40        Cow::Owned(value.0)
41    }
42}
43
44impl<'a> From<&'a ConversationId> for Cow<'a, [u8]> {
45    fn from(value: &'a ConversationId) -> Self {
46        Cow::Borrowed(value.as_ref())
47    }
48}
49
50/// Reference to a ConversationId.
51///
52/// This type is `!Sized` and is only ever seen as a reference, like `str` or `[u8]`.
53//
54// pattern from https://stackoverflow.com/a/64990850
55#[repr(transparent)]
56#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
57pub struct ConversationIdRef([u8]);
58
59impl ConversationIdRef {
60    /// Creates a `ConversationId` Ref, needed to implement `Borrow<ConversationIdRef>` for `T`
61    pub fn new<Bytes>(bytes: &Bytes) -> &ConversationIdRef
62    where
63        Bytes: AsRef<[u8]> + ?Sized,
64    {
65        // safety: because of `repr(transparent)` we know that `ConversationIdRef` has a memory layout
66        // identical to `[u8]`, so we can perform this cast
67        unsafe { &*(bytes.as_ref() as *const [u8] as *const ConversationIdRef) }
68    }
69
70    pub(crate) fn keystore(&self) -> &core_crypto_keystore::entities::ConversationIdRef {
71        self.into()
72    }
73}
74
75impl ConversationIdRef {
76    pub(crate) fn to_bytes(&self) -> Vec<u8> {
77        self.0.to_owned()
78    }
79}
80
81impl ToOwned for ConversationIdRef {
82    type Owned = ConversationId;
83
84    fn to_owned(&self) -> Self::Owned {
85        ConversationId(self.0.to_owned())
86    }
87}
88
89impl AsRef<[u8]> for ConversationIdRef {
90    fn as_ref(&self) -> &[u8] {
91        &self.0
92    }
93}
94
95impl<'a> From<&'a ConversationIdRef> for Cow<'a, [u8]> {
96    fn from(value: &'a ConversationIdRef) -> Self {
97        Cow::Borrowed(value.as_ref())
98    }
99}
100
101impl<'a> From<&'a ConversationIdRef> for &'a core_crypto_keystore::entities::ConversationIdRef {
102    fn from(value: &'a ConversationIdRef) -> Self {
103        core_crypto_keystore::entities::ConversationIdRef::new(value.as_ref())
104    }
105}
106
107impl From<&ConversationIdRef> for core_crypto_keystore::entities::ConversationId {
108    fn from(value: &ConversationIdRef) -> Self {
109        core_crypto_keystore::entities::ConversationId::from(value.as_ref())
110    }
111}
112
113// Cross-type equality so that `&ConversationIdRef` can be used to look up an entry
114// in a hash-keyed collection (e.g. `schnellru::LruMap<ConversationId, _>`) without
115// allocating a `ConversationId`. The `Hash` derives on both types compare equal
116// bytes to equal hashes, keeping these impls consistent with the `Hash` impls.
117impl PartialEq<ConversationId> for ConversationIdRef {
118    fn eq(&self, other: &ConversationId) -> bool {
119        self.as_ref() == other.as_ref()
120    }
121}
122
123impl PartialEq<ConversationIdRef> for ConversationId {
124    fn eq(&self, other: &ConversationIdRef) -> bool {
125        self.as_ref() == other.as_ref()
126    }
127}