Skip to main content

trustfall_git_adapter/
lib.rs

1use std::sync::LazyLock;
2
3use trustfall::{
4    Schema,
5    provider::{Adapter, resolve_coercion_using_schema},
6};
7
8use crate::{types::Repository, vertex::Vertex};
9
10mod edges;
11mod properties;
12mod types;
13mod vertex;
14
15static SCHEMA: LazyLock<Schema> =
16    LazyLock::new(|| Schema::parse(include_str!("schema.graphql")).expect("schema not valid"));
17
18pub struct GitAdapter<'a> {
19    git2_repo: &'a git2::Repository,
20}
21
22impl<'a> GitAdapter<'a> {
23    pub fn new(git2_repo: &'a git2::Repository) -> Self {
24        GitAdapter { git2_repo }
25    }
26
27    pub fn schema(&self) -> &Schema {
28        &SCHEMA
29    }
30}
31
32impl<'a> Adapter<'a> for &'a GitAdapter<'a> {
33    type Vertex = Vertex<'a>;
34
35    fn resolve_starting_vertices(
36        &self,
37        edge_name: &std::sync::Arc<str>,
38        _parameters: &trustfall_core::ir::EdgeParameters,
39        _resolve_info: &trustfall::provider::ResolveInfo,
40    ) -> trustfall::provider::VertexIterator<'a, Self::Vertex> {
41        match edge_name.as_ref() {
42            "repository" => {
43                let repo_name = match self.git2_repo.find_remote("origin") {
44                    Ok(remote) => remote.url().and_then(|url| {
45                        url.trim_end_matches(".git")
46                            .rsplit('/')
47                            .next()
48                            .map(|s| s.to_string())
49                    }),
50                    Err(_) => None,
51                }
52                .unwrap_or_else(|| {
53                    // Fallback to directory name if no remote origin
54                    self.git2_repo
55                        .path()
56                        .parent()
57                        .and_then(|p| p.file_name())
58                        .and_then(|name| name.to_str())
59                        .map(|s| s.to_string())
60                        .unwrap_or_else(|| "unknown".to_string())
61                });
62
63                Box::new(std::iter::once(Vertex::Repository(Repository::new(
64                    repo_name,
65                ))))
66            }
67            _ => unreachable!("resolve_starting_vertices {edge_name}"),
68        }
69    }
70
71    fn resolve_property<V: trustfall::provider::AsVertex<Self::Vertex> + 'a>(
72        &self,
73        contexts: trustfall::provider::ContextIterator<'a, V>,
74        type_name: &std::sync::Arc<str>,
75        property_name: &std::sync::Arc<str>,
76        _resolve_info: &trustfall::provider::ResolveInfo,
77    ) -> trustfall::provider::ContextOutcomeIterator<'a, V, trustfall::FieldValue> {
78        match type_name.as_ref() {
79            "Repository" => properties::resolve_repository_property(contexts, property_name),
80            "Branch" => properties::resolve_branch_property(contexts, property_name),
81            "Commit" => properties::resolve_commit_property(contexts, property_name),
82            "Tag" => properties::resolve_tag_property(contexts, property_name),
83            _ => unreachable!("resolve_property {type_name}"),
84        }
85    }
86
87    fn resolve_neighbors<V: trustfall::provider::AsVertex<Self::Vertex> + 'a>(
88        &self,
89        contexts: trustfall::provider::ContextIterator<'a, V>,
90        type_name: &std::sync::Arc<str>,
91        edge_name: &std::sync::Arc<str>,
92        parameters: &trustfall_core::ir::EdgeParameters,
93        _resolve_info: &trustfall::provider::ResolveEdgeInfo,
94    ) -> trustfall::provider::ContextOutcomeIterator<
95        'a,
96        V,
97        trustfall::provider::VertexIterator<'a, Self::Vertex>,
98    > {
99        match type_name.as_ref() {
100            "Repository" => edges::resolve_repository_edge(self, contexts, edge_name, parameters),
101            "Branch" => edges::resolve_branch_edge(self, contexts, edge_name),
102            "Tag" => edges::resolve_tag_edge(self, contexts, edge_name),
103            _ => unreachable!("resolve_neighbors {type_name}"),
104        }
105    }
106
107    fn resolve_coercion<V: trustfall::provider::AsVertex<Self::Vertex> + 'a>(
108        &self,
109        contexts: trustfall::provider::ContextIterator<'a, V>,
110        _type_name: &std::sync::Arc<str>,
111        coerce_to_type: &std::sync::Arc<str>,
112        _resolve_info: &trustfall::provider::ResolveInfo,
113    ) -> trustfall::provider::ContextOutcomeIterator<'a, V, bool> {
114        resolve_coercion_using_schema(contexts, &SCHEMA, coerce_to_type)
115    }
116}