Skip to main content

alembic_engine/
lib.rs

1//! engine orchestration: load, validate, plan, apply.
2
3mod adapter_ops;
4mod apply_retry;
5mod drift;
6mod errors;
7pub mod external;
8mod extract;
9pub mod journal;
10mod loader;
11pub mod mapping;
12mod pipeline;
13mod planner;
14mod predicate;
15mod pretty_printing;
16mod render;
17#[cfg(feature = "starlark")]
18mod starlark_transforms;
19mod state;
20mod transform;
21mod types;
22use alembic_core::{key_string, validate_inventory, Inventory, Object, ValidationReport};
23use anyhow::{anyhow, Result};
24
25#[cfg(test)]
26mod tests;
27
28pub use adapter_ops::{
29    build_key_from_schema, build_request_body, query_filters_from_key, resolve_value_for_type,
30};
31pub use apply_retry::{apply_non_delete_with_retries, RetryApplyDriver, RetryApplyResult};
32pub use drift::{ChangedEntry, DriftEntry, DriftReport};
33pub use errors::AdapterApplyError;
34pub use external::{
35    run_external_adapter, ExternalAdapter, ExternalEnvelope, ExternalObject, ExternalRequest,
36    ExternalResponse, EXTERNAL_PROTOCOL_VERSION,
37};
38pub use extract::{import_inventory, ImportReport};
39pub use loader::load_inventory;
40pub use planner::{plan, sort_ops_for_apply};
41pub use state::{PostgresTlsMode, StateData, StateStore};
42pub use transform::{compile_map, eval_map_transform, load_map_spec, MapSpec, TransformsSpec};
43pub use types::{
44    Adapter, AppliedOp, ApplyReport, Backend, BackendId, Emitter, FieldChange, ObservedObject,
45    ObservedState, Observer, Op, Plan, PlanSummary, ProvisionReport,
46};
47
48/// validate an inventory and return the report.
49pub fn validate(inventory: &Inventory) -> ValidationReport {
50    validate_inventory(inventory)
51}
52
53/// helper to format a validation report into a Result.
54pub fn report_to_result(report: ValidationReport) -> Result<()> {
55    report_to_result_with_sources(report, &[])
56}
57
58/// helper to format a validation report with source locations into a Result.
59pub fn report_to_result_with_sources(report: ValidationReport, objects: &[Object]) -> Result<()> {
60    if report.is_ok() {
61        return Ok(());
62    }
63
64    let located_errors = report.with_sources(objects);
65    let mut message = String::from("validation failed:\n");
66    for error in located_errors {
67        message.push_str(&format!("- {error}\n"));
68    }
69    Err(anyhow!(message))
70}
71
72/// observe backend state and produce a deterministic plan.
73pub async fn build_plan(
74    adapter: &(dyn Observer + '_),
75    inventory: &Inventory,
76    state: &mut StateStore,
77    allow_delete: bool,
78) -> Result<Plan> {
79    let observed = pipeline::observe(adapter, inventory, state).await?;
80    Ok(plan(
81        &inventory.objects,
82        &observed,
83        state,
84        &inventory.schema,
85        allow_delete,
86    ))
87}
88
89pub(crate) fn bootstrap_state_from_observed(
90    state: &mut StateStore,
91    desired: &[Object],
92    observed: &ObservedState,
93) -> bool {
94    let mut updated = false;
95    for object in desired {
96        if state
97            .backend_id(object.type_name.clone(), object.uid)
98            .is_some()
99        {
100            continue;
101        }
102        if let Some(obs) = observed
103            .by_key
104            .get(&(object.type_name.clone(), key_string(&object.key)))
105        {
106            if obs.type_name != object.type_name {
107                continue;
108            }
109            if let Some(backend_id) = &obs.backend_id {
110                state.set_backend_id(object.type_name.clone(), object.uid, backend_id.clone());
111                updated = true;
112            }
113        }
114    }
115    updated
116}
117
118/// apply a plan and update the state store. full adapters provision schema
119/// before writing; emitters only write.
120pub async fn apply_plan(
121    backend: &Backend,
122    plan: &Plan,
123    state: &mut StateStore,
124    allow_delete: bool,
125) -> Result<ApplyReport> {
126    pipeline::apply(backend, plan, state, allow_delete).await
127}