diff --git a/crates/plaixt/src/main.rs b/crates/plaixt/src/main.rs index 45e85d3..9611dee 100644 --- a/crates/plaixt/src/main.rs +++ b/crates/plaixt/src/main.rs @@ -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::, FieldValue>::from([("search".into(), "trust".into())]), diff --git a/crates/plaixt/src/parsing.rs b/crates/plaixt/src/parsing.rs index b591b42..df05113 100644 --- a/crates/plaixt/src/parsing.rs +++ b/crates/plaixt/src/parsing.rs @@ -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, } -pub(crate) fn parse_definition(bytes: &str) -> miette::Result> { +pub(crate) fn parse_definition( + bytes: &str, + definition_name: String, +) -> miette::Result> { let doc: KdlDocument = bytes.parse()?; let mut defs = vec![]; @@ -309,7 +313,11 @@ pub(crate) fn parse_definition(bytes: &str) -> miette::Result> { }) .collect::>()?; - 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")) })?, )) diff --git a/crates/plaixt/src/trustfall_plaixt.rs b/crates/plaixt/src/trustfall_plaixt.rs index c3c43b7..4c27f8d 100644 --- a/crates/plaixt/src/trustfall_plaixt.rs +++ b/crates/plaixt/src/trustfall_plaixt.rs @@ -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, + implements: Vec, +} + +impl VertexType { + pub fn new( + adapter_name: String, + vertex_name: String, + vertex_fields: HashMap, + implements: Vec, + ) -> 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, + types: Vec, +} + +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>) -> 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::>() - .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::>() - .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 for TrustfallMultiVertex { + fn as_vertex(&self) -> Option<&PlaixtVertex> { + self.as_plaixt() + } + + fn into_vertex(self) -> Option { + 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, + 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( + &self, + contexts: trustfall::provider::ContextIterator<'v, V>, + type_name: &Arc, + property_name: &Arc, + resolve_info: &trustfall::provider::ResolveInfo, + ) -> trustfall::provider::ContextOutcomeIterator<'v, V, FieldValue> + where + V: trustfall::provider::AsVertex + 'v, + { + let (adapter_name, type_name) = type_name.split_once(ADAPTER_SEP).unwrap(); + + match adapter_name { + "Plaixt" => { + let contexts = contexts.collect::>(); + + 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>( + &self, + contexts: trustfall::provider::ContextIterator<'v, V>, + type_name: &Arc, + edge_name: &Arc, + 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::>(); + + 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>( + &self, + contexts: trustfall::provider::ContextIterator<'v, V>, + type_name: &Arc, + coerce_to_type: &Arc, + 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::>(); + + 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, } @@ -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!(), diff --git a/query b/query index f6bb732..80fc919 100644 --- a/query +++ b/query @@ -1,6 +1,6 @@ { - RecordsAll { - ... on changelog { + Plaixt__RecordsAll { + ... on Plaixt__changelog { at @output kind @output fields {