use std::fmt::{self, Display};
use super::aggregate::RawAggregateFunction;
use super::candidate::InputDataType;
use super::documentation::Documentation;
use super::scalar::RawScalarFunction;
use super::table::{RawTableFunction, TableFunctionType};
use super::{CandidateSignature, Signature};
use crate::arrays::datatype::DataTypeId;
use crate::util::fmt::displayable::IntoDisplayableSlice;
pub type ScalarFunctionSet = FunctionSet<RawScalarFunction>;
pub type AggregateFunctionSet = FunctionSet<RawAggregateFunction>;
pub type TableFunctionSet = FunctionSet<RawTableFunction>;
#[derive(Debug, Clone, Copy)]
pub struct FunctionSet<T: 'static> {
pub name: &'static str,
pub aliases: &'static [&'static str],
pub doc: &'static [&'static Documentation],
pub functions: &'static [T],
}
impl<T> FunctionSet<T>
where
T: FunctionInfo,
{
pub fn find_exact(&self, inputs: &[DataTypeId]) -> Option<&T> {
self.functions
.iter()
.find(|func| func.signature().exact_match(inputs))
}
pub fn candidates(&self, inputs: &[InputDataType]) -> Vec<CandidateSignature> {
CandidateSignature::find_candidates(
inputs,
self.functions.iter().map(|func| func.signature()),
)
}
pub fn get(&self, idx: usize) -> Option<&T> {
self.functions.get(idx)
}
pub fn no_function_matches<'a>(
&'a self,
datatypes: &'a [InputDataType],
) -> NoFunctionMatches<'a, T> {
NoFunctionMatches {
datatypes,
function: self,
}
}
}
impl TableFunctionSet {
pub fn is_scan_function(&self) -> bool {
self.functions
.iter()
.any(|func| func.function_type() == TableFunctionType::Scan)
}
}
pub trait FunctionInfo: Sized + Copy {
fn signature(&self) -> &Signature;
}
impl FunctionInfo for RawScalarFunction {
fn signature(&self) -> &Signature {
RawScalarFunction::signature(self)
}
}
impl FunctionInfo for RawAggregateFunction {
fn signature(&self) -> &Signature {
RawAggregateFunction::signature(self)
}
}
impl FunctionInfo for RawTableFunction {
fn signature(&self) -> &Signature {
RawTableFunction::signature(self)
}
}
#[derive(Debug)]
pub struct NoFunctionMatches<'a, T: 'static> {
datatypes: &'a [InputDataType],
function: &'a FunctionSet<T>,
}
impl<T> Display for NoFunctionMatches<'_, T>
where
T: FunctionInfo + 'static,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"No function matches '{}({})'. You may need to add explicit type casts.",
self.function.name,
self.datatypes.display_as_list()
)?;
const MAX_NUM_SIGS: usize = 8;
let count = usize::min(MAX_NUM_SIGS, self.function.functions.len());
if count > 0 {
write!(f, "\nCandidate functions:")?;
}
for func in self.function.functions.iter().take(count) {
let sig = FunctionInfo::signature(func);
write!(
f,
"\n {}({}) -> {}",
self.function.name,
sig.positional_args.display_as_list(),
sig.return_type,
)?;
}
if count < self.function.functions.len() {
let not_shown = self.function.functions.len() - count;
write!(f, "\n ...")?;
write!(
f,
"\n {not_shown} not shown. View the full list of functions in the catalog."
)?;
}
Ok(())
}
}