weavatrix_worktree/
options.rs1use crate::{
2 error::{TransactionPhase, WorktreeError, WorktreeErrorCode},
3 limits::WorktreeLimits,
4};
5
6pub const DEFAULT_AUTO_WORKERS: usize = 4;
8
9#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
11pub struct WorktreeOptions {
12 pub limits: WorktreeLimits,
13 pub parallelism: usize,
15}
16
17impl WorktreeOptions {
18 #[must_use]
19 pub const fn with_limits(mut self, limits: WorktreeLimits) -> Self {
20 self.limits = limits;
21 self
22 }
23
24 #[must_use]
26 pub const fn with_parallelism(mut self, parallelism: usize) -> Self {
27 self.parallelism = parallelism;
28 self
29 }
30
31 pub fn validate(&self) -> Result<(), WorktreeError> {
32 self.limits.validate()?;
33 if self.parallelism > self.limits.max_workers {
34 return Err(WorktreeError::new(
35 WorktreeErrorCode::InvalidOptions,
36 TransactionPhase::Validate,
37 format!(
38 "parallelism {} exceeds max_workers {}",
39 self.parallelism, self.limits.max_workers
40 ),
41 ));
42 }
43 Ok(())
44 }
45
46 #[must_use]
48 pub fn worker_count(&self, file_count: usize) -> usize {
49 if file_count == 0 {
50 return 0;
51 }
52 let requested = if self.parallelism == 0 {
53 std::thread::available_parallelism()
54 .map_or(1, core::num::NonZeroUsize::get)
55 .min(DEFAULT_AUTO_WORKERS)
56 } else {
57 self.parallelism
58 };
59 requested
60 .max(1)
61 .min(self.limits.max_workers)
62 .min(file_count)
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use crate::options::{DEFAULT_AUTO_WORKERS, WorktreeOptions};
69
70 #[test]
71 fn automatic_and_explicit_parallelism_are_bounded() {
72 let automatic = WorktreeOptions::default();
73 assert!((1..=DEFAULT_AUTO_WORKERS).contains(&automatic.worker_count(10)));
74 assert_eq!(automatic.worker_count(0), 0);
75
76 let explicit = automatic.with_parallelism(8);
77 assert_eq!(explicit.worker_count(5), 5);
78 assert!(explicit.validate().is_ok());
79 }
80}