1use alloc::{boxed::Box, vec::Vec};
6
7#[cfg(feature = "by_ref_proposal")]
8use crate::tree_kem::leaf_node::LeafNode;
9
10use crate::{
11 client::MlsError, tree_kem::node::LeafIndex, CipherSuite, KeyPackage, MlsMessage,
12 ProtocolVersion,
13};
14use core::fmt::{self, Debug};
15use mls_rs_codec::{MlsDecode, MlsEncode, MlsSize};
16use mls_rs_core::{group::Capabilities, identity::SigningIdentity};
17
18#[cfg(feature = "by_ref_proposal")]
19use crate::group::proposal_ref::ProposalRef;
20
21pub use mls_rs_core::extension::ExtensionList;
22pub use mls_rs_core::group::ProposalType;
23
24#[cfg(feature = "psk")]
25use crate::psk::{ExternalPskId, JustPreSharedKeyID, PreSharedKeyID};
26
27#[derive(Clone, Debug, PartialEq, MlsSize, MlsEncode, MlsDecode)]
28#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30pub struct AddProposal {
32 pub(crate) key_package: KeyPackage,
33}
34
35impl AddProposal {
36 pub fn key_package(&self) -> &KeyPackage {
39 &self.key_package
40 }
41
42 pub fn signing_identity(&self) -> &SigningIdentity {
45 self.key_package.signing_identity()
46 }
47
48 pub fn capabilities(&self) -> Capabilities {
51 self.key_package.leaf_node.ungreased_capabilities()
52 }
53
54 pub fn key_package_extensions(&self) -> ExtensionList {
57 self.key_package.ungreased_extensions()
58 }
59
60 pub fn leaf_node_extensions(&self) -> ExtensionList {
63 self.key_package.leaf_node.ungreased_extensions()
64 }
65}
66
67impl From<KeyPackage> for AddProposal {
68 fn from(key_package: KeyPackage) -> Self {
69 Self { key_package }
70 }
71}
72
73impl TryFrom<MlsMessage> for AddProposal {
74 type Error = MlsError;
75
76 fn try_from(value: MlsMessage) -> Result<Self, Self::Error> {
77 value
78 .into_key_package()
79 .ok_or(MlsError::UnexpectedMessageType)
80 .map(Into::into)
81 }
82}
83
84#[cfg(feature = "by_ref_proposal")]
85#[derive(Clone, Debug, PartialEq, MlsSize, MlsEncode, MlsDecode)]
86#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
87#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
88pub struct UpdateProposal {
91 pub(crate) leaf_node: LeafNode,
92}
93
94#[cfg(feature = "by_ref_proposal")]
95impl UpdateProposal {
96 pub fn signing_identity(&self) -> &SigningIdentity {
99 &self.leaf_node.signing_identity
100 }
101
102 pub fn capabilities(&self) -> Capabilities {
105 self.leaf_node.ungreased_capabilities()
106 }
107
108 pub fn leaf_node_extensions(&self) -> ExtensionList {
111 self.leaf_node.ungreased_extensions()
112 }
113}
114
115#[derive(Clone, Debug, PartialEq, Eq, MlsSize, MlsEncode, MlsDecode)]
116#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
117#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
118pub struct RemoveProposal {
121 pub(crate) to_remove: LeafIndex,
122}
123
124impl RemoveProposal {
125 pub fn to_remove(&self) -> u32 {
128 *self.to_remove
129 }
130}
131
132impl RemoveProposal {
133 pub fn removing(member_index: u32) -> Result<Self, MlsError> {
134 Ok(Self {
135 to_remove: LeafIndex::try_from(member_index)?,
136 })
137 }
138}
139
140impl TryFrom<u32> for RemoveProposal {
141 type Error = MlsError;
142
143 fn try_from(value: u32) -> Result<Self, Self::Error> {
144 Self::removing(value)
145 }
146}
147
148#[cfg(feature = "psk")]
149#[derive(Clone, Debug, PartialEq, Eq, MlsSize, MlsEncode, MlsDecode)]
150#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
151#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
152pub struct PreSharedKeyProposal {
154 pub(crate) psk: PreSharedKeyID,
155}
156
157#[cfg(feature = "psk")]
158impl PreSharedKeyProposal {
159 pub fn external_psk_id(&self) -> Option<&ExternalPskId> {
166 match self.psk.key_id {
167 JustPreSharedKeyID::External(ref ext) => Some(ext),
168 JustPreSharedKeyID::Resumption(_) => None,
169 }
170 }
171}
172
173#[derive(Clone, PartialEq, MlsSize, MlsEncode, MlsDecode)]
174#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
175#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
176pub struct ReInitProposal {
178 #[mls_codec(with = "mls_rs_codec::byte_vec")]
179 #[cfg_attr(feature = "serde", serde(with = "mls_rs_core::vec_serde"))]
180 pub(crate) group_id: Vec<u8>,
181 pub(crate) version: ProtocolVersion,
182 pub(crate) cipher_suite: CipherSuite,
183 pub(crate) extensions: ExtensionList,
184}
185
186impl Debug for ReInitProposal {
187 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 f.debug_struct("ReInitProposal")
189 .field(
190 "group_id",
191 &mls_rs_core::debug::pretty_group_id(&self.group_id),
192 )
193 .field("version", &self.version)
194 .field("cipher_suite", &self.cipher_suite)
195 .field("extensions", &self.extensions)
196 .finish()
197 }
198}
199
200impl ReInitProposal {
201 pub fn group_id(&self) -> &[u8] {
203 &self.group_id
204 }
205
206 pub fn new_version(&self) -> ProtocolVersion {
208 self.version
209 }
210
211 pub fn new_cipher_suite(&self) -> CipherSuite {
213 self.cipher_suite
214 }
215
216 pub fn new_group_context_extensions(&self) -> &ExtensionList {
218 &self.extensions
219 }
220}
221
222#[derive(Clone, PartialEq, Eq, MlsSize, MlsEncode, MlsDecode)]
223#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
224#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
225pub struct ExternalInit {
227 #[mls_codec(with = "mls_rs_codec::byte_vec")]
228 #[cfg_attr(feature = "serde", serde(with = "mls_rs_core::vec_serde"))]
229 pub(crate) kem_output: Vec<u8>,
230}
231
232impl Debug for ExternalInit {
233 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234 f.debug_struct("ExternalInit").finish()
235 }
236}
237
238#[derive(Clone, PartialEq, Eq, MlsSize, MlsEncode, MlsDecode, Debug)]
239#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
240#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
241#[cfg(all(
242 feature = "by_ref_proposal",
243 feature = "custom_proposal",
244 feature = "self_remove_proposal"
245))]
246pub struct SelfRemoveProposal {}
249
250#[cfg(feature = "custom_proposal")]
251#[derive(Clone, PartialEq, Eq)]
252#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
253#[cfg_attr(
254 all(feature = "ffi", not(test)),
255 safer_ffi_gen::ffi_type(clone, opaque)
256)]
257#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
258pub struct CustomProposal {
262 proposal_type: ProposalType,
263 #[cfg_attr(feature = "serde", serde(with = "mls_rs_core::vec_serde"))]
264 data: Vec<u8>,
265}
266
267#[cfg(feature = "custom_proposal")]
268impl Debug for CustomProposal {
269 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270 f.debug_struct("CustomProposal")
271 .field("proposal_type", &self.proposal_type)
272 .field("data", &mls_rs_core::debug::pretty_bytes(&self.data))
273 .finish()
274 }
275}
276
277#[cfg(feature = "custom_proposal")]
278#[cfg_attr(all(feature = "ffi", not(test)), safer_ffi_gen::safer_ffi_gen)]
279impl CustomProposal {
280 pub fn new(proposal_type: ProposalType, data: Vec<u8>) -> Self {
288 Self {
289 proposal_type,
290 data,
291 }
292 }
293
294 pub fn proposal_type(&self) -> ProposalType {
296 self.proposal_type
297 }
298
299 pub fn data(&self) -> &[u8] {
301 &self.data
302 }
303}
304
305#[cfg(feature = "custom_proposal")]
306pub trait CustomDecoder: Sized {
311 fn encode_from_bytes(
312 data: &Vec<u8>,
313 writer: &mut Vec<u8>,
314 _proposal_type: &ProposalType,
315 ) -> Result<(), mls_rs_codec::Error> {
316 mls_rs_codec::byte_vec::mls_encode(data, writer)
317 }
318 fn decode_from_bytes(
319 reader: &mut &[u8],
320 _proposal_type: &ProposalType,
321 ) -> Result<Vec<u8>, mls_rs_codec::Error> {
322 mls_rs_codec::byte_vec::mls_decode(reader)
323 }
324 fn encoded_byte_len(data: &Vec<u8>, _proposal_type: &ProposalType) -> usize {
325 mls_rs_codec::byte_vec::mls_encoded_len(data)
326 }
327}
328
329#[cfg(all(feature = "custom_proposal", not(feature = "gsma_rcs_e2ee_feature")))]
330impl CustomDecoder for CustomProposal {}
331
332#[cfg(all(feature = "custom_proposal", feature = "gsma_rcs_e2ee_feature"))]
333impl CustomDecoder for CustomProposal {
334 fn encode_from_bytes(
335 data: &Vec<u8>,
336 writer: &mut Vec<u8>,
337 proposal_type: &ProposalType,
338 ) -> Result<(), mls_rs_codec::Error> {
339 match proposal_type {
340 &ProposalType::RCS_SIGNATURE | &ProposalType::RCS_SERVER_REMOVE => {
343 writer.extend(data);
344 Ok(())
345 }
346 _ => mls_rs_codec::byte_vec::mls_encode(data, writer),
347 }
348 }
349 fn decode_from_bytes(
350 reader: &mut &[u8],
351 proposal_type: &ProposalType,
352 ) -> Result<Vec<u8>, mls_rs_codec::Error> {
353 match *proposal_type {
354 ProposalType::RCS_SIGNATURE => Ok(Vec::new()),
356 ProposalType::RCS_SERVER_REMOVE => {
358 let decoded = RemoveProposal::mls_decode(reader)?;
359 let mut writer = Vec::new();
360 RemoveProposal::mls_encode(&decoded, &mut writer)?;
361 Ok(writer)
363 }
364 _ => mls_rs_codec::byte_vec::mls_decode(reader),
365 }
366 }
367 fn encoded_byte_len(data: &Vec<u8>, proposal_type: &ProposalType) -> usize {
368 match proposal_type {
369 &ProposalType::RCS_SIGNATURE | &ProposalType::RCS_SERVER_REMOVE => data.len(),
370 _ => mls_rs_codec::byte_vec::mls_encoded_len(data),
371 }
372 }
373}
374
375#[cfg(feature = "custom_proposal")]
378pub trait MlsCustomProposal: MlsSize + MlsEncode + MlsDecode + Sized {
379 fn proposal_type() -> ProposalType;
380
381 fn to_custom_proposal(&self) -> Result<CustomProposal, mls_rs_codec::Error> {
382 Ok(CustomProposal::new(
383 Self::proposal_type(),
384 self.mls_encode_to_vec()?,
385 ))
386 }
387
388 fn from_custom_proposal(proposal: &CustomProposal) -> Result<Self, mls_rs_codec::Error> {
389 if proposal.proposal_type() != Self::proposal_type() {
390 return Err(mls_rs_codec::Error::Custom(4));
397 }
398
399 Self::mls_decode(&mut proposal.data())
400 }
401}
402
403#[allow(clippy::large_enum_variant)]
404#[derive(Clone, Debug, PartialEq)]
405#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
406#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
407#[repr(u16)]
408#[non_exhaustive]
409pub enum Proposal {
411 Add(alloc::boxed::Box<AddProposal>),
412 #[cfg(feature = "by_ref_proposal")]
413 Update(UpdateProposal),
414 Remove(RemoveProposal),
415 #[cfg(feature = "psk")]
416 Psk(PreSharedKeyProposal),
417 ReInit(ReInitProposal),
418 ExternalInit(ExternalInit),
419 GroupContextExtensions(ExtensionList),
420 #[cfg(all(
421 feature = "by_ref_proposal",
422 feature = "custom_proposal",
423 feature = "self_remove_proposal"
424 ))]
425 SelfRemove(SelfRemoveProposal),
426 #[cfg(feature = "custom_proposal")]
427 Custom(CustomProposal),
428}
429
430impl MlsSize for Proposal {
431 fn mls_encoded_len(&self) -> usize {
432 let inner_len = match self {
433 Proposal::Add(p) => p.mls_encoded_len(),
434 #[cfg(feature = "by_ref_proposal")]
435 Proposal::Update(p) => p.mls_encoded_len(),
436 Proposal::Remove(p) => p.mls_encoded_len(),
437 #[cfg(feature = "psk")]
438 Proposal::Psk(p) => p.mls_encoded_len(),
439 Proposal::ReInit(p) => p.mls_encoded_len(),
440 Proposal::ExternalInit(p) => p.mls_encoded_len(),
441 Proposal::GroupContextExtensions(p) => p.mls_encoded_len(),
442 #[cfg(all(
443 feature = "by_ref_proposal",
444 feature = "custom_proposal",
445 feature = "self_remove_proposal"
446 ))]
447 Proposal::SelfRemove(p) => p.mls_encoded_len(),
448 #[cfg(feature = "custom_proposal")]
449 Proposal::Custom(p) => CustomProposal::encoded_byte_len(&p.data, &p.proposal_type),
450 };
451
452 self.proposal_type().mls_encoded_len() + inner_len
453 }
454}
455
456impl MlsEncode for Proposal {
457 fn mls_encode(&self, writer: &mut Vec<u8>) -> Result<(), mls_rs_codec::Error> {
458 self.proposal_type().mls_encode(writer)?;
459
460 match self {
461 Proposal::Add(p) => p.mls_encode(writer),
462 #[cfg(feature = "by_ref_proposal")]
463 Proposal::Update(p) => p.mls_encode(writer),
464 Proposal::Remove(p) => p.mls_encode(writer),
465 #[cfg(feature = "psk")]
466 Proposal::Psk(p) => p.mls_encode(writer),
467 Proposal::ReInit(p) => p.mls_encode(writer),
468 Proposal::ExternalInit(p) => p.mls_encode(writer),
469 Proposal::GroupContextExtensions(p) => p.mls_encode(writer),
470 #[cfg(all(
471 feature = "by_ref_proposal",
472 feature = "custom_proposal",
473 feature = "self_remove_proposal"
474 ))]
475 Proposal::SelfRemove(p) => p.mls_encode(writer),
476 #[cfg(feature = "custom_proposal")]
477 Proposal::Custom(p) => {
478 if p.proposal_type.raw_value() <= 7 {
479 return Err(mls_rs_codec::Error::Custom(2));
486 }
487 CustomProposal::encode_from_bytes(&p.data, writer, &p.proposal_type)
488 }
489 }
490 }
491}
492
493impl MlsDecode for Proposal {
494 fn mls_decode(reader: &mut &[u8]) -> Result<Self, mls_rs_codec::Error> {
495 let proposal_type = ProposalType::mls_decode(reader)?;
496
497 Ok(match proposal_type {
498 ProposalType::ADD => {
499 Proposal::Add(alloc::boxed::Box::new(AddProposal::mls_decode(reader)?))
500 }
501 #[cfg(feature = "by_ref_proposal")]
502 ProposalType::UPDATE => Proposal::Update(UpdateProposal::mls_decode(reader)?),
503 ProposalType::REMOVE => Proposal::Remove(RemoveProposal::mls_decode(reader)?),
504 #[cfg(feature = "psk")]
505 ProposalType::PSK => Proposal::Psk(PreSharedKeyProposal::mls_decode(reader)?),
506 ProposalType::RE_INIT => Proposal::ReInit(ReInitProposal::mls_decode(reader)?),
507 ProposalType::EXTERNAL_INIT => {
508 Proposal::ExternalInit(ExternalInit::mls_decode(reader)?)
509 }
510 ProposalType::GROUP_CONTEXT_EXTENSIONS => {
511 Proposal::GroupContextExtensions(ExtensionList::mls_decode(reader)?)
512 }
513 #[cfg(all(
514 feature = "by_ref_proposal",
515 feature = "custom_proposal",
516 feature = "self_remove_proposal"
517 ))]
518 ProposalType::SELF_REMOVE => {
519 Proposal::SelfRemove(SelfRemoveProposal::mls_decode(reader)?)
520 }
521 #[cfg(feature = "custom_proposal")]
522 custom => Proposal::Custom(CustomProposal {
523 proposal_type: custom,
524 data: CustomProposal::decode_from_bytes(reader, &custom)?,
525 }),
526 #[cfg(not(feature = "custom_proposal"))]
528 _ => return Err(mls_rs_codec::Error::Custom(3)),
529 })
530 }
531}
532
533impl Proposal {
534 pub fn proposal_type(&self) -> ProposalType {
535 match self {
536 Proposal::Add(_) => ProposalType::ADD,
537 #[cfg(feature = "by_ref_proposal")]
538 Proposal::Update(_) => ProposalType::UPDATE,
539 Proposal::Remove(_) => ProposalType::REMOVE,
540 #[cfg(feature = "psk")]
541 Proposal::Psk(_) => ProposalType::PSK,
542 Proposal::ReInit(_) => ProposalType::RE_INIT,
543 Proposal::ExternalInit(_) => ProposalType::EXTERNAL_INIT,
544 Proposal::GroupContextExtensions(_) => ProposalType::GROUP_CONTEXT_EXTENSIONS,
545 #[cfg(all(
546 feature = "by_ref_proposal",
547 feature = "custom_proposal",
548 feature = "self_remove_proposal"
549 ))]
550 Proposal::SelfRemove(_) => ProposalType::SELF_REMOVE,
551 #[cfg(feature = "custom_proposal")]
552 Proposal::Custom(c) => c.proposal_type,
553 }
554 }
555}
556
557#[derive(Clone, Debug, PartialEq)]
558pub enum BorrowedProposal<'a> {
560 Add(&'a AddProposal),
561 #[cfg(feature = "by_ref_proposal")]
562 Update(&'a UpdateProposal),
563 Remove(&'a RemoveProposal),
564 #[cfg(feature = "psk")]
565 Psk(&'a PreSharedKeyProposal),
566 ReInit(&'a ReInitProposal),
567 ExternalInit(&'a ExternalInit),
568 GroupContextExtensions(&'a ExtensionList),
569 #[cfg(all(
570 feature = "by_ref_proposal",
571 feature = "custom_proposal",
572 feature = "self_remove_proposal"
573 ))]
574 SelfRemove(&'a SelfRemoveProposal),
575 #[cfg(feature = "custom_proposal")]
576 Custom(&'a CustomProposal),
577}
578
579impl<'a> From<BorrowedProposal<'a>> for Proposal {
580 fn from(value: BorrowedProposal<'a>) -> Self {
581 match value {
582 BorrowedProposal::Add(add) => Proposal::Add(alloc::boxed::Box::new(add.clone())),
583 #[cfg(feature = "by_ref_proposal")]
584 BorrowedProposal::Update(update) => Proposal::Update(update.clone()),
585 BorrowedProposal::Remove(remove) => Proposal::Remove(remove.clone()),
586 #[cfg(feature = "psk")]
587 BorrowedProposal::Psk(psk) => Proposal::Psk(psk.clone()),
588 BorrowedProposal::ReInit(reinit) => Proposal::ReInit(reinit.clone()),
589 BorrowedProposal::ExternalInit(external) => Proposal::ExternalInit(external.clone()),
590 BorrowedProposal::GroupContextExtensions(ext) => {
591 Proposal::GroupContextExtensions(ext.clone())
592 }
593 #[cfg(all(
594 feature = "by_ref_proposal",
595 feature = "custom_proposal",
596 feature = "self_remove_proposal"
597 ))]
598 BorrowedProposal::SelfRemove(self_remove) => Proposal::SelfRemove(self_remove.clone()),
599 #[cfg(feature = "custom_proposal")]
600 BorrowedProposal::Custom(custom) => Proposal::Custom(custom.clone()),
601 }
602 }
603}
604
605impl BorrowedProposal<'_> {
606 pub fn proposal_type(&self) -> ProposalType {
607 match self {
608 BorrowedProposal::Add(_) => ProposalType::ADD,
609 #[cfg(feature = "by_ref_proposal")]
610 BorrowedProposal::Update(_) => ProposalType::UPDATE,
611 BorrowedProposal::Remove(_) => ProposalType::REMOVE,
612 #[cfg(feature = "psk")]
613 BorrowedProposal::Psk(_) => ProposalType::PSK,
614 BorrowedProposal::ReInit(_) => ProposalType::RE_INIT,
615 BorrowedProposal::ExternalInit(_) => ProposalType::EXTERNAL_INIT,
616 BorrowedProposal::GroupContextExtensions(_) => ProposalType::GROUP_CONTEXT_EXTENSIONS,
617 #[cfg(all(
618 feature = "by_ref_proposal",
619 feature = "custom_proposal",
620 feature = "self_remove_proposal"
621 ))]
622 BorrowedProposal::SelfRemove(_) => ProposalType::SELF_REMOVE,
623 #[cfg(feature = "custom_proposal")]
624 BorrowedProposal::Custom(c) => c.proposal_type,
625 }
626 }
627}
628
629impl<'a> From<&'a Proposal> for BorrowedProposal<'a> {
630 fn from(p: &'a Proposal) -> Self {
631 match p {
632 Proposal::Add(p) => BorrowedProposal::Add(p),
633 #[cfg(feature = "by_ref_proposal")]
634 Proposal::Update(p) => BorrowedProposal::Update(p),
635 Proposal::Remove(p) => BorrowedProposal::Remove(p),
636 #[cfg(feature = "psk")]
637 Proposal::Psk(p) => BorrowedProposal::Psk(p),
638 Proposal::ReInit(p) => BorrowedProposal::ReInit(p),
639 Proposal::ExternalInit(p) => BorrowedProposal::ExternalInit(p),
640 Proposal::GroupContextExtensions(p) => BorrowedProposal::GroupContextExtensions(p),
641 #[cfg(all(
642 feature = "by_ref_proposal",
643 feature = "custom_proposal",
644 feature = "self_remove_proposal"
645 ))]
646 Proposal::SelfRemove(p) => BorrowedProposal::SelfRemove(p),
647 #[cfg(feature = "custom_proposal")]
648 Proposal::Custom(p) => BorrowedProposal::Custom(p),
649 }
650 }
651}
652
653impl<'a> From<&'a AddProposal> for BorrowedProposal<'a> {
654 fn from(p: &'a AddProposal) -> Self {
655 Self::Add(p)
656 }
657}
658
659#[cfg(feature = "by_ref_proposal")]
660impl<'a> From<&'a UpdateProposal> for BorrowedProposal<'a> {
661 fn from(p: &'a UpdateProposal) -> Self {
662 Self::Update(p)
663 }
664}
665
666impl<'a> From<&'a RemoveProposal> for BorrowedProposal<'a> {
667 fn from(p: &'a RemoveProposal) -> Self {
668 Self::Remove(p)
669 }
670}
671
672#[cfg(feature = "psk")]
673impl<'a> From<&'a PreSharedKeyProposal> for BorrowedProposal<'a> {
674 fn from(p: &'a PreSharedKeyProposal) -> Self {
675 Self::Psk(p)
676 }
677}
678
679impl<'a> From<&'a ReInitProposal> for BorrowedProposal<'a> {
680 fn from(p: &'a ReInitProposal) -> Self {
681 Self::ReInit(p)
682 }
683}
684
685impl<'a> From<&'a ExternalInit> for BorrowedProposal<'a> {
686 fn from(p: &'a ExternalInit) -> Self {
687 Self::ExternalInit(p)
688 }
689}
690
691impl<'a> From<&'a ExtensionList> for BorrowedProposal<'a> {
692 fn from(p: &'a ExtensionList) -> Self {
693 Self::GroupContextExtensions(p)
694 }
695}
696
697#[cfg(all(
698 feature = "by_ref_proposal",
699 feature = "custom_proposal",
700 feature = "self_remove_proposal"
701))]
702impl<'a> From<&'a SelfRemoveProposal> for BorrowedProposal<'a> {
703 fn from(p: &'a SelfRemoveProposal) -> Self {
704 Self::SelfRemove(p)
705 }
706}
707
708#[cfg(feature = "custom_proposal")]
709impl<'a> From<&'a CustomProposal> for BorrowedProposal<'a> {
710 fn from(p: &'a CustomProposal) -> Self {
711 Self::Custom(p)
712 }
713}
714
715#[derive(Clone, Debug, PartialEq, MlsSize, MlsEncode, MlsDecode)]
716#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
717#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
718#[repr(u8)]
719pub(crate) enum ProposalOrRef {
720 Proposal(Box<Proposal>) = 1u8,
721 #[cfg(feature = "by_ref_proposal")]
722 Reference(ProposalRef) = 2u8,
723}
724
725impl From<Proposal> for ProposalOrRef {
726 fn from(proposal: Proposal) -> Self {
727 Self::Proposal(Box::new(proposal))
728 }
729}
730
731#[cfg(feature = "by_ref_proposal")]
732impl From<ProposalRef> for ProposalOrRef {
733 fn from(r: ProposalRef) -> Self {
734 Self::Reference(r)
735 }
736}