use borderless_hash::Hash256;
use git_info::GitInfo;
use serde::{Deserialize, Serialize};
pub use crate::author::Author;
use crate::dto::*;
pub use crate::semver::SemVer;
mod author;
pub mod dto;
pub mod git_info;
pub mod semver;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Registry {
#[serde(default)]
pub registry_type: Option<String>,
pub registry_hostname: String,
pub namespace: String,
}
impl Registry {
pub fn into_dto(self) -> RegistryDto {
self.into()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum SourceType {
Registry { registry: Registry },
Wasm {
#[serde(with = "code_as_base64")]
wasm: Vec<u8>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
git_info: Option<GitInfo>,
},
}
mod code_as_base64 {
use base64::prelude::*;
use serde::{Deserialize, Serialize};
use serde::{Deserializer, Serializer};
pub fn serialize<S: Serializer>(v: &Vec<u8>, s: S) -> Result<S::Ok, S::Error> {
let base64 = BASE64_STANDARD.encode(v);
String::serialize(&base64, s)
}
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
let b64 = String::deserialize(d)?;
BASE64_STANDARD
.decode(b64.as_bytes())
.map_err(serde::de::Error::custom)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Source {
pub version: SemVer,
pub digest: Hash256,
#[serde(flatten)]
pub code: SourceType,
}
impl Source {
pub fn flatten(self) -> SourceFlattened {
let (registry, wasm, git_info) = match self.code {
SourceType::Registry { registry } => (Some(registry), None, None),
SourceType::Wasm { wasm, git_info } => (None, Some(wasm), git_info),
};
SourceFlattened {
version: self.version,
digest: self.digest,
registry,
wasm,
git_info,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SourceFlattened {
pub version: SemVer,
pub digest: Hash256,
#[serde(default)]
registry: Option<Registry>,
#[serde(default)]
#[serde(with = "serde_bytes")]
wasm: Option<Vec<u8>>,
#[serde(default)]
git_info: Option<GitInfo>,
}
impl SourceFlattened {
pub fn unflatten(self) -> Source {
let code = match (self.registry, self.wasm) {
(Some(registry), None) => SourceType::Registry { registry },
(None, Some(wasm)) => SourceType::Wasm { wasm, git_info: self.git_info },
_ => panic!("Failed to convert into `Source` - either `registry` or `wasm` must be set, but neither both or none"),
};
Source {
version: self.version,
digest: self.digest,
code,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct PkgMeta {
#[serde(default)]
pub authors: Vec<Author>,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub documentation: Option<String>,
#[serde(default)]
pub license: Option<String>,
#[serde(default)]
pub repository: Option<String>,
}
impl PkgMeta {
pub fn into_dto(self) -> PkgMetaDto {
self.into()
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum PkgType {
Contract,
Agent,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Capabilities {
pub network: bool,
pub websocket: bool,
pub url_whitelist: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WasmPkg {
pub name: String,
#[serde(default)]
pub app_name: Option<String>,
#[serde(default)]
pub app_module: Option<String>,
#[serde(default)]
pub capabilities: Option<Capabilities>,
pub pkg_type: PkgType,
#[serde(default)]
pub meta: PkgMeta,
pub source: Source,
}
impl WasmPkg {
pub fn into_def_and_source(self) -> (WasmPkgNoSource, Source) {
let pkg_def = WasmPkgNoSource {
name: self.name,
app_name: self.app_name,
app_module: self.app_module,
capabilities: self.capabilities,
pkg_type: self.pkg_type,
meta: self.meta,
};
let source = self.source;
(pkg_def, source)
}
pub fn from_def_and_source(pkg_def: WasmPkgNoSource, source: Source) -> Self {
Self {
name: pkg_def.name,
app_name: pkg_def.app_name,
app_module: pkg_def.app_module,
capabilities: pkg_def.capabilities,
pkg_type: pkg_def.pkg_type,
meta: pkg_def.meta,
source,
}
}
pub fn into_dto(self) -> WasmPkgDto {
self.into()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WasmPkgNoSource {
pub name: String,
#[serde(default)]
pub app_name: Option<String>,
#[serde(default)]
pub app_module: Option<String>,
#[serde(default)]
pub capabilities: Option<Capabilities>,
pub pkg_type: PkgType,
#[serde(default)]
pub meta: PkgMeta,
}
impl WasmPkgNoSource {
pub fn into_dto(self) -> WasmPkgNoSourceDto {
self.into()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WasmPkgSigned {
#[serde(flatten)]
pub pkg: WasmPkg,
pub signature: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn source_deserialize() {
let s = r#"{
"version": "1.2.3",
"digest": "",
"wasm": "AGFzbQEAAAABnAIqYAF/"
}"#;
let source: Result<Source, _> = serde_json::from_str(s);
assert!(source.is_ok());
}
}