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/// ```
149#[derive(Eq, PartialEq, Clone, Debug)]
150#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
151pub enum UnchangedSharedKind {
152    /// Read-only shared objects from the input. We don't really need ObjectDigest
153    /// for protocol correctness, but it will make it easier to verify untrusted read.
154    ReadOnlyRoot {
155        version: Version,
156        digest: ObjectDigest,
157    },
158
159    /// Deleted shared objects that appear mutably/owned in the input.
160    MutateDeleted { version: Version },
161
162    /// Deleted shared objects that appear as read-only in the input.
163    ReadDeleted { version: Version },
164
165    /// Shared objects in canceled transaction. The sequence number embed cancellation reason.
166    Canceled { version: Version },
167
168    /// Read of a per-epoch config object that should remain the same during an epoch.
169    PerEpochConfig,
170}
171
172/// State of an object prior to execution
173///
174/// If an object exists (at root-level) in the store prior to this transaction,
175/// it should be Exist, otherwise it's NonExist, e.g. wrapped objects should be
176/// NonExist.
177///
178/// # BCS
179///
180/// The BCS serialized form for this type is defined by the following ABNF:
181///
182/// ```text
183/// object-in = object-in-not-exist / object-in-exist
184///
185/// object-in-not-exist = %x00
186/// object-in-exist     = %x01 u64 digest owner
187/// ```
188#[derive(Eq, PartialEq, Clone, Debug)]
189#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
190pub enum ObjectIn {
191    NotExist,
192
193    /// The old version, digest and owner.
194    Exist {
195        version: Version,
196        digest: ObjectDigest,
197        owner: Owner,
198    },
199}
200
201/// State of an object after execution
202///
203/// # BCS
204///
205/// The BCS serialized form for this type is defined by the following ABNF:
206///
207/// ```text
208/// object-out  =  object-out-not-exist
209///             =/ object-out-object-write
210///             =/ object-out-package-write
211///
212///
213/// object-out-not-exist        = %x00
214/// object-out-object-write     = %x01 digest owner
215/// object-out-package-write    = %x02 version digest
216/// ```
217#[derive(Eq, PartialEq, Clone, Debug)]
218#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
219pub enum ObjectOut {
220    /// Same definition as in ObjectIn.
221    NotExist,
222
223    /// Any written object, including all of mutated, created, unwrapped today.
224    ObjectWrite { digest: ObjectDigest, owner: Owner },
225
226    /// Packages writes need to be tracked separately with version because
227    /// we don't use lamport version for package publish and upgrades.
228    PackageWrite {
229        version: Version,
230        digest: ObjectDigest,
231    },
232}
233
234/// Defines what happened to an ObjectId during execution
235///
236/// # BCS
237///
238/// The BCS serialized form for this type is defined by the following ABNF:
239///
240/// ```text
241/// id-operation =  id-operation-none
242///              =/ id-operation-created
243///              =/ id-operation-deleted
244///
245/// id-operation-none       = %x00
246/// id-operation-created    = %x01
247/// id-operation-deleted    = %x02
248/// ```
249#[derive(Eq, PartialEq, Copy, Clone, Debug)]
250#[cfg_attr(
251    feature = "serde",
252    derive(serde_derive::Serialize, serde_derive::Deserialize),
253    serde(rename_all = "lowercase")
254)]
255#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
256pub enum IdOperation {
257    None,
258    Created,
259    Deleted,
260}
261
262impl TransactionEffectsV2 {
263    /// The status of the execution
264    pub fn status(&self) -> &ExecutionStatus {
265        &self.status
266    }
267
268    /// The epoch when this transaction was executed.
269    pub fn epoch(&self) -> EpochId {
270        self.epoch
271    }
272
273    /// The gas used in this transaction.
274    pub fn gas_summary(&self) -> &GasCostSummary {
275        &self.gas_used
276    }
277}
278
279#[cfg(feature = "serde")]
280#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
281mod serialization {
282    use serde::Deserialize;
283    use serde::Deserializer;
284    use serde::Serialize;
285    use serde::Serializer;
286
287    use super::*;
288
289    #[derive(serde_derive::Serialize)]
290    struct ReadableTransactionEffectsV2Ref<'a> {
291        #[serde(flatten)]
292        status: &'a ExecutionStatus,
293        #[serde(with = "crate::_serde::ReadableDisplay")]
294        epoch: &'a EpochId,
295        gas_used: &'a GasCostSummary,
296        transaction_digest: &'a TransactionDigest,
297        gas_object_index: &'a Option<u32>,
298        events_digest: &'a Option<TransactionEventsDigest>,
299        dependencies: &'a Vec<TransactionDigest>,
300        #[serde(with = "crate::_serde::ReadableDisplay")]
301        lamport_version: &'a Version,
302        changed_objects: &'a Vec<ChangedObject>,
303        unchanged_shared_objects: &'a Vec<UnchangedSharedObject>,
304        auxiliary_data_digest: &'a Option<EffectsAuxiliaryDataDigest>,
305    }
306
307    #[derive(serde_derive::Deserialize)]
308    struct ReadableTransactionEffectsV2 {
309        #[serde(flatten)]
310        status: ExecutionStatus,
311        #[serde(with = "crate::_serde::ReadableDisplay")]
312        epoch: EpochId,
313        gas_used: GasCostSummary,
314        transaction_digest: TransactionDigest,
315        gas_object_index: Option<u32>,
316        events_digest: Option<TransactionEventsDigest>,
317        dependencies: Vec<TransactionDigest>,
318        #[serde(with = "crate::_serde::ReadableDisplay")]
319        lamport_version: Version,
320        changed_objects: Vec<ChangedObject>,
321        unchanged_shared_objects: Vec<UnchangedSharedObject>,
322        auxiliary_data_digest: Option<EffectsAuxiliaryDataDigest>,
323    }
324
325    #[derive(serde_derive::Serialize)]
326    struct BinaryTransactionEffectsV2Ref<'a> {
327        status: &'a ExecutionStatus,
328        epoch: &'a EpochId,
329        gas_used: &'a GasCostSummary,
330        transaction_digest: &'a TransactionDigest,
331        gas_object_index: &'a Option<u32>,
332        events_digest: &'a Option<TransactionEventsDigest>,
333        dependencies: &'a Vec<TransactionDigest>,
334        lamport_version: &'a Version,
335        changed_objects: &'a Vec<ChangedObject>,
336        unchanged_shared_objects: &'a Vec<UnchangedSharedObject>,
337        auxiliary_data_digest: &'a Option<EffectsAuxiliaryDataDigest>,
338    }
339
340    #[derive(serde_derive::Deserialize)]
341    struct BinaryTransactionEffectsV2 {
342        status: ExecutionStatus,
343        epoch: EpochId,
344        gas_used: GasCostSummary,
345        transaction_digest: TransactionDigest,
346        gas_object_index: Option<u32>,
347        events_digest: Option<TransactionEventsDigest>,
348        dependencies: Vec<TransactionDigest>,
349        lamport_version: Version,
350        changed_objects: Vec<ChangedObject>,
351        unchanged_shared_objects: Vec<UnchangedSharedObject>,
352        auxiliary_data_digest: Option<EffectsAuxiliaryDataDigest>,
353    }
354
355    impl Serialize for TransactionEffectsV2 {
356        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
357        where
358            S: Serializer,
359        {
360            let Self {
361                status,
362                epoch,
363                gas_used,
364                transaction_digest,
365                gas_object_index,
366                events_digest,
367                dependencies,
368                lamport_version,
369                changed_objects,
370                unchanged_shared_objects,
371                auxiliary_data_digest,
372            } = self;
373            if serializer.is_human_readable() {
374                let readable = ReadableTransactionEffectsV2Ref {
375                    status,
376                    epoch,
377                    gas_used,
378                    transaction_digest,
379                    gas_object_index,
380                    events_digest,
381                    dependencies,
382                    lamport_version,
383                    changed_objects,
384                    unchanged_shared_objects,
385                    auxiliary_data_digest,
386                };
387                readable.serialize(serializer)
388            } else {
389                let binary = BinaryTransactionEffectsV2Ref {
390                    status,
391                    epoch,
392                    gas_used,
393                    transaction_digest,
394                    gas_object_index,
395                    events_digest,
396                    dependencies,
397                    lamport_version,
398                    changed_objects,
399                    unchanged_shared_objects,
400                    auxiliary_data_digest,
401                };
402                binary.serialize(serializer)
403            }
404        }
405    }
406
407    impl<'de> Deserialize<'de> for TransactionEffectsV2 {
408        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
409        where
410            D: Deserializer<'de>,
411        {
412            if deserializer.is_human_readable() {
413                let ReadableTransactionEffectsV2 {
414                    status,
415                    epoch,
416                    gas_used,
417                    transaction_digest,
418                    gas_object_index,
419                    events_digest,
420                    dependencies,
421                    lamport_version,
422                    changed_objects,
423                    unchanged_shared_objects,
424                    auxiliary_data_digest,
425                } = Deserialize::deserialize(deserializer)?;
426                Ok(Self {
427                    status,
428                    epoch,
429                    gas_used,
430                    transaction_digest,
431                    gas_object_index,
432                    events_digest,
433                    dependencies,
434                    lamport_version,
435                    changed_objects,
436                    unchanged_shared_objects,
437                    auxiliary_data_digest,
438                })
439            } else {
440                let BinaryTransactionEffectsV2 {
441                    status,
442                    epoch,
443                    gas_used,
444                    transaction_digest,
445                    gas_object_index,
446                    events_digest,
447                    dependencies,
448                    lamport_version,
449                    changed_objects,
450                    unchanged_shared_objects,
451                    auxiliary_data_digest,
452                } = Deserialize::deserialize(deserializer)?;
453                Ok(Self {
454                    status,
455                    epoch,
456                    gas_used,
457                    transaction_digest,
458                    gas_object_index,
459                    events_digest,
460                    dependencies,
461                    lamport_version,
462                    changed_objects,
463                    unchanged_shared_objects,
464                    auxiliary_data_digest,
465                })
466            }
467        }
468    }
469
470    #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
471    #[serde(tag = "kind", rename_all = "snake_case")]
472    enum ReadableUnchangedSharedKind {
473        ReadOnlyRoot {
474            #[serde(with = "crate::_serde::ReadableDisplay")]
475            version: Version,
476            digest: ObjectDigest,
477        },
478        MutateDeleted {
479            #[serde(with = "crate::_serde::ReadableDisplay")]
480            version: Version,
481        },
482        ReadDeleted {
483            #[serde(with = "crate::_serde::ReadableDisplay")]
484            version: Version,
485        },
486        Canceled {
487            #[serde(with = "crate::_serde::ReadableDisplay")]
488            version: Version,
489        },
490        PerEpochConfig,
491    }
492
493    #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
494    enum BinaryUnchangedSharedKind {
495        ReadOnlyRoot {
496            version: Version,
497            digest: ObjectDigest,
498        },
499        MutateDeleted {
500            version: Version,
501        },
502        ReadDeleted {
503            version: Version,
504        },
505        Canceled {
506            version: Version,
507        },
508        PerEpochConfig,
509    }
510
511    impl Serialize for UnchangedSharedKind {
512        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
513        where
514            S: Serializer,
515        {
516            if serializer.is_human_readable() {
517                let readable = match self.clone() {
518                    UnchangedSharedKind::ReadOnlyRoot { version, digest } => {
519                        ReadableUnchangedSharedKind::ReadOnlyRoot { version, digest }
520                    }
521                    UnchangedSharedKind::MutateDeleted { version } => {
522                        ReadableUnchangedSharedKind::MutateDeleted { version }
523                    }
524                    UnchangedSharedKind::ReadDeleted { version } => {
525                        ReadableUnchangedSharedKind::ReadDeleted { version }
526                    }
527                    UnchangedSharedKind::Canceled { version } => {
528                        ReadableUnchangedSharedKind::Canceled { version }
529                    }
530                    UnchangedSharedKind::PerEpochConfig => {
531                        ReadableUnchangedSharedKind::PerEpochConfig
532                    }
533                };
534                readable.serialize(serializer)
535            } else {
536                let binary = match self.clone() {
537                    UnchangedSharedKind::ReadOnlyRoot { version, digest } => {
538                        BinaryUnchangedSharedKind::ReadOnlyRoot { version, digest }
539                    }
540                    UnchangedSharedKind::MutateDeleted { version } => {
541                        BinaryUnchangedSharedKind::MutateDeleted { version }
542                    }
543                    UnchangedSharedKind::ReadDeleted { version } => {
544                        BinaryUnchangedSharedKind::ReadDeleted { version }
545                    }
546                    UnchangedSharedKind::Canceled { version } => {
547                        BinaryUnchangedSharedKind::Canceled { version }
548                    }
549                    UnchangedSharedKind::PerEpochConfig => {
550                        BinaryUnchangedSharedKind::PerEpochConfig
551                    }
552                };
553                binary.serialize(serializer)
554            }
555        }
556    }
557
558    impl<'de> Deserialize<'de> for UnchangedSharedKind {
559        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
560        where
561            D: Deserializer<'de>,
562        {
563            if deserializer.is_human_readable() {
564                ReadableUnchangedSharedKind::deserialize(deserializer).map(
565                    |readable| match readable {
566                        ReadableUnchangedSharedKind::ReadOnlyRoot { version, digest } => {
567                            Self::ReadOnlyRoot { version, digest }
568                        }
569                        ReadableUnchangedSharedKind::MutateDeleted { version } => {
570                            Self::MutateDeleted { version }
571                        }
572                        ReadableUnchangedSharedKind::ReadDeleted { version } => {
573                            Self::ReadDeleted { version }
574                        }
575                        ReadableUnchangedSharedKind::Canceled { version } => {
576                            Self::Canceled { version }
577                        }
578                        ReadableUnchangedSharedKind::PerEpochConfig => Self::PerEpochConfig,
579                    },
580                )
581            } else {
582                BinaryUnchangedSharedKind::deserialize(deserializer).map(|binary| match binary {
583                    BinaryUnchangedSharedKind::ReadOnlyRoot { version, digest } => {
584                        Self::ReadOnlyRoot { version, digest }
585                    }
586                    BinaryUnchangedSharedKind::MutateDeleted { version } => {
587                        Self::MutateDeleted { version }
588                    }
589                    BinaryUnchangedSharedKind::ReadDeleted { version } => {
590                        Self::ReadDeleted { version }
591                    }
592                    BinaryUnchangedSharedKind::Canceled { version } => Self::Canceled { version },
593                    BinaryUnchangedSharedKind::PerEpochConfig => Self::PerEpochConfig,
594                })
595            }
596        }
597    }
598
599    #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
600    #[serde(tag = "state", rename_all = "snake_case")]
601    enum ReadableObjectIn {
602        NotExist,
603        Exist {
604            #[serde(with = "crate::_serde::ReadableDisplay")]
605            version: Version,
606            digest: ObjectDigest,
607            owner: Owner,
608        },
609    }
610
611    #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
612    enum BinaryObjectIn {
613        NotExist,
614        Exist {
615            version: Version,
616            digest: ObjectDigest,
617            owner: Owner,
618        },
619    }
620
621    impl Serialize for ObjectIn {
622        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
623        where
624            S: Serializer,
625        {
626            if serializer.is_human_readable() {
627                let readable = match self.clone() {
628                    ObjectIn::NotExist => ReadableObjectIn::NotExist,
629                    ObjectIn::Exist {
630                        version,
631                        digest,
632                        owner,
633                    } => ReadableObjectIn::Exist {
634                        version,
635                        digest,
636                        owner,
637                    },
638                };
639                readable.serialize(serializer)
640            } else {
641                let binary = match self.clone() {
642                    ObjectIn::NotExist => BinaryObjectIn::NotExist,
643                    ObjectIn::Exist {
644                        version,
645                        digest,
646                        owner,
647                    } => BinaryObjectIn::Exist {
648                        version,
649                        digest,
650                        owner,
651                    },
652                };
653                binary.serialize(serializer)
654            }
655        }
656    }
657
658    impl<'de> Deserialize<'de> for ObjectIn {
659        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
660        where
661            D: Deserializer<'de>,
662        {
663            if deserializer.is_human_readable() {
664                ReadableObjectIn::deserialize(deserializer).map(|readable| match readable {
665                    ReadableObjectIn::NotExist => Self::NotExist,
666                    ReadableObjectIn::Exist {
667                        version,
668                        digest,
669                        owner,
670                    } => Self::Exist {
671                        version,
672                        digest,
673                        owner,
674                    },
675                })
676            } else {
677                BinaryObjectIn::deserialize(deserializer).map(|binary| match binary {
678                    BinaryObjectIn::NotExist => Self::NotExist,
679                    BinaryObjectIn::Exist {
680                        version,
681                        digest,
682                        owner,
683                    } => Self::Exist {
684                        version,
685                        digest,
686                        owner,
687                    },
688                })
689            }
690        }
691    }
692
693    #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
694    #[serde(tag = "state", rename_all = "snake_case")]
695    enum ReadableObjectOut {
696        NotExist,
697        ObjectWrite {
698            digest: ObjectDigest,
699            owner: Owner,
700        },
701        PackageWrite {
702            #[serde(with = "crate::_serde::ReadableDisplay")]
703            version: Version,
704            digest: ObjectDigest,
705        },
706    }
707
708    #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
709    enum BinaryObjectOut {
710        NotExist,
711        ObjectWrite {
712            digest: ObjectDigest,
713            owner: Owner,
714        },
715        PackageWrite {
716            #[serde(with = "crate::_serde::ReadableDisplay")]
717            version: Version,
718            digest: ObjectDigest,
719        },
720    }
721
722    impl Serialize for ObjectOut {
723        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
724        where
725            S: Serializer,
726        {
727            if serializer.is_human_readable() {
728                let readable = match self.clone() {
729                    ObjectOut::NotExist => ReadableObjectOut::NotExist,
730                    ObjectOut::ObjectWrite { digest, owner } => {
731                        ReadableObjectOut::ObjectWrite { digest, owner }
732                    }
733                    ObjectOut::PackageWrite { version, digest } => {
734                        ReadableObjectOut::PackageWrite { version, digest }
735                    }
736                };
737                readable.serialize(serializer)
738            } else {
739                let binary = match self.clone() {
740                    ObjectOut::NotExist => BinaryObjectOut::NotExist,
741                    ObjectOut::ObjectWrite { digest, owner } => {
742                        BinaryObjectOut::ObjectWrite { digest, owner }
743                    }
744                    ObjectOut::PackageWrite { version, digest } => {
745                        BinaryObjectOut::PackageWrite { version, digest }
746                    }
747                };
748                binary.serialize(serializer)
749            }
750        }
751    }
752
753    impl<'de> Deserialize<'de> for ObjectOut {
754        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
755        where
756            D: Deserializer<'de>,
757        {
758            if deserializer.is_human_readable() {
759                ReadableObjectOut::deserialize(deserializer).map(|readable| match readable {
760                    ReadableObjectOut::NotExist => Self::NotExist,
761                    ReadableObjectOut::ObjectWrite { digest, owner } => {
762                        Self::ObjectWrite { digest, owner }
763                    }
764                    ReadableObjectOut::PackageWrite { version, digest } => {
765                        Self::PackageWrite { version, digest }
766                    }
767                })
768            } else {
769                BinaryObjectOut::deserialize(deserializer).map(|binary| match binary {
770                    BinaryObjectOut::NotExist => Self::NotExist,
771                    BinaryObjectOut::ObjectWrite { digest, owner } => {
772                        Self::ObjectWrite { digest, owner }
773                    }
774                    BinaryObjectOut::PackageWrite { version, digest } => {
775                        Self::PackageWrite { version, digest }
776                    }
777                })
778            }
779        }
780    }
781}