sim_lib_doc_core/
shape.rs1use std::sync::Arc;
4
5use sim_kernel::{
6 Cx, DefaultFactory, Expr, Factory, ShapeRef, Symbol, Value,
7 shape::{MatchScore, Shape, ShapeDoc, ShapeMatch},
8};
9
10use crate::OfficeError;
11use crate::model::{Doc, DocKind};
12
13#[derive(Clone, Debug, PartialEq, Eq)]
15pub struct DocKindShape {
16 kind: DocKind,
17}
18
19impl DocKindShape {
20 #[must_use]
22 pub fn new(kind: DocKind) -> Self {
23 Self { kind }
24 }
25
26 #[must_use]
28 pub fn kind(&self) -> &DocKind {
29 &self.kind
30 }
31}
32
33pub fn doc_shape(kind: &DocKind) -> Result<ShapeRef, OfficeError> {
35 DefaultFactory
36 .opaque(Arc::new(DocKindShape::new(kind.clone())))
37 .map_err(|err| OfficeError::ShapeBuild(err.to_string()))
38}
39
40impl Shape for DocKindShape {
41 fn symbol(&self) -> Option<Symbol> {
42 Some(Symbol::qualified("office/doc", self.kind.0.clone()))
43 }
44
45 fn check_value(&self, _cx: &mut Cx, value: Value) -> sim_kernel::Result<ShapeMatch> {
46 let Some(doc) = value.object().downcast_ref::<Doc>() else {
47 return Ok(ShapeMatch::reject(format!(
48 "expected {} document value",
49 self.kind.0
50 )));
51 };
52 if doc.kind == self.kind {
53 Ok(ShapeMatch::accept(MatchScore::exact(20)))
54 } else {
55 Ok(ShapeMatch::reject(format!(
56 "expected {} document value, found {}",
57 self.kind.0, doc.kind.0
58 )))
59 }
60 }
61
62 fn check_expr(&self, _cx: &mut Cx, _expr: &Expr) -> sim_kernel::Result<ShapeMatch> {
63 Ok(ShapeMatch::reject(format!(
64 "expected {} document value",
65 self.kind.0
66 )))
67 }
68
69 fn describe(&self, _cx: &mut Cx) -> sim_kernel::Result<ShapeDoc> {
70 Ok(ShapeDoc::new(format!("office document {}", self.kind.0))
71 .with_detail("matches Doc values by open DocKind string"))
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use std::sync::Arc;
78
79 use sim_kernel::{DefaultFactory, NoopEvalPolicy};
80
81 use super::*;
82
83 #[test]
84 fn doc_shape_is_object_accessible() {
85 let value = doc_shape(&DocKind::new("sheet")).unwrap();
86 assert!(value.object().as_shape().is_some());
87 }
88
89 #[test]
90 fn doc_shape_matches_document_kind() {
91 let mut cx = Cx::new(
92 Arc::new(NoopEvalPolicy),
93 Arc::new(DefaultFactory),
94 sim_kernel::HandleSeed::new(0xfae8_8f06_e204_cabf),
95 );
96 let body = cx.factory().nil().unwrap();
97 let doc = Doc::new(
98 DocKind::new("report"),
99 crate::DocId::new("r1"),
100 body,
101 vec![],
102 );
103 let value = cx.factory().opaque(Arc::new(doc)).unwrap();
104 let shape_value = doc_shape(&DocKind::new("report")).unwrap();
105 let shape = shape_value.object().as_shape().unwrap();
106
107 assert!(shape.check_value(&mut cx, value).unwrap().accepted);
108 assert_eq!(
109 shape.describe(&mut cx).unwrap().name,
110 "office document report"
111 );
112 }
113}