Skip to main content

hedl_cli/batch/
results.rs

1// Dweve HEDL - Hierarchical Entity Data Language
2//
3// Copyright (c) 2025 Dweve IP B.V. and individual contributors.
4//
5// SPDX-License-Identifier: Apache-2.0
6//
7// Licensed under the Apache License, Version 2.0 (the "License");
8// you may not use this file except in compliance with the License.
9// You may obtain a copy of the License in the LICENSE file at the
10// root of this repository or at: http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Batch processing result types.
19
20use crate::error::CliError;
21use std::path::PathBuf;
22
23/// Result of processing a single file in a batch operation.
24///
25/// Contains the file path and either a success value or an error.
26///
27/// # Type Parameters
28///
29/// * `T` - The success type returned by the operation
30#[derive(Debug, Clone)]
31pub struct FileResult<T> {
32    /// The file path that was processed
33    pub path: PathBuf,
34    /// The result of processing (Ok or Err)
35    pub result: Result<T, CliError>,
36}
37
38impl<T> FileResult<T> {
39    /// Create a successful file result.
40    pub fn success(path: PathBuf, value: T) -> Self {
41        Self {
42            path,
43            result: Ok(value),
44        }
45    }
46
47    /// Create a failed file result.
48    #[must_use]
49    pub fn failure(path: PathBuf, error: CliError) -> Self {
50        Self {
51            path,
52            result: Err(error),
53        }
54    }
55
56    /// Check if the result is successful.
57    pub fn is_success(&self) -> bool {
58        self.result.is_ok()
59    }
60
61    /// Check if the result is a failure.
62    pub fn is_failure(&self) -> bool {
63        self.result.is_err()
64    }
65}
66
67/// Aggregated results from a batch processing operation.
68///
69/// Contains all individual file results and provides statistics.
70///
71/// # Type Parameters
72///
73/// * `T` - The success type returned by the operation
74#[derive(Debug, Clone)]
75pub struct BatchResults<T> {
76    /// Individual results for each processed file
77    pub results: Vec<FileResult<T>>,
78    /// Total processing time in milliseconds
79    pub elapsed_ms: u128,
80}
81
82impl<T> BatchResults<T> {
83    /// Create new batch results from a vector of file results.
84    #[must_use]
85    pub fn new(results: Vec<FileResult<T>>, elapsed_ms: u128) -> Self {
86        Self {
87            results,
88            elapsed_ms,
89        }
90    }
91
92    /// Get the total number of files processed.
93    #[must_use]
94    pub fn total_files(&self) -> usize {
95        self.results.len()
96    }
97
98    /// Get the number of successfully processed files.
99    #[must_use]
100    pub fn success_count(&self) -> usize {
101        self.results.iter().filter(|r| r.is_success()).count()
102    }
103
104    /// Get the number of failed files.
105    #[must_use]
106    pub fn failure_count(&self) -> usize {
107        self.results.iter().filter(|r| r.is_failure()).count()
108    }
109
110    /// Check if all files were processed successfully.
111    #[must_use]
112    pub fn all_succeeded(&self) -> bool {
113        self.results.iter().all(FileResult::is_success)
114    }
115
116    /// Check if any files failed.
117    #[must_use]
118    pub fn has_failures(&self) -> bool {
119        self.results.iter().any(FileResult::is_failure)
120    }
121
122    /// Get an iterator over successful results.
123    pub fn successes(&self) -> impl Iterator<Item = &FileResult<T>> {
124        self.results.iter().filter(|r| r.is_success())
125    }
126
127    /// Get an iterator over failed results.
128    pub fn failures(&self) -> impl Iterator<Item = &FileResult<T>> {
129        self.results.iter().filter(|r| r.is_failure())
130    }
131
132    /// Get processing throughput in files per second.
133    #[must_use]
134    pub fn throughput(&self) -> f64 {
135        if self.elapsed_ms == 0 {
136            0.0
137        } else {
138            (self.total_files() as f64) / (self.elapsed_ms as f64 / 1000.0)
139        }
140    }
141}