use crate::schema::object::TableObjectType;
use crate::types::GraphQLType;
use std::collections::HashMap;
pub const NUMERIC_AGGREGATES: &[(&str, Returns)] = &[
("sum", Returns::Column),
("avg", Returns::Float),
("stddev", Returns::Float),
("stddev_samp", Returns::Float),
("stddev_pop", Returns::Float),
("variance", Returns::Float),
("var_samp", Returns::Float),
("var_pop", Returns::Float),
];
pub const ORDERED_AGGREGATES: &[(&str, Returns)] =
&[("max", Returns::Column), ("min", Returns::Column)];
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Returns {
Column,
Float,
}
pub fn aggregate_type_name(base_name: &str) -> String {
format!("{}_aggregate", base_name)
}
pub fn aggregate_fields_type_name(base_name: &str) -> String {
format!("{}_aggregate_fields", base_name)
}
pub fn function_fields_type_name(base_name: &str, function: &str) -> String {
format!("{}_{}_fields", base_name, function)
}
pub fn is_incrementable(graphql_type: &GraphQLType) -> bool {
is_numeric(graphql_type) || matches!(graphql_type, GraphQLType::Custom(name) if name == "money")
}
pub fn is_numeric(graphql_type: &GraphQLType) -> bool {
matches!(
graphql_type,
GraphQLType::Int | GraphQLType::Float | GraphQLType::BigInt | GraphQLType::BigDecimal
)
}
pub fn is_ordered(graphql_type: &GraphQLType) -> bool {
match graphql_type {
GraphQLType::Json | GraphQLType::List(_) => false,
GraphQLType::Boolean => false,
GraphQLType::Custom(name) => !matches!(name.as_str(), "json" | "raster"),
_ => true,
}
}
pub fn functions_for(object: &TableObjectType) -> Vec<(&'static str, Returns, Vec<String>)> {
let mut out = Vec::new();
for (function, returns) in NUMERIC_AGGREGATES {
let columns: Vec<String> = object
.fields
.iter()
.filter(|f| is_numeric(&f.graphql_type))
.map(|f| f.name.clone())
.collect();
if !columns.is_empty() {
out.push((*function, *returns, columns));
}
}
for (function, returns) in ORDERED_AGGREGATES {
let columns: Vec<String> = object
.fields
.iter()
.filter(|f| is_ordered(&f.graphql_type))
.map(|f| f.name.clone())
.collect();
if !columns.is_empty() {
out.push((*function, *returns, columns));
}
}
out
}
pub fn field_type_for(object: &TableObjectType, column: &str, returns: Returns) -> String {
match returns {
Returns::Float => "Float".to_string(),
Returns::Column => object
.fields
.iter()
.find(|f| f.name == column)
.map(|f| f.graphql_type.to_string())
.unwrap_or_else(|| "String".to_string()),
}
}
pub fn tables(object_types: &HashMap<String, TableObjectType>) -> Vec<&String> {
let mut names: Vec<&String> = object_types.keys().collect();
names.sort();
names
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn names_follow_the_table() {
assert_eq!(aggregate_type_name("author"), "author_aggregate");
assert_eq!(
aggregate_fields_type_name("author"),
"author_aggregate_fields"
);
assert_eq!(
function_fields_type_name("author", "sum"),
"author_sum_fields"
);
}
#[test]
fn only_numbers_are_summed() {
assert!(is_numeric(&GraphQLType::Int));
assert!(is_numeric(&GraphQLType::BigDecimal));
assert!(!is_numeric(&GraphQLType::String));
assert!(!is_numeric(&GraphQLType::Json));
}
#[test]
fn json_has_no_maximum() {
assert!(!is_ordered(&GraphQLType::Json));
assert!(!is_ordered(&GraphQLType::Custom("json".to_string())));
assert!(is_ordered(&GraphQLType::Custom("timestamp".to_string())));
assert!(is_ordered(&GraphQLType::String));
assert!(is_ordered(&GraphQLType::DateTime));
}
}