use vantage_table::column::core::Column;
use vantage_table::column::flags::ColumnFlag;
use vantage_table::table::Table;
use vantage_table::traits::column_like::ColumnLike;
use vantage_types::Entity;
use crate::graphql::api::GraphqlApi;
use crate::graphql::condition::GraphqlCondition;
use crate::graphql::operation::GraphqlOperation;
use crate::graphql::types::AnyGraphqlType;
pub(crate) fn escape_like(text: &str) -> String {
let mut out = String::with_capacity(text.len());
for c in text.chars() {
if matches!(c, '\\' | '%' | '_') {
out.push('\\');
}
out.push(c);
}
out
}
pub(crate) fn contains_pattern(text: &str) -> String {
format!("%{}%", escape_like(text))
}
pub(crate) fn match_nothing() -> GraphqlCondition {
GraphqlCondition::Not(Box::new(GraphqlCondition::And(Vec::new())))
}
fn search_columns<E: Entity<AnyGraphqlType>>(table: &Table<GraphqlApi, E>) -> Vec<String> {
let columns = table.search_columns();
let flagged = columns
.iter()
.any(|col| col.flags().contains(&ColumnFlag::Searchable));
let id = table.id_field().map(|c| c.name().to_string());
columns
.into_iter()
.filter(|col| flagged || col.get_type() == std::any::type_name::<String>())
.filter(|col| flagged || Some(col.name()) != id.as_deref())
.filter(|col| !col.name().contains('.'))
.map(|col| col.name().to_string())
.collect()
}
pub(crate) fn search_condition<E: Entity<AnyGraphqlType>>(
table: &Table<GraphqlApi, E>,
text: &str,
) -> GraphqlCondition {
let pattern = contains_pattern(text);
let mut conditions: Vec<GraphqlCondition> = search_columns(table)
.iter()
.map(|name| Column::<AnyGraphqlType>::new(name).ilike(pattern.clone()))
.collect();
match conditions.len() {
0 => match_nothing(),
1 => conditions.remove(0),
_ => GraphqlCondition::Or(conditions),
}
}
pub(crate) fn unescape_like(text: &str) -> String {
let mut out = String::with_capacity(text.len());
let mut chars = text.chars();
while let Some(c) = chars.next() {
match c {
'\\' => out.extend(chars.next()),
c => out.push(c),
}
}
out
}