Skip to main content

geop_core_editor/
registry.rs

1//! [`Registry`]: everything an editor offers, gathered in one builder that
2//! each operation crate adds to with its one `register` function — the
3//! operations a step can be, how what they build is drawn and listed
4//! ([`Drawer`]), the exporters and inspectors ([`crate::output`]), the
5//! panels ([`Panel`]), the tools ([`Tool`]), what settles the program's
6//! state after every edit ([`Settle`]) and the examples
7//! ([`ExampleSet`]).
8//!
9//! The [`crate::editor::Editor`] names none of them: what it shows and does
10//! beyond editing steps is what its registry holds.
11
12use std::{cell::RefCell, collections::BTreeMap, rc::Rc};
13
14use geop_core_math::geop_error::{GeopError, GeopResult};
15use geop_core_program::{
16    element::PickedElement,
17    kept::{Keeper, made_kept},
18    operation_registry::OperationRegistry,
19    program::Program,
20    reader::{Reader, Reads},
21    runner::Runner,
22    values::StateValues,
23};
24use serde::{Serialize, de::DeserializeOwned};
25
26use crate::{
27    editable::{Editable, Editors, Listed},
28    editing::Pull,
29    editor::{Changed, EditorContext, Rerun},
30    output::{Exporter, Inspector, Outputs},
31    tools::{Tool, ToolInfo, pick::PickTool},
32    view::{PartView, annotations_of, listed_by_steps},
33};
34
35/// What a crate draws of the part, and lists of it: the targets it
36/// defines. A part is drawn by every drawer of the registry in turn, into
37/// one [`PartView`] — the parts placed in it as instances of components,
38/// never one mesh per placement (see [`crate::view::ViewInstance`]).
39pub trait Drawer: 'static {
40    /// Draws what of the part `reader` reads is this crate's into `view`.
41    fn draw(&self, reader: &Reader, view: &mut PartView) -> GeopResult<()>;
42
43    /// What of the part is this crate's, to list: each drawn item shown
44    /// (`visible: Some(true)`) — the editor hides what is hidden — and
45    /// each one that is not drawn with `visible: None`.
46    fn structure(&self, reader: &Reader) -> GeopResult<Vec<StructureItem>>;
47
48    /// The names of what it draws only to be picked: shown only while a
49    /// field waiting for a pick could pick it (see
50    /// [`crate::editing::Reference::accepts`]) — none, unless it says
51    /// otherwise.
52    fn hidden(&self, _reader: &Reader) -> Vec<String> {
53        Vec::new()
54    }
55
56    /// Whether what it draws is kept between processes, as what a program
57    /// builds is (see [`geop_core_program::kept`]): for a drawer whose
58    /// drawing takes work — the model's, which meshes its faces — so that a
59    /// program reopened is drawn without that work. Not, unless it says so.
60    fn kept(&self) -> bool {
61        false
62    }
63}
64
65/// How a drawing is kept between processes ([`Drawer::kept`]).
66const KEPT_DRAWING: Keeper<PartView> = Keeper {
67    write: crate::view::kept::write,
68    read: crate::view::kept::read,
69};
70
71/// What each drawer of a registry drew last ([`Registry::draw`]), kept by
72/// what it read ([`Reads`]): a part shown again with other sketches — a
73/// sketch opened, or closed — draws its sketches again, not its faces.
74#[derive(Default)]
75pub struct Drawings(RefCell<BTreeMap<usize, Vec<Drawn>>>);
76
77/// What a drawer drew, and what it read to draw it.
78type Drawn = (Reads, Rc<PartView>);
79
80/// How many drawings of each drawer [`Drawings`] keeps: of the part before
81/// the step edited, the part with it, and those of the last steps opened.
82const KEPT_DRAWINGS: usize = 4;
83
84impl Drawings {
85    /// What the drawer `drawer` draws of the part `reader` reads, by
86    /// `draw`: as it drew it last of what reads the same — or, given the
87    /// runner of the program whose part it is, as an earlier process drew
88    /// it of what reads the same now (see [`Drawer::kept`]).
89    fn drawn(
90        &self,
91        drawer: usize,
92        reader: &Reader,
93        kept_by: Option<&Runner>,
94        draw: impl FnOnce(&Reader) -> GeopResult<PartView>,
95    ) -> GeopResult<Rc<PartView>> {
96        let mut kept = self.0.borrow_mut();
97        let kept = kept.entry(drawer).or_default();
98        let entry = match kept.iter().position(|(reads, _)| reader.reads_same(reads)) {
99            Some(k) => kept.remove(k),
100            None => match kept_by {
101                Some(runner) => {
102                    let key = format!("drawn:{drawer}");
103                    let (drawn, reads) = made_kept(reader, runner, &key, KEPT_DRAWING, draw);
104                    (reads, drawn?)
105                }
106                None => {
107                    let (drawn, reads) = reader.recorded(draw);
108                    (reads, Rc::new(drawn?))
109                }
110            },
111        };
112        let drawn = entry.1.clone();
113        kept.push(entry);
114        if kept.len() > KEPT_DRAWINGS {
115            kept.remove(0);
116        }
117        Ok(drawn)
118    }
119}
120
121/// An entry of the structure of the part drawn, by name: what
122/// [`Drawer::structure`] lists.
123#[derive(Clone, Debug, PartialEq, Serialize)]
124pub struct StructureItem {
125    /// What it is: `datum`, `sketch`, `solid`, `face`, `part`, or what a
126    /// step lists it as.
127    pub kind: String,
128    /// What a list calls the things of its kind: `Datums`.
129    pub kind_label: String,
130    /// What it is shown by — and shown and hidden by, its key.
131    pub name: String,
132    /// What is said beside its name, quieter: the file a placed part is
133    /// from, `iso4762_socket_screw`. Its name is what it is called.
134    pub detail: Option<String>,
135    /// Whether it is shown, if it is drawn at all: what an editor's
136    /// visibility switches. None for what a step lists, which is not drawn.
137    pub visible: Option<bool>,
138    /// What it refers to: the elements a mate is between, lit while it is
139    /// pointed at.
140    pub elements: Vec<PickedElement>,
141}
142
143impl StructureItem {
144    /// The item `name`, of the kind `kind` listed as `kind_label`, drawn and
145    /// shown.
146    pub fn drawn(kind: &str, kind_label: &str, name: &str) -> Self {
147        Self {
148            kind: kind.to_string(),
149            kind_label: kind_label.to_string(),
150            name: name.to_string(),
151            detail: None,
152            visible: Some(true),
153            elements: Vec::new(),
154        }
155    }
156
157    /// The same item, said `detail` of beside its name (see
158    /// [`Self::detail`]).
159    pub fn detailed(self, detail: String) -> Self {
160        Self {
161            detail: Some(detail),
162            ..self
163        }
164    }
165}
166
167/// A view of the program a front end shows beside the steps, and the
168/// commands it is sent from there ([`crate::editor::Command::Panel`]): the
169/// program's parameters, the joints of an assembly. A front end looks its
170/// component up by [`Panel::KEY`].
171pub trait Panel: 'static {
172    /// The panel's key: what its state is under in
173    /// [`crate::editor::ProgramState::panels`], and what its commands name.
174    const KEY: &'static str;
175    /// What it shows.
176    type State: Serialize;
177    /// What it is sent.
178    type Command: DeserializeOwned;
179
180    /// What it shows of `program`, the part `part` reads being what the
181    /// program built as far as it runs.
182    fn state(&self, program: &Program, part: &Reader) -> Self::State;
183
184    /// What the part's structure says beside its items, by their names,
185    /// from what the panel shows (`state`): how free a placed part is.
186    /// Nothing, unless it says otherwise.
187    fn details(&self, state: &Self::State) -> BTreeMap<String, String> {
188        let _ = state;
189        BTreeMap::new()
190    }
191
192    /// Does `command`, saying what it changed.
193    fn command(&self, ctx: &mut EditorContext, command: Self::Command) -> GeopResult<Changed>;
194
195    /// Told once an edit — after a command changed the program or the step
196    /// being edited, once it has run again and settled — never for every
197    /// move of the pointer: where a question about the whole of what was
198    /// built is asked. Nothing, unless it says otherwise.
199    fn after_edit(&self, _ctx: &mut EditorContext) {}
200}
201
202/// A [`Panel`] with its types erased, to keep panels of every kind in one
203/// registry.
204pub trait ErasedPanel {
205    fn key(&self) -> &'static str;
206    /// What it shows of `program` (see [`Panel::state`]), and what the
207    /// structure says beside its items from it (see [`Panel::details`]).
208    fn state(
209        &self,
210        program: &Program,
211        part: &Reader,
212    ) -> GeopResult<(serde_json::Value, BTreeMap<String, String>)>;
213    fn command(&self, ctx: &mut EditorContext, payload: serde_json::Value) -> GeopResult<Changed>;
214    fn after_edit(&self, ctx: &mut EditorContext);
215}
216
217impl<P: Panel> ErasedPanel for P {
218    fn key(&self) -> &'static str {
219        P::KEY
220    }
221
222    fn state(
223        &self,
224        program: &Program,
225        part: &Reader,
226    ) -> GeopResult<(serde_json::Value, BTreeMap<String, String>)> {
227        let state = Panel::state(self, program, part);
228        let details = Panel::details(self, &state);
229        let state = serde_json::to_value(state)
230            .map_err(|e| GeopError::new(format!("the state of the panel `{}`: {e}", P::KEY)))?;
231        Ok((state, details))
232    }
233
234    fn command(&self, ctx: &mut EditorContext, payload: serde_json::Value) -> GeopResult<Changed> {
235        let command = serde_json::from_value(payload)
236            .map_err(|e| GeopError::new(format!("a command of the panel `{}`: {e}", P::KEY)))?;
237        Panel::command(self, ctx, command)
238    }
239
240    fn after_edit(&self, ctx: &mut EditorContext) {
241        Panel::after_edit(self, ctx)
242    }
243}
244
245/// What settles a program's state after every edit: moves what the steps
246/// say a solve moves — where placed parts are — so that the constraints
247/// they declare hold. At most one per registry; with none, the state is
248/// kept as given. When to settle, and what of the program runs first, is
249/// the editor's.
250pub trait Settle: 'static {
251    /// The values of `rerun.program`'s state it moves, and where to: first
252    /// towards what `pulls` pull — every value free to give way — or else,
253    /// where the constraints do not hold, the state of the step `own`
254    /// being edited first. None where nothing needs to move.
255    fn settle(
256        &self,
257        rerun: &mut Rerun,
258        pulls: &[Pull],
259        own: Option<&str>,
260    ) -> GeopResult<StateValues>;
261
262    /// The names of the state it solves for in the part `reader` reads:
263    /// once the whole program has run, the rest of the state goes.
264    fn solvable(&self, reader: &Reader) -> GeopResult<Vec<String>>;
265}
266
267/// An example by name, with what makes it: a program, or the files of one
268/// made of several. Listing examples builds none.
269pub type Example<T> = (&'static str, fn() -> T);
270
271/// The files of an example made of several, by path: the first is the one
272/// edited.
273pub type ExampleFiles = Vec<(&'static str, Program)>;
274
275/// Example programs, and examples made of several files, that an editor
276/// offers to load ([`crate::editor::Command::LoadExample`]).
277#[derive(Default)]
278pub struct ExampleSet {
279    pub programs: Vec<Example<Program>>,
280    pub workspaces: Vec<Example<ExampleFiles>>,
281}
282
283/// Makes a tool to take in hand.
284type MakeTool = Rc<dyn Fn() -> Box<dyn Tool>>;
285
286/// Everything an editor offers: see the module docs.
287#[derive(Default)]
288pub struct Registry {
289    editors: Editors,
290    operations: OperationRegistry,
291    drawers: Vec<Rc<dyn Drawer>>,
292    outputs: Outputs,
293    panels: Vec<Rc<dyn ErasedPanel>>,
294    tools: Vec<(ToolInfo, MakeTool)>,
295    /// The key of the tool in hand while none is taken (see
296    /// [`Registry::idle_tool`]).
297    idle: Option<String>,
298    settler: Option<Rc<dyn Settle>>,
299    examples: Vec<ExampleSet>,
300}
301
302impl Registry {
303    /// Adds the operation `operation`, in the order an editor offers its
304    /// group's operations. Fails for a second of the same kind.
305    pub fn operation<O: Editable>(mut self, operation: O) -> GeopResult<Self> {
306        let operation = Rc::new(operation);
307        self.editors.add(operation.clone())?;
308        self.operations = self.operations.with_erased(operation)?;
309        Ok(self)
310    }
311
312    /// Adds `drawer`, drawing after those already there.
313    pub fn drawer<D: Drawer>(mut self, drawer: D) -> GeopResult<Self> {
314        self.drawers.push(Rc::new(drawer));
315        Ok(self)
316    }
317
318    /// Adds `exporter`, offered after those already there.
319    pub fn exporter<E: Exporter>(mut self, exporter: E) -> GeopResult<Self> {
320        self.outputs = self.outputs.exporter(exporter)?;
321        Ok(self)
322    }
323
324    /// Adds `inspector`, offered after those already there.
325    pub fn inspector<I: Inspector>(mut self, inspector: I) -> GeopResult<Self> {
326        self.outputs = self.outputs.inspector(inspector)?;
327        Ok(self)
328    }
329
330    /// Adds `panel`. Fails for a second of the same key.
331    pub fn panel<P: Panel>(mut self, panel: P) -> GeopResult<Self> {
332        if self.panels.iter().any(|p| p.key() == P::KEY) {
333            return Err(GeopError::new(format!(
334                "the panel `{}` is registered twice",
335                P::KEY
336            )));
337        }
338        self.panels.push(Rc::new(panel));
339        Ok(self)
340    }
341
342    /// Adds `tool`, offered as `info` says: what is taken in hand by its
343    /// key is a copy of it, as it is now. Fails for a second of the same
344    /// key, and for a key a pick tool's could be.
345    pub fn tool<T: Tool + Clone>(mut self, info: ToolInfo, tool: T) -> GeopResult<Self> {
346        let key = info.key;
347        if self.tools.iter().any(|(i, _)| i.key == key) || key.starts_with(PickTool::PREFIX) {
348            return Err(GeopError::new(format!(
349                "the tool `{key}` is registered twice"
350            )));
351        }
352        self.tools
353            .push((info, Rc::new(move || Box::new(tool.clone()))));
354        Ok(self)
355    }
356
357    /// Makes the tool `key`, added before, the one in hand while no other
358    /// is taken and no step is edited: what a press and a move in the view
359    /// do by default — drag a placed part, say, where a part is placed.
360    /// Fails for a key not added, and if there is one already: two would
361    /// each take the same press their own way.
362    pub fn idle_tool(mut self, key: &str) -> GeopResult<Self> {
363        if !self.tools.iter().any(|(info, _)| info.key == key) {
364            return Err(GeopError::new(format!(
365                "the idle tool `{key}` is no tool added"
366            )));
367        }
368        if let Some(idle) = &self.idle {
369            return Err(GeopError::new(format!(
370                "the idle tool is `{idle}` already, not also `{key}`"
371            )));
372        }
373        self.idle = Some(key.to_string());
374        Ok(self)
375    }
376
377    /// Makes `settler` what settles the program's state. Fails if there is
378    /// one already: two would move the same state each its own way.
379    pub fn settler<Scalar: Settle>(mut self, settler: Scalar) -> GeopResult<Self> {
380        if self.settler.is_some() {
381            return Err(GeopError::new("a settler is registered twice"));
382        }
383        self.settler = Some(Rc::new(settler));
384        Ok(self)
385    }
386
387    /// Adds the examples `set`. Fails for an example of a name already
388    /// there.
389    pub fn examples(mut self, set: ExampleSet) -> GeopResult<Self> {
390        let names = (set.programs.iter().map(|(name, _)| name))
391            .chain(set.workspaces.iter().map(|(name, _)| name));
392        for name in names {
393            let mut known =
394                (self.example_names().chain(self.workspace_example_names())).map(|n| n == *name);
395            if known.any(|same| same) {
396                return Err(GeopError::new(format!(
397                    "the example `{name}` is registered twice"
398                )));
399            }
400        }
401        self.examples.push(set);
402        Ok(self)
403    }
404
405    /// The operations, as a program runner runs them.
406    pub fn operations(&self) -> OperationRegistry {
407        self.operations.clone()
408    }
409
410    /// The operations, and how each is edited.
411    pub fn editors(&self) -> &Editors {
412        &self.editors
413    }
414
415    /// What each operation is, in the order they were added.
416    pub fn infos(&self) -> Vec<Listed> {
417        self.editors.infos()
418    }
419
420    /// The exporters and inspectors.
421    pub fn outputs(&self) -> &Outputs {
422        &self.outputs
423    }
424
425    /// The exporters and inspectors, without the rest.
426    pub fn into_outputs(self) -> Outputs {
427        self.outputs
428    }
429
430    /// The part `reader` reads, as every drawer draws it, with what its
431    /// steps annotate it with: what a drawer drew before of what reads the
432    /// same, as `drawings` kept it — and, given the runner `kept_by` of the
433    /// program whose part it is, what a drawer that keeps its drawings
434    /// ([`Drawer::kept`]) drew of it in an earlier process.
435    pub fn draw(
436        &self,
437        reader: &Reader,
438        drawings: &Drawings,
439        kept_by: Option<&Runner>,
440    ) -> GeopResult<PartView> {
441        let mut view = PartView::new();
442        for (k, drawer) in self.drawers.iter().enumerate() {
443            let kept_by = kept_by.filter(|_| drawer.kept());
444            let drawn = drawings.drawn(k, reader, kept_by, |reader| {
445                let mut drawn = PartView::new();
446                drawer.draw(reader, &mut drawn)?;
447                Ok(drawn)
448            })?;
449            view.add(&drawn);
450        }
451        view.annotations = annotations_of(reader);
452        Ok(view.finish())
453    }
454
455    /// What the part `reader` reads has, as every drawer lists it, each
456    /// drawn item shown unless `hidden` names it — and whatever its steps
457    /// list.
458    pub fn structure(&self, reader: &Reader, hidden: &[String]) -> GeopResult<Vec<StructureItem>> {
459        let mut items = Vec::new();
460        for drawer in &self.drawers {
461            items.extend(drawer.structure(reader)?);
462        }
463        for item in &mut items {
464            if item.visible.is_some() {
465                item.visible = Some(!hidden.contains(&item.name));
466            }
467        }
468        items.extend(
469            listed_by_steps(reader)
470                .into_iter()
471                .map(|listed| StructureItem {
472                    kind_label: plural(&listed.kind),
473                    kind: listed.kind,
474                    name: listed.name,
475                    detail: None,
476                    visible: None,
477                    elements: listed.elements,
478                }),
479        );
480        Ok(items)
481    }
482
483    /// What the drawers draw of the part `reader` reads only to be picked
484    /// (see [`Drawer::hidden`]).
485    pub fn hidden(&self, reader: &Reader) -> Vec<String> {
486        (self.drawers.iter())
487            .flat_map(|drawer| drawer.hidden(reader))
488            .collect()
489    }
490
491    /// The panels, in the order they were added.
492    pub fn panels(&self) -> impl Iterator<Item = &dyn ErasedPanel> {
493        self.panels.iter().map(Rc::as_ref)
494    }
495
496    /// The panel `key`.
497    pub fn get_panel(&self, key: &str) -> GeopResult<&dyn ErasedPanel> {
498        (self.panels())
499            .find(|panel| panel.key() == key)
500            .ok_or_else(|| GeopError::new(format!("there is no panel `{key}`")))
501    }
502
503    /// The tools added, as a toolbar offers them, in the order they were
504    /// added.
505    pub fn tools(&self) -> Vec<&ToolInfo> {
506        self.tools.iter().map(|(info, _)| info).collect()
507    }
508
509    /// A new tool `key`, to take in hand: one added, or, for
510    /// `pick:<inspector>`, the pick tool of that inspector.
511    pub fn tool_in_hand(&self, key: &str) -> GeopResult<Box<dyn Tool>> {
512        if let Some(inspector) = key.strip_prefix(PickTool::PREFIX) {
513            let info = self.outputs.get_inspector(inspector)?.info();
514            return Ok(Box::new(PickTool::new(info)?));
515        }
516        let (_, make) = (self.tools.iter())
517            .find(|(info, _)| info.key == key)
518            .ok_or_else(|| GeopError::new(format!("there is no tool `{key}`")))?;
519        Ok(make())
520    }
521
522    /// The key of the tool in hand while none is taken, and a new one of
523    /// it, if there is one (see [`Registry::idle_tool`]).
524    pub fn idle_in_hand(&self) -> Option<(String, Box<dyn Tool>)> {
525        let key = self.idle.as_ref()?;
526        Some((key.clone(), self.tool_in_hand(key).ok()?))
527    }
528
529    /// What settles the program's state, if anything does.
530    pub fn get_settler(&self) -> Option<&dyn Settle> {
531        self.settler.as_deref()
532    }
533
534    /// The names of the example programs.
535    pub fn example_names(&self) -> impl Iterator<Item = &'static str> + '_ {
536        (self.examples.iter()).flat_map(|set| set.programs.iter().map(|(name, _)| *name))
537    }
538
539    /// The example programs, each by name with what makes it: listing them
540    /// builds none.
541    pub fn example_programs(&self) -> Vec<Example<Program>> {
542        (self.examples.iter())
543            .flat_map(|set| set.programs.iter().copied())
544            .collect()
545    }
546
547    /// The names of the examples made of several files.
548    pub fn workspace_example_names(&self) -> impl Iterator<Item = &'static str> + '_ {
549        (self.examples.iter()).flat_map(|set| set.workspaces.iter().map(|(name, _)| *name))
550    }
551
552    /// The example program `name`, made.
553    pub fn example(&self, name: &str) -> GeopResult<Program> {
554        (self.examples.iter())
555            .flat_map(|set| &set.programs)
556            .find(|(n, _)| *n == name)
557            .map(|(_, make)| make())
558            .ok_or_else(|| GeopError::new(format!("there is no example {name:?}")))
559    }
560
561    /// The files of the example of several files `name`, made.
562    pub fn workspace_example(&self, name: &str) -> GeopResult<ExampleFiles> {
563        (self.examples.iter())
564            .flat_map(|set| &set.workspaces)
565            .find(|(n, _)| *n == name)
566            .map(|(_, make)| make())
567            .ok_or_else(|| GeopError::new(format!("there is no example {name:?}")))
568    }
569}
570
571/// What a list calls the things of the kind `kind` a step lists: `mate`
572/// as `Mates`.
573fn plural(kind: &str) -> String {
574    let words = kind.replace('_', " ");
575    let mut chars = words.chars();
576    match chars.next() {
577        Some(first) => format!("{}{}s", first.to_uppercase(), chars.as_str()),
578        None => String::new(),
579    }
580}