1use crate::ast::GqlQuery;
4use crate::error::QueryError;
5use crate::parsers;
6use crate::parsers::sparql::leading_keyword;
7
8pub(crate) const GQL_WRITE_KEYWORDS: &[&str] = &[
9 "CREATE", "DELETE", "DETACH", "SET", "REMOVE", "MERGE", "INSERT", "UPDATE",
10];
11pub(crate) const SPARQL_WRITE_KEYWORDS: &[&str] = &[
12 "INSERT", "DELETE", "WITH", "LOAD", "CLEAR", "CREATE", "DROP", "COPY", "MOVE", "ADD",
13];
14pub(crate) const DEFAULT_HOP_CAP: usize = 5;
15
16pub(crate) fn read_only_error(constructor: fn(String) -> QueryError) -> QueryError {
17 constructor(
18 "the query verb is read-only; \
19 to mutate the graph use: create, update, link, merge, delete"
20 .into(),
21 )
22}
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub enum QueryLanguage {
27 Gql,
28 Sparql,
29}
30
31pub fn parse(language: QueryLanguage, input: &str) -> Result<GqlQuery, QueryError> {
38 match language {
39 QueryLanguage::Gql => parsers::gql::parse(input),
40 QueryLanguage::Sparql => parsers::sparql::parse(input),
41 }
42}
43
44pub fn parse_auto(input: &str) -> Result<GqlQuery, QueryError> {
53 parse_auto_with_language(input).map(|(_, query)| query)
54}
55
56pub fn parse_auto_with_language(input: &str) -> Result<(QueryLanguage, GqlQuery), QueryError> {
62 let trimmed = input.trim();
63 reject_write(trimmed)?;
64 let language = if trimmed
65 .as_bytes()
66 .get(..6)
67 .is_some_and(|p| p.eq_ignore_ascii_case(b"SELECT"))
68 {
69 QueryLanguage::Sparql
70 } else {
71 QueryLanguage::Gql
72 };
73 parse(language, trimmed).map(|query| (language, query))
74}
75
76fn reject_write(input: &str) -> Result<(), QueryError> {
78 let keyword = leading_keyword(input);
79 if GQL_WRITE_KEYWORDS.contains(&keyword.as_str())
80 || SPARQL_WRITE_KEYWORDS.contains(&keyword.as_str())
81 {
82 Err(read_only_error(QueryError::Unsupported))
83 } else {
84 Ok(())
85 }
86}
87
88#[cfg(test)]
89#[path = "language_tests.rs"]
90mod tests;