1use crate::core::{map_entries, native_variadic_function, ExtensionValue, Value};
7use crate::lang::data::{Keyword, Map as PMap, Vector as PVector};
8use std::cell::RefCell;
9use std::collections::HashMap;
10
11const PROVIDER: &str = "std.lang";
12
13#[derive(Clone)]
14enum SnapshotKind {
15 Library,
16 Harness,
17}
18
19#[derive(Clone)]
20enum Record {
21 Immutable {
22 type_name: String,
23 data: Value,
24 },
25 Library {
26 config: Value,
27 books: HashMap<String, Value>,
28 revision: i64,
29 },
30 Snapshot {
31 kind: SnapshotKind,
32 owner: u64,
33 books: HashMap<String, Value>,
34 library_revision: i64,
35 runtime_closed: bool,
36 runtime_revision: i64,
37 harness_closed: bool,
38 },
39 Runtime {
40 config: Value,
41 closed: bool,
42 revision: i64,
43 },
44 Harness {
45 config: Value,
46 library: u64,
47 runtime: u64,
48 closed: bool,
49 },
50}
51
52#[derive(Default)]
53struct State {
54 next: u64,
55 records: HashMap<u64, Record>,
56}
57
58thread_local! {
59 static STATE: RefCell<State> = RefCell::new(State::default());
60}
61
62pub fn function(type_name: &str, method: &str) -> Result<Value, String> {
64 let type_name = type_name.to_owned();
65 let method = method.to_owned();
66 let display = format!("std.lang.{type_name}/{method}");
67 Ok(native_variadic_function(&display, move |values| {
68 invoke(&type_name, &method, values)
69 }))
70}
71
72fn invoke(type_name: &str, method: &str, values: Vec<Value>) -> Result<Value, String> {
73 match type_name {
74 "BookMeta" | "BookEntry" | "BookModule" | "Book" | "Compilation" => {
75 immutable(type_name, method, values, "data")
76 }
77 "Compiler" => immutable(type_name, method, values, "config"),
78 "Library" => library(method, values),
79 "Snapshot" => snapshot(method, values),
80 "Runtime" => runtime(method, values),
81 "Harness" => harness(method, values),
82 _ => Err(failure(type_name, method, "is not installed")),
83 }
84}
85
86fn immutable(
87 type_name: &str,
88 method: &str,
89 values: Vec<Value>,
90 accessor: &str,
91) -> Result<Value, String> {
92 match method {
93 "create" => {
94 require_arity(type_name, method, &values, 1)?;
95 let data = config(&values[0], &format!("{type_name}/create"))?;
96 Ok(allocate(Record::Immutable {
97 type_name: type_name.into(),
98 data,
99 }, type_name))
100 }
101 value if value == accessor => {
102 require_arity(type_name, method, &values, 1)?;
103 let handle = handle(&values[0], type_name, method)?;
104 STATE.with(|state| match state.borrow().records.get(&handle) {
105 Some(Record::Immutable { type_name: actual, data }) if actual == type_name => {
106 Ok(data.clone())
107 }
108 _ => Err(failure(type_name, method, "expects its matching std.lang value")),
109 })
110 }
111 _ => Err(failure(type_name, method, "is not installed")),
112 }
113}
114
115fn library(method: &str, values: Vec<Value>) -> Result<Value, String> {
116 match method {
117 "create" => {
118 require_arity("Library", method, &values, 1)?;
119 Ok(allocate(
120 Record::Library {
121 config: config(&values[0], "Library/create")?,
122 books: HashMap::new(),
123 revision: 0,
124 },
125 "Library",
126 ))
127 }
128 "config" => {
129 require_arity("Library", method, &values, 1)?;
130 let handle = handle(&values[0], "Library", method)?;
131 with_library(handle, method, |config, _, _| Ok(config.clone()))
132 }
133 "install" => {
134 require_arity("Library", method, &values, 2)?;
135 let library = handle(&values[0], "Library", method)?;
136 let book = handle(&values[1], "Book", method)?;
137 let key = immutable_data(book, "Book", method).and_then(|data| book_key(&data, method))?;
138 STATE.with(|state| {
139 let mut state = state.borrow_mut();
140 let Some(Record::Library { books, revision, .. }) = state.records.get_mut(&library) else {
141 return Err(failure("Library", method, "expects a std.lang.Library value"));
142 };
143 books.insert(key, values[1].clone());
144 *revision += 1;
145 Ok(values[0].clone())
146 })
147 }
148 "remove" => {
149 require_arity("Library", method, &values, 2)?;
150 let library = handle(&values[0], "Library", method)?;
151 let key = book_key_argument(&values[1], method)?;
152 STATE.with(|state| {
153 let mut state = state.borrow_mut();
154 let Some(Record::Library { books, revision, .. }) = state.records.get_mut(&library) else {
155 return Err(failure("Library", method, "expects a std.lang.Library value"));
156 };
157 let removed = books.remove(&key).unwrap_or(Value::Nil);
158 if !matches!(removed, Value::Nil) {
159 *revision += 1;
160 }
161 Ok(removed)
162 })
163 }
164 "resolve" => {
165 require_arity("Library", method, &values, 2)?;
166 let library = handle(&values[0], "Library", method)?;
167 let key = book_key_argument(&values[1], method)?;
168 with_library(library, method, |_, books, _| {
169 Ok(books.get(&key).cloned().unwrap_or(Value::Nil))
170 })
171 }
172 "books" => {
173 require_arity("Library", method, &values, 1)?;
174 let library = handle(&values[0], "Library", method)?;
175 with_library(library, method, |_, books, _| {
176 Ok(Value::Vector(PVector::from_iter(books.values().cloned())))
177 })
178 }
179 "snapshot" => {
180 require_arity("Library", method, &values, 1)?;
181 let library = handle(&values[0], "Library", method)?;
182 let (books, revision) = with_library(library, method, |_, books, revision| {
183 Ok((books.clone(), *revision))
184 })?;
185 Ok(allocate(
186 Record::Snapshot {
187 kind: SnapshotKind::Library,
188 owner: library,
189 books,
190 library_revision: revision,
191 runtime_closed: false,
192 runtime_revision: 0,
193 harness_closed: false,
194 },
195 "Snapshot",
196 ))
197 }
198 "restore" => {
199 require_arity("Library", method, &values, 2)?;
200 let library = handle(&values[0], "Library", method)?;
201 let snapshot = handle(&values[1], "Snapshot", method)?;
202 let (books, revision) = snapshot_library(snapshot, library, method)?;
203 STATE.with(|state| {
204 let mut state = state.borrow_mut();
205 let Some(Record::Library { books: target, revision: target_revision, .. }) = state.records.get_mut(&library) else {
206 return Err(failure("Library", method, "expects a std.lang.Library value"));
207 };
208 *target = books;
209 *target_revision = revision;
210 Ok(values[0].clone())
211 })
212 }
213 "reset" => {
214 require_arity("Library", method, &values, 1)?;
215 let library = handle(&values[0], "Library", method)?;
216 STATE.with(|state| {
217 let mut state = state.borrow_mut();
218 let Some(Record::Library { books, revision, .. }) = state.records.get_mut(&library) else {
219 return Err(failure("Library", method, "expects a std.lang.Library value"));
220 };
221 books.clear();
222 *revision = 0;
223 Ok(values[0].clone())
224 })
225 }
226 "state" => {
227 require_arity("Library", method, &values, 1)?;
228 let library = handle(&values[0], "Library", method)?;
229 with_library(library, method, |_, books, revision| {
230 Ok(map(vec![
231 ("revision", Value::Number(*revision)),
232 ("book-count", Value::Number(books.len() as i64)),
233 ("books", Value::Vector(PVector::from_iter(books.values().cloned()))),
234 ]))
235 })
236 }
237 _ => Err(failure("Library", method, "is not installed")),
238 }
239}
240
241fn snapshot(method: &str, values: Vec<Value>) -> Result<Value, String> {
242 if method != "data" {
243 return Err(failure("Snapshot", method, "is not installed"));
244 }
245 require_arity("Snapshot", method, &values, 1)?;
246 let snapshot = handle(&values[0], "Snapshot", method)?;
247 STATE.with(|state| match state.borrow().records.get(&snapshot) {
248 Some(Record::Snapshot { kind, books, library_revision, runtime_closed, runtime_revision, harness_closed, .. }) => Ok(map(vec![
249 ("kind", Value::Keyword(Keyword::from(match kind { SnapshotKind::Library => "library", SnapshotKind::Harness => "harness" }))),
250 ("library-revision", Value::Number(*library_revision)),
251 ("books", Value::Vector(PVector::from_iter(books.values().cloned()))),
252 ("runtime-closed?", Value::Bool(*runtime_closed)),
253 ("runtime-revision", Value::Number(*runtime_revision)),
254 ("harness-closed?", Value::Bool(*harness_closed)),
255 ])),
256 _ => Err(failure("Snapshot", method, "expects a std.lang.Snapshot value")),
257 })
258}
259
260fn runtime(method: &str, values: Vec<Value>) -> Result<Value, String> {
261 match method {
262 "create" => {
263 require_arity("Runtime", method, &values, 1)?;
264 Ok(allocate(Record::Runtime { config: config(&values[0], "Runtime/create")?, closed: false, revision: 0 }, "Runtime"))
265 }
266 "config" => runtime_config(method, values),
267 "state" => runtime_state(method, values),
268 "reset" => runtime_mutate(method, values, false, true),
269 "close" => runtime_mutate(method, values, true, false),
270 "closed?" => {
271 require_arity("Runtime", method, &values, 1)?;
272 let runtime = handle(&values[0], "Runtime", method)?;
273 with_runtime(runtime, method, |_, closed, _| Ok(Value::Bool(*closed)))
274 }
275 _ => Err(failure("Runtime", method, "is not installed")),
276 }
277}
278
279fn runtime_config(method: &str, values: Vec<Value>) -> Result<Value, String> {
280 require_arity("Runtime", method, &values, 1)?;
281 let runtime = handle(&values[0], "Runtime", method)?;
282 with_runtime(runtime, method, |config, _, _| Ok(config.clone()))
283}
284
285fn runtime_state(method: &str, values: Vec<Value>) -> Result<Value, String> {
286 require_arity("Runtime", method, &values, 1)?;
287 let runtime = handle(&values[0], "Runtime", method)?;
288 with_runtime(runtime, method, |_, closed, revision| {
289 Ok(runtime_state_value(*closed, *revision))
290 })
291}
292
293fn runtime_mutate(method: &str, values: Vec<Value>, close: bool, reset: bool) -> Result<Value, String> {
294 require_arity("Runtime", method, &values, 1)?;
295 let runtime = handle(&values[0], "Runtime", method)?;
296 STATE.with(|state| {
297 let mut state = state.borrow_mut();
298 let Some(Record::Runtime { closed, revision, .. }) = state.records.get_mut(&runtime) else {
299 return Err(failure("Runtime", method, "expects a std.lang.Runtime value"));
300 };
301 if close { *closed = true; }
302 if reset { *closed = false; *revision = 0; }
303 Ok(values[0].clone())
304 })
305}
306
307fn harness(method: &str, values: Vec<Value>) -> Result<Value, String> {
308 match method {
309 "create" => {
310 require_arity("Harness", method, &values, 1)?;
311 let config = config(&values[0], "Harness/create")?;
312 let library = optional_handle(&config, "library", "Library", method)?
313 .unwrap_or_else(|| allocate(Record::Library { config: empty_map(), books: HashMap::new(), revision: 0 }, "Library").extension_handle().unwrap());
314 let runtime = optional_handle(&config, "runtime", "Runtime", method)?
315 .unwrap_or_else(|| allocate(Record::Runtime { config: empty_map(), closed: false, revision: 0 }, "Runtime").extension_handle().unwrap());
316 Ok(allocate(Record::Harness { config, library, runtime, closed: false }, "Harness"))
317 }
318 "config" => {
319 require_arity("Harness", method, &values, 1)?;
320 let harness = handle(&values[0], "Harness", method)?;
321 with_harness(harness, method, |config, _, _, _| Ok(config.clone()))
322 }
323 "library" => {
324 require_arity("Harness", method, &values, 1)?;
325 let harness = handle(&values[0], "Harness", method)?;
326 with_harness(harness, method, |_, library, _, _| Ok(extension("Library", *library)))
327 }
328 "runtime" => {
329 require_arity("Harness", method, &values, 1)?;
330 let harness = handle(&values[0], "Harness", method)?;
331 with_harness(harness, method, |_, _, runtime, _| Ok(extension("Runtime", *runtime)))
332 }
333 "snapshot" => harness_snapshot(method, values),
334 "restore" => harness_restore(method, values),
335 "reset" => harness_reset(method, values),
336 "close" => harness_close(method, values),
337 "closed?" => {
338 require_arity("Harness", method, &values, 1)?;
339 let harness = handle(&values[0], "Harness", method)?;
340 with_harness(harness, method, |_, _, _, closed| Ok(Value::Bool(*closed)))
341 }
342 "state" => harness_state(method, values),
343 _ => Err(failure("Harness", method, "is not installed")),
344 }
345}
346
347fn harness_snapshot(method: &str, values: Vec<Value>) -> Result<Value, String> {
348 require_arity("Harness", method, &values, 1)?;
349 let harness = handle(&values[0], "Harness", method)?;
350 let (_, library, runtime, closed) = with_harness(harness, method, |config, library, runtime, closed| Ok((config.clone(), *library, *runtime, *closed)))?;
351 let (books, library_revision) = with_library(library, method, |_, books, revision| Ok((books.clone(), *revision)))?;
352 let (_, runtime_closed, runtime_revision) = with_runtime(runtime, method, |config, closed, revision| Ok((config.clone(), *closed, *revision)))?;
353 Ok(allocate(Record::Snapshot { kind: SnapshotKind::Harness, owner: harness, books, library_revision, runtime_closed, runtime_revision, harness_closed: closed }, "Snapshot"))
354}
355
356fn harness_restore(method: &str, values: Vec<Value>) -> Result<Value, String> {
357 require_arity("Harness", method, &values, 2)?;
358 let harness = handle(&values[0], "Harness", method)?;
359 let snapshot = handle(&values[1], "Snapshot", method)?;
360 let (books, library_revision, runtime_closed, runtime_revision, harness_closed) = snapshot_harness(snapshot, harness, method)?;
361 let (_, library, runtime, _) = with_harness(harness, method, |config, library, runtime, closed| Ok((config.clone(), *library, *runtime, *closed)))?;
362 restore_library(library, books, library_revision, method)?;
363 restore_runtime(runtime, runtime_closed, runtime_revision, method)?;
364 STATE.with(|state| match state.borrow_mut().records.get_mut(&harness) {
365 Some(Record::Harness { closed, .. }) => { *closed = harness_closed; Ok(values[0].clone()) }
366 _ => Err(failure("Harness", method, "expects a std.lang.Harness value")),
367 })
368}
369
370fn harness_reset(method: &str, values: Vec<Value>) -> Result<Value, String> {
371 require_arity("Harness", method, &values, 1)?;
372 let harness = handle(&values[0], "Harness", method)?;
373 let (_, library, runtime, _) = with_harness(harness, method, |config, library, runtime, closed| Ok((config.clone(), *library, *runtime, *closed)))?;
374 restore_library(library, HashMap::new(), 0, method)?;
375 restore_runtime(runtime, false, 0, method)?;
376 STATE.with(|state| match state.borrow_mut().records.get_mut(&harness) {
377 Some(Record::Harness { closed, .. }) => { *closed = false; Ok(values[0].clone()) }
378 _ => Err(failure("Harness", method, "expects a std.lang.Harness value")),
379 })
380}
381
382fn harness_close(method: &str, values: Vec<Value>) -> Result<Value, String> {
383 require_arity("Harness", method, &values, 1)?;
384 let harness = handle(&values[0], "Harness", method)?;
385 let (_, _, runtime, _) = with_harness(harness, method, |config, library, runtime, closed| Ok((config.clone(), *library, *runtime, *closed)))?;
386 restore_runtime(runtime, true, runtime_revision(runtime, method)?, method)?;
387 STATE.with(|state| match state.borrow_mut().records.get_mut(&harness) {
388 Some(Record::Harness { closed, .. }) => { *closed = true; Ok(values[0].clone()) }
389 _ => Err(failure("Harness", method, "expects a std.lang.Harness value")),
390 })
391}
392
393fn harness_state(method: &str, values: Vec<Value>) -> Result<Value, String> {
394 require_arity("Harness", method, &values, 1)?;
395 let harness = handle(&values[0], "Harness", method)?;
396 let (_, library, runtime, closed) = with_harness(harness, method, |config, library, runtime, closed| Ok((config.clone(), *library, *runtime, *closed)))?;
397 let library_value = with_library(library, method, |_, books, revision| Ok(map(vec![("revision", Value::Number(*revision)), ("book-count", Value::Number(books.len() as i64)), ("books", Value::Vector(PVector::from_iter(books.values().cloned())))])))?;
398 let runtime_value = with_runtime(runtime, method, |_, runtime_closed, revision| Ok(runtime_state_value(*runtime_closed, *revision)))?;
399 Ok(map(vec![("state", Value::Keyword(Keyword::from(if closed { "closed" } else { "ready" }))), ("library", library_value), ("runtime", runtime_value)]))
400}
401
402fn allocate(record: Record, type_name: &str) -> Value {
403 STATE.with(|state| {
404 let mut state = state.borrow_mut();
405 state.next += 1;
406 let handle = state.next;
407 state.records.insert(handle, record);
408 extension(type_name, handle)
409 })
410}
411
412fn extension(type_name: &str, handle: u64) -> Value {
413 Value::Extension(ExtensionValue { provider: PROVIDER.into(), type_name: type_name.into(), handle })
414}
415
416trait ExtensionHandle {
417 fn extension_handle(&self) -> Option<u64>;
418}
419
420impl ExtensionHandle for Value {
421 fn extension_handle(&self) -> Option<u64> {
422 match self { Value::Extension(value) if value.provider == PROVIDER => Some(value.handle), _ => None }
423 }
424}
425
426fn handle(value: &Value, expected: &str, method: &str) -> Result<u64, String> {
427 match value {
428 Value::Extension(value) if value.provider == PROVIDER && value.type_name == expected => Ok(value.handle),
429 _ => Err(failure(expected, method, &format!("expects a std.lang.{expected} value"))),
430 }
431}
432
433fn config(value: &Value, operation: &str) -> Result<Value, String> {
434 map_entries(value).map(|_| value.clone()).ok_or_else(|| format!("std.lang.{operation} expects a configuration map"))
435}
436
437fn lookup(config: &Value, name: &str) -> Option<Value> {
438 map_entries(config)?.into_iter().find_map(|(key, value)| match key {
439 Value::Keyword(key) if key.as_str() == name => Some(value),
440 _ => None,
441 })
442}
443
444fn book_key(config: &Value, method: &str) -> Result<String, String> {
445 lookup(config, "coordinate")
446 .map(|value| value.display())
447 .ok_or_else(|| failure("Library", method, "requires Book :coordinate"))
448}
449
450fn book_key_argument(value: &Value, method: &str) -> Result<String, String> {
451 if let Value::Extension(extension) = value {
452 if extension.provider == PROVIDER && extension.type_name == "Book" {
453 return immutable_data(extension.handle, "Book", method).and_then(|data| book_key(&data, method));
454 }
455 }
456 if map_entries(value).is_some() { return book_key(value, method); }
457 Ok(value.display())
458}
459
460fn immutable_data(handle: u64, expected: &str, method: &str) -> Result<Value, String> {
461 STATE.with(|state| match state.borrow().records.get(&handle) {
462 Some(Record::Immutable { type_name, data }) if type_name == expected => Ok(data.clone()),
463 _ => Err(failure(expected, method, "expects its matching std.lang value")),
464 })
465}
466
467fn with_library<T>(handle: u64, method: &str, operation: impl FnOnce(&Value, &HashMap<String, Value>, &i64) -> Result<T, String>) -> Result<T, String> {
468 STATE.with(|state| match state.borrow().records.get(&handle) {
469 Some(Record::Library { config, books, revision }) => operation(config, books, revision),
470 _ => Err(failure("Library", method, "expects a std.lang.Library value")),
471 })
472}
473
474fn with_runtime<T>(handle: u64, method: &str, operation: impl FnOnce(&Value, &bool, &i64) -> Result<T, String>) -> Result<T, String> {
475 STATE.with(|state| match state.borrow().records.get(&handle) {
476 Some(Record::Runtime { config, closed, revision }) => operation(config, closed, revision),
477 _ => Err(failure("Runtime", method, "expects a std.lang.Runtime value")),
478 })
479}
480
481fn with_harness<T>(handle: u64, method: &str, operation: impl FnOnce(&Value, &u64, &u64, &bool) -> Result<T, String>) -> Result<T, String> {
482 STATE.with(|state| match state.borrow().records.get(&handle) {
483 Some(Record::Harness { config, library, runtime, closed }) => operation(config, library, runtime, closed),
484 _ => Err(failure("Harness", method, "expects a std.lang.Harness value")),
485 })
486}
487
488fn snapshot_library(snapshot: u64, owner: u64, method: &str) -> Result<(HashMap<String, Value>, i64), String> {
489 STATE.with(|state| match state.borrow().records.get(&snapshot) {
490 Some(Record::Snapshot { kind: SnapshotKind::Library, owner: snapshot_owner, books, library_revision, .. }) if *snapshot_owner == owner => Ok((books.clone(), *library_revision)),
491 _ => Err(failure("Library", method, "requires a snapshot from the same Library")),
492 })
493}
494
495fn snapshot_harness(snapshot: u64, owner: u64, method: &str) -> Result<(HashMap<String, Value>, i64, bool, i64, bool), String> {
496 STATE.with(|state| match state.borrow().records.get(&snapshot) {
497 Some(Record::Snapshot { kind: SnapshotKind::Harness, owner: snapshot_owner, books, library_revision, runtime_closed, runtime_revision, harness_closed }) if *snapshot_owner == owner => Ok((books.clone(), *library_revision, *runtime_closed, *runtime_revision, *harness_closed)),
498 _ => Err(failure("Harness", method, "requires a snapshot from the same Harness")),
499 })
500}
501
502fn restore_library(handle: u64, books: HashMap<String, Value>, revision: i64, method: &str) -> Result<(), String> {
503 STATE.with(|state| match state.borrow_mut().records.get_mut(&handle) {
504 Some(Record::Library { books: target, revision: target_revision, .. }) => { *target = books; *target_revision = revision; Ok(()) }
505 _ => Err(failure("Library", method, "expects a std.lang.Library value")),
506 })
507}
508
509fn restore_runtime(handle: u64, closed: bool, revision: i64, method: &str) -> Result<(), String> {
510 STATE.with(|state| match state.borrow_mut().records.get_mut(&handle) {
511 Some(Record::Runtime { closed: target_closed, revision: target_revision, .. }) => { *target_closed = closed; *target_revision = revision; Ok(()) }
512 _ => Err(failure("Runtime", method, "expects a std.lang.Runtime value")),
513 })
514}
515
516fn runtime_revision(handle: u64, method: &str) -> Result<i64, String> {
517 with_runtime(handle, method, |_, _, revision| Ok(*revision))
518}
519
520fn optional_handle(config: &Value, name: &str, expected: &str, method: &str) -> Result<Option<u64>, String> {
521 lookup(config, name).map(|value| handle(&value, expected, method)).transpose()
522}
523
524fn runtime_state_value(closed: bool, revision: i64) -> Value {
525 map(vec![
526 ("state", Value::Keyword(Keyword::from(if closed { "closed" } else { "ready" }))),
527 ("revision", Value::Number(revision)),
528 ])
529}
530
531fn empty_map() -> Value { Value::Map(PMap::new()) }
532
533fn map(entries: Vec<(&str, Value)>) -> Value {
534 Value::Map(PMap::from_iter(entries.into_iter().map(|(key, value)| (Value::Keyword(Keyword::from(key)), value))))
535}
536
537fn require_arity(type_name: &str, method: &str, values: &[Value], expected: usize) -> Result<(), String> {
538 if values.len() == expected { Ok(()) } else { Err(failure(type_name, method, &format!("expects {expected} argument{}", if expected == 1 { "" } else { "s" }))) }
539}
540
541fn failure(type_name: &str, method: &str, message: &str) -> String {
542 format!("std.lang.{type_name}/{method} {message}")
543}