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

143
Cargo.lock generated
View file

@ -82,6 +82,36 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "anyhow"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
[[package]]
name = "async-graphql-parser"
version = "7.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8531ee6d292c26df31c18c565ff22371e7bdfffe7f5e62b69537db0b8fd554dc"
dependencies = [
"async-graphql-value",
"pest",
"serde",
"serde_json",
]
[[package]]
name = "async-graphql-value"
version = "7.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "741110dda927420a28fbc1c310543d3416f789a6ba96859c2c265843a0a96887"
dependencies = [
"bytes",
"indexmap",
"serde",
"serde_json",
]
[[package]]
name = "autocfg"
version = "1.4.0"
@ -144,6 +174,9 @@ name = "bytes"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
dependencies = [
"serde",
]
[[package]]
name = "camino"
@ -233,6 +266,12 @@ dependencies = [
"powerfmt",
]
[[package]]
name = "either"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "equivalent"
version = "1.0.1"
@ -407,6 +446,7 @@ checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
dependencies = [
"equivalent",
"hashbrown",
"serde",
]
[[package]]
@ -421,6 +461,15 @@ version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.14"
@ -464,7 +513,7 @@ checksum = "412e2cf22cb560469db5b211c594ff9dcd490c6964e284ea64eddffe41c2249c"
dependencies = [
"miette",
"num",
"thiserror",
"thiserror 1.0.69",
"winnow",
]
@ -508,6 +557,12 @@ version = "0.4.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
[[package]]
name = "maplit"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
[[package]]
name = "matchers"
version = "0.1.0"
@ -540,7 +595,7 @@ dependencies = [
"syntect",
"terminal_size",
"textwrap",
"thiserror",
"thiserror 1.0.69",
"unicode-width",
]
@ -756,6 +811,17 @@ dependencies = [
"windows-targets",
]
[[package]]
name = "pest"
version = "2.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc"
dependencies = [
"memchr",
"thiserror 2.0.11",
"ucd-trie",
]
[[package]]
name = "pin-project-lite"
version = "0.2.16"
@ -790,6 +856,7 @@ dependencies = [
"tokio-stream",
"tracing",
"tracing-subscriber",
"trustfall",
]
[[package]]
@ -1025,6 +1092,9 @@ name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
dependencies = [
"serde",
]
[[package]]
name = "socket2"
@ -1091,7 +1161,7 @@ dependencies = [
"serde",
"serde_derive",
"serde_json",
"thiserror",
"thiserror 1.0.69",
"walkdir",
"yaml-rust",
]
@ -1122,7 +1192,16 @@ version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl",
"thiserror-impl 1.0.69",
]
[[package]]
name = "thiserror"
version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
dependencies = [
"thiserror-impl 2.0.11",
]
[[package]]
@ -1136,6 +1215,17 @@ dependencies = [
"syn",
]
[[package]]
name = "thiserror-impl"
version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.8"
@ -1325,6 +1415,51 @@ dependencies = [
"tracing-log",
]
[[package]]
name = "trustfall"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c4d6f50f158998ff48a905a4f12e918b9dfd1274c0711c0f4485d8f7ff015e"
dependencies = [
"anyhow",
"trustfall_core",
"trustfall_derive",
]
[[package]]
name = "trustfall_core"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "363322af9d4d04fb0e298d8e9f97d9ef316a796f6f4e5f241060ad50a07d0334"
dependencies = [
"async-graphql-parser",
"async-graphql-value",
"itertools",
"maplit",
"regex",
"serde",
"serde_json",
"smallvec",
"thiserror 1.0.69",
]
[[package]]
name = "trustfall_derive"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb14eb4f23b3b669d232a5c7d2b3d6c89ad9e15be1cbdd2c1e14d87d62569ec"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "ucd-trie"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
[[package]]
name = "unicode-ident"
version = "1.0.16"

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)
}

View file

@ -15,3 +15,9 @@ changelog "2025-02-01" {
version "0.1.0"
kind "Feature"
}
changelog "2025-02-07" {
title "Added trustfall as a query frontend"
version "0.1.0"
kind "Feature"
}