sui_sdk_types/effects/
v2.rs

1use crate::digest::EffectsAuxiliaryDataDigest;
2use crate::execution_status::ExecutionStatus;
3use crate::object::Owner;
4use crate::object::Version;
5use crate::EpochId;
6use crate::GasCostSummary;
7use crate::ObjectDigest;
8use crate::ObjectId;
9use crate::TransactionDigest;
10use crate::TransactionEventsDigest;
11
12/// Version 2 of TransactionEffects
13///
14/// # BCS
15///
16/// The BCS serialized form for this type is defined by the following ABNF:
17///
18/// ```text
19/// effects-v2 = execution-status
20///              u64                                ; epoch
21///              gas-cost-summary
22///              digest                             ; transaction digest
23///              (option u32)                       ; gas object index
24///              (option digest)                    ; events digest
25///              (vector digest)                    ; list of transaction dependencies
26///              u64                                ; lamport version
27///              (vector changed-object)
28///              (vector unchanged-shared-object)
29///              (option digest)                    ; auxiliary data digest
30/// ```
31#[derive(Eq, PartialEq, Clone, Debug)]
32#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
33pub struct TransactionEffectsV2 {
34    /// The status of the execution
35    pub status: ExecutionStatus,
36
37    /// The epoch when this transaction was executed.
38    pub epoch: EpochId,
39
40    /// The gas used by this transaction
41    pub gas_used: GasCostSummary,
42
43    /// The transaction digest
44    pub transaction_digest: TransactionDigest,
45
46    /// The updated gas object reference, as an index into the `changed_objects` vector.
47    /// Having a dedicated field for convenient access.
48    /// System transaction that don't require gas will leave this as None.
49    pub gas_object_index: Option<u32>,
50
51    /// The digest of the events emitted during execution,
52    /// can be None if the transaction does not emit any event.
53    pub events_digest: Option<TransactionEventsDigest>,
54
55    /// The set of transaction digests this transaction depends on.
56    #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=5).lift()))]
57    pub dependencies: Vec<TransactionDigest>,
58
59    /// The version number of all the written Move objects by this transaction.
60    pub lamport_version: Version,
61
62    /// Objects whose state are changed in the object store.
63    #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))]
64    pub changed_objects: Vec<ChangedObject>,
65
66    /// Shared objects that are not mutated in this transaction. Unlike owned objects,
67    /// read-only shared objects' version are not committed in the transaction,
68    /// and in order for a node to catch up and execute it without consensus sequencing,
69    /// the version needs to be committed in the effects.
70    #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))]
71    pub unchanged_shared_objects: Vec<UnchangedSharedObject>,
72
73    /// Auxiliary data that are not protocol-critical, generated as part of the effects but are stored separately.
74    /// Storing it separately allows us to avoid bloating the effects with data that are not critical.
75    /// It also provides more flexibility on the format and type of the data.
76    pub auxiliary_data_digest: Option<EffectsAuxiliaryDataDigest>,
77}
78
79/// Input/output state of an object that was changed during execution
80///
81/// # BCS
82///
83/// The BCS serialized form for this type is defined by the following ABNF:
84///
85/// ```text
86/// changed-object = object-id object-in object-out id-operation
87/// ```
88#[derive(Eq, PartialEq, Clone, Debug)]
89#[cfg_attr(
90    feature = "serde",
91    derive(serde_derive::Serialize, serde_derive::Deserialize)
92)]
93#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
94pub struct ChangedObject {
95    /// Id of the object
96    pub object_id: ObjectId,
97
98    /// State of the object in the store prior to this transaction.
99    pub input_state: ObjectIn,
100
101    /// State of the object in the store after this transaction.
102    pub output_state: ObjectOut,
103
104    /// Whether this object ID is created or deleted in this transaction.
105    /// This information isn't required by the protocol but is useful for providing more detailed
106    /// semantics on object changes.
107    pub id_operation: IdOperation,
108}
109
110/// A shared object that wasn't changed during execution
111///
112/// # BCS
113///
114/// The BCS serialized form for this type is defined by the following ABNF:
115///
116/// ```text
117/// unchanged-shared-object = object-id unchanged-shared-object-kind
118/// ```
119#[derive(Eq, PartialEq, Clone, Debug)]
120#[cfg_attr(
121    feature = "serde",
122    derive(serde_derive::Serialize, serde_derive::Deserialize)
123)]
124#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
125pub struct UnchangedSharedObject {
126    pub object_id: ObjectId,
127    pub kind: UnchangedSharedKind,
128}
129
130/// Type of unchanged shared object
131///
132/// # BCS
133///
134/// The BCS serialized form for this type is defined by the following ABNF:
135///
136/// ```text
137/// unchanged-shared-object-kind =  read-only-root
138///                              =/ mutate-deleted
139///                              =/ read-deleted
140///                              =/ canceled
141///                              =/ per-epoch-config
142///
143/// read-only-root                           = %x00 u64 digest
144/// mutate-deleted                           = %x01 u64
145/// read-deleted                             = %x02 u64
146/// canceled                                 = %x03 u64
147/// per-epoch-config                         = %x04
148/// per-epoch-config-with-sequence-number    = %x05 u64
149/// ```
150#[derive(Eq, PartialEq, Clone, Debug)]
151#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
152pub enum UnchangedSharedKind {
153    /// Read-only shared objects from the input. We don't really need ObjectDigest
154    /// for protocol correctness, but it will make it easier to verify untrusted read.
155    ReadOnlyRoot {
156        version: Version,
157        digest: ObjectDigest,
158    },
159
160    /// Deleted shared objects that appear mutably/owned in the input.
161    MutateDeleted { version: Version },
162
163    /// Deleted shared objects that appear as read-only in the input.
164    ReadDeleted { version: Version },
165
166    /// Shared objects in canceled transaction. The sequence number embed cancellation reason.
167    Canceled { version: Version },
168
169    /// Read of a per-epoch config object that should remain the same during an epoch.
170    /// NOTE: Will be deprecated in the near future in favor of `PerEpochConfigWithSequenceNumber`.
171    PerEpochConfig,
172
173    /// Read of a per-epoch config and it's starting sequence number in the epoch.
174    PerEpochConfigWithSequenceNumber { version: Version },
175}
176
177/// State of an object prior to execution
178///
179/// If an object exists (at root-level) in the store prior to this transaction,
180/// it should be Exist, otherwise it's NonExist, e.g. wrapped objects should be
181/// NonExist.
182///
183/// # BCS
184///
185/// The BCS serialized form for this type is defined by the following ABNF:
186///
187/// ```text
188/// object-in = object-in-not-exist / object-in-exist
189///
190/// object-in-not-exist = %x00
191/// object-in-exist     = %x01 u64 digest owner
192/// ```
193#[derive(Eq, PartialEq, Clone, Debug)]
194#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
195pub enum ObjectIn {
196    NotExist,
197
198    /// The old version, digest and owner.
199    Exist {
200        version: Version,
201        digest: ObjectDigest,
202        owner: Owner,
203    },
204}
205
206/// State of an object after execution
207///
208/// # BCS
209///
210/// The BCS serialized form for this type is defined by the following ABNF:
211///
212/// ```text
213/// object-out  =  object-out-not-exist
214///             =/ object-out-object-write
215///             =/ object-out-package-write
216///
217///
218/// object-out-not-exist        = %x00
219/// object-out-object-write     = %x01 digest owner
220/// object-out-package-write    = %x02 version digest
221/// ```
222#[derive(Eq, PartialEq, Clone, Debug)]
223#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
224pub enum ObjectOut {
225    /// Same definition as in ObjectIn.
226    NotExist,
227
228    /// Any written object, including all of mutated, created, unwrapped today.
229    ObjectWrite { digest: ObjectDigest, owner: Owner },
230
231    /// Packages writes need to be tracked separately with version because
232    /// we don't use lamport version for package publish and upgrades.
233    PackageWrite {
234        version: Version,
235        digest: ObjectDigest,
236    },
237}
238
239/// Defines what happened to an ObjectId during execution
240///
241/// # BCS
242///
243/// The BCS serialized form for this type is defined by the following ABNF:
244///
245/// ```text
246/// id-operation =  id-operation-none
247///              =/ id-operation-created
248///              =/ id-operation-deleted
249///
250/// id-operation-none       = %x00
251/// id-operation-created    = %x01
252/// id-operation-deleted    = %x02
253/// ```
254#[derive(Eq, PartialEq, Copy, Clone, Debug)]
255#[cfg_attr(
256    feature = "serde",
257    derive(serde_derive::Serialize, serde_derive::Deserialize),
258    serde(rename_all = "lowercase")
259)]
260#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
261pub enum IdOperation {
262    None,
263    Created,
264    Deleted,
265}
266
267impl TransactionEffectsV2 {
268    /// The status of the execution
269    pub fn status(&self) -> &ExecutionStatus {
270        &self.status
271    }
272
273    /// The epoch when this transaction was executed.
274    pub fn epoch(&self) -> EpochId {
275        self.epoch
276    }
277
278    /// The gas used in this transaction.
279    pub fn gas_summary(&self) -> &GasCostSummary {
280        &self.gas_used
281    }
282}
283
284#[cfg(feature = "serde")]
285#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
286mod serialization {
287    use serde::Deserialize;
288    use serde::Deserializer;
289    use serde::Serialize;
290    use serde::Serializer;
291
292    use super::*;
293
294    #[derive(serde_derive::Serialize)]
295    struct ReadableTransactionEffectsV2Ref<'a> {
296        #[serde(flatten)]
297        status: &'a ExecutionStatus,
298        #[serde(with = "crate::_serde::ReadableDisplay")]
299        epoch: &'a EpochId,
300        gas_used: &'a GasCostSummary,
301        transaction_digest: &'a TransactionDigest,
302        gas_object_index: &'a Option<u32>,
303        events_digest: &'a Option<TransactionEventsDigest>,
304        dependencies: &'a Vec<TransactionDigest>,
305        #[serde(with = "crate::_serde::ReadableDisplay")]
306        lamport_version: &'a Version,
307        changed_objects: &'a Vec<ChangedObject>,
308        unchanged_shared_objects: &'a Vec<UnchangedSharedObject>,
309        auxiliary_data_digest: &'a Option<EffectsAuxiliaryDataDigest>,
310    }
311
312    #[derive(serde_derive::Deserialize)]
313    struct ReadableTransactionEffectsV2 {
314        #[serde(flatten)]
315        status: ExecutionStatus,
316        #[serde(with = "crate::_serde::ReadableDisplay")]
317        epoch: EpochId,
318        gas_used: GasCostSummary,
319        transaction_digest: TransactionDigest,
320        gas_object_index: Option<u32>,
321        events_digest: Option<TransactionEventsDigest>,
322        dependencies: Vec<TransactionDigest>,
323        #[serde(with = "crate::_serde::ReadableDisplay")]
324        lamport_version: Version,
325        changed_objects: Vec<ChangedObject>,
326        unchanged_shared_objects: Vec<UnchangedSharedObject>,
327        auxiliary_data_digest: Option<EffectsAuxiliaryDataDigest>,
328    }
329
330    #[derive(serde_derive::Serialize)]
331    struct BinaryTransactionEffectsV2Ref<'a> {
332        status: &'a ExecutionStatus,
333        epoch: &'a EpochId,
334        gas_used: &'a GasCostSummary,
335        transaction_digest: &'a TransactionDigest,
336        gas_object_index: &'a Option<u32>,
337        events_digest: &'a Option<TransactionEventsDigest>,
338        dependencies: &'a Vec<TransactionDigest>,
339        lamport_version: &'a Version,
340        changed_objects: &'a Vec<ChangedObject>,
341        unchanged_shared_objects: &'a Vec<UnchangedSharedObject>,
342        auxiliary_data_digest: &'a Option<EffectsAuxiliaryDataDigest>,
343    }
344
345    #[derive(serde_derive::Deserialize)]
346    struct BinaryTransactionEffectsV2 {
347        status: ExecutionStatus,
348        epoch: EpochId,
349        gas_used: GasCostSummary,
350        transaction_digest: TransactionDigest,
351        gas_object_index: Option<u32>,
352        events_digest: Option<TransactionEventsDigest>,
353        dependencies: Vec<TransactionDigest>,
354        lamport_version: Version,
355        changed_objects: Vec<ChangedObject>,
356        unchanged_shared_objects: Vec<UnchangedSharedObject>,
357        auxiliary_data_digest: Option<EffectsAuxiliaryDataDigest>,
358    }
359
360    impl Serialize for TransactionEffectsV2 {
361        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
362        where
363            S: Serializer,
364        {
365            let Self {
366                status,
367                epoch,
368                gas_used,
369                transaction_digest,
370                gas_object_index,
371                events_digest,
372                dependencies,
373                lamport_version,
374                changed_objects,
375                unchanged_shared_objects,
376                auxiliary_data_digest,
377            } = self;
378            if serializer.is_human_readable() {
379                let readable = ReadableTransactionEffectsV2Ref {
380                    status,
381                    epoch,
382                    gas_used,
383                    transaction_digest,
384                    gas_object_index,
385                    events_digest,
386                    dependencies,
387                    lamport_version,
388                    changed_objects,
389                    unchanged_shared_objects,
390                    auxiliary_data_digest,
391                };
392                readable.serialize(serializer)
393            } else {
394                let binary = BinaryTransactionEffectsV2Ref {
395                    status,
396                    epoch,
397                    gas_used,
398                    transaction_digest,
399                    gas_object_index,
400                    events_digest,
401                    dependencies,
402                    lamport_version,
403                    changed_objects,
404                    unchanged_shared_objects,
405                    auxiliary_data_digest,
406                };
407                binary.serialize(serializer)
408            }
409        }
410    }
411
412    impl<'de> Deserialize<'de> for TransactionEffectsV2 {
413        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
414        where
415            D: Deserializer<'de>,
416        {
417            if deserializer.is_human_readable() {
418                let ReadableTransactionEffectsV2 {
419                    status,
420                    epoch,
421                    gas_used,
422                    transaction_digest,
423                    gas_object_index,
424                    events_digest,
425                    dependencies,
426                    lamport_version,
427                    changed_objects,
428                    unchanged_shared_objects,
429                    auxiliary_data_digest,
430                } = Deserialize::deserialize(deserializer)?;
431                Ok(Self {
432                    status,
433                    epoch,
434                    gas_used,
435                    transaction_digest,
436                    gas_object_index,
437                    events_digest,
438                    dependencies,
439                    lamport_version,
440                    changed_objects,
441                    unchanged_shared_objects,
442                    auxiliary_data_digest,
443                })
444            } else {
445                let BinaryTransactionEffectsV2 {
446                    status,
447                    epoch,
448                    gas_used,
449                    transaction_digest,
450                    gas_object_index,
451                    events_digest,
452                    dependencies,
453                    lamport_version,
454                    changed_objects,
455                    unchanged_shared_objects,
456                    auxiliary_data_digest,
457                } = Deserialize::deserialize(deserializer)?;
458                Ok(Self {
459                    status,
460                    epoch,
461                    gas_used,
462                    transaction_digest,
463                    gas_object_index,
464                    events_digest,
465                    dependencies,
466                    lamport_version,
467                    changed_objects,
468                    unchanged_shared_objects,
469                    auxiliary_data_digest,
470                })
471            }
472        }
473    }
474
475    #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
476    #[serde(tag = "kind", rename_all = "snake_case")]
477    enum ReadableUnchangedSharedKind {
478        ReadOnlyRoot {
479            #[serde(with = "crate::_serde::ReadableDisplay")]
480            version: Version,
481            digest: ObjectDigest,
482        },
483        MutateDeleted {
484            #[serde(with = "crate::_serde::ReadableDisplay")]
485            version: Version,
486        },
487        ReadDeleted {
488            #[serde(with = "crate::_serde::ReadableDisplay")]
489            version: Version,
490        },
491        Canceled {
492            #[serde(with = "crate::_serde::ReadableDisplay")]
493            version: Version,
494        },
495        PerEpochConfig,
496        PerEpochConfigWithSequenceNumber {
497            #[serde(with = "crate::_serde::ReadableDisplay")]
498            version: Version,
499        },
500    }
501
502    #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
503    enum BinaryUnchangedSharedKind {
504        ReadOnlyRoot {
505            version: Version,
506            digest: ObjectDigest,
507        },
508        MutateDeleted {
509            version: Version,
510        },
511        ReadDeleted {
512            version: Version,
513        },
514        Canceled {
515            version: Version,
516        },
517        PerEpochConfig,
518        PerEpochConfigWithSequenceNumber {
519            version: Version,
520        },
521    }
522
523    impl Serialize for UnchangedSharedKind {
524        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
525        where
526            S: Serializer,
527        {
528            if serializer.is_human_readable() {
529                let readable = match self.clone() {
530                    UnchangedSharedKind::ReadOnlyRoot { version, digest } => {
531                        ReadableUnchangedSharedKind::ReadOnlyRoot { version, digest }
532                    }
533                    UnchangedSharedKind::MutateDeleted { version } => {
534                        ReadableUnchangedSharedKind::MutateDeleted { version }
535                    }
536                    UnchangedSharedKind::ReadDeleted { version } => {
537                        ReadableUnchangedSharedKind::ReadDeleted { version }
538                    }
539                    UnchangedSharedKind::Canceled { version } => {
540                        ReadableUnchangedSharedKind::Canceled { version }
541                    }
542                    UnchangedSharedKind::PerEpochConfig => {
543                        ReadableUnchangedSharedKind::PerEpochConfig
544                    }
545                    UnchangedSharedKind::PerEpochConfigWithSequenceNumber { version } => {
546                        ReadableUnchangedSharedKind::PerEpochConfigWithSequenceNumber { version }
547                    }
548                };
549                readable.serialize(serializer)
550            } else {
551                let binary = match self.clone() {
552                    UnchangedSharedKind::ReadOnlyRoot { version, digest } => {
553                        BinaryUnchangedSharedKind::ReadOnlyRoot { version, digest }
554                    }
555                    UnchangedSharedKind::MutateDeleted { version } => {
556                        BinaryUnchangedSharedKind::MutateDeleted { version }
557                    }
558                    UnchangedSharedKind::ReadDeleted { version } => {
559                        BinaryUnchangedSharedKind::ReadDeleted { version }
560                    }
561                    UnchangedSharedKind::Canceled { version } => {
562                        BinaryUnchangedSharedKind::Canceled { version }
563                    }
564                    UnchangedSharedKind::PerEpochConfig => {
565                        BinaryUnchangedSharedKind::PerEpochConfig
566                    }
567                    UnchangedSharedKind::PerEpochConfigWithSequenceNumber { version } => {
568                        BinaryUnchangedSharedKind::PerEpochConfigWithSequenceNumber { version }
569                    }
570                };
571                binary.serialize(serializer)
572            }
573        }
574    }
575
576    impl<'de> Deserialize<'de> for UnchangedSharedKind {
577        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
578        where
579            D: Deserializer<'de>,
580        {
581            if deserializer.is_human_readable() {
582                ReadableUnchangedSharedKind::deserialize(deserializer).map(
583                    |readable| match readable {
584                        ReadableUnchangedSharedKind::ReadOnlyRoot { version, digest } => {
585                            Self::ReadOnlyRoot { version, digest }
586                        }
587                        ReadableUnchangedSharedKind::MutateDeleted { version } => {
588                            Self::MutateDeleted { version }
589                        }
590                        ReadableUnchangedSharedKind::ReadDeleted { version } => {
591                            Self::ReadDeleted { version }
592                        }
593                        ReadableUnchangedSharedKind::Canceled { version } => {
594                            Self::Canceled { version }
595                        }
596                        ReadableUnchangedSharedKind::PerEpochConfig => Self::PerEpochConfig,
597                        ReadableUnchangedSharedKind::PerEpochConfigWithSequenceNumber {
598                            version,
599                        } => Self::PerEpochConfigWithSequenceNumber { version },
600                    },
601                )
602            } else {
603                BinaryUnchangedSharedKind::deserialize(deserializer).map(|binary| match binary {
604                    BinaryUnchangedSharedKind::ReadOnlyRoot { version, digest } => {
605                        Self::ReadOnlyRoot { version, digest }
606                    }
607                    BinaryUnchangedSharedKind::MutateDeleted { version } => {
608                        Self::MutateDeleted { version }
609                    }
610                    BinaryUnchangedSharedKind::ReadDeleted { version } => {
611                        Self::ReadDeleted { version }
612                    }
613                    BinaryUnchangedSharedKind::Canceled { version } => Self::Canceled { version },
614                    BinaryUnchangedSharedKind::PerEpochConfig => Self::PerEpochConfig,
615                    BinaryUnchangedSharedKind::PerEpochConfigWithSequenceNumber { version } => {
616                        Self::PerEpochConfigWithSequenceNumber { version }
617                    }
618                })
619            }
620        }
621    }
622
623    #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
624    #[serde(tag = "state", rename_all = "snake_case")]
625    enum ReadableObjectIn {
626        NotExist,
627        Exist {
628            #[serde(with = "crate::_serde::ReadableDisplay")]
629            version: Version,
630            digest: ObjectDigest,
631            owner: Owner,
632        },
633    }
634
635    #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
636    enum BinaryObjectIn {
637        NotExist,
638        Exist {
639            version: Version,
640            digest: ObjectDigest,
641            owner: Owner,
642        },
643    }
644
645    impl Serialize for ObjectIn {
646        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
647        where
648            S: Serializer,
649        {
650            if serializer.is_human_readable() {
651                let readable = match self.clone() {
652                    ObjectIn::NotExist => ReadableObjectIn::NotExist,
653                    ObjectIn::Exist {
654                        version,
655                        digest,
656                        owner,
657                    } => ReadableObjectIn::Exist {
658                        version,
659                        digest,
660                        owner,
661                    },
662                };
663                readable.serialize(serializer)
664            } else {
665                let binary = match self.clone() {
666                    ObjectIn::NotExist => BinaryObjectIn::NotExist,
667                    ObjectIn::Exist {
668                        version,
669                        digest,
670                        owner,
671                    } => BinaryObjectIn::Exist {
672                        version,
673                        digest,
674                        owner,
675                    },
676                };
677                binary.serialize(serializer)
678            }
679        }
680    }
681
682    impl<'de> Deserialize<'de> for ObjectIn {
683        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
684        where
685            D: Deserializer<'de>,
686        {
687            if deserializer.is_human_readable() {
688                ReadableObjectIn::deserialize(deserializer).map(|readable| match readable {
689                    ReadableObjectIn::NotExist => Self::NotExist,
690                    ReadableObjectIn::Exist {
691                        version,
692                        digest,
693                        owner,
694                    } => Self::Exist {
695                        version,
696                        digest,
697                        owner,
698                    },
699                })
700            } else {
701                BinaryObjectIn::deserialize(deserializer).map(|binary| match binary {
702                    BinaryObjectIn::NotExist => Self::NotExist,
703                    BinaryObjectIn::Exist {
704                        version,
705                        digest,
706                        owner,
707                    } => Self::Exist {
708                        version,
709                        digest,
710                        owner,
711                    },
712                })
713            }
714        }
715    }
716
717    #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
718    #[serde(tag = "state", rename_all = "snake_case")]
719    enum ReadableObjectOut {
720        NotExist,
721        ObjectWrite {
722            digest: ObjectDigest,
723            owner: Owner,
724        },
725        PackageWrite {
726            #[serde(with = "crate::_serde::ReadableDisplay")]
727            version: Version,
728            digest: ObjectDigest,
729        },
730    }
731
732    #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
733    enum BinaryObjectOut {
734        NotExist,
735        ObjectWrite {
736            digest: ObjectDigest,
737            owner: Owner,
738        },
739        PackageWrite {
740            #[serde(with = "crate::_serde::ReadableDisplay")]
741            version: Version,
742            digest: ObjectDigest,
743        },
744    }
745
746    impl Serialize for ObjectOut {
747        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
748        where
749            S: Serializer,
750        {
751            if serializer.is_human_readable() {
752                let readable = match self.clone() {
753                    ObjectOut::NotExist => ReadableObjectOut::NotExist,
754                    ObjectOut::ObjectWrite { digest, owner } => {
755                        ReadableObjectOut::ObjectWrite { digest, owner }
756                    }
757                    ObjectOut::PackageWrite { version, digest } => {
758                        ReadableObjectOut::PackageWrite { version, digest }
759                    }
760                };
761                readable.serialize(serializer)
762            } else {
763                let binary = match self.clone() {
764                    ObjectOut::NotExist => BinaryObjectOut::NotExist,
765                    ObjectOut::ObjectWrite { digest, owner } => {
766                        BinaryObjectOut::ObjectWrite { digest, owner }
767                    }
768                    ObjectOut::PackageWrite { version, digest } => {
769                        BinaryObjectOut::PackageWrite { version, digest }
770                    }
771                };
772                binary.serialize(serializer)
773            }
774        }
775    }
776
777    impl<'de> Deserialize<'de> for ObjectOut {
778        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
779        where
780            D: Deserializer<'de>,
781        {
782            if deserializer.is_human_readable() {
783                ReadableObjectOut::deserialize(deserializer).map(|readable| match readable {
784                    ReadableObjectOut::NotExist => Self::NotExist,
785                    ReadableObjectOut::ObjectWrite { digest, owner } => {
786                        Self::ObjectWrite { digest, owner }
787                    }
788                    ReadableObjectOut::PackageWrite { version, digest } => {
789                        Self::PackageWrite { version, digest }
790                    }
791                })
792            } else {
793                BinaryObjectOut::deserialize(deserializer).map(|binary| match binary {
794                    BinaryObjectOut::NotExist => Self::NotExist,
795                    BinaryObjectOut::ObjectWrite { digest, owner } => {
796                        Self::ObjectWrite { digest, owner }
797                    }
798                    BinaryObjectOut::PackageWrite { version, digest } => {
799                        Self::PackageWrite { version, digest }
800                    }
801                })
802            }
803        }
804    }
805}