locutus_stdlib/
versioning.rs1use std::fmt;
2use std::fmt::{Display, Formatter};
3use std::fs::File;
4use std::io::{Cursor, Read};
5use std::path::Path;
6use std::sync::Arc;
7
8use byteorder::{BigEndian, ReadBytesExt};
9use semver::Version;
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12
13use crate::prelude::WrappedContract;
14use crate::{
15 contract_interface::ContractKey,
16 prelude::{ContractCode, Parameters, TryFromTsStd, WsApiError},
17};
18
19#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
22pub enum ContractContainer {
23 Wasm(WasmAPIVersion),
24}
25
26impl ContractContainer {
27 pub(crate) fn get_contract_data_from_fs(path: &Path) -> Result<Vec<u8>, std::io::Error> {
29 let mut contract_file = File::open(path)?;
30 let mut contract_data = if let Ok(md) = contract_file.metadata() {
31 Vec::with_capacity(md.len() as usize)
32 } else {
33 Vec::new()
34 };
35 contract_file.read_to_end(&mut contract_data)?;
36 Ok(contract_data)
37 }
38
39 pub fn key(&self) -> ContractKey {
41 match self {
42 Self::Wasm(WasmAPIVersion::V1(contract_v1)) => contract_v1.key().clone(),
43 }
44 }
45
46 pub fn params(&self) -> Parameters<'static> {
48 match self {
49 Self::Wasm(WasmAPIVersion::V1(contract_v1)) => contract_v1.params().clone(),
50 }
51 }
52
53 pub fn data(&self) -> Vec<u8> {
55 match self {
56 Self::Wasm(WasmAPIVersion::V1(contract_v1)) => contract_v1.clone().try_into().unwrap(),
57 }
58 }
59}
60
61impl Display for ContractContainer {
62 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
63 match self {
64 ContractContainer::Wasm(wasm_version) => {
65 write!(f, "Wasm container {wasm_version}")
66 }
67 }
68 }
69}
70
71impl<'a> TryFrom<(&'a Path, Parameters<'static>)> for ContractContainer {
72 type Error = std::io::Error;
73
74 fn try_from((path, params): (&'a Path, Parameters<'static>)) -> Result<Self, Self::Error> {
75 let mut contract_data =
76 Cursor::new(ContractContainer::get_contract_data_from_fs(path).unwrap());
77
78 let version_size = contract_data
80 .read_u32::<BigEndian>()
81 .map_err(|_| std::io::ErrorKind::InvalidData)?;
82 let mut version_data = vec![0; version_size as usize];
83 contract_data
84 .read_exact(&mut version_data)
85 .map_err(|_| std::io::ErrorKind::InvalidData)?;
86 let version: Version = serde_json::from_slice(version_data.as_slice())
87 .map_err(|_| std::io::ErrorKind::InvalidData)?;
88
89 let mut code_data: Vec<u8> = vec![];
91 contract_data
92 .read_to_end(&mut code_data)
93 .map_err(|_| std::io::ErrorKind::InvalidData)?;
94 let contract_code = Arc::new(ContractCode::from(code_data));
95
96 match version.to_string().as_str() {
97 "0.0.1" => Ok(ContractContainer::Wasm(WasmAPIVersion::V1(
98 WrappedContract::new(contract_code, params),
99 ))),
100 _ => Err(std::io::ErrorKind::InvalidData.into()),
101 }
102 }
103}
104
105impl TryFromTsStd<&rmpv::Value> for ContractContainer {
106 fn try_decode(value: &rmpv::Value) -> Result<Self, WsApiError> {
107 let container_map: HashMap<&str, &rmpv::Value> = match value.as_map() {
108 Some(map_value) => HashMap::from_iter(
109 map_value
110 .iter()
111 .map(|(key, val)| (key.as_str().unwrap(), val)),
112 ),
113 _ => {
114 return Err(WsApiError::MsgpackDecodeError {
115 cause: "Failed decoding ContractContainer, input value is not a map"
116 .to_string(),
117 })
118 }
119 };
120
121 let container_version = match container_map.get("version") {
122 Some(version_value) => (*version_value).as_str().unwrap(),
123 _ => {
124 return Err(WsApiError::MsgpackDecodeError {
125 cause: "Failed decoding ContractContainer, version not found".to_string(),
126 })
127 }
128 };
129
130 match container_version {
131 "V1" => {
132 let contract = WrappedContract::try_decode(value).map_err(|e| {
133 WsApiError::MsgpackDecodeError {
134 cause: format!("{e}"),
135 }
136 })?;
137 Ok(ContractContainer::Wasm(WasmAPIVersion::V1(contract)))
138 }
139 _ => unreachable!(),
140 }
141 }
142}
143
144#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
146pub enum WasmAPIVersion {
147 V1(WrappedContract),
148}
149
150impl Display for WasmAPIVersion {
151 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
152 match self {
153 WasmAPIVersion::V1(contract_v1) => {
154 write!(f, "version 0.0.1 of contract {contract_v1}")
155 }
156 }
157 }
158}
159
160impl From<ContractContainer> for Version {
161 fn from(contract: ContractContainer) -> Version {
162 match contract {
163 ContractContainer::Wasm(WasmAPIVersion::V1(_)) => Version::new(0, 0, 1),
164 }
165 }
166}