cog_task/server/task/
mod.rs1pub 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}