Skip to main content

borderless_pkg/
dto.rs

1//! Digital Transfer Object (DTO) definitions
2//!
3//! Since some serializers (like postcard e.g.) cannot work with serde's skip feature (e.g. `#[serde(skip_serializing_if = "Option::is_none")]`),
4//! the base definitions in this crate do not use this feature. However, when working with API's we typically encode things as jsons
5//! (and the json serializer does not have this problem). In such cases, including fields that are not present creates a lot of waste in the encoding:
6//! ```json
7//! {
8//!  "name": "flipper-contract",
9//!  "app_name": null,
10//!  "app_module": null,
11//!  "capabilities": null,
12//!  "pkg_type": "contract",
13//!  "meta": {
14//!    "authors": [],
15//!    "description": null,
16//!    "documentation": null,
17//!    "license": null,
18//!    "repository": null
19//!  }
20//!  // ...
21//! }
22//! ````
23//! This is where the DTO's come in handy. They are an almost identical representation, but with additional serde features, that make working with
24//! APIs more easy and produce a cleaner user experience.
25//!
26//! Please note: Not every datatype requires a DTO version, since this is only necessary if some conditional serialization for defaultable fields is involved.
27
28use super::*;
29
30/// DTO for [`Registry`]
31#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct RegistryDto {
33    #[serde(default)]
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub registry_type: Option<String>, // NOTE: We can expand on that later
36    pub registry_hostname: String,
37    pub namespace: String,
38}
39
40impl From<RegistryDto> for Registry {
41    fn from(value: RegistryDto) -> Self {
42        Self {
43            registry_type: value.registry_type,
44            registry_hostname: value.registry_hostname,
45            namespace: value.namespace,
46        }
47    }
48}
49
50impl From<Registry> for RegistryDto {
51    fn from(value: Registry) -> Self {
52        Self {
53            registry_type: value.registry_type,
54            registry_hostname: value.registry_hostname,
55            namespace: value.namespace,
56        }
57    }
58}
59
60/// DTO for [`PkgMeta`]
61#[derive(Debug, Clone, Default, Serialize, Deserialize)]
62pub struct PkgMetaDto {
63    /// Authors of the package
64    #[serde(default)]
65    #[serde(skip_serializing_if = "Vec::is_empty")]
66    pub authors: Vec<Author>,
67
68    #[serde(default)]
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub description: Option<String>,
71
72    #[serde(default)]
73    #[serde(skip_serializing_if = "Option::is_none")]
74    pub documentation: Option<String>,
75
76    #[serde(default)]
77    #[serde(skip_serializing_if = "Option::is_none")]
78    pub license: Option<String>,
79
80    #[serde(default)]
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub repository: Option<String>,
83}
84
85impl From<PkgMetaDto> for PkgMeta {
86    fn from(value: PkgMetaDto) -> Self {
87        Self {
88            authors: value.authors,
89            description: value.description,
90            documentation: value.documentation,
91            license: value.license,
92            repository: value.repository,
93        }
94    }
95}
96
97impl From<PkgMeta> for PkgMetaDto {
98    fn from(value: PkgMeta) -> Self {
99        Self {
100            authors: value.authors,
101            description: value.description,
102            documentation: value.documentation,
103            license: value.license,
104            repository: value.repository,
105        }
106    }
107}
108
109impl PkgMetaDto {
110    /// Returns true, if all fields are either empty or set to their default value
111    pub fn is_empty(&self) -> bool {
112        self.authors.is_empty()
113            && self.description.is_none()
114            && self.documentation.is_none()
115            && self.license.is_none()
116            && self.repository.is_none()
117    }
118}
119
120/// DTO for [`WasmPkg`]
121#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct WasmPkgDto {
123    pub name: String,
124
125    #[serde(default)]
126    #[serde(skip_serializing_if = "Option::is_none")]
127    pub app_name: Option<String>,
128
129    #[serde(default)]
130    #[serde(skip_serializing_if = "Option::is_none")]
131    pub app_module: Option<String>,
132
133    #[serde(default)]
134    #[serde(skip_serializing_if = "Option::is_none")]
135    pub capabilities: Option<Capabilities>,
136
137    pub pkg_type: PkgType,
138
139    #[serde(default)]
140    #[serde(skip_serializing_if = "PkgMetaDto::is_empty")]
141    pub meta: PkgMetaDto,
142
143    pub source: Source,
144}
145
146impl From<WasmPkgDto> for WasmPkg {
147    fn from(value: WasmPkgDto) -> Self {
148        Self {
149            name: value.name,
150            app_name: value.app_name,
151            app_module: value.app_module,
152            capabilities: value.capabilities,
153            pkg_type: value.pkg_type,
154            meta: value.meta.into(),
155            source: value.source,
156        }
157    }
158}
159
160impl From<WasmPkg> for WasmPkgDto {
161    fn from(value: WasmPkg) -> Self {
162        Self {
163            name: value.name,
164            app_name: value.app_name,
165            app_module: value.app_module,
166            capabilities: value.capabilities,
167            pkg_type: value.pkg_type,
168            meta: value.meta.into(),
169            source: value.source,
170        }
171    }
172}
173
174/// DTO for [`WasmPkgNoSource`]
175#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct WasmPkgNoSourceDto {
177    pub name: String,
178
179    #[serde(default)]
180    #[serde(skip_serializing_if = "Option::is_none")]
181    pub app_name: Option<String>,
182
183    #[serde(default)]
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub app_module: Option<String>,
186
187    #[serde(default)]
188    #[serde(skip_serializing_if = "Option::is_none")]
189    pub capabilities: Option<Capabilities>,
190
191    pub pkg_type: PkgType,
192
193    #[serde(default)]
194    #[serde(skip_serializing_if = "PkgMetaDto::is_empty")]
195    pub meta: PkgMetaDto,
196}
197
198impl From<WasmPkgNoSourceDto> for WasmPkgNoSource {
199    fn from(value: WasmPkgNoSourceDto) -> Self {
200        Self {
201            name: value.name,
202            app_name: value.app_name,
203            app_module: value.app_module,
204            capabilities: value.capabilities,
205            pkg_type: value.pkg_type,
206            meta: value.meta.into(),
207        }
208    }
209}
210
211impl From<WasmPkgNoSource> for WasmPkgNoSourceDto {
212    fn from(value: WasmPkgNoSource) -> Self {
213        Self {
214            name: value.name,
215            app_name: value.app_name,
216            app_module: value.app_module,
217            capabilities: value.capabilities,
218            pkg_type: value.pkg_type,
219            meta: value.meta.into(),
220        }
221    }
222}