ya_agreement_utils/
typed_props.rs

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// golem.inf.cpu.architecture
171// golem.inf.cpu.cores
172// golem.inf.cpu.threads
173
174#[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// golem.inf.mem.gib
214// golem.inf.storage.gib
215// R: golem.activity.timeout_secs
216
217// golem.com.payment.scheme="payu"
218// golem.com.payment.scheme.payu.debit-note.interval-sec?=3600
219// golem.com.pricing.model="linear"
220// golem.com.pricing.model.linear.coeffs=[0.3, 0]
221// golem.usage.vector=["golem.usage.duration_sec"]
222
223#[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}