scientific_workflow/
project.rs1use std::fmt;
21use std::path::{Path, PathBuf};
22
23use serde::Serialize;
24use thiserror::Error;
25
26use crate::configuration::{
27 ConfigurationError, MatchingTaskConfigIter, ParameterSpace, ProjectConfig, ProjectPaths,
28 TaskConfig, TaskConfigIter,
29};
30use crate::system_state::{StateError, SystemStateSchema};
31
32const STATE_SCHEMA_FILE: &str = "state.json";
34
35#[derive(Debug, Error)]
37#[non_exhaustive]
38pub enum ScientificProjectError {
39 #[error(transparent)]
41 Configuration(#[from] ConfigurationError),
42 #[error(transparent)]
44 State(#[from] StateError),
45}
46
47#[derive(Clone)]
52pub struct ScientificProject {
53 configuration: ProjectConfig,
54 state_schema: SystemStateSchema,
55}
56
57impl ScientificProject {
58 pub fn load(project_root: impl Into<PathBuf>) -> Result<Self, ScientificProjectError> {
60 let project_root = project_root.into();
61 let configuration = ProjectConfig::load(&project_root)?;
62 let state_schema = SystemStateSchema::load_json_template(
63 configuration
64 .configuration_directory()
65 .join(STATE_SCHEMA_FILE),
66 )?;
67 Ok(Self {
68 configuration,
69 state_schema,
70 })
71 }
72
73 pub fn project_root(&self) -> &Path {
75 self.configuration.project_root()
76 }
77
78 pub fn configuration_directory(&self) -> &Path {
80 self.configuration.configuration_directory()
81 }
82
83 pub fn parameters(&self) -> &ParameterSpace {
85 self.configuration.parameters()
86 }
87
88 pub fn paths(&self) -> &ProjectPaths {
90 self.configuration.paths()
91 }
92
93 pub fn resolve_path(&self, key: &str) -> Result<PathBuf, ConfigurationError> {
95 self.configuration.paths().resolve_path(key)
96 }
97
98 pub fn task_count(&self) -> u64 {
100 self.configuration.task_count()
101 }
102
103 pub fn task_config(&self, ordinal: u64) -> Result<TaskConfig, ConfigurationError> {
105 self.configuration.task_config(ordinal)
106 }
107
108 pub fn task_configs(&self) -> TaskConfigIter {
110 self.configuration.task_configs()
111 }
112
113 pub fn task_configs_matching<V>(
115 &self,
116 key: impl Into<String>,
117 value: V,
118 ) -> Result<MatchingTaskConfigIter, ConfigurationError>
119 where
120 V: Serialize,
121 {
122 self.configuration.task_configs_matching(key, value)
123 }
124
125 pub fn unique_task_config_matching<V>(
127 &self,
128 key: impl Into<String>,
129 value: V,
130 ) -> Result<TaskConfig, ConfigurationError>
131 where
132 V: Serialize,
133 {
134 self.configuration.unique_task_config_matching(key, value)
135 }
136
137 pub fn state_schema(&self) -> &SystemStateSchema {
139 &self.state_schema
140 }
141
142 pub fn configuration(&self) -> &ProjectConfig {
144 &self.configuration
145 }
146
147 pub fn into_parts(self) -> (ProjectConfig, SystemStateSchema) {
149 (self.configuration, self.state_schema)
150 }
151}
152
153impl fmt::Debug for ScientificProject {
154 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
156 formatter
157 .debug_struct("ScientificProject")
158 .field("project_root", &self.project_root())
159 .field("parameters", &self.parameters().parameter_count())
160 .field("tasks", &self.task_count())
161 .field("paths", &self.paths().len())
162 .field("state_fields", &self.state_schema().len())
163 .finish()
164 }
165}