homestar_invocation/task/
config.rs

1//! [Invocation] configuration, typically expressed as metadata for tasks.
2//!
3//! [Invocation]: crate::Invocation
4
5use crate::{consts, Error, Unit};
6use libipld::{serde::from_ipld, Ipld};
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize};
9use std::{collections::BTreeMap, time::Duration};
10
11const FUEL_KEY: &str = "fuel";
12const MEMORY_KEY: &str = "memory";
13const TIMEOUT_KEY: &str = "time";
14
15/// Resource configuration for defining fuel quota, timeout, etc.
16#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, JsonSchema)]
17#[schemars(
18    rename = "resources",
19    description = "Resource configuration for fuel quota, memory allowance, and timeout"
20)]
21pub struct Resources {
22    fuel: Option<u64>,
23    #[schemars(description = "Memory in bytes")]
24    memory: Option<u64>,
25    #[schemars(with = "Option<u64>", description = "Timeout in milliseconds")]
26    time: Option<Duration>,
27}
28
29impl Default for Resources {
30    fn default() -> Self {
31        Self {
32            fuel: Some(u64::MAX),
33            memory: Some(consts::WASM_MAX_MEMORY),
34            time: Some(Duration::from_millis(100_000)),
35        }
36    }
37}
38
39impl Resources {
40    /// Create new [Resources] configuration.
41    pub fn new(fuel: u64, memory: u64, time: Duration) -> Self {
42        Self {
43            fuel: Some(fuel),
44            memory: Some(memory),
45            time: Some(time),
46        }
47    }
48
49    /// Get fuel limit.
50    pub fn fuel(&self) -> Option<u64> {
51        self.fuel
52    }
53
54    /// Set fuel limit.
55    pub fn set_fuel(&mut self, fuel: u64) {
56        self.fuel = Some(fuel)
57    }
58
59    /// Get timeout.
60    pub fn time(&self) -> Option<Duration> {
61        self.time
62    }
63
64    /// Set timeout.
65    pub fn set_time(&mut self, time: Duration) {
66        self.time = Some(time)
67    }
68
69    /// Get max memory.
70    pub fn memory(&self) -> Option<u64> {
71        self.memory
72    }
73
74    /// Set max memory.
75    pub fn set_memory(&mut self, memory: u64) {
76        self.memory = Some(memory)
77    }
78}
79
80impl From<Resources> for Ipld {
81    fn from(resources: Resources) -> Ipld {
82        Ipld::Map(BTreeMap::from([
83            (
84                FUEL_KEY.into(),
85                resources.fuel().map(Ipld::from).unwrap_or(Ipld::Null),
86            ),
87            (
88                MEMORY_KEY.into(),
89                resources.memory().map(Ipld::from).unwrap_or(Ipld::Null),
90            ),
91            (
92                TIMEOUT_KEY.into(),
93                resources
94                    .time()
95                    .map(|t| Ipld::from(t.as_millis() as i128))
96                    .unwrap_or(Ipld::Null),
97            ),
98        ]))
99    }
100}
101
102impl<'a> TryFrom<&'a Ipld> for Resources {
103    type Error = Error<Unit>;
104
105    fn try_from(ipld: &Ipld) -> Result<Self, Self::Error> {
106        Resources::try_from(ipld.to_owned())
107    }
108}
109
110impl TryFrom<Ipld> for Resources {
111    type Error = Error<Unit>;
112
113    fn try_from(ipld: Ipld) -> Result<Self, Self::Error> {
114        let map = from_ipld::<BTreeMap<String, Ipld>>(ipld)?;
115
116        let fuel = map.get(FUEL_KEY).and_then(|ipld| match ipld {
117            Ipld::Null => None,
118            ipld => from_ipld(ipld.to_owned()).ok(),
119        });
120
121        let memory = map.get(MEMORY_KEY).and_then(|ipld| match ipld {
122            Ipld::Null => None,
123            ipld => from_ipld(ipld.to_owned()).ok(),
124        });
125
126        let time = map.get(TIMEOUT_KEY).and_then(|ipld| match ipld {
127            Ipld::Null => None,
128            ipld => {
129                let time = from_ipld(ipld.to_owned()).unwrap_or(100_000);
130                Some(Duration::from_millis(time))
131            }
132        });
133
134        Ok(Resources { fuel, memory, time })
135    }
136}
137
138#[cfg(test)]
139mod test {
140    use super::*;
141
142    #[test]
143    fn ipld_roundtrip() {
144        let config = Resources::default();
145        let ipld = Ipld::from(config.clone());
146
147        assert_eq!(
148            ipld,
149            Ipld::Map(BTreeMap::from([
150                (FUEL_KEY.into(), Ipld::Integer(u64::MAX.into())),
151                (
152                    MEMORY_KEY.into(),
153                    Ipld::Integer(consts::WASM_MAX_MEMORY.into())
154                ),
155                (TIMEOUT_KEY.into(), Ipld::Integer(100_000))
156            ]))
157        );
158        assert_eq!(config, ipld.try_into().unwrap())
159    }
160
161    #[test]
162    fn ser_de() {
163        let config = Resources::default();
164        let ser = serde_json::to_string(&config).unwrap();
165        let de = serde_json::from_str(&ser).unwrap();
166
167        assert_eq!(config, de);
168    }
169}