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