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