1pub mod options;
6pub mod source;
7pub mod volume;
8
9mod encode;
10mod metal;
11mod output;
12mod plan;
13mod transform;
14
15pub use encode::ForwardKernel;
16pub(crate) use encode::configured_create_threads as configured_create_threads_for_pool;
22pub use options::{BlockSizing, CreationBackend, Par2CreatorOptions, RecoveryAmount, VolumeScheme};
23pub use output::Par2CreateOutcome;
24pub use plan::{Par2CreatePlan, Par2MemoryPlan};
25pub use source::CreationSource;
26pub use volume::RecoveryVolumePlan;
27
28use crate::error::{Par2Error, Result};
29
30use self::output::write_outputs;
31use self::plan::build_plan_with_cache;
32
33#[derive(Clone)]
35pub struct Par2Creator {
36 options: Par2CreatorOptions,
37 scan: std::sync::Arc<self::source::SourceScanCache>,
43}
44
45impl Par2Creator {
46 pub fn new(options: Par2CreatorOptions) -> Self {
48 Self {
49 options,
50 scan: std::sync::Arc::new(self::source::SourceScanCache::new()),
51 }
52 }
53
54 pub fn options(&self) -> &Par2CreatorOptions {
56 &self.options
57 }
58
59 pub fn plan(&self) -> Result<Par2CreatePlan> {
61 build_plan_with_cache(&self.options, Some(&self.scan))
62 }
63
64 pub fn create(&self, plan: &Par2CreatePlan) -> Result<Par2CreateOutcome> {
66 if self.options.cancellation.is_cancelled() {
67 return Err(Par2Error::Cancelled);
68 }
69 reedsolomon_rs::threading::ensure_pool(self::encode::configured_create_threads);
77 plan.validate_integrity()?;
78 self::plan::validate_output_targets(
79 &plan.output_paths,
80 &plan.sources,
81 self.options.overwrite,
82 )?;
83 let canonical = self::plan::build_plan_with_cache(&self.options, Some(&self.scan))?;
88 if plan != &canonical {
89 return Err(Par2Error::InvalidCreationOptions {
90 reason: "creation plan differs from creator options or current inputs".to_string(),
91 });
92 }
93 let slice_size =
94 usize::try_from(plan.slice_size).map_err(|_| Par2Error::ResourceLimitExceeded {
95 reason: "slice size exceeds addressable memory".to_string(),
96 })?;
97 let selected = metal::select_backend(
98 self.options.backend,
99 slice_size,
100 plan.source_slice_count as usize,
101 plan.recovery_count as usize,
102 self.options.memory_limit,
103 )?;
104 let selected_backend = metal::selected_policy(&selected);
105 if self.options.dry_run {
106 return Ok(Par2CreateOutcome {
107 recovery_set_id: plan.recovery_set_id,
108 main_path: plan.main_path.clone(),
109 volume_paths: plan.volume_paths.clone(),
110 output_paths: plan.output_paths.clone(),
111 source_slice_count: plan.source_slice_count,
112 recovery_count: plan.recovery_count,
113 bytes_written: 0,
114 dry_run: true,
115 requested_backend: self.options.backend,
116 selected_backend,
117 });
118 }
119
120 write_outputs(plan, canonical.sources, &self.options, selected)
121 }
122}