Start using trustfall to query the repo

Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
Marcel Müller 2025-02-07 21:57:28 +01:00
parent 68fbc3ce25
commit 6501b42328
5 changed files with 277 additions and 18 deletions

View file

@ -18,6 +18,7 @@ tokio = { version = "1.43.0", features = ["full"] }
tokio-stream = { version = "0.1.17", features = ["full"] }
tracing = "0.1.41"
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
trustfall = "0.8.1"
[lints]
workspace = true

View file

@ -1,11 +1,22 @@
#![allow(dead_code)]
use std::collections::BTreeMap;
use std::sync::Arc;
use camino::Utf8PathBuf;
use clap::Parser;
use clap::Subcommand;
use clap::ValueHint;
use human_panic::Metadata;
use parsing::Definition;
use parsing::Record;
use tracing::info;
use trustfall::execute_query;
use trustfall::provider::field_property;
use trustfall::provider::resolve_property_with;
use trustfall::provider::Adapter;
use trustfall::FieldValue;
use trustfall::Schema;
mod config;
mod parsing;
@ -46,18 +57,127 @@ async fn main() -> miette::Result<()> {
let config = config::parse_config(&args.config).await?;
let root_folder = args.root_folder.as_ref().unwrap_or(&config.root_folder);
let load_records = async {
let definitions = parsing::load_definitions(&root_folder.join("definitions")).await?;
parsing::load_records(root_folder, &definitions).await
};
let definitions = parsing::load_definitions(&root_folder.join("definitions")).await?;
let records = parsing::load_records(root_folder, &definitions).await?;
let schema = to_schema(&definitions);
let result = execute_query(
&schema,
Arc::new(PlaixtAdapter {
records: records.clone(),
}),
"{
RecordsAll {
at @output
kind @output @filter(op: \"=\", value: [\"$foobar\"])
}
}",
[(
Arc::from("foobar"),
FieldValue::String(Arc::from("changelog")),
)]
.into(),
)
.unwrap()
.collect::<Vec<_>>();
match args.mode {
ArgMode::Dump => {
let records = load_records.await?;
info!("Got records: {records:#?}");
info!("Got records: {result:#?}");
}
}
Ok(())
}
fn to_schema(_definitions: &BTreeMap<String, Vec<Definition>>) -> Schema {
Schema::parse(format!(
r#"schema {{
query: RootSchemaQuery
}}
{}
type RootSchemaQuery {{
RecordsAll: [Record!]!
}}
interface Record {{
at: String!,
kind: String!,
}}
"#,
Schema::ALL_DIRECTIVE_DEFINITIONS
))
.unwrap()
}
struct PlaixtAdapter {
records: Vec<Record>,
}
impl<'a> Adapter<'a> for PlaixtAdapter {
type Vertex = Record;
fn resolve_starting_vertices(
&self,
edge_name: &Arc<str>,
_parameters: &trustfall::provider::EdgeParameters,
_resolve_info: &trustfall::provider::ResolveInfo,
) -> trustfall::provider::VertexIterator<'a, Self::Vertex> {
match edge_name.as_ref() {
"RecordsAll" => Box::new(self.records.clone().into_iter()),
_ => unreachable!(),
}
}
fn resolve_property<V: trustfall::provider::AsVertex<Self::Vertex> + 'a>(
&self,
contexts: trustfall::provider::ContextIterator<'a, V>,
type_name: &Arc<str>,
property_name: &Arc<str>,
_resolve_info: &trustfall::provider::ResolveInfo,
) -> trustfall::provider::ContextOutcomeIterator<'a, V, trustfall::FieldValue> {
match (type_name.as_ref(), property_name.as_ref()) {
(_, "__typename") => Box::new(contexts.map(|ctx| {
let value = match ctx.active_vertex() {
Some(_record) => "Record".into(),
None => FieldValue::Null,
};
(ctx, value)
})),
("Record", "at") => {
resolve_property_with(contexts, field_property!(at, { at.to_string().into() }))
}
("Record", "kind") => resolve_property_with(contexts, field_property!(kind)),
_ => unreachable!(),
}
}
fn resolve_neighbors<V: trustfall::provider::AsVertex<Self::Vertex> + 'a>(
&self,
_contexts: trustfall::provider::ContextIterator<'a, V>,
_type_name: &Arc<str>,
_edge_name: &Arc<str>,
_parameters: &trustfall::provider::EdgeParameters,
_resolve_info: &trustfall::provider::ResolveEdgeInfo,
) -> trustfall::provider::ContextOutcomeIterator<
'a,
V,
trustfall::provider::VertexIterator<'a, Self::Vertex>,
> {
unreachable!()
}
fn resolve_coercion<V: trustfall::provider::AsVertex<Self::Vertex> + 'a>(
&self,
_contexts: trustfall::provider::ContextIterator<'a, V>,
_type_name: &Arc<str>,
_coerce_to_type: &Arc<str>,
_resolve_info: &trustfall::provider::ResolveInfo,
) -> trustfall::provider::ContextOutcomeIterator<'a, V, bool> {
unreachable!()
}
}

View file

@ -15,7 +15,7 @@ use miette::NamedSource;
use owo_colors::OwoColorize;
use tokio_stream::wrappers::ReadDirStream;
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Record {
pub(crate) kind: String,
pub(crate) at: Timestamp,
@ -82,8 +82,7 @@ pub(crate) fn parse_record(
.map(|field| {
let Some(get) = field.get(0) else {
return Err(miette::diagnostic!(
labels =
vec![LabeledSpan::new_primary_with_span(None, at_entry.span())],
labels = vec![LabeledSpan::new_primary_with_span(None, at_entry.span())],
"This datetime should be a string formatted as RFC3339."
))?;
};
@ -143,9 +142,8 @@ pub(crate) async fn load_records(
})
.flat_map(|val| futures::stream::iter(val.transpose()))
.and_then(|(name, bytes)| async move {
parse_record(&bytes, definitions).map_err(|e| {
e.with_source_code(NamedSource::new(name, bytes).with_language("kdl"))
})
parse_record(&bytes, definitions)
.map_err(|e| e.with_source_code(NamedSource::new(name, bytes).with_language("kdl")))
})
.map(|val| val.map(|recs| futures::stream::iter(recs).map(Ok::<_, miette::Report>)))
.try_flatten()
@ -344,4 +342,3 @@ pub(crate) async fn load_definitions(
Ok(defs)
}