scientific_workflow/configuration/
settings.rs1use std::fmt;
4use std::path::{Path, PathBuf};
5use std::sync::Arc;
6
7use serde::Deserialize;
8use serde::de::DeserializeOwned;
9use serde_json::{Map, Value};
10
11use super::error::ConfigurationError;
12use super::source::{invalid, parse_strict_json, read_source};
13
14const STUDY_SETTINGS_FILE: &str = "study.json";
15
16#[derive(Clone)]
23pub struct StudySettings {
24 inner: Arc<StudySettingsInner>,
25}
26
27impl StudySettings {
28 pub fn load(study_root: impl Into<PathBuf>) -> Result<Self, ConfigurationError> {
30 let study_root = study_root.into();
31 let source_path = study_root.join(STUDY_SETTINGS_FILE);
32 let source = read_source(&source_path)?;
33 let document = parse_strict_json(&source_path, &source)?.into_json();
34 let raw: RawStudySettings = serde_json::from_value(document).map_err(|source| {
35 ConfigurationError::InvalidConfigurationDocument {
36 path: source_path.clone(),
37 reason: source.to_string(),
38 }
39 })?;
40 if raw.replicate_settings.replicates == 0 {
41 return invalid(
42 &source_path,
43 "replicate_settings.replicates must be positive",
44 );
45 }
46
47 Ok(Self {
48 inner: Arc::new(StudySettingsInner {
49 study_root,
50 source_path,
51 source: source.into_boxed_slice(),
52 replicate_settings: ReplicateSettings {
53 replicates: raw.replicate_settings.replicates,
54 scheduling: raw.replicate_settings.scheduling,
55 failure_policy: raw.replicate_settings.failure_policy,
56 base_seed: raw.replicate_settings.base_seed,
57 },
58 application: raw.application,
59 }),
60 })
61 }
62
63 pub fn study_root(&self) -> &Path {
65 &self.inner.study_root
66 }
67
68 pub fn source_path(&self) -> &Path {
70 &self.inner.source_path
71 }
72
73 pub fn source_json(&self) -> &[u8] {
75 &self.inner.source
76 }
77
78 pub fn replicate_settings(&self) -> ReplicateSettings {
80 self.inner.replicate_settings
81 }
82
83 pub fn application<T>(&self) -> Result<T, ConfigurationError>
89 where
90 T: DeserializeOwned,
91 {
92 serde_json::from_value(Value::Object(self.inner.application.clone())).map_err(|source| {
93 ConfigurationError::InvalidConfigurationDocument {
94 path: self.source_path().to_path_buf(),
95 reason: format!("application settings do not match the requested type: {source}"),
96 }
97 })
98 }
99}
100
101impl fmt::Debug for StudySettings {
102 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
103 formatter
104 .debug_struct("StudySettings")
105 .field("study_root", &self.study_root())
106 .field("source_path", &self.source_path())
107 .field("replicate_settings", &self.replicate_settings())
108 .field("application_fields", &self.inner.application.len())
109 .finish_non_exhaustive()
110 }
111}
112
113struct StudySettingsInner {
114 study_root: PathBuf,
115 source_path: PathBuf,
116 source: Box<[u8]>,
117 replicate_settings: ReplicateSettings,
118 application: Map<String, Value>,
119}
120
121#[derive(Clone, Copy, Debug, Eq, PartialEq)]
123pub struct ReplicateSettings {
124 replicates: u64,
125 scheduling: ReplicateScheduling,
126 failure_policy: ReplicateFailurePolicy,
127 base_seed: u64,
128}
129
130impl ReplicateSettings {
131 pub const fn replicates(self) -> u64 {
133 self.replicates
134 }
135
136 pub const fn scheduling(self) -> ReplicateScheduling {
138 self.scheduling
139 }
140
141 pub const fn failure_policy(self) -> ReplicateFailurePolicy {
143 self.failure_policy
144 }
145
146 pub const fn base_seed(self) -> u64 {
148 self.base_seed
149 }
150}
151
152#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
154#[serde(rename_all = "snake_case")]
155pub enum ReplicateScheduling {
156 Sequential,
158 Parallel,
160}
161
162impl ReplicateScheduling {
163 pub const fn as_str(self) -> &'static str {
165 match self {
166 Self::Sequential => "sequential",
167 Self::Parallel => "parallel",
168 }
169 }
170}
171
172#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
174#[serde(rename_all = "snake_case")]
175pub enum ReplicateFailurePolicy {
176 FailFast,
178 FinishAll,
180}
181
182impl ReplicateFailurePolicy {
183 pub const fn as_str(self) -> &'static str {
185 match self {
186 Self::FailFast => "fail_fast",
187 Self::FinishAll => "finish_all",
188 }
189 }
190}
191
192#[derive(Deserialize)]
193#[serde(deny_unknown_fields)]
194struct RawStudySettings {
195 replicate_settings: RawReplicateSettings,
196 #[serde(default)]
197 application: Map<String, Value>,
198}
199
200#[derive(Deserialize)]
201#[serde(deny_unknown_fields)]
202struct RawReplicateSettings {
203 replicates: u64,
204 scheduling: ReplicateScheduling,
205 failure_policy: ReplicateFailurePolicy,
206 base_seed: u64,
207}