casper_types/transaction/
package_identifier.rs1use alloc::{string::String, vec::Vec};
2use core::fmt::{self, Display, Formatter};
3
4#[cfg(feature = "datasize")]
5use datasize::DataSize;
6use hex_fmt::HexFmt;
7#[cfg(any(feature = "testing", test))]
8use rand::Rng;
9#[cfg(feature = "json-schema")]
10use schemars::JsonSchema;
11use serde::{Deserialize, Serialize};
12
13#[cfg(any(feature = "testing", test))]
14use crate::testing::TestRng;
15use crate::{
16 bytesrepr::{self, FromBytes, ToBytes, U8_SERIALIZED_LENGTH},
17 EntityVersion, PackageHash,
18};
19#[cfg(doc)]
20use crate::{ExecutableDeployItem, TransactionTarget};
21
22const HASH_TAG: u8 = 0;
23const NAME_TAG: u8 = 1;
24
25#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize, Debug)]
28#[cfg_attr(feature = "datasize", derive(DataSize))]
29#[cfg_attr(
30 feature = "json-schema",
31 derive(JsonSchema),
32 schemars(
33 description = "Identifier for the package object within a `Stored` transaction target or \
34 an `ExecutableDeployItem`."
35 )
36)]
37pub enum PackageIdentifier {
38 Hash {
40 package_hash: PackageHash,
42 version: Option<EntityVersion>,
46 },
47 Name {
49 name: String,
51 version: Option<EntityVersion>,
55 },
56}
57
58impl PackageIdentifier {
59 pub fn version(&self) -> Option<EntityVersion> {
63 match self {
64 PackageIdentifier::Hash { version, .. } | PackageIdentifier::Name { version, .. } => {
65 *version
66 }
67 }
68 }
69
70 #[cfg(any(feature = "testing", test))]
72 pub fn random(rng: &mut TestRng) -> Self {
73 let version = rng.gen::<bool>().then(|| rng.gen::<EntityVersion>());
74 if rng.gen() {
75 PackageIdentifier::Hash {
76 package_hash: PackageHash::new(rng.gen()),
77 version,
78 }
79 } else {
80 PackageIdentifier::Name {
81 name: rng.random_string(1..21),
82 version,
83 }
84 }
85 }
86}
87
88impl Display for PackageIdentifier {
89 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
90 match self {
91 PackageIdentifier::Hash {
92 package_hash: contract_package_hash,
93 version: Some(ver),
94 } => write!(
95 formatter,
96 "package-id({}, version {})",
97 HexFmt(contract_package_hash),
98 ver
99 ),
100 PackageIdentifier::Hash {
101 package_hash: contract_package_hash,
102 ..
103 } => write!(
104 formatter,
105 "package-id({}, latest)",
106 HexFmt(contract_package_hash),
107 ),
108 PackageIdentifier::Name {
109 name,
110 version: Some(ver),
111 } => write!(formatter, "package-id({}, version {})", name, ver),
112 PackageIdentifier::Name { name, .. } => {
113 write!(formatter, "package-id({}, latest)", name)
114 }
115 }
116 }
117}
118
119impl ToBytes for PackageIdentifier {
120 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
121 match self {
122 PackageIdentifier::Hash {
123 package_hash,
124 version,
125 } => {
126 HASH_TAG.write_bytes(writer)?;
127 package_hash.write_bytes(writer)?;
128 version.write_bytes(writer)
129 }
130 PackageIdentifier::Name { name, version } => {
131 NAME_TAG.write_bytes(writer)?;
132 name.write_bytes(writer)?;
133 version.write_bytes(writer)
134 }
135 }
136 }
137
138 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
139 let mut buffer = bytesrepr::allocate_buffer(self)?;
140 self.write_bytes(&mut buffer)?;
141 Ok(buffer)
142 }
143
144 fn serialized_length(&self) -> usize {
145 U8_SERIALIZED_LENGTH
146 + match self {
147 PackageIdentifier::Hash {
148 package_hash,
149 version,
150 } => package_hash.serialized_length() + version.serialized_length(),
151 PackageIdentifier::Name { name, version } => {
152 name.serialized_length() + version.serialized_length()
153 }
154 }
155 }
156}
157
158impl FromBytes for PackageIdentifier {
159 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
160 let (tag, remainder) = u8::from_bytes(bytes)?;
161 match tag {
162 HASH_TAG => {
163 let (package_hash, remainder) = PackageHash::from_bytes(remainder)?;
164 let (version, remainder) = Option::<EntityVersion>::from_bytes(remainder)?;
165 let id = PackageIdentifier::Hash {
166 package_hash,
167 version,
168 };
169 Ok((id, remainder))
170 }
171 NAME_TAG => {
172 let (name, remainder) = String::from_bytes(remainder)?;
173 let (version, remainder) = Option::<EntityVersion>::from_bytes(remainder)?;
174 let id = PackageIdentifier::Name { name, version };
175 Ok((id, remainder))
176 }
177 _ => Err(bytesrepr::Error::Formatting),
178 }
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185
186 #[test]
187 fn bytesrepr_roundtrip() {
188 let rng = &mut TestRng::new();
189 bytesrepr::test_serialization_roundtrip(&PackageIdentifier::random(rng));
190 }
191}