use crate::operations::{AnalysisType, FileOperation, OutputFormat};
use crate::types::{JobContext, JobId, Priority, ResourceRequirements, RetryPolicy, Schedule};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchJob {
pub id: JobId,
pub name: String,
pub operation: BatchOperation,
pub inputs: Vec<InputSpec>,
pub outputs: Vec<OutputSpec>,
pub priority: Priority,
pub retry: RetryPolicy,
pub dependencies: Vec<JobId>,
pub schedule: Schedule,
pub resources: ResourceRequirements,
pub metadata: HashMap<String, String>,
#[serde(skip)]
pub context: Option<JobContext>,
}
impl BatchJob {
#[must_use]
pub fn new(name: String, operation: BatchOperation) -> Self {
let id = JobId::new();
Self {
id: id.clone(),
name: name.clone(),
operation,
inputs: Vec::new(),
outputs: Vec::new(),
priority: Priority::default(),
retry: RetryPolicy::default(),
dependencies: Vec::new(),
schedule: Schedule::default(),
resources: ResourceRequirements::default(),
metadata: HashMap::new(),
context: Some(JobContext::new(id, name)),
}
}
pub fn add_input(&mut self, input: InputSpec) {
self.inputs.push(input);
}
pub fn add_output(&mut self, output: OutputSpec) {
self.outputs.push(output);
}
pub fn add_dependency(&mut self, job_id: JobId) {
self.dependencies.push(job_id);
}
pub fn set_priority(&mut self, priority: Priority) {
self.priority = priority;
}
pub fn set_retry_policy(&mut self, retry: RetryPolicy) {
self.retry = retry;
}
pub fn set_schedule(&mut self, schedule: Schedule) {
self.schedule = schedule;
}
pub fn set_resources(&mut self, resources: ResourceRequirements) {
self.resources = resources;
}
pub fn add_metadata(&mut self, key: String, value: String) {
self.metadata.insert(key, value);
}
#[must_use]
pub fn get_metadata(&self, key: &str) -> Option<&String> {
self.metadata.get(key)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BatchOperation {
Transcode {
preset: String,
},
QualityCheck {
profile: String,
},
Analyze {
analysis_type: AnalysisType,
},
FileOp {
operation: FileOperation,
},
Custom {
script: PathBuf,
},
Pipeline {
steps: Vec<PipelineStep>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PipelineStep {
pub name: String,
pub operation: BatchOperation,
pub continue_on_error: bool,
pub condition: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InputSpec {
pub pattern: String,
pub recursive: bool,
pub filters: Vec<FileFilter>,
pub base_dir: Option<PathBuf>,
}
impl InputSpec {
#[must_use]
pub fn new(pattern: String) -> Self {
Self {
pattern,
recursive: false,
filters: Vec::new(),
base_dir: None,
}
}
#[must_use]
pub fn recursive(mut self, recursive: bool) -> Self {
self.recursive = recursive;
self
}
#[must_use]
pub fn with_filter(mut self, filter: FileFilter) -> Self {
self.filters.push(filter);
self
}
#[must_use]
pub fn with_base_dir(mut self, base_dir: PathBuf) -> Self {
self.base_dir = Some(base_dir);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FileFilter {
Extension(String),
MinSize(u64),
MaxSize(u64),
ModifiedAfter(String),
ModifiedBefore(String),
Custom(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OutputSpec {
pub template: String,
pub format: OutputFormat,
pub overwrite: bool,
pub create_dirs: bool,
pub options: HashMap<String, String>,
}
impl OutputSpec {
#[must_use]
pub fn new(template: String, format: OutputFormat) -> Self {
Self {
template,
format,
overwrite: false,
create_dirs: true,
options: HashMap::new(),
}
}
#[must_use]
pub fn overwrite(mut self, overwrite: bool) -> Self {
self.overwrite = overwrite;
self
}
#[must_use]
pub fn create_dirs(mut self, create_dirs: bool) -> Self {
self.create_dirs = create_dirs;
self
}
#[must_use]
pub fn with_option(mut self, key: String, value: String) -> Self {
self.options.insert(key, value);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_batch_job_creation() {
let job = BatchJob::new(
"test-job".to_string(),
BatchOperation::FileOp {
operation: FileOperation::Copy { overwrite: false },
},
);
assert_eq!(job.name, "test-job");
assert_eq!(job.priority, Priority::Normal);
assert!(job.inputs.is_empty());
assert!(job.outputs.is_empty());
}
#[test]
fn test_add_input() {
let mut job = BatchJob::new(
"test-job".to_string(),
BatchOperation::FileOp {
operation: FileOperation::Copy { overwrite: false },
},
);
job.add_input(InputSpec::new("*.mp4".to_string()));
assert_eq!(job.inputs.len(), 1);
}
#[test]
fn test_add_output() {
let mut job = BatchJob::new(
"test-job".to_string(),
BatchOperation::FileOp {
operation: FileOperation::Copy { overwrite: false },
},
);
job.add_output(OutputSpec::new("output.mp4".to_string(), OutputFormat::Mp4));
assert_eq!(job.outputs.len(), 1);
}
#[test]
fn test_add_dependency() {
let mut job = BatchJob::new(
"test-job".to_string(),
BatchOperation::FileOp {
operation: FileOperation::Copy { overwrite: false },
},
);
job.add_dependency(JobId::new());
assert_eq!(job.dependencies.len(), 1);
}
#[test]
fn test_set_priority() {
let mut job = BatchJob::new(
"test-job".to_string(),
BatchOperation::FileOp {
operation: FileOperation::Copy { overwrite: false },
},
);
job.set_priority(Priority::High);
assert_eq!(job.priority, Priority::High);
}
#[test]
fn test_metadata() {
let mut job = BatchJob::new(
"test-job".to_string(),
BatchOperation::FileOp {
operation: FileOperation::Copy { overwrite: false },
},
);
job.add_metadata("project".to_string(), "test-project".to_string());
assert_eq!(
job.get_metadata("project"),
Some(&"test-project".to_string())
);
}
#[test]
fn test_input_spec_builder() {
let tmp = std::env::temp_dir();
let input = InputSpec::new("*.mp4".to_string())
.recursive(true)
.with_filter(FileFilter::Extension("mp4".to_string()))
.with_base_dir(tmp.clone());
assert!(input.recursive);
assert_eq!(input.filters.len(), 1);
assert_eq!(input.base_dir, Some(tmp));
}
#[test]
fn test_output_spec_builder() {
let output = OutputSpec::new("output.mp4".to_string(), OutputFormat::Mp4)
.overwrite(true)
.create_dirs(false)
.with_option("bitrate".to_string(), "5000k".to_string());
assert!(output.overwrite);
assert!(!output.create_dirs);
assert_eq!(output.options.get("bitrate"), Some(&"5000k".to_string()));
}
}