use crate::hyperql::{CodeGenerator, CompilerError, CompilerResult, PatternMatcher, QueryPattern};
use audb::model::Query;
use proc_macro2::TokenStream;
pub struct HyperQLCompiler {
pattern_matcher: PatternMatcher,
code_generator: CodeGenerator,
}
impl HyperQLCompiler {
pub fn new() -> Self {
Self {
pattern_matcher: PatternMatcher::new(),
code_generator: CodeGenerator::new(),
}
}
pub fn compile(&self, query: &Query) -> CompilerResult<TokenStream> {
let ast = self.parse_hyperql(&query.source)?;
let pattern = self.pattern_matcher.analyze(&ast, query)?;
let code = self.code_generator.generate(&pattern, query)?;
Ok(code)
}
fn parse_hyperql(&self, source: &str) -> CompilerResult<hyperQL::ast::Statement> {
hyperQL::parser::parse_statement(source)
.map_err(|e| CompilerError::ParseError(format!("{:?}", e)))
}
}
impl Default for HyperQLCompiler {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use audb::model::query::{Parameter, QueryLanguage};
#[test]
fn test_compiler_creation() {
let compiler = HyperQLCompiler::new();
assert!(true); }
#[test]
fn test_parse_simple_select() {
let compiler = HyperQLCompiler::new();
let source = "SELECT * FROM users WHERE id = :id";
let result = compiler.parse_hyperql(source);
assert!(
result.is_ok(),
"Failed to parse simple SELECT: {:?}",
result
);
}
#[test]
fn test_compile_point_query_option_return() {
let compiler = HyperQLCompiler::new();
let mut query = Query::new(
"get_user".to_string(),
QueryLanguage::HyperQL,
"SELECT * FROM users WHERE id = :id".to_string(),
"Option<User>".to_string(),
);
query.add_param(Parameter::new("id".to_string(), "EntityId".to_string()));
let result = compiler.compile(&query);
assert!(
result.is_ok(),
"Failed to compile point query: {:?}",
result
);
let code = result.unwrap();
let code_str = code.to_string();
assert!(
code_str.contains("User"),
"Generated code should reference User type"
);
assert!(
code_str.contains("get"),
"Generated code should call get method"
);
assert!(
code_str.contains("id"),
"Generated code should use id parameter"
);
}
#[test]
fn test_compile_point_query_non_option_return() {
let compiler = HyperQLCompiler::new();
let mut query = Query::new(
"get_user_required".to_string(),
QueryLanguage::HyperQL,
"SELECT * FROM users WHERE id = :id".to_string(),
"User".to_string(), );
query.add_param(Parameter::new("id".to_string(), "EntityId".to_string()));
let result = compiler.compile(&query);
assert!(
result.is_ok(),
"Failed to compile point query: {:?}",
result
);
let code = result.unwrap();
let code_str = code.to_string();
assert!(
code_str.contains("User"),
"Generated code should reference User type"
);
assert!(
code_str.contains("get"),
"Generated code should call get method"
);
assert!(
code_str.contains("ok_or_else"),
"Generated code should unwrap Option"
);
assert!(
code_str.contains("NotFound"),
"Generated code should return NotFound error"
);
}
#[test]
fn test_compile_point_query_plural_table() {
let compiler = HyperQLCompiler::new();
let mut query = Query::new(
"get_post".to_string(),
QueryLanguage::HyperQL,
"SELECT * FROM posts WHERE id = :post_id".to_string(),
"Option<Post>".to_string(),
);
query.add_param(Parameter::new(
"post_id".to_string(),
"EntityId".to_string(),
));
let result = compiler.compile(&query);
assert!(
result.is_ok(),
"Failed to compile point query: {:?}",
result
);
let code = result.unwrap();
let code_str = code.to_string();
assert!(
code_str.contains("Post"),
"Generated code should use singular type name"
);
assert!(
code_str.contains("post_id"),
"Generated code should use post_id parameter"
);
}
#[test]
fn test_parse_select_with_filters() {
let compiler = HyperQLCompiler::new();
let source = "SELECT * FROM users WHERE age > 25 AND active = true";
let result = compiler.parse_hyperql(source);
assert!(
result.is_ok(),
"Failed to parse SELECT with filters: {:?}",
result
);
}
#[test]
fn test_parse_select_with_order_by() {
let compiler = HyperQLCompiler::new();
let source = "SELECT * FROM users ORDER BY created_at DESC LIMIT 10";
let result = compiler.parse_hyperql(source);
assert!(
result.is_ok(),
"Failed to parse SELECT with ORDER BY: {:?}",
result
);
}
#[test]
fn test_compile_ordered_query() {
let compiler = HyperQLCompiler::new();
let query = Query::new(
"recent_users".to_string(),
QueryLanguage::HyperQL,
"SELECT * FROM users ORDER BY created_at DESC LIMIT 10".to_string(),
"Vec<User>".to_string(),
);
let result = compiler.compile(&query);
assert!(
result.is_ok(),
"Failed to compile ordered query: {:?}",
result
);
let code = result.unwrap();
let code_str = code.to_string();
assert!(
code_str.contains("list_all"),
"Generated code should call list_all"
);
assert!(
code_str.contains("sort_by"),
"Generated code should sort results"
);
assert!(
code_str.contains("truncate"),
"Generated code should apply limit"
);
}
#[test]
fn test_compile_aggregation_count() {
let compiler = HyperQLCompiler::new();
let query = Query::new(
"count_users".to_string(),
QueryLanguage::HyperQL,
"SELECT COUNT(*) FROM users WHERE active = true".to_string(),
"i64".to_string(),
);
let result = compiler.compile(&query);
match result {
Ok(code) => {
let code_str = code.to_string();
assert!(
code_str.contains("count"),
"Generated code should have count variable"
);
}
Err(_) => {
assert!(true);
}
}
}
#[test]
fn test_compile_relationship_query() {
let compiler = HyperQLCompiler::new();
let query = Query::new(
"user_posts".to_string(),
QueryLanguage::HyperQL,
"SELECT p.* FROM posts p TRAVERSE author -> User WHERE User.id = :user_id".to_string(),
"Vec<Post>".to_string(),
);
let result = compiler.compile(&query);
match result {
Ok(code) => {
let code_str = code.to_string();
assert!(true);
}
Err(_) => {
assert!(true);
}
}
}
#[test]
fn test_compile_filter_with_ordering() {
let compiler = HyperQLCompiler::new();
let mut query = Query::new(
"find_active_recent".to_string(),
QueryLanguage::HyperQL,
"SELECT * FROM users WHERE active = true ORDER BY created_at DESC LIMIT 5".to_string(),
"Vec<User>".to_string(),
);
let result = compiler.compile(&query);
assert!(
result.is_ok(),
"Failed to compile filter + order query: {:?}",
result
);
let code = result.unwrap();
let code_str = code.to_string();
assert!(
code_str.contains("active"),
"Generated code should reference active field"
);
assert!(code_str.contains("sort_by"), "Generated code should sort");
}
}