iota_sdk_types/effects/
v1.rs

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