homestar_invocation/
invocation.rs

1//! [Invocation] is a signed [Task].
2//!
3//! [Task]: super::Task
4
5use crate::{ipld::DagCbor, Error, Pointer, Task, Unit};
6use libipld::{serde::from_ipld, Ipld};
7use serde::{Deserialize, Serialize};
8use std::collections::BTreeMap;
9
10const TASK_KEY: &str = "task";
11
12/// A signed [Task] wrapper/container.
13#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
14pub struct Invocation<'a, T> {
15    task: Task<'a, T>,
16}
17
18impl<'a, T> From<Task<'a, T>> for Invocation<'a, T>
19where
20    Ipld: From<T>,
21{
22    fn from(task: Task<'a, T>) -> Self {
23        Invocation::new(task)
24    }
25}
26
27impl<'a, T> Invocation<'a, T>
28where
29    Ipld: From<T>,
30{
31    /// Create a new [Invocation] container.
32    pub fn new(task: Task<'a, T>) -> Self {
33        Self { task }
34    }
35}
36
37impl<T> From<Invocation<'_, T>> for Ipld
38where
39    Ipld: From<T>,
40{
41    fn from(invocation: Invocation<'_, T>) -> Self {
42        Ipld::Map(BTreeMap::from([(TASK_KEY.into(), invocation.task.into())]))
43    }
44}
45
46impl<T> TryFrom<Ipld> for Invocation<'_, T>
47where
48    T: From<Ipld>,
49{
50    type Error = Error<Unit>;
51
52    fn try_from(ipld: Ipld) -> Result<Self, Self::Error> {
53        let map = from_ipld::<BTreeMap<String, Ipld>>(ipld)?;
54
55        Ok(Self {
56            task: Task::try_from(
57                map.get(TASK_KEY)
58                    .ok_or_else(|| Error::<Unit>::MissingField(TASK_KEY.to_string()))?
59                    .to_owned(),
60            )?,
61        })
62    }
63}
64
65impl<T> TryFrom<Invocation<'_, T>> for Pointer
66where
67    Ipld: From<T>,
68{
69    type Error = Error<Unit>;
70
71    fn try_from(invocation: Invocation<'_, T>) -> Result<Self, Self::Error> {
72        Ok(Pointer::new(invocation.to_cid()?))
73    }
74}
75
76impl<'a, T> DagCbor for Invocation<'a, T> where Ipld: From<T> {}
77
78#[cfg(test)]
79mod test {
80    use super::*;
81    use crate::{
82        authority::UcanPrf,
83        task::{instruction::RunInstruction, Resources},
84        test_utils,
85    };
86
87    #[test]
88    fn ipld_roundtrip() {
89        let config = Resources::default();
90        let instruction = test_utils::instruction::<Unit>();
91        let task = Task::new(
92            RunInstruction::Expanded(instruction.clone()),
93            config.into(),
94            UcanPrf::default(),
95        );
96
97        let invocation = Invocation::new(task);
98        let ipld = Ipld::from(invocation.clone());
99        assert_eq!(invocation, Invocation::try_from(ipld).unwrap());
100    }
101
102    #[test]
103    fn ser_de() {
104        let config = Resources::default();
105        let instruction = test_utils::instruction::<Unit>();
106        let task = Task::new(
107            RunInstruction::Expanded(instruction.clone()),
108            config.into(),
109            UcanPrf::default(),
110        );
111        let invocation = Invocation::new(task);
112
113        let ser = serde_json::to_string(&invocation).unwrap();
114        let de = serde_json::from_str(&ser).unwrap();
115
116        assert_eq!(invocation, de);
117    }
118}