sbatch_rs/sbatch/mod.rs
1//! This module provides a builder for the `sbatch` command in Slurm.
2
3use std::collections::BTreeSet;
4use thiserror::Error;
5
6use crate::{SbatchOption, SbatchOptionError};
7
8/// sbatch command builder
9///
10/// # Examples
11///
12/// ```
13/// use sbatch_rs::{Sbatch, SbatchOption};
14///
15/// // Create a new `Sbatch` instance
16/// let sbatch = Sbatch::new()
17/// .add_option(SbatchOption::JobName("test".to_string())).unwrap()
18/// .add_option(SbatchOption::Output("test.out".to_string())).unwrap()
19/// .add_option(SbatchOption::Error("test.err".to_string())).unwrap()
20/// .set_script("test.sh".to_string()).unwrap()
21/// .build();
22///
23/// // Verify that the `sbatch` command was built properly
24/// assert!(sbatch.is_ok());
25/// assert_eq!(sbatch.unwrap(), "sbatch --error=test.err --job-name=test --output=test.out test.sh");
26/// ```
27#[derive(Debug, Clone)]
28pub struct Sbatch {
29 sbatch_options: Option<BTreeSet<SbatchOption>>,
30 script: Option<String>,
31}
32
33/// The `SbatchError` enum represents an error that can occur when building an `sbatch` command.
34///
35/// Errors include:
36/// - No options or script provided
37/// - Script is empty
38/// - Sbatch option error
39#[derive(Debug, Error)]
40pub enum SbatchError {
41 #[error("No sbatch options or script provided")]
42 NoOptionsOrScript,
43 #[error("Script is empty")]
44 ScriptEmpty,
45 #[error("Sbatch option error: {0}")]
46 SbatchOptionError(#[from] SbatchOptionError),
47 #[error("Execution failed: {0}")]
48 SbatchExecutionError(String),
49}
50
51impl Sbatch {
52 /// Creates a new `Sbatch` instance.
53 ///
54 /// # Examples
55 ///
56 /// ```
57 /// use sbatch_rs::Sbatch;
58 ///
59 /// // Create a new `Sbatch` instance
60 /// let sbatch = Sbatch::new();
61 /// ```
62 pub fn new() -> Self {
63 Sbatch {
64 sbatch_options: None,
65 script: None,
66 }
67 }
68
69 /// Adds an `SbatchOption` to the `Sbatch` instance.
70 ///
71 /// # Arguments
72 ///
73 /// * `option` - An `SbatchOption` to add to the `Sbatch` instance.
74 ///
75 /// # Returns
76 ///
77 /// This function returns a mutable reference to the `Sbatch` instance.
78 ///
79 /// # Errors
80 ///
81 /// This function returns a `SbatchError` if the `SbatchOption` is invalid.
82 ///
83 /// # Examples
84 ///
85 /// ```
86 /// use sbatch_rs::{Sbatch, SbatchOption};
87 ///
88 /// // Create a new `Sbatch` instance
89 /// let sbatch = Sbatch::new()
90 /// .add_option(SbatchOption::JobName("test".to_string())).unwrap()
91 /// .add_option(SbatchOption::Output("test.out".to_string())).unwrap()
92 /// .add_option(SbatchOption::Error("test.err".to_string())).unwrap()
93 /// .add_option(SbatchOption::Wrap("test".to_string())).unwrap()
94 /// .build();
95 ///
96 /// // Verify that the `sbatch` command was built properly
97 /// assert!(sbatch.is_ok());
98 /// assert_eq!(sbatch.unwrap(), "sbatch --error=test.err --job-name=test --output=test.out --wrap=\"test\"");
99 /// ```
100 pub fn add_option(&mut self, option: SbatchOption) -> Result<&mut Self, SbatchError> {
101 // Validate the option
102 option.validate()?;
103
104 // Add the option to the set
105 self.sbatch_options
106 .get_or_insert_with(BTreeSet::new)
107 .insert(option);
108 Ok(self)
109 }
110
111 /// Sets the script for the `Sbatch` instance.
112 ///
113 /// # Arguments
114 ///
115 /// * `script` - A string representing the script to run.
116 ///
117 /// # Returns
118 ///
119 /// This function returns a mutable reference to the `Sbatch` instance.
120 ///
121 /// # Errors
122 ///
123 /// This function returns a `SbatchError` if the script is empty.
124 ///
125 /// # Examples
126 ///
127 /// ```
128 /// use sbatch_rs::Sbatch;
129 ///
130 /// // Create a new `Sbatch` instance
131 /// let sbatch = Sbatch::new()
132 /// .set_script("test.sh".to_string()).unwrap()
133 /// .build();
134 ///
135 /// // Verify that the `sbatch` command was built properly
136 /// assert!(sbatch.is_ok());
137 /// assert_eq!(sbatch.unwrap(), "sbatch test.sh");
138 /// ```
139 pub fn set_script(&mut self, script: String) -> Result<&mut Self, SbatchError> {
140 let script = script.trim().to_string();
141 if script.is_empty() {
142 Err(SbatchError::ScriptEmpty)
143 } else {
144 self.script = Some(script);
145 Ok(self)
146 }
147 }
148
149 /// Builds the `sbatch` command.
150 ///
151 /// # Returns
152 ///
153 /// This function returns a string representing the `sbatch` command.
154 ///
155 /// # Errors
156 ///
157 /// This function returns a `SbatchError` if no options or script are provided.
158 ///
159 /// # Examples
160 ///
161 /// ```
162 /// use sbatch_rs::{Sbatch, SbatchOption};
163 ///
164 /// // Create a new `Sbatch` instance
165 /// let sbatch = Sbatch::new()
166 /// .add_option(SbatchOption::JobName("test".to_string())).unwrap()
167 /// .add_option(SbatchOption::Output("test.out".to_string())).unwrap()
168 /// .add_option(SbatchOption::Error("test.err".to_string())).unwrap()
169 /// .set_script("test.sh".to_string()).unwrap()
170 /// .build();
171 ///
172 /// // Verify that the `sbatch` command was built properly
173 /// assert!(sbatch.is_ok());
174 /// assert_eq!(sbatch.unwrap(), "sbatch --error=test.err --job-name=test --output=test.out test.sh");
175 pub fn build(&self) -> Result<String, SbatchError> {
176 // Convert the sbatch options to a space-joined string
177 let options: Option<String> = self.sbatch_options.as_ref().map(|options| {
178 options
179 .iter()
180 .map(|o| o.to_string())
181 .collect::<Vec<String>>()
182 .join(" ")
183 });
184
185 // Combine the options and script
186 match (options, &self.script) {
187 (Some(o), Some(s)) => Ok(format!("sbatch {o} {s}")),
188 (Some(o), None) => Ok(format!("sbatch {o}")),
189 (None, Some(s)) => Ok(format!("sbatch {s}")),
190 (None, None) => Err(SbatchError::NoOptionsOrScript),
191 }
192 }
193}
194
195impl Default for Sbatch {
196 /// Creates a default `Sbatch` instance.
197 ///
198 /// # Examples
199 ///
200 /// ```
201 /// use sbatch_rs::Sbatch;
202 ///
203 /// // Create a default `Sbatch` instance
204 /// let sbatch = Sbatch::default();
205 /// ```
206 fn default() -> Self {
207 Self::new()
208 }
209}