1use crate::OfferTemplate;
2
3use serde_json::Value;
4
5pub trait OfferBuilder {
6 fn build(&self) -> Value;
7}
8
9#[derive(Clone)]
10pub struct OfferDefinition {
11 pub node_info: NodeInfo,
12 pub srv_info: ServiceInfo,
13 pub com_info: ComInfo,
14 pub offer: OfferTemplate,
15}
16
17impl OfferDefinition {
18 pub fn into_json(self) -> Value {
19 self.into_template().properties
20 }
21
22 pub fn into_template(self) -> OfferTemplate {
23 let mut base = serde_json::Map::new();
24 self.node_info.write_json(&mut base);
25 self.srv_info.write_json(&mut base);
26 self.com_info.write_json(&mut base);
27
28 let template = OfferTemplate::new(serde_json::json!({ "golem": base }));
29 template.patch(self.offer)
30 }
31}
32
33#[derive(Clone)]
34pub struct NodeInfo {
35 pub name: Option<String>,
36 pub subnet: Option<String>,
37 pub geo_country_code: Option<String>,
38 pub is_public: bool,
39}
40
41impl NodeInfo {
42 pub fn with_name(name: impl Into<String>) -> Self {
43 NodeInfo {
44 name: Some(name.into()),
45 geo_country_code: None,
46 subnet: None,
47 is_public: false,
48 }
49 }
50
51 pub fn with_subnet(&mut self, subnet: String) -> &mut Self {
52 self.subnet = Some(subnet);
53 self
54 }
55
56 fn write_json(self, map: &mut serde_json::Map<String, Value>) {
57 let mut node = serde_json::Map::new();
58 if let Some(name) = self.name {
59 let _ = node.insert("id".into(), serde_json::json!({ "name": name }));
60 }
61 if let Some(cc) = self.geo_country_code {
62 let _ = node.insert("geo".into(), serde_json::json!({ "country_code": cc }));
63 }
64 if let Some(subnet) = self.subnet {
65 let _ = node.insert("debug".into(), serde_json::json!({ "subnet": subnet }));
66 }
67 let _ = node.insert(
68 "net".into(),
69 serde_json::json!({"is-public": self.is_public}),
70 );
71 map.insert("node".into(), node.into());
72 }
73}
74
75#[derive(Clone)]
76pub struct ServiceInfo {
77 inf: InfNodeInfo,
78 exeunit_info: Value,
79 multi_activity: bool,
80 payload_manifest: bool,
81}
82
83impl ServiceInfo {
84 pub fn new(inf: InfNodeInfo, exeunit_info: Value) -> ServiceInfo {
85 ServiceInfo {
86 inf,
87 exeunit_info,
88 multi_activity: true,
89 payload_manifest: true,
90 }
91 }
92
93 pub fn support_multi_activity(self, multi_activity: bool) -> Self {
94 Self {
95 multi_activity,
96 ..self
97 }
98 }
99
100 pub fn support_payload_manifest(self, payload_manifest: bool) -> Self {
101 Self {
102 payload_manifest,
103 ..self
104 }
105 }
106
107 fn write_json(self, map: &mut serde_json::Map<String, Value>) {
108 self.inf.write_json(map);
109 let _ = map.insert("runtime".into(), self.exeunit_info);
110
111 let srv_map = serde_json::json!({
112 "caps": {
113 "multi-activity": self.multi_activity,
114 "payload-manifest": self.payload_manifest
115 },
116 });
117 let _ = map.insert("srv".into(), srv_map);
118 }
119}
120
121#[derive(Default, Clone)]
122pub struct InfNodeInfo {
123 mem_gib: Option<f64>,
124 storage_gib: Option<f64>,
125 cpu_info: Option<CpuInfo>,
126}
127
128impl InfNodeInfo {
129 #[deprecated(note = "Please use Default::default instead")]
130 pub fn new() -> Self {
131 Self::default()
132 }
133
134 pub fn with_mem(self, mem_gib: f64) -> Self {
135 Self {
136 mem_gib: Some(mem_gib),
137 ..self
138 }
139 }
140
141 pub fn with_storage(self, storage_gib: f64) -> Self {
142 Self {
143 storage_gib: Some(storage_gib),
144 ..self
145 }
146 }
147
148 pub fn with_cpu(self, cpu_info: CpuInfo) -> Self {
149 Self {
150 cpu_info: Some(cpu_info),
151 ..self
152 }
153 }
154
155 fn write_json(self, map: &mut serde_json::Map<String, Value>) {
156 let mut inf_map = serde_json::Map::new();
157 if let Some(mem) = self.mem_gib {
158 let _ = inf_map.insert("mem".to_string(), serde_json::json!({ "gib": mem }));
159 }
160 if let Some(storage) = self.storage_gib {
161 let _ = inf_map.insert("storage".to_string(), serde_json::json!({ "gib": storage }));
162 }
163 if let Some(cpu) = self.cpu_info {
164 cpu.write_json(&mut inf_map);
165 }
166 let _ = map.insert("inf".to_string(), inf_map.into());
167 }
168}
169
170#[derive(Default, Clone)]
175pub struct CpuInfo {
176 pub architecture: String,
177 pub cores: u32,
178 pub threads: u32,
179}
180
181impl CpuInfo {
182 pub fn for_wasm(cores: u32) -> Self {
183 CpuInfo {
184 architecture: "wasm32".to_string(),
185 cores,
186 threads: cores,
187 }
188 }
189
190 fn write_json(self, map: &mut serde_json::Map<String, Value>) {
191 let _ = map.insert(
192 "cpu".to_string(),
193 serde_json::json!({
194 "architecture": self.architecture,
195 "cores": self.cores,
196 "threads": self.threads
197 }),
198 );
199 }
200}
201
202#[derive(Default, Clone)]
203pub struct ComInfo {
204 pub params: Value,
205}
206
207impl ComInfo {
208 fn write_json(self, map: &mut serde_json::Map<String, Value>) {
209 let _ = map.insert("com".to_string(), self.params);
210 }
211}
212
213#[cfg(test)]
224mod test {
225 use super::*;
226
227 #[test]
228 fn offer_json_test() {
229 let offer = OfferDefinition {
230 node_info: NodeInfo::with_name("dany"),
231 srv_info: ServiceInfo {
232 inf: InfNodeInfo::default().with_mem(5.0).with_storage(50.0),
233 exeunit_info: serde_json::json!({"wasm.wasi.version@v".to_string(): "0.9.0".to_string()}),
234 multi_activity: false,
235 payload_manifest: true,
236 },
237 com_info: Default::default(),
238 offer: OfferTemplate::default(),
239 };
240
241 let offer = serde_json::to_string_pretty(&offer.into_json()).unwrap();
242
243 let expected_offer = r#"
244{
245 "golem.com": null,
246 "golem.inf.mem.gib": 5.0,
247 "golem.inf.storage.gib": 50.0,
248 "golem.node.id.name": "dany",
249 "golem.node.net.is-public": false,
250 "golem.runtime.wasm.wasi.version@v": "0.9.0",
251 "golem.srv.caps.multi-activity": false,
252 "golem.srv.caps.payload-manifest": true
253}
254"#;
255
256 assert_eq!(expected_offer.trim(), offer)
257 }
258}