use crate::{EdgedbObject, EdgedbPrim, EdgedbQueryArgs, EdgedbSetValue, Ref};
pub use edgedb_composable_query_derive::{EdgedbComposableQuery, EdgedbComposableSelector};
use edgedb_tokio::Client;
use nonempty::NonEmpty;
use crate::Result;
pub enum ComposableQueryResultKind {
Field,
Selector,
FreeObject,
}
pub trait EdgedbComposableSelector {
const RESULT_TYPE: ComposableQueryResultKind;
fn format_selector(fmt: &mut impl std::fmt::Write) -> Result<(), std::fmt::Error>;
fn format_subquery(fmt: &mut impl std::fmt::Write) -> Result<(), std::fmt::Error> {
match Self::RESULT_TYPE {
ComposableQueryResultKind::Field => {
return Ok(());
}
ComposableQueryResultKind::Selector => fmt.write_str(": {\n")?,
ComposableQueryResultKind::FreeObject => fmt.write_str(" := {\n")?,
};
Self::format_selector(fmt)?;
fmt.write_str("\n}")
}
}
impl<T: EdgedbPrim> EdgedbComposableSelector for T {
const RESULT_TYPE: ComposableQueryResultKind = ComposableQueryResultKind::Field;
fn format_selector(fmt: &mut impl std::fmt::Write) -> Result<(), std::fmt::Error> {
Ok(())
}
fn format_subquery(fmt: &mut impl std::fmt::Write) -> Result<(), std::fmt::Error> {
Ok(())
}
}
impl<T: EdgedbComposableSelector> EdgedbComposableSelector for Vec<T> {
const RESULT_TYPE: ComposableQueryResultKind = T::RESULT_TYPE;
fn format_selector(fmt: &mut impl std::fmt::Write) -> Result<(), std::fmt::Error> {
T::format_selector(fmt)
}
}
impl<T: EdgedbComposableSelector> EdgedbComposableSelector for Option<T> {
const RESULT_TYPE: ComposableQueryResultKind = T::RESULT_TYPE;
fn format_selector(fmt: &mut impl std::fmt::Write) -> Result<(), std::fmt::Error> {
T::format_selector(fmt)
}
}
impl<T: EdgedbComposableSelector> EdgedbComposableSelector for NonEmpty<T> {
const RESULT_TYPE: ComposableQueryResultKind = T::RESULT_TYPE;
fn format_selector(fmt: &mut impl std::fmt::Write) -> Result<(), std::fmt::Error> {
T::format_selector(fmt)
}
}
impl<T: EdgedbComposableSelector + EdgedbObject> EdgedbComposableSelector for Ref<T> {
const RESULT_TYPE: ComposableQueryResultKind = ComposableQueryResultKind::Selector;
fn format_selector(fmt: &mut impl std::fmt::Write) -> Result<(), std::fmt::Error> {
fmt.write_str("\tid,\n")?;
T::format_selector(fmt)?;
Ok(())
}
}
pub trait EdgedbComposableQuery {
const ARG_NAMES: &'static [&'static str];
type ArgTypes: EdgedbQueryArgs;
type ReturnType: EdgedbSetValue;
fn format_query(
fmt: &mut impl std::fmt::Write,
args: &::std::collections::HashMap<&str, String>,
) -> Result<(), std::fmt::Error>;
fn query() -> String {
let mut buf = String::new();
let args = Self::ARG_NAMES
.iter()
.enumerate()
.map(|(i, n)| (*n, format!("${i}")))
.collect();
Self::format_query(&mut buf, &args).unwrap();
buf
}
}
pub async fn run_query<T: EdgedbComposableQuery>(
client: &Client,
args: T::ArgTypes,
) -> Result<T::ReturnType>
where
<T as EdgedbComposableQuery>::ArgTypes: Send,
{
let query_s = T::query();
crate::query(client, &query_s, args).await
}
#[cfg(test)]
mod test {
use edgedb_protocol::model::Uuid;
use crate::composable::EdgedbComposableQuery;
use crate::composable::EdgedbComposableSelector;
use crate::{EdgedbObject, Ref};
#[derive(
Debug, PartialEq, Eq, EdgedbObject, EdgedbComposableSelector, EdgedbComposableQuery,
)]
#[select("select Inner limit 1")]
struct InnerQuery {
req: String,
opt: Option<String>,
}
#[derive(Debug, PartialEq, Eq, EdgedbObject, EdgedbComposableSelector)]
struct InnerSelector {
req: String,
opt: Option<String>,
}
#[derive(Debug, PartialEq, Eq, EdgedbComposableQuery)]
#[select("select Inner limit 1")]
struct OneInnerBySelector(InnerSelector);
#[derive(Debug, PartialEq, Eq, EdgedbComposableQuery)]
#[params(id: Uuid)]
#[select("select Inner filter .id = id")]
struct OneInnerBySelectorById(InnerSelector);
#[derive(Debug, PartialEq, Eq, EdgedbComposableQuery)]
#[select("select Inner limit 10")]
struct ManyInnersBySelector(Vec<InnerSelector>);
#[derive(
Debug, PartialEq, Eq, EdgedbObject, EdgedbComposableSelector, EdgedbComposableQuery,
)]
#[select("select Outer limit 1")]
struct OuterQuery {
inner: Option<InnerSelector>,
some_field: Option<String>,
other_field: String,
}
#[derive(
Debug, PartialEq, Eq, EdgedbObject, EdgedbComposableSelector, EdgedbComposableQuery,
)]
#[select("select Outer limit 1")]
struct OuterQueryWithRef {
inner: Option<Ref<InnerSelector>>,
some_field: Option<String>,
other_field: String,
}
#[test]
fn selector_tests() {
let mut buf = String::new();
InnerSelector::format_selector(&mut buf).unwrap();
insta::assert_snapshot!(buf);
let mut buf = String::new();
Option::<InnerSelector>::format_selector(&mut buf).unwrap();
insta::assert_snapshot!(buf);
}
#[test]
fn query_tests() {
insta::assert_snapshot!(InnerQuery::query());
insta::assert_snapshot!(OuterQuery::query());
insta::assert_snapshot!(OuterQueryWithRef::query());
insta::assert_snapshot!(OneInnerBySelector::query());
insta::assert_snapshot!(OneInnerBySelectorById::query());
insta::assert_snapshot!(ManyInnersBySelector::query());
}
}