1use panproto_expr::Expr;
7
8use crate::error::VcsError;
9use crate::hash::ObjectId;
10use crate::object::Object;
11use crate::store::Store;
12
13pub fn store_expr(store: &mut dyn Store, expr: &Expr) -> Result<ObjectId, VcsError> {
23 let object = Object::Expr(Box::new(expr.clone()));
24 store.put(&object)
25}
26
27pub fn load_expr(store: &dyn Store, id: &ObjectId) -> Result<Expr, VcsError> {
35 match store.get(id)? {
36 Object::Expr(expr) => Ok(*expr),
37 other => Err(VcsError::WrongObjectType {
38 expected: "expr",
39 found: other.type_name(),
40 }),
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47 use panproto_expr::{BuiltinOp, Literal};
48
49 use crate::MemStore;
50
51 #[test]
52 fn store_load_round_trip() -> Result<(), VcsError> {
53 let mut store = MemStore::new();
54 let expr = Expr::let_in(
55 "x",
56 Expr::Lit(Literal::Int(42)),
57 Expr::builtin(
58 BuiltinOp::Add,
59 vec![Expr::var("x"), Expr::Lit(Literal::Int(1))],
60 ),
61 );
62
63 let id = store_expr(&mut store, &expr)?;
64 let loaded = load_expr(&store, &id)?;
65 assert_eq!(loaded, expr);
66 Ok(())
67 }
68
69 #[test]
70 fn store_idempotent() -> Result<(), VcsError> {
71 let mut store = MemStore::new();
72 let expr = Expr::Lit(Literal::Str("hello".into()));
73
74 let id1 = store_expr(&mut store, &expr)?;
75 let id2 = store_expr(&mut store, &expr)?;
76 assert_eq!(id1, id2);
77 Ok(())
78 }
79
80 #[test]
81 fn load_wrong_type_returns_error() -> Result<(), VcsError> {
82 use std::collections::HashMap;
83
84 let mut store = MemStore::new();
85 let schema = panproto_schema::Schema {
86 protocol: "test".into(),
87 vertices: HashMap::new(),
88 edges: HashMap::new(),
89 hyper_edges: HashMap::new(),
90 constraints: HashMap::new(),
91 required: HashMap::new(),
92 nsids: HashMap::new(),
93 entries: Vec::new(),
94 variants: HashMap::new(),
95 orderings: HashMap::new(),
96 recursion_points: HashMap::new(),
97 spans: HashMap::new(),
98 usage_modes: HashMap::new(),
99 nominal: HashMap::new(),
100 coercions: HashMap::new(),
101 mergers: HashMap::new(),
102 defaults: HashMap::new(),
103 policies: HashMap::new(),
104 outgoing: HashMap::new(),
105 incoming: HashMap::new(),
106 between: HashMap::new(),
107 };
108 let id = crate::tree::store_schema_as_tree(&mut store, schema)?;
109 let result = load_expr(&store, &id);
110 assert!(matches!(
111 result,
112 Err(VcsError::WrongObjectType {
113 expected: "expr",
114 ..
115 })
116 ));
117 Ok(())
118 }
119
120 #[test]
121 fn load_nonexistent_returns_error() {
122 let store = MemStore::new();
123 let result = load_expr(&store, &ObjectId::ZERO);
124 assert!(matches!(result, Err(VcsError::ObjectNotFound { .. })));
125 }
126
127 #[test]
128 fn different_exprs_get_different_ids() -> Result<(), VcsError> {
129 let mut store = MemStore::new();
130 let e1 = Expr::Lit(Literal::Int(1));
131 let e2 = Expr::Lit(Literal::Int(2));
132
133 let id1 = store_expr(&mut store, &e1)?;
134 let id2 = store_expr(&mut store, &e2)?;
135 assert_ne!(id1, id2);
136 Ok(())
137 }
138
139 #[test]
140 fn complex_expr_round_trip() -> Result<(), VcsError> {
141 let mut store = MemStore::new();
142 let expr = Expr::lam(
143 "record",
144 Expr::builtin(
145 BuiltinOp::Concat,
146 vec![
147 Expr::field(Expr::var("record"), "first_name"),
148 Expr::Lit(Literal::Str(" ".into())),
149 ],
150 ),
151 );
152
153 let id = store_expr(&mut store, &expr)?;
154 let loaded = load_expr(&store, &id)?;
155 assert_eq!(loaded, expr);
156 Ok(())
157 }
158}