use super::contracts_map::contract_view;
use super::contracts_profile::resolve_profile;
use super::{ArgMessage, EXIT_CONTRACT_ERROR, arg_failure, cached_schema_availability, op_failure};
use crate::commands::output::{emit, failure_message, result};
use crate::config::runtime::RuntimeConfig;
use crate::contracts::args::parse_qualified;
use crate::contracts::{
RecallBounds, RecallMode, RecallRequest, RetrievalPolicy, now_unix_ms, recall, review_queue,
show as show_contract,
};
use crate::render::{RenderFormat, TerminalEvent};
use saya_store::{KnowledgeItemStore, SqliteStateStore};
use saya_types::{DatabaseObjectKind, DatabaseObjectRef};
const STORE_UNAVAILABLE_MSG: &str = "Local state store unavailable; contracts could not be read.";
pub(super) async fn list(
store: &SqliteStateStore,
runtime: &RuntimeConfig,
format: RenderFormat,
profile: Option<&str>,
) -> Result<i32, Box<dyn std::error::Error>> {
let (name, identity) = match resolve_profile(runtime, profile) {
Ok(value) => value,
Err((code, message)) => return failure_message(code, message, format),
};
let explicit_refs: Vec<DatabaseObjectRef> = match store.objects_for_profile(&identity).await {
Ok(objects) => objects,
Err(_) => {
return failure_message(EXIT_CONTRACT_ERROR, STORE_UNAVAILABLE_MSG.into(), format);
}
};
let cached = cached_schema_availability(store, &identity).await;
let schema_pair = (identity.clone(), cached);
let schemas = std::slice::from_ref(&schema_pair);
let request = RecallRequest {
profiles: std::slice::from_ref(&identity),
explicit_refs: &explicit_refs,
terms: &[],
allow_database_context: true,
schemas,
now_unix_ms: now_unix_ms(),
bounds: RecallBounds::defaults(),
recall_mode: RecallMode::Confirmed,
admit_candidate: None,
policy: RetrievalPolicy::ForHumanReview,
};
let outcome = recall(store, request).await;
if outcome.diagnostics.store_unavailable {
return failure_message(EXIT_CONTRACT_ERROR, STORE_UNAVAILABLE_MSG.into(), format);
}
let contracts: Vec<_> = outcome
.contracts
.iter()
.map(|c| contract_view(c, &name))
.collect();
emit(TerminalEvent::ContractList { contracts }, format);
Ok(0)
}
pub(super) async fn show(
store: &SqliteStateStore,
runtime: &RuntimeConfig,
format: RenderFormat,
table: &str,
profile: Option<&str>,
) -> Result<i32, Box<dyn std::error::Error>> {
let (name, identity) = match resolve_profile(runtime, profile) {
Ok(value) => value,
Err((code, message)) => return failure_message(code, message, format),
};
let qualified = match parse_qualified(table) {
Ok(q) => q,
Err(_) => return arg_failure(ArgMessage::MalformedTable, format),
};
let object = match DatabaseObjectRef::new(
identity.clone(),
&qualified.catalog,
&qualified.schema,
&qualified.object,
DatabaseObjectKind::Table,
) {
Ok(object) => object,
Err(_) => return arg_failure(ArgMessage::MalformedTable, format),
};
let cached = cached_schema_availability(store, &identity).await;
let retrieved = match show_contract(
store,
&object,
&cached,
RetrievalPolicy::ForHumanReview,
now_unix_ms(),
)
.await
{
Ok(retrieved) => retrieved,
Err(error) => return op_failure(error, format),
};
let Some(contract) = retrieved else {
return result(
format!("No contract for {}.", object.qualified_name()),
format,
);
};
emit(
TerminalEvent::ContractShow {
contract: contract_view(&contract, &name),
},
format,
);
Ok(0)
}
pub(super) async fn queue(
store: &SqliteStateStore,
runtime: &RuntimeConfig,
format: RenderFormat,
profile: Option<&str>,
limit: Option<usize>,
) -> Result<i32, Box<dyn std::error::Error>> {
let (name, identity) = match resolve_profile(runtime, profile) {
Ok(value) => value,
Err((code, message)) => return failure_message(code, message, format),
};
let limit = limit.unwrap_or(crate::contracts::QUEUE_DEFAULT_LIMIT);
let cached = cached_schema_availability(store, &identity).await;
let schema_pair = (identity.clone(), cached);
let schemas = std::slice::from_ref(&schema_pair);
let queued = match review_queue(store, std::slice::from_ref(&identity), schemas, limit).await {
Ok(queued) => queued,
Err(_) => {
return failure_message(EXIT_CONTRACT_ERROR, STORE_UNAVAILABLE_MSG.into(), format);
}
};
let items: Vec<_> = queued
.iter()
.map(|c| super::queue_item_view(c, &name))
.collect();
emit(TerminalEvent::ContractQueue { items }, format);
Ok(0)
}