axiolid_dispatch/
reconstruction.rs1use std::sync::Arc;
5
6use axiolid_contracts::{BackendId, ExecutionOptions, GeomError, GeomResult, Operation};
7
8use crate::device::matches_device;
9use axiolid_pointcloud::PointCloud;
10use axiolid_pointcloud_reconstruction_contract::{
11 conformance, PointcloudReconstruction, Reconstruction, ReconstructionRequest,
12};
13
14#[derive(Clone)]
15struct RegisteredReconstruction {
16 priority: i32,
17 provider: Arc<dyn PointcloudReconstruction>,
18}
19
20impl core::fmt::Debug for RegisteredReconstruction {
21 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
22 f.debug_struct("RegisteredReconstruction")
23 .field("priority", &self.priority)
24 .field("backend", &self.provider.descriptor().id)
25 .finish()
26 }
27}
28
29#[derive(Debug, Clone, Default)]
36pub struct PointcloudReconstructionRegistry {
37 providers: Vec<RegisteredReconstruction>,
38}
39
40impl PointcloudReconstructionRegistry {
41 pub const fn new() -> Self {
43 Self {
44 providers: Vec::new(),
45 }
46 }
47
48 pub fn register<B>(&mut self, priority: i32, provider: B)
50 where
51 B: PointcloudReconstruction + 'static,
52 {
53 self.register_arc(priority, Arc::new(provider));
54 }
55
56 pub fn register_conformant<B>(
67 &mut self,
68 priority: i32,
69 provider: B,
70 ) -> Result<(), Box<conformance::ConformanceReport>>
71 where
72 B: PointcloudReconstruction + 'static,
73 {
74 let report = conformance::run(&provider);
75 if !report.is_conformant() {
76 return Err(Box::new(report));
77 }
78 self.register_arc(priority, Arc::new(provider));
79 Ok(())
80 }
81
82 pub fn register_arc(&mut self, priority: i32, provider: Arc<dyn PointcloudReconstruction>) {
84 self.providers
85 .push(RegisteredReconstruction { priority, provider });
86 self.providers
87 .sort_by_key(|entry| std::cmp::Reverse(entry.priority));
88 }
89
90 pub fn providers(&self) -> impl Iterator<Item = &dyn PointcloudReconstruction> {
92 self.providers.iter().map(|entry| entry.provider.as_ref())
93 }
94
95 pub fn is_empty(&self) -> bool {
97 self.providers.is_empty()
98 }
99
100 pub fn reconstruct(
108 &self,
109 cloud: &PointCloud,
110 request: &ReconstructionRequest,
111 options: &ExecutionOptions,
112 ) -> GeomResult<Reconstruction> {
113 let mut last_retryable = None;
114 let mut over_budget = None;
115
116 for entry in &self.providers {
117 let descriptor = entry.provider.descriptor();
118 if !matches_device(options.device(), descriptor.id, descriptor.target) {
119 continue;
120 }
121 if !entry
126 .provider
127 .scratch_requirement()
128 .fits_budget(options, cloud.len())
129 {
130 over_budget = Some(GeomError::BudgetExceeded { resource: "memory" });
131 continue;
132 }
133 match entry.provider.reconstruct(cloud, request, options) {
134 Ok(result) => return Ok(result),
138 Err(error @ (GeomError::Unsupported { .. } | GeomError::Unavailable { .. })) => {
139 last_retryable = Some(error);
140 }
141 Err(error) => return Err(error),
142 }
143 }
144
145 Err(last_retryable
146 .or(over_budget)
147 .unwrap_or(GeomError::Unsupported {
148 backend: BackendId::new("pointcloud-reconstruction-registry"),
149 operation: Operation::PointcloudReconstruction,
150 }))
151 }
152}