Add a first attempt at multiplexing adapters

Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
Marcel Müller 2025-02-08 11:58:14 +01:00
parent 3112e78bb4
commit 73391b5b7b
4 changed files with 349 additions and 52 deletions

View file

@ -75,8 +75,10 @@ async fn main() -> miette::Result<()> {
let result = execute_query(
&schema,
Arc::new(trustfall_plaixt::PlaixtAdapter {
records: records.clone(),
Arc::new(trustfall_plaixt::TrustfallMultiAdapter {
plaixt: trustfall_plaixt::PlaixtAdapter {
records: records.clone(),
},
}),
&query,
BTreeMap::<Arc<str>, FieldValue>::from([("search".into(), "trust".into())]),

View file

@ -194,11 +194,15 @@ impl TryFrom<&str> for DefinitionKind {
#[derive(Debug)]
pub struct Definition {
pub(crate) name: String,
pub(crate) since: Timestamp,
pub(crate) fields: HashMap<String, DefinitionKind>,
}
pub(crate) fn parse_definition(bytes: &str) -> miette::Result<Vec<Definition>> {
pub(crate) fn parse_definition(
bytes: &str,
definition_name: String,
) -> miette::Result<Vec<Definition>> {
let doc: KdlDocument = bytes.parse()?;
let mut defs = vec![];
@ -309,7 +313,11 @@ pub(crate) fn parse_definition(bytes: &str) -> miette::Result<Vec<Definition>> {
})
.collect::<miette::Result<_>>()?;
defs.push(Definition { since, fields });
defs.push(Definition {
since,
fields,
name: definition_name.clone(),
});
}
unknown => {
return Err(miette::diagnostic!(
@ -349,9 +357,10 @@ pub(crate) async fn load_definitions(
})
.flat_map(|val| futures::stream::iter(val.transpose()))
.and_then(|(name, bytes)| async move {
let definition_name = name.file_stem().unwrap().to_string();
Ok((
name.file_stem().unwrap().to_string(),
parse_definition(&bytes).map_err(|e| {
definition_name.clone(),
parse_definition(&bytes, definition_name).map_err(|e| {
e.with_source_code(NamedSource::new(name, bytes).with_language("kdl"))
})?,
))

View file

@ -1,4 +1,7 @@
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::fmt::Write;
use std::ops::Not;
use std::sync::Arc;
use kdl::KdlValue;
@ -9,69 +12,351 @@ use trustfall::provider::resolve_coercion_with;
use trustfall::provider::resolve_neighbors_with;
use trustfall::provider::resolve_property_with;
use trustfall::provider::Adapter;
use trustfall::provider::AsVertex;
use trustfall::FieldValue;
use trustfall::Schema;
use crate::parsing::Definition;
use crate::parsing::Record;
const ADAPTER_SEP: &str = "__";
#[derive(Debug, Default)]
pub struct StartingVertex {
adapter_name: String,
start_vertex_name: String,
vertex_type: String,
}
impl StartingVertex {
pub fn new(adapter_name: String, start_vertex_name: String, start_vertex_type: String) -> Self {
Self {
adapter_name,
start_vertex_name,
vertex_type: start_vertex_type,
}
}
pub fn schema_name(&self) -> String {
format!(
"{}{ADAPTER_SEP}{}",
self.adapter_name, self.start_vertex_name
)
}
pub fn vertex_type(&self) -> &str {
&self.vertex_type
}
}
#[derive(Debug, Default)]
pub struct VertexType {
adapter_name: String,
vertex_name: String,
vertex_fields: HashMap<String, String>,
implements: Vec<String>,
}
impl VertexType {
pub fn new(
adapter_name: String,
vertex_name: String,
vertex_fields: HashMap<String, String>,
implements: Vec<String>,
) -> Self {
Self {
adapter_name,
vertex_name,
vertex_fields,
implements,
}
}
pub fn schema_name(&self) -> String {
format!("{}{ADAPTER_SEP}{}", self.adapter_name, self.vertex_name)
}
pub fn schema_type(&self) -> String {
format!(
r#"type {name} {impls} {{ {fields} }}"#,
name = self.schema_name(),
impls = self
.implements
.is_empty()
.not()
.then(|| format!("implements {}", self.implements.join(" & ")))
.unwrap_or_else(String::new),
fields = self.vertex_fields.iter().fold(String::new(), |mut out, f| {
write!(out, "{}: {}, ", f.0, f.1).unwrap();
out
}),
)
}
}
#[derive(Debug, Default)]
pub struct DynamicSchema {
roots: Vec<StartingVertex>,
types: Vec<VertexType>,
}
impl DynamicSchema {
pub fn new() -> Self {
Self::default()
}
pub fn add_root(&mut self, root: StartingVertex) {
self.roots.push(root);
}
pub fn add_type(&mut self, kind: VertexType) {
self.types.push(kind);
}
}
pub(crate) fn to_schema(definitions: &BTreeMap<String, Vec<Definition>>) -> Schema {
let custom_schemas = definitions
.iter()
.map(|(name, def)| {
let fields = def
.last()
.unwrap()
let mut schema = DynamicSchema::new();
schema.add_root(StartingVertex::new(
"Plaixt".to_string(),
"RecordsAll".to_string(),
"[Record!]!".to_string(),
));
for definition in definitions.values().flat_map(|d| d.first()) {
let fields = VertexType::new(
"Plaixt".to_string(),
format!("{}Fields", definition.name),
definition
.fields
.iter()
.map(|(name, def)| format!("{name}: {}!", def.trustfall_kind()))
.collect::<Vec<_>>()
.join("\n");
let field_type = format!("{name}Fields");
format!(
r#"
type {field_type} {{
{fields}
}}
type {name} implements Record {{
at: String!
kind: String!
fields: {field_type}!
}}
"#
)
})
.collect::<Vec<_>>()
.join("");
.map(|(name, val)| (name.clone(), format!("{}!", val.trustfall_kind())))
.collect(),
vec![],
);
schema.add_type(VertexType::new(
"Plaixt".to_string(),
definition.name.clone(),
[
(String::from("at"), String::from("String!")),
(String::from("kind"), String::from("String!")),
(String::from("fields"), format!("{}!", fields.schema_name())),
]
.into(),
vec![String::from("Record")],
));
schema.add_type(fields);
}
let schema = format!(
r#"schema {{
query: RootSchemaQuery
}}
{}
query: RootSchemaQuery
}}
{}
type RootSchemaQuery {{
{roots}
}}
interface Record {{
at: String!,
kind: String!,
}}
type RootSchemaQuery {{
RecordsAll: [Record!]!
}}
interface Record {{
at: String!,
kind: String!,
}}
{}
"#,
{types}
"#,
Schema::ALL_DIRECTIVE_DEFINITIONS,
custom_schemas
roots = schema.roots.iter().fold(String::new(), |mut out, r| {
write!(out, "{}: {}, ", r.schema_name(), r.vertex_type()).unwrap();
out
}),
types = schema.types.iter().fold(String::new(), |mut out, t| {
writeln!(out, "{}", t.schema_type()).unwrap();
out
}),
);
trace!(%schema, "Using schema");
Schema::parse(schema).unwrap()
}
pub struct TrustfallMultiAdapter {
pub plaixt: PlaixtAdapter,
}
#[derive(Debug, Clone)]
pub enum TrustfallMultiVertex {
Plaixt(PlaixtVertex),
}
impl AsVertex<PlaixtVertex> for TrustfallMultiVertex {
fn as_vertex(&self) -> Option<&PlaixtVertex> {
self.as_plaixt()
}
fn into_vertex(self) -> Option<PlaixtVertex> {
self.as_plaixt().cloned()
}
}
impl TrustfallMultiVertex {
pub fn as_plaixt(&self) -> Option<&PlaixtVertex> {
if let Self::Plaixt(v) = self {
Some(v)
} else {
None
}
}
}
impl<'v> Adapter<'v> for TrustfallMultiAdapter {
type Vertex = TrustfallMultiVertex;
fn resolve_starting_vertices(
&self,
edge_name: &Arc<str>,
parameters: &trustfall::provider::EdgeParameters,
resolve_info: &trustfall::provider::ResolveInfo,
) -> trustfall::provider::VertexIterator<'v, Self::Vertex> {
let (adapter_name, edge_name) = edge_name.split_once(ADAPTER_SEP).unwrap();
trace!(?adapter_name, ?edge_name, "Got start vertex");
match adapter_name {
"Plaixt" => {
let iter = self.plaixt.resolve_starting_vertices(
&Arc::from(edge_name),
parameters,
resolve_info,
);
Box::new(iter.map(TrustfallMultiVertex::Plaixt))
}
_ => unreachable!(),
}
}
fn resolve_property<V>(
&self,
contexts: trustfall::provider::ContextIterator<'v, V>,
type_name: &Arc<str>,
property_name: &Arc<str>,
resolve_info: &trustfall::provider::ResolveInfo,
) -> trustfall::provider::ContextOutcomeIterator<'v, V, FieldValue>
where
V: trustfall::provider::AsVertex<Self::Vertex> + 'v,
{
let (adapter_name, type_name) = type_name.split_once(ADAPTER_SEP).unwrap();
match adapter_name {
"Plaixt" => {
let contexts = contexts.collect::<Vec<_>>();
let properties = self.plaixt.resolve_property(
Box::new(
contexts
.clone()
.into_iter()
.map(|v| v.map(&mut |v: V| v.into_vertex().unwrap())),
),
&Arc::from(type_name),
property_name,
resolve_info,
);
Box::new(
properties
.into_iter()
.zip(contexts)
.map(|((_ctx, name), og_ctx)| (og_ctx, name)),
)
}
_ => unreachable!(),
}
}
fn resolve_neighbors<V: trustfall::provider::AsVertex<Self::Vertex> + 'v>(
&self,
contexts: trustfall::provider::ContextIterator<'v, V>,
type_name: &Arc<str>,
edge_name: &Arc<str>,
parameters: &trustfall::provider::EdgeParameters,
resolve_info: &trustfall::provider::ResolveEdgeInfo,
) -> trustfall::provider::ContextOutcomeIterator<
'v,
V,
trustfall::provider::VertexIterator<'v, Self::Vertex>,
> {
let (adapter_name, type_name) = type_name.split_once(ADAPTER_SEP).unwrap();
match adapter_name {
"Plaixt" => {
let contexts = contexts.collect::<Vec<_>>();
let properties = self.plaixt.resolve_neighbors(
Box::new(
contexts
.clone()
.into_iter()
.map(|v| v.map(&mut |v: V| v.into_vertex().unwrap())),
),
&Arc::from(type_name),
edge_name,
parameters,
resolve_info,
);
Box::new(
properties
.into_iter()
.zip(contexts)
.map(|((_ctx, vals), og_ctx)| {
(
og_ctx,
Box::new(vals.map(TrustfallMultiVertex::Plaixt)) as Box<_>,
)
}),
)
}
_ => unreachable!(),
}
}
fn resolve_coercion<V: trustfall::provider::AsVertex<Self::Vertex> + 'v>(
&self,
contexts: trustfall::provider::ContextIterator<'v, V>,
type_name: &Arc<str>,
coerce_to_type: &Arc<str>,
resolve_info: &trustfall::provider::ResolveInfo,
) -> trustfall::provider::ContextOutcomeIterator<'v, V, bool> {
trace!(?type_name, ?coerce_to_type, "Trying to coerce");
let (adapter_name, coerce_to_type) = coerce_to_type.split_once(ADAPTER_SEP).unwrap();
match adapter_name {
"Plaixt" => {
let contexts = contexts.collect::<Vec<_>>();
let properties = self.plaixt.resolve_coercion(
Box::new(
contexts
.clone()
.into_iter()
.map(|v| v.map(&mut |v: V| v.into_vertex().unwrap())),
),
type_name,
&Arc::from(coerce_to_type),
resolve_info,
);
Box::new(
properties
.into_iter()
.zip(contexts)
.map(|((_ctx, val), og_ctx)| (og_ctx, val)),
)
}
_ => unreachable!(),
}
}
}
pub(crate) struct PlaixtAdapter {
pub(crate) records: Vec<Record>,
}
@ -119,6 +404,7 @@ impl<'a> Adapter<'a> for PlaixtAdapter {
_parameters: &trustfall::provider::EdgeParameters,
_resolve_info: &trustfall::provider::ResolveInfo,
) -> trustfall::provider::VertexIterator<'a, Self::Vertex> {
trace!(?edge_name, "Resolving start vertex");
match edge_name.as_ref() {
"RecordsAll" => Box::new(self.records.clone().into_iter().map(PlaixtVertex::Record)),
_ => unreachable!(),

4
query
View file

@ -1,6 +1,6 @@
{
RecordsAll {
... on changelog {
Plaixt__RecordsAll {
... on Plaixt__changelog {
at @output
kind @output
fields {