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")
235 .field(
236 "kem_output",
237 &mls_rs_core::debug::pretty_bytes(&self.kem_output),
238 )
239 .finish()
240 }
241}
242
243#[derive(Clone, PartialEq, Eq, MlsSize, MlsEncode, MlsDecode, Debug)]
244#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
245#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
246#[cfg(all(
247 feature = "by_ref_proposal",
248 feature = "custom_proposal",
249 feature = "self_remove_proposal"
250))]
251pub struct SelfRemoveProposal {}
254
255#[cfg(feature = "custom_proposal")]
256#[derive(Clone, PartialEq, Eq)]
257#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
258#[cfg_attr(
259 all(feature = "ffi", not(test)),
260 safer_ffi_gen::ffi_type(clone, opaque)
261)]
262#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
263pub struct CustomProposal {
267 proposal_type: ProposalType,
268 #[cfg_attr(feature = "serde", serde(with = "mls_rs_core::vec_serde"))]
269 data: Vec<u8>,
270}
271
272#[cfg(feature = "custom_proposal")]
273impl Debug for CustomProposal {
274 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275 f.debug_struct("CustomProposal")
276 .field("proposal_type", &self.proposal_type)
277 .field("data", &mls_rs_core::debug::pretty_bytes(&self.data))
278 .finish()
279 }
280}
281
282#[cfg(feature = "custom_proposal")]
283#[cfg_attr(all(feature = "ffi", not(test)), safer_ffi_gen::safer_ffi_gen)]
284impl CustomProposal {
285 pub fn new(proposal_type: ProposalType, data: Vec<u8>) -> Self {
293 Self {
294 proposal_type,
295 data,
296 }
297 }
298
299 pub fn proposal_type(&self) -> ProposalType {
301 self.proposal_type
302 }
303
304 pub fn data(&self) -> &[u8] {
306 &self.data
307 }
308}
309
310#[cfg(feature = "custom_proposal")]
311pub trait CustomDecoder: Sized {
316 fn encode_from_bytes(
317 data: &Vec<u8>,
318 writer: &mut Vec<u8>,
319 _proposal_type: &ProposalType,
320 ) -> Result<(), mls_rs_codec::Error> {
321 mls_rs_codec::byte_vec::mls_encode(data, writer)
322 }
323 fn decode_from_bytes(
324 reader: &mut &[u8],
325 _proposal_type: &ProposalType,
326 ) -> Result<Vec<u8>, mls_rs_codec::Error> {
327 mls_rs_codec::byte_vec::mls_decode(reader)
328 }
329 fn encoded_byte_len(data: &Vec<u8>, _proposal_type: &ProposalType) -> usize {
330 mls_rs_codec::byte_vec::mls_encoded_len(data)
331 }
332}
333
334#[cfg(all(feature = "custom_proposal", not(feature = "gsma_rcs_e2ee_feature")))]
335impl CustomDecoder for CustomProposal {}
336
337#[cfg(all(feature = "custom_proposal", feature = "gsma_rcs_e2ee_feature"))]
338impl CustomDecoder for CustomProposal {
339 fn encode_from_bytes(
340 data: &Vec<u8>,
341 writer: &mut Vec<u8>,
342 proposal_type: &ProposalType,
343 ) -> Result<(), mls_rs_codec::Error> {
344 match proposal_type {
345 &ProposalType::RCS_SIGNATURE | &ProposalType::RCS_SERVER_REMOVE => {
348 writer.extend(data);
349 Ok(())
350 }
351 _ => mls_rs_codec::byte_vec::mls_encode(data, writer),
352 }
353 }
354 fn decode_from_bytes(
355 reader: &mut &[u8],
356 proposal_type: &ProposalType,
357 ) -> Result<Vec<u8>, mls_rs_codec::Error> {
358 match *proposal_type {
359 ProposalType::RCS_SIGNATURE => Ok(Vec::new()),
361 ProposalType::RCS_SERVER_REMOVE => {
363 let decoded = RemoveProposal::mls_decode(reader)?;
364 let mut writer = Vec::new();
365 RemoveProposal::mls_encode(&decoded, &mut writer)?;
366 Ok(writer)
368 }
369 _ => mls_rs_codec::byte_vec::mls_decode(reader),
370 }
371 }
372 fn encoded_byte_len(data: &Vec<u8>, proposal_type: &ProposalType) -> usize {
373 match proposal_type {
374 &ProposalType::RCS_SIGNATURE | &ProposalType::RCS_SERVER_REMOVE => data.len(),
375 _ => mls_rs_codec::byte_vec::mls_encoded_len(data),
376 }
377 }
378}
379
380#[cfg(feature = "custom_proposal")]
383pub trait MlsCustomProposal: MlsSize + MlsEncode + MlsDecode + Sized {
384 fn proposal_type() -> ProposalType;
385
386 fn to_custom_proposal(&self) -> Result<CustomProposal, mls_rs_codec::Error> {
387 Ok(CustomProposal::new(
388 Self::proposal_type(),
389 self.mls_encode_to_vec()?,
390 ))
391 }
392
393 fn from_custom_proposal(proposal: &CustomProposal) -> Result<Self, mls_rs_codec::Error> {
394 if proposal.proposal_type() != Self::proposal_type() {
395 return Err(mls_rs_codec::Error::Custom(4));
402 }
403
404 Self::mls_decode(&mut proposal.data())
405 }
406}
407
408#[allow(clippy::large_enum_variant)]
409#[derive(Clone, Debug, PartialEq)]
410#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
411#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
412#[repr(u16)]
413#[non_exhaustive]
414pub enum Proposal {
416 Add(alloc::boxed::Box<AddProposal>),
417 #[cfg(feature = "by_ref_proposal")]
418 Update(UpdateProposal),
419 Remove(RemoveProposal),
420 #[cfg(feature = "psk")]
421 Psk(PreSharedKeyProposal),
422 ReInit(ReInitProposal),
423 ExternalInit(ExternalInit),
424 GroupContextExtensions(ExtensionList),
425 #[cfg(all(
426 feature = "by_ref_proposal",
427 feature = "custom_proposal",
428 feature = "self_remove_proposal"
429 ))]
430 SelfRemove(SelfRemoveProposal),
431 #[cfg(feature = "custom_proposal")]
432 Custom(CustomProposal),
433}
434
435impl MlsSize for Proposal {
436 fn mls_encoded_len(&self) -> usize {
437 let inner_len = match self {
438 Proposal::Add(p) => p.mls_encoded_len(),
439 #[cfg(feature = "by_ref_proposal")]
440 Proposal::Update(p) => p.mls_encoded_len(),
441 Proposal::Remove(p) => p.mls_encoded_len(),
442 #[cfg(feature = "psk")]
443 Proposal::Psk(p) => p.mls_encoded_len(),
444 Proposal::ReInit(p) => p.mls_encoded_len(),
445 Proposal::ExternalInit(p) => p.mls_encoded_len(),
446 Proposal::GroupContextExtensions(p) => p.mls_encoded_len(),
447 #[cfg(all(
448 feature = "by_ref_proposal",
449 feature = "custom_proposal",
450 feature = "self_remove_proposal"
451 ))]
452 Proposal::SelfRemove(p) => p.mls_encoded_len(),
453 #[cfg(feature = "custom_proposal")]
454 Proposal::Custom(p) => CustomProposal::encoded_byte_len(&p.data, &p.proposal_type),
455 };
456
457 self.proposal_type().mls_encoded_len() + inner_len
458 }
459}
460
461impl MlsEncode for Proposal {
462 fn mls_encode(&self, writer: &mut Vec<u8>) -> Result<(), mls_rs_codec::Error> {
463 self.proposal_type().mls_encode(writer)?;
464
465 match self {
466 Proposal::Add(p) => p.mls_encode(writer),
467 #[cfg(feature = "by_ref_proposal")]
468 Proposal::Update(p) => p.mls_encode(writer),
469 Proposal::Remove(p) => p.mls_encode(writer),
470 #[cfg(feature = "psk")]
471 Proposal::Psk(p) => p.mls_encode(writer),
472 Proposal::ReInit(p) => p.mls_encode(writer),
473 Proposal::ExternalInit(p) => p.mls_encode(writer),
474 Proposal::GroupContextExtensions(p) => p.mls_encode(writer),
475 #[cfg(all(
476 feature = "by_ref_proposal",
477 feature = "custom_proposal",
478 feature = "self_remove_proposal"
479 ))]
480 Proposal::SelfRemove(p) => p.mls_encode(writer),
481 #[cfg(feature = "custom_proposal")]
482 Proposal::Custom(p) => {
483 if p.proposal_type.raw_value() <= 7 {
484 return Err(mls_rs_codec::Error::Custom(2));
491 }
492 CustomProposal::encode_from_bytes(&p.data, writer, &p.proposal_type)
493 }
494 }
495 }
496}
497
498impl MlsDecode for Proposal {
499 fn mls_decode(reader: &mut &[u8]) -> Result<Self, mls_rs_codec::Error> {
500 let proposal_type = ProposalType::mls_decode(reader)?;
501
502 Ok(match proposal_type {
503 ProposalType::ADD => {
504 Proposal::Add(alloc::boxed::Box::new(AddProposal::mls_decode(reader)?))
505 }
506 #[cfg(feature = "by_ref_proposal")]
507 ProposalType::UPDATE => Proposal::Update(UpdateProposal::mls_decode(reader)?),
508 ProposalType::REMOVE => Proposal::Remove(RemoveProposal::mls_decode(reader)?),
509 #[cfg(feature = "psk")]
510 ProposalType::PSK => Proposal::Psk(PreSharedKeyProposal::mls_decode(reader)?),
511 ProposalType::RE_INIT => Proposal::ReInit(ReInitProposal::mls_decode(reader)?),
512 ProposalType::EXTERNAL_INIT => {
513 Proposal::ExternalInit(ExternalInit::mls_decode(reader)?)
514 }
515 ProposalType::GROUP_CONTEXT_EXTENSIONS => {
516 Proposal::GroupContextExtensions(ExtensionList::mls_decode(reader)?)
517 }
518 #[cfg(all(
519 feature = "by_ref_proposal",
520 feature = "custom_proposal",
521 feature = "self_remove_proposal"
522 ))]
523 ProposalType::SELF_REMOVE => {
524 Proposal::SelfRemove(SelfRemoveProposal::mls_decode(reader)?)
525 }
526 #[cfg(feature = "custom_proposal")]
527 custom => Proposal::Custom(CustomProposal {
528 proposal_type: custom,
529 data: CustomProposal::decode_from_bytes(reader, &custom)?,
530 }),
531 #[cfg(not(feature = "custom_proposal"))]
533 _ => return Err(mls_rs_codec::Error::Custom(3)),
534 })
535 }
536}
537
538impl Proposal {
539 pub fn proposal_type(&self) -> ProposalType {
540 match self {
541 Proposal::Add(_) => ProposalType::ADD,
542 #[cfg(feature = "by_ref_proposal")]
543 Proposal::Update(_) => ProposalType::UPDATE,
544 Proposal::Remove(_) => ProposalType::REMOVE,
545 #[cfg(feature = "psk")]
546 Proposal::Psk(_) => ProposalType::PSK,
547 Proposal::ReInit(_) => ProposalType::RE_INIT,
548 Proposal::ExternalInit(_) => ProposalType::EXTERNAL_INIT,
549 Proposal::GroupContextExtensions(_) => ProposalType::GROUP_CONTEXT_EXTENSIONS,
550 #[cfg(all(
551 feature = "by_ref_proposal",
552 feature = "custom_proposal",
553 feature = "self_remove_proposal"
554 ))]
555 Proposal::SelfRemove(_) => ProposalType::SELF_REMOVE,
556 #[cfg(feature = "custom_proposal")]
557 Proposal::Custom(c) => c.proposal_type,
558 }
559 }
560}
561
562#[derive(Clone, Debug, PartialEq)]
563pub enum BorrowedProposal<'a> {
565 Add(&'a AddProposal),
566 #[cfg(feature = "by_ref_proposal")]
567 Update(&'a UpdateProposal),
568 Remove(&'a RemoveProposal),
569 #[cfg(feature = "psk")]
570 Psk(&'a PreSharedKeyProposal),
571 ReInit(&'a ReInitProposal),
572 ExternalInit(&'a ExternalInit),
573 GroupContextExtensions(&'a ExtensionList),
574 #[cfg(all(
575 feature = "by_ref_proposal",
576 feature = "custom_proposal",
577 feature = "self_remove_proposal"
578 ))]
579 SelfRemove(&'a SelfRemoveProposal),
580 #[cfg(feature = "custom_proposal")]
581 Custom(&'a CustomProposal),
582}
583
584impl<'a> From<BorrowedProposal<'a>> for Proposal {
585 fn from(value: BorrowedProposal<'a>) -> Self {
586 match value {
587 BorrowedProposal::Add(add) => Proposal::Add(alloc::boxed::Box::new(add.clone())),
588 #[cfg(feature = "by_ref_proposal")]
589 BorrowedProposal::Update(update) => Proposal::Update(update.clone()),
590 BorrowedProposal::Remove(remove) => Proposal::Remove(remove.clone()),
591 #[cfg(feature = "psk")]
592 BorrowedProposal::Psk(psk) => Proposal::Psk(psk.clone()),
593 BorrowedProposal::ReInit(reinit) => Proposal::ReInit(reinit.clone()),
594 BorrowedProposal::ExternalInit(external) => Proposal::ExternalInit(external.clone()),
595 BorrowedProposal::GroupContextExtensions(ext) => {
596 Proposal::GroupContextExtensions(ext.clone())
597 }
598 #[cfg(all(
599 feature = "by_ref_proposal",
600 feature = "custom_proposal",
601 feature = "self_remove_proposal"
602 ))]
603 BorrowedProposal::SelfRemove(self_remove) => Proposal::SelfRemove(self_remove.clone()),
604 #[cfg(feature = "custom_proposal")]
605 BorrowedProposal::Custom(custom) => Proposal::Custom(custom.clone()),
606 }
607 }
608}
609
610impl BorrowedProposal<'_> {
611 pub fn proposal_type(&self) -> ProposalType {
612 match self {
613 BorrowedProposal::Add(_) => ProposalType::ADD,
614 #[cfg(feature = "by_ref_proposal")]
615 BorrowedProposal::Update(_) => ProposalType::UPDATE,
616 BorrowedProposal::Remove(_) => ProposalType::REMOVE,
617 #[cfg(feature = "psk")]
618 BorrowedProposal::Psk(_) => ProposalType::PSK,
619 BorrowedProposal::ReInit(_) => ProposalType::RE_INIT,
620 BorrowedProposal::ExternalInit(_) => ProposalType::EXTERNAL_INIT,
621 BorrowedProposal::GroupContextExtensions(_) => ProposalType::GROUP_CONTEXT_EXTENSIONS,
622 #[cfg(all(
623 feature = "by_ref_proposal",
624 feature = "custom_proposal",
625 feature = "self_remove_proposal"
626 ))]
627 BorrowedProposal::SelfRemove(_) => ProposalType::SELF_REMOVE,
628 #[cfg(feature = "custom_proposal")]
629 BorrowedProposal::Custom(c) => c.proposal_type,
630 }
631 }
632}
633
634impl<'a> From<&'a Proposal> for BorrowedProposal<'a> {
635 fn from(p: &'a Proposal) -> Self {
636 match p {
637 Proposal::Add(p) => BorrowedProposal::Add(p),
638 #[cfg(feature = "by_ref_proposal")]
639 Proposal::Update(p) => BorrowedProposal::Update(p),
640 Proposal::Remove(p) => BorrowedProposal::Remove(p),
641 #[cfg(feature = "psk")]
642 Proposal::Psk(p) => BorrowedProposal::Psk(p),
643 Proposal::ReInit(p) => BorrowedProposal::ReInit(p),
644 Proposal::ExternalInit(p) => BorrowedProposal::ExternalInit(p),
645 Proposal::GroupContextExtensions(p) => BorrowedProposal::GroupContextExtensions(p),
646 #[cfg(all(
647 feature = "by_ref_proposal",
648 feature = "custom_proposal",
649 feature = "self_remove_proposal"
650 ))]
651 Proposal::SelfRemove(p) => BorrowedProposal::SelfRemove(p),
652 #[cfg(feature = "custom_proposal")]
653 Proposal::Custom(p) => BorrowedProposal::Custom(p),
654 }
655 }
656}
657
658impl<'a> From<&'a AddProposal> for BorrowedProposal<'a> {
659 fn from(p: &'a AddProposal) -> Self {
660 Self::Add(p)
661 }
662}
663
664#[cfg(feature = "by_ref_proposal")]
665impl<'a> From<&'a UpdateProposal> for BorrowedProposal<'a> {
666 fn from(p: &'a UpdateProposal) -> Self {
667 Self::Update(p)
668 }
669}
670
671impl<'a> From<&'a RemoveProposal> for BorrowedProposal<'a> {
672 fn from(p: &'a RemoveProposal) -> Self {
673 Self::Remove(p)
674 }
675}
676
677#[cfg(feature = "psk")]
678impl<'a> From<&'a PreSharedKeyProposal> for BorrowedProposal<'a> {
679 fn from(p: &'a PreSharedKeyProposal) -> Self {
680 Self::Psk(p)
681 }
682}
683
684impl<'a> From<&'a ReInitProposal> for BorrowedProposal<'a> {
685 fn from(p: &'a ReInitProposal) -> Self {
686 Self::ReInit(p)
687 }
688}
689
690impl<'a> From<&'a ExternalInit> for BorrowedProposal<'a> {
691 fn from(p: &'a ExternalInit) -> Self {
692 Self::ExternalInit(p)
693 }
694}
695
696impl<'a> From<&'a ExtensionList> for BorrowedProposal<'a> {
697 fn from(p: &'a ExtensionList) -> Self {
698 Self::GroupContextExtensions(p)
699 }
700}
701
702#[cfg(all(
703 feature = "by_ref_proposal",
704 feature = "custom_proposal",
705 feature = "self_remove_proposal"
706))]
707impl<'a> From<&'a SelfRemoveProposal> for BorrowedProposal<'a> {
708 fn from(p: &'a SelfRemoveProposal) -> Self {
709 Self::SelfRemove(p)
710 }
711}
712
713#[cfg(feature = "custom_proposal")]
714impl<'a> From<&'a CustomProposal> for BorrowedProposal<'a> {
715 fn from(p: &'a CustomProposal) -> Self {
716 Self::Custom(p)
717 }
718}
719
720#[derive(Clone, Debug, PartialEq, MlsSize, MlsEncode, MlsDecode)]
721#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
722#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
723#[repr(u8)]
724pub(crate) enum ProposalOrRef {
725 Proposal(Box<Proposal>) = 1u8,
726 #[cfg(feature = "by_ref_proposal")]
727 Reference(ProposalRef) = 2u8,
728}
729
730impl From<Proposal> for ProposalOrRef {
731 fn from(proposal: Proposal) -> Self {
732 Self::Proposal(Box::new(proposal))
733 }
734}
735
736#[cfg(feature = "by_ref_proposal")]
737impl From<ProposalRef> for ProposalOrRef {
738 fn from(r: ProposalRef) -> Self {
739 Self::Reference(r)
740 }
741}