use std::sync::Arc;
use indexmap::IndexMap;
use vantage_core::Result;
use vantage_table::table::Table;
use vantage_types::{EmptyEntity, Entity};
use vantage_vista::{Capability, SpecResolver, Vista, VistaCapabilities};
use crate::mongodb::MongoDB;
use crate::mongodb::impls::joins::table_joins;
use crate::types::AnyMongoType;
use crate::vista::spec::MongoVistaSpec;
mod assemble;
mod build;
mod columns;
mod ref_keys;
pub(crate) use build::build_mongo_table;
pub(crate) use columns::{build_column, paths_from_table_columns};
#[derive(Clone)]
pub struct MongoVistaFactory {
pub(crate) mongo: MongoDB,
pub(crate) resolver: Option<Arc<dyn SpecResolver<MongoVistaSpec>>>,
pub(crate) change_streams: Option<bool>,
}
impl MongoVistaFactory {
pub fn new(mongo: MongoDB) -> Self {
Self {
mongo,
resolver: None,
change_streams: None,
}
}
pub fn with_change_streams(mut self, enabled: bool) -> Self {
self.change_streams = Some(enabled);
self
}
pub(crate) fn live_enabled(&self) -> bool {
self.change_streams
.unwrap_or_else(|| self.mongo.supports_change_streams())
}
pub fn with_resolver(mut self, resolver: Arc<dyn SpecResolver<MongoVistaSpec>>) -> Self {
self.resolver = Some(resolver);
self
}
fn resolver_with_fallback(&self) -> Arc<dyn SpecResolver<MongoVistaSpec>> {
let user = self.resolver.clone();
Arc::new(move |name: &str| {
user.as_ref()
.and_then(|user| user.resolve(name))
.or_else(|| Some(MongoVistaSpec::new(name)))
})
}
pub fn from_table<E>(&self, table: Table<MongoDB, E>) -> Result<Vista>
where
E: Entity<AnyMongoType> + 'static,
{
let name = table.table_name().to_string();
let any_table = table.into_entity::<EmptyEntity>();
table_joins(&any_table)?;
ref_keys::check_table(&any_table)?;
let column_paths = column_paths(&any_table);
let metadata = assemble::mongo_metadata(&any_table);
Ok(self.wrap_with_paths(any_table, column_paths, name, metadata))
}
pub fn table_from_spec(&self, spec: &MongoVistaSpec) -> Result<Table<MongoDB, EmptyEntity>> {
let resolver = self.resolver_with_fallback();
ref_keys::check_spec(spec, Some(&resolver))?;
build_mongo_table(spec, self.mongo.clone(), Some(resolver))
}
}
pub(crate) fn mongo_capabilities(writable: bool, live: bool) -> VistaCapabilities {
let reads = [
Capability::Order,
Capability::Search,
Capability::FilterOps,
Capability::NativeConditions,
Capability::Window,
Capability::Cursor,
Capability::Traverse,
];
let writes = [
Capability::Insert,
Capability::Update,
Capability::Delete,
Capability::ConfineWrites,
];
let streams = [Capability::Subscribe, Capability::Import];
reads
.into_iter()
.chain(writes.into_iter().filter(|_| writable))
.chain(streams.into_iter().filter(|_| live))
.collect()
}
fn column_paths(table: &Table<MongoDB, EmptyEntity>) -> IndexMap<String, Vec<String>> {
let bare = table.columns().len() == 1 && table.columns().contains_key("_id");
if bare {
IndexMap::new()
} else {
paths_from_table_columns(table)
}
}