Skip to main content

brep_app/
instructions.rs

1//! Manufacturing instructions in the app: making an instructions document
2//! from an assembly, the open one or a stored one.
3//!
4//! The new document places the assembly as one grounded component (its
5//! store key and content signature recorded, so Update components sees a new
6//! revision) and records the same key as the instructions' target: in a PLM
7//! session that is `part/<id>/rev/<rev>`, from which the server derives what
8//! the instructions describe.
9use crate::document::{Document, Documents, EMPTY_DOCUMENT};
10use crate::document_class::{self, DocumentClass};
11use crate::store::ModelStore;
12use brep_instructions::model::Target;
13use brep_render::engine_state::{document_signature, ComponentInsert};
14use serde_json::Value;
15
16/// Where the assembly comes from.
17pub enum Source<'a> {
18    /// The active tab's document, which must have been saved.
19    Active,
20    /// A document in the store, by its key.
21    Stored(&'a str),
22}
23
24/// Open a new tab holding instructions for the assembly, with the wizard's
25/// proposal when `wizard` (made once the assembly's run lands). Returns the new tab's index; nothing opens on a
26/// refusal.
27pub fn new_instructions(docs: &mut Documents, store: &dyn ModelStore, source: Source<'_>, wizard: bool) -> Result<usize, String> {
28    let (key, json, name) = match source {
29        Source::Active => {
30            let key = docs
31                .active()
32                .name()
33                .map(str::to_string)
34                .ok_or("save the assembly first: instructions describe a saved revision")?;
35            let name = docs.active().title();
36            (key, docs.tab_engine().history.request_json(), name)
37        }
38        Source::Stored(key) => {
39            let json = store.read(key).ok_or_else(|| format!("no document '{key}'"))?;
40            let name = document_class::strip_class_extension(&crate::store::file_name_of(key)).to_string();
41            (key.to_string(), json, name)
42        }
43    };
44    let document: Value = serde_json::from_str(&json).map_err(|e| format!("'{key}' is not a document: {e}"))?;
45    if DocumentClass::of_document(&document) == DocumentClass::Instructions {
46        return Err("that document is already instructions; open an assembly".into());
47    }
48    let signature = document_signature(&json);
49    let target = Target {
50        source_key: key.clone(),
51        part_number: String::new(),
52        revision: String::new(),
53        signature: signature.clone(),
54        name: name.clone(),
55    };
56    let mut engine = docs.spawn_engine();
57    engine.set_history_json(EMPTY_DOCUMENT)?;
58    // Instructions are edited in their own workbench, and saved with it.
59    engine.settings.workbench = crate::workbench::instructions::INSTRUCTIONS.id.into();
60    engine.instructions_start(
61        ComponentInsert::New { name: &name, source_key: &key, source_signature: &signature, document_json: &json },
62        target,
63    )?;
64    if wizard {
65        engine.instructions_run_wizard_when_built()?;
66    }
67    Ok(docs.open_document(Document::new(engine)))
68}