cog_task/server/task/
mod.rs

1pub mod block;
2pub mod config;
3
4pub use block::Block;
5pub use config::Config;
6
7use crate::util::Hash;
8use crate::verify_features;
9use eyre::{eyre, Context, Result};
10use itertools::Itertools;
11use once_cell::sync::OnceCell;
12use serde::{Deserialize, Serialize};
13use std::fs;
14use std::path::{Path, PathBuf};
15
16pub static ROOT_DIR: OnceCell<PathBuf> = OnceCell::new();
17pub static BASE_CFG: OnceCell<Config> = OnceCell::new();
18
19#[derive(Deserialize, Debug, Default, Serialize)]
20#[serde(deny_unknown_fields)]
21pub struct Task {
22    name: String,
23    version: String,
24    blocks: Vec<Block>,
25    #[serde(default)]
26    config: Config,
27    #[serde(default)]
28    description: String,
29}
30
31impl Task {
32    pub fn new(root_dir: &Path) -> Result<Self> {
33        ROOT_DIR.set(root_dir.to_owned()).unwrap();
34
35        let path = root_dir.join("task.ron");
36        let content =
37            fs::read_to_string(&path).wrap_err("Failed to read task description file.")?;
38
39        verify_features(&content)?;
40
41        ron::from_str::<Task>(&content)
42            .wrap_err_with(|| eyre!("Failed to deserialize task file ({path:?})."))?
43            .init(root_dir)
44    }
45
46    pub fn init(mut self, root_dir: &Path) -> Result<Self> {
47        for block in self.blocks.iter_mut() {
48            block
49                .init()
50                .wrap_err_with(|| eyre!("Failed to verify block ({}).", block.label()))?;
51        }
52
53        for (name, count) in self.block_labels().into_iter().counts() {
54            if count > 1 {
55                Err(eyre!(
56                    "Block names have to be unique within a task ('{name}' is repeated)."
57                ))?;
58            }
59        }
60
61        if self.description.is_empty() {
62            let path = root_dir.join("description.txt");
63            let description = fs::read_to_string(&path)
64                .wrap_err_with(|| format!("Unable to open task description file ({path:?})."))?;
65            self.description = description;
66        }
67
68        self.config.init()?;
69        self.config.verify_checksum(self.hash())?;
70        BASE_CFG.set(self.config.clone()).unwrap();
71
72        Ok(self)
73    }
74
75    #[inline(always)]
76    pub fn name(&self) -> &String {
77        &self.name
78    }
79
80    #[inline(always)]
81    pub fn version(&self) -> &String {
82        &self.version
83    }
84
85    #[inline(always)]
86    pub fn title(&self) -> String {
87        format!("{} ({})", self.name, self.version)
88    }
89
90    #[inline(always)]
91    pub fn config(&self) -> &Config {
92        &self.config
93    }
94
95    #[inline(always)]
96    pub fn block(&self, i: usize) -> &Block {
97        &self.blocks[i]
98    }
99
100    pub fn block_labels(&self) -> Vec<String> {
101        self.blocks.iter().map(|b| b.label().to_string()).collect()
102    }
103
104    #[inline(always)]
105    pub fn description(&self) -> &str {
106        &self.description
107    }
108}
109
110impl Hash for Task {
111    fn hash(&self) -> String {
112        use sha2::{Digest, Sha256};
113        let mut hasher = Sha256::default();
114        let blocks: Vec<_> = self.blocks.iter().map(|b| b.hash()).collect();
115        hasher.update(&serde_cbor::to_vec(&blocks).unwrap());
116        hex::encode(hasher.finalize())
117    }
118}