Skip to main content

icydb_schema/build/
mod.rs

1use crate::{Error, ThisError, node::Schema, prelude::*, validate::validate_schema};
2use std::sync::{LazyLock, OnceLock, RwLock, RwLockReadGuard, RwLockWriteGuard};
3
4///
5/// BuildError
6///
7/// Error returned when the process-global schema graph fails validation before
8/// build-time code generation.
9///
10
11#[derive(Debug, ThisError)]
12pub enum BuildError {
13    #[error("validation failed: {0}")]
14    Validation(ErrorTree),
15}
16
17/// Process-global schema graph used during build-time code generation.
18static SCHEMA: LazyLock<RwLock<Schema>> = LazyLock::new(|| RwLock::new(Schema::new()));
19
20static SCHEMA_VALIDATED: OnceLock<bool> = OnceLock::new();
21
22/// Acquire a write guard to the global schema during build-time codegen.
23///
24/// # Panics
25///
26/// Panics if the process-global schema lock has been poisoned.
27pub fn schema_write() -> RwLockWriteGuard<'static, Schema> {
28    SCHEMA
29        .write()
30        .expect("schema RwLock poisoned while acquiring write lock")
31}
32
33/// Read the schema graph without triggering validation.
34pub(crate) fn schema_read() -> RwLockReadGuard<'static, Schema> {
35    SCHEMA
36        .read()
37        .expect("schema RwLock poisoned while acquiring read lock")
38}
39
40/// Read the global schema, validating it exactly once per process.
41pub fn get_schema() -> Result<RwLockReadGuard<'static, Schema>, Error> {
42    let schema = schema_read();
43    validate(&schema).map_err(BuildError::Validation)?;
44
45    Ok(schema)
46}
47
48/// Validate the schema once per process before exposing it to codegen.
49fn validate(schema: &Schema) -> Result<(), ErrorTree> {
50    if *SCHEMA_VALIDATED.get_or_init(|| false) {
51        return Ok(());
52    }
53
54    validate_schema(schema)?;
55
56    SCHEMA_VALIDATED.set(true).ok();
57
58    Ok(())
59}