uqa_sql/semantics/
graph_functions.rs1use crate::SQLError;
10use uqa_core::Value;
11
12pub trait GraphNameCatalog {
13 fn list_graphs(&self) -> Result<Vec<String>, SQLError>;
14}
15
16pub(crate) fn default_graph_name(
17 catalog: &dyn GraphNameCatalog,
18 function_name: &str,
19) -> Result<String, SQLError> {
20 let graphs = catalog.list_graphs()?;
21 match graphs.as_slice() {
22 [name] => Ok(name.clone()),
23 [] => Err(SQLError::Unsupported(format!(
24 "{function_name} requires a graph argument because no graph is registered"
25 ))),
26 _ => Err(SQLError::Unsupported(format!(
27 "{function_name} requires a graph argument because multiple graphs are registered: {}",
28 graphs.join(", ")
29 ))),
30 }
31}
32
33pub fn expect_optional_graph_value(
34 catalog: &dyn GraphNameCatalog,
35 value: Option<&Value>,
36 function_name: &str,
37) -> Result<String, SQLError> {
38 match value {
39 Some(Value::Str(name)) => Ok(name.clone()),
40 Some(other) => Err(SQLError::TypeMismatch(format!(
41 "{function_name}.graph must be string, got {other:?}"
42 ))),
43 None => default_graph_name(catalog, function_name),
44 }
45}
46
47pub fn centrality_graph(
48 catalog: &dyn GraphNameCatalog,
49 evaluated: &[Value],
50 lower: &str,
51) -> Result<String, SQLError> {
52 if evaluated.len() > 1 {
53 return Err(SQLError::TypeMismatch(format!(
54 "{lower} accepts at most one graph argument"
55 )));
56 }
57 let graph = expect_optional_graph_value(catalog, evaluated.first(), lower)?;
58 Ok(graph)
59}
60
61pub fn regular_path_arguments(
62 catalog: &dyn GraphNameCatalog,
63 evaluated: &[Value],
64) -> Result<(String, u64, String), SQLError> {
65 if !(2..=3).contains(&evaluated.len()) {
66 return Err(SQLError::TypeMismatch(
67 "rpq requires 2 or 3 args (expr, start [, graph])".into(),
68 ));
69 }
70 let expr_str = match &evaluated[0] {
71 Value::Str(s) => s.clone(),
72 _ => return Err(SQLError::TypeMismatch("rpq.expr must be string".into())),
73 };
74 let start = match &evaluated[1] {
75 Value::Int(n) => u64::try_from(*n).map_err(|_| {
76 SQLError::TypeMismatch("rpq.start must be a non-negative integer".into())
77 })?,
78 _ => return Err(SQLError::TypeMismatch("rpq.start must be integer".into())),
79 };
80 let graph = expect_optional_graph_value(catalog, evaluated.get(2), "rpq")?;
81 Ok((expr_str, start, graph))
82}