mod contracts_decide;
mod contracts_map;
mod contracts_profile;
mod contracts_read;
mod contracts_remember_schema;
mod contracts_write;
pub(crate) use contracts_map::contract_view;
pub(crate) use contracts_map::queue_item_view;
pub(crate) use contracts_profile::resolve_profile;
use super::output::failure_message;
use crate::cli::ContractsCommand;
use crate::config::runtime::RuntimeConfig;
use crate::contracts::ContractOpError;
use crate::contracts::SchemaAvailability;
use crate::render::RenderFormat;
use saya_store::{SchemaStore, SqliteStateStore};
use saya_types::{ClaimId, FINGERPRINT_VERSION, ProfileIdentity, SchemaFingerprint, SchemaTree};
pub(super) const EXIT_CONTRACT_ERROR: i32 = 2;
pub async fn run_contracts(
command: ContractsCommand,
runtime: &RuntimeConfig,
format: RenderFormat,
store: &SqliteStateStore,
) -> Result<i32, Box<dyn std::error::Error>> {
match command {
ContractsCommand::List { profile } => {
contracts_read::list(store, runtime, format, profile.as_deref()).await
}
ContractsCommand::Show { table, profile } => {
contracts_read::show(store, runtime, format, &table, profile.as_deref()).await
}
ContractsCommand::Queue { profile, limit } => {
contracts_read::queue(store, runtime, format, profile.as_deref(), limit).await
}
ContractsCommand::Remember {
table,
kind,
value,
column,
reason,
profile,
} => match resolve_profile(runtime, profile.as_deref()) {
Ok((name, identity)) => {
contracts_write::remember(
contracts_write::RememberRequest {
table: &table,
kind,
value: &value,
column: column.as_deref(),
reason: reason.as_deref(),
},
contracts_write::RememberContext {
store,
format,
profile_name: &name,
identity: &identity,
},
)
.await
}
Err((code, message)) => failure_message(code, message, format),
},
ContractsCommand::Decide {
prefix,
decision,
profile,
} => match resolve_profile(runtime, profile.as_deref()) {
Ok((name, identity)) => {
contracts_decide::decide(store, format, &prefix, decision, &name, &identity).await
}
Err((code, message)) => failure_message(code, message, format),
},
ContractsCommand::Forget { claim_id, reason } => {
contracts_write::forget_claim(store, format, &claim_id, reason).await
}
}
}
pub(super) fn op_failure(
error: ContractOpError,
format: RenderFormat,
) -> Result<i32, Box<dyn std::error::Error>> {
failure_message(EXIT_CONTRACT_ERROR, error.to_string(), format)
}
pub(super) fn arg_failure(
message: ArgMessage,
format: RenderFormat,
) -> Result<i32, Box<dyn std::error::Error>> {
failure_message(EXIT_CONTRACT_ERROR, message.to_string(), format)
}
pub(crate) fn unobserved_fingerprint() -> SchemaFingerprint {
SchemaFingerprint::from_parts(FINGERPRINT_VERSION, &"0".repeat(64))
.expect("current format with a 64-hex-zero digest is a valid fingerprint")
}
pub(crate) async fn cached_schema(
store: &SqliteStateStore,
identity: &ProfileIdentity,
) -> Option<SchemaTree> {
store
.get_schema(identity.as_str())
.await
.ok()
.flatten()
.map(|cached| cached.schema)
}
pub(crate) async fn cached_schema_availability(
store: &SqliteStateStore,
identity: &ProfileIdentity,
) -> SchemaAvailability {
match store.get_schema(identity.as_str()).await {
Ok(Some(cached)) => SchemaAvailability::available(cached.schema, cached.updated_unix_ms),
Ok(None) => SchemaAvailability::Missing,
Err(_) => SchemaAvailability::Unavailable,
}
}
pub(super) fn parse_claim_id(input: &str) -> Result<ClaimId, String> {
ClaimId::parse(input).map_err(|_| ArgMessage::MalformedClaimId.to_string())
}
#[derive(Debug, Clone, Copy)]
pub(super) enum ArgMessage {
MalformedTable,
MalformedClaimId,
BadValue,
AmbiguousPrefix,
PrefixNotFound,
}
impl std::fmt::Display for ArgMessage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::MalformedTable => write!(
f,
"qualified name must be exactly three dot-separated parts: catalog.schema.object"
),
Self::MalformedClaimId => write!(f, "claim id must be alphanumeric, '-', or '_'"),
Self::BadValue => write!(f, "claim value is invalid"),
Self::AmbiguousPrefix => write!(
f,
"that claim reference matches more than one claim; type more characters"
),
Self::PrefixNotFound => write!(
f,
"no claim matches that reference; it may have been forgotten, or the prefix is too short"
),
}
}
}