brep_kernel/feature_pipeline/
extension.rs1use super::{FeatureContext, FeatureResult};
3use serde::{Deserialize, Serialize};
4use std::{cell::RefCell, rc::Rc};
5#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
6#[serde(rename_all = "camelCase")]
7pub struct PluginPin {
8 pub id: String,
9 pub version: String,
10 pub api_version: u32,
11 pub digest: String,
12}
13pub trait ExtensionProvider {
14 fn identity(&self, pins: &[PluginPin], feature_type: &str) -> Result<String, String>;
15 fn execute(&self, pins: &[PluginPin], ctx: &FeatureContext<'_>) -> FeatureResult;
16 fn execute_annotation(
18 &self,
19 _pins: &[PluginPin],
20 annotation: &super::pmi::PmiAnnotation,
21 _context: &super::pmi::PmiContext<'_>,
22 ) -> Result<super::pmi::PluginAnnotationOutput, String> {
23 Err(format!("unavailable plugin annotation provider for '{}'", annotation.kind))
24 }
25}
26thread_local! {
27 static PROVIDER:RefCell<Option<Rc<dyn ExtensionProvider>>>=RefCell::new(None);
28 static PINS:RefCell<Vec<PluginPin>>=const {RefCell::new(Vec::new())};
29}
30pub fn with_provider<R>(provider: Rc<dyn ExtensionProvider>, f: impl FnOnce() -> R) -> R {
31 struct Restore(Option<Rc<dyn ExtensionProvider>>);
32 impl Drop for Restore {
33 fn drop(&mut self) {
34 PROVIDER.with(|p| *p.borrow_mut() = self.0.take());
35 }
36 }
37 let _restore = Restore(PROVIDER.with(|p| p.replace(Some(provider))));
38 f()
39}
40pub(crate) struct PinScope(Vec<PluginPin>);
41impl Drop for PinScope {
42 fn drop(&mut self) {
43 PINS.with(|p| *p.borrow_mut() = std::mem::take(&mut self.0));
44 }
45}
46pub(crate) fn enter(pins: &[PluginPin]) -> PinScope {
47 PinScope(PINS.with(|p| p.replace(pins.to_vec())))
48}
49pub(crate) fn identity(kind: &str) -> String {
50 if !kind.contains('/') {
51 return String::new();
52 }
53 let provider = PROVIDER.with(|p| p.borrow().clone());
54 let pins = PINS.with(|p| p.borrow().clone());
55 match provider {
56 Some(p) => p
57 .identity(&pins, kind)
58 .unwrap_or_else(|e| format!("unavailable:{e}")),
59 None => "unavailable plugin provider".into(),
60 }
61}
62pub(crate) fn execute(ctx: &FeatureContext<'_>) -> FeatureResult {
63 let provider = PROVIDER.with(|p| p.borrow().clone());
64 let pins = PINS.with(|p| p.borrow().clone());
65 match provider {
66 Some(p) => p.execute(&pins, ctx),
67 None => ctx.fail(format!(
68 "unavailable plugin provider for '{}'",
69 ctx.feature_type
70 )),
71 }
72}
73pub fn clone_reference(ctx: &FeatureContext<'_>, name: &str) -> Result<crate::BrepSolid, String> {
75 let handle = ctx
76 .scene
77 .resolve_solid(name)
78 .ok_or_else(|| format!("unresolved plugin reference {name}"))?;
79 crate::with_registered_solid_str(handle, |s| Ok(s.clone()))
80}
81pub fn publish(mut solid: crate::BrepSolid, name: &str) -> super::AddedSolid {
83 for (i, face) in solid
84 .shells
85 .iter_mut()
86 .flat_map(|s| s.faces.iter_mut())
87 .enumerate()
88 {
89 face.name = Some(format!("{name}:face{i}"));
90 }
91 super::features::common::register_added(solid, name)
92}
93pub fn transform(
94 solid: &crate::BrepSolid,
95 position: [f64; 3],
96 rotation_degrees: [f64; 3],
97 scale: [f64; 3],
98) -> Result<crate::BrepSolid, String> {
99 let m = super::features::common::compose_trs_matrix(
100 position,
101 rotation_degrees.map(f64::to_radians),
102 scale,
103 [0.; 3],
104 );
105 crate::transform_brep(solid, crate::AffineTransform::new(m)?, false)
106}
107pub(crate) fn document_uses_extensions(value: &serde_json::Value) -> bool {
110 match value {
111 serde_json::Value::Object(o) => {
112 o.get("type")
113 .and_then(|v| v.as_str())
114 .is_some_and(|t| t.contains('/'))
115 || o.values().any(document_uses_extensions)
116 }
117 serde_json::Value::Array(a) => a.iter().any(document_uses_extensions),
118 _ => false,
119 }
120}
121
122pub(crate) fn execute_annotation(
124 annotation: &super::pmi::PmiAnnotation,
125 context: &super::pmi::PmiContext<'_>,
126) -> Result<super::pmi::PluginAnnotationOutput, String> {
127 let provider = PROVIDER.with(|p| p.borrow().clone());
128 provider.ok_or_else(|| format!("unavailable plugin annotation provider for '{}'", annotation.kind))?
129 .execute_annotation(&context.request.plugins, annotation, context)
130}