trustfall_core 0.8.1

The trustfall query engine, empowering you to query everything.
Documentation
#![allow(dead_code)]

use std::fs::{self, ReadDir};
use std::iter;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::Arc;

use serde::{Deserialize, Serialize};

use crate::interpreter::AsVertex;
use crate::{
    interpreter::{
        Adapter, ContextIterator, ContextOutcomeIterator, DataContext, ResolveEdgeInfo,
        ResolveInfo, VertexIterator,
    },
    ir::{EdgeParameters, FieldValue},
};

#[derive(Debug, Clone)]
pub struct FilesystemInterpreter {
    origin: Rc<String>,
}

impl FilesystemInterpreter {
    pub fn new(origin: String) -> FilesystemInterpreter {
        FilesystemInterpreter { origin: Rc::new(origin) }
    }
}

#[derive(Debug)]
struct OriginIterator {
    origin_vertex: DirectoryVertex,
    produced: bool,
}

impl OriginIterator {
    pub fn new(vertex: DirectoryVertex) -> OriginIterator {
        OriginIterator { origin_vertex: vertex, produced: false }
    }
}

impl Iterator for OriginIterator {
    type Item = FilesystemVertex;

    fn next(&mut self) -> Option<FilesystemVertex> {
        if self.produced {
            None
        } else {
            self.produced = true;
            Some(FilesystemVertex::Directory(self.origin_vertex.clone()))
        }
    }
}

#[derive(Debug)]
struct DirectoryContainsFileIterator {
    origin: Rc<String>,
    directory: DirectoryVertex,
    file_iter: ReadDir,
}

impl DirectoryContainsFileIterator {
    pub fn new(origin: Rc<String>, directory: &DirectoryVertex) -> DirectoryContainsFileIterator {
        let mut buf = PathBuf::new();
        buf.extend([&*origin, &directory.path]);
        DirectoryContainsFileIterator {
            origin,
            directory: directory.clone(),
            file_iter: fs::read_dir(buf).unwrap(),
        }
    }
}

impl Iterator for DirectoryContainsFileIterator {
    type Item = FilesystemVertex;

    fn next(&mut self) -> Option<FilesystemVertex> {
        loop {
            if let Some(outcome) = self.file_iter.next() {
                match outcome {
                    Ok(dir_entry) => {
                        let metadata = match dir_entry.metadata() {
                            Ok(res) => res,
                            _ => continue,
                        };
                        if metadata.is_file() {
                            let name = dir_entry.file_name().to_str().unwrap().to_owned();
                            let mut buf = PathBuf::new();
                            buf.extend([&self.directory.path, &name]);
                            let extension = Path::new(&name)
                                .extension()
                                .map(|x| x.to_str().unwrap().to_owned());
                            let result = FileVertex {
                                name,
                                extension,
                                path: buf.to_str().unwrap().to_owned(),
                            };
                            return Some(FilesystemVertex::File(result));
                        }
                    }
                    _ => continue,
                }
            } else {
                return None;
            }
        }
    }
}

#[derive(Debug)]
struct SubdirectoryIterator {
    origin: Rc<String>,
    directory: DirectoryVertex,
    dir_iter: ReadDir,
}

impl SubdirectoryIterator {
    pub fn new(origin: Rc<String>, directory: &DirectoryVertex) -> Self {
        let mut buf = PathBuf::new();
        buf.extend([&*origin, &directory.path]);
        Self { origin, directory: directory.clone(), dir_iter: fs::read_dir(buf).unwrap() }
    }
}

impl Iterator for SubdirectoryIterator {
    type Item = FilesystemVertex;

    fn next(&mut self) -> Option<FilesystemVertex> {
        loop {
            if let Some(outcome) = self.dir_iter.next() {
                match outcome {
                    Ok(dir_entry) => {
                        let metadata = match dir_entry.metadata() {
                            Ok(res) => res,
                            _ => continue,
                        };
                        if metadata.is_dir() {
                            let name = dir_entry.file_name().to_str().unwrap().to_owned();
                            if name == ".git" || name == ".vscode" || name == "target" {
                                continue;
                            }

                            let mut buf = PathBuf::new();
                            buf.extend([&self.directory.path, &name]);
                            let result =
                                DirectoryVertex { name, path: buf.to_str().unwrap().to_owned() };
                            return Some(FilesystemVertex::Directory(result));
                        }
                    }
                    _ => continue,
                }
            } else {
                return None;
            }
        }
    }
}

pub type ContextAndValue = (DataContext<FilesystemVertex>, FieldValue);

type IndividualEdgeResolver<'a> =
    fn(Rc<String>, &FilesystemVertex) -> VertexIterator<'a, FilesystemVertex>;
type ContextAndIterableOfEdges<'a, V> = (DataContext<V>, VertexIterator<'a, FilesystemVertex>);

struct EdgeResolverIterator<'a, V: AsVertex<FilesystemVertex>> {
    origin: Rc<String>,
    contexts: VertexIterator<'a, DataContext<V>>,
    edge_resolver: IndividualEdgeResolver<'a>,
}

impl<'a, V: AsVertex<FilesystemVertex>> EdgeResolverIterator<'a, V> {
    pub fn new(
        origin: Rc<String>,
        contexts: VertexIterator<'a, DataContext<V>>,
        edge_resolver: IndividualEdgeResolver<'a>,
    ) -> Self {
        Self { origin, contexts, edge_resolver }
    }
}

impl<'a, V: AsVertex<FilesystemVertex>> Iterator for EdgeResolverIterator<'a, V> {
    type Item = (DataContext<V>, VertexIterator<'a, FilesystemVertex>);

    fn next(&mut self) -> Option<ContextAndIterableOfEdges<'a, V>> {
        if let Some(context) = self.contexts.next() {
            if let Some(vertex) = context.active_vertex::<FilesystemVertex>() {
                let neighbors = (self.edge_resolver)(self.origin.clone(), vertex);
                Some((context, neighbors))
            } else {
                let empty_iterator: iter::Empty<FilesystemVertex> = iter::empty();
                Some((context, Box::new(empty_iterator)))
            }
        } else {
            None
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum FilesystemVertex {
    Directory(DirectoryVertex),
    File(FileVertex),
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DirectoryVertex {
    pub name: String,
    pub path: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FileVertex {
    pub name: String,
    pub extension: Option<String>,
    pub path: String,
}

fn directory_contains_file_handler<'a>(
    origin: Rc<String>,
    vertex: &FilesystemVertex,
) -> VertexIterator<'a, FilesystemVertex> {
    let directory_vertex = match vertex {
        FilesystemVertex::Directory(dir) => dir,
        _ => unreachable!(),
    };
    Box::from(DirectoryContainsFileIterator::new(origin, directory_vertex))
}

fn directory_subdirectory_handler<'a>(
    origin: Rc<String>,
    vertex: &FilesystemVertex,
) -> VertexIterator<'a, FilesystemVertex> {
    let directory_vertex = match vertex {
        FilesystemVertex::Directory(dir) => dir,
        _ => unreachable!(),
    };
    Box::from(SubdirectoryIterator::new(origin, directory_vertex))
}

#[allow(unused_variables)]
impl<'a> Adapter<'a> for FilesystemInterpreter {
    type Vertex = FilesystemVertex;

    fn resolve_starting_vertices(
        &self,
        edge_name: &Arc<str>,
        parameters: &EdgeParameters,
        resolve_info: &ResolveInfo,
    ) -> VertexIterator<'a, Self::Vertex> {
        assert!(edge_name.as_ref() == "OriginDirectory");
        assert!(parameters.is_empty());
        let vertex = DirectoryVertex { name: "<origin>".to_owned(), path: "".to_owned() };
        Box::new(OriginIterator::new(vertex))
    }

    fn resolve_property<V: AsVertex<Self::Vertex> + 'a>(
        &self,
        contexts: ContextIterator<'a, V>,
        type_name: &Arc<str>,
        property_name: &Arc<str>,
        resolve_info: &ResolveInfo,
    ) -> ContextOutcomeIterator<'a, V, FieldValue> {
        match type_name.as_ref() {
            "Directory" => {
                match property_name.as_ref() {
                    "name" => Box::new(contexts.map(|context| {
                        match context.active_vertex::<Self::Vertex>() {
                            None => (context, FieldValue::Null),
                            Some(FilesystemVertex::Directory(ref x)) => {
                                let value = FieldValue::String(x.name.clone().into());
                                (context, value)
                            }
                            _ => unreachable!(),
                        }
                    })),
                    "path" => Box::new(contexts.map(|context| {
                        match context.active_vertex::<Self::Vertex>() {
                            None => (context, FieldValue::Null),
                            Some(FilesystemVertex::Directory(ref x)) => {
                                let value = FieldValue::String(x.path.clone().into());
                                (context, value)
                            }
                            _ => unreachable!(),
                        }
                    })),
                    "__typename" => Box::new(contexts.map(|context| {
                        match context.active_vertex::<Self::Vertex>() {
                            None => (context, FieldValue::Null),
                            Some(_) => (context, "Directory".into()),
                        }
                    })),
                    _ => todo!(),
                }
            }
            "File" => {
                match property_name.as_ref() {
                    "name" => Box::new(contexts.map(|context| {
                        match context.active_vertex::<Self::Vertex>() {
                            None => (context, FieldValue::Null),
                            Some(FilesystemVertex::File(ref x)) => {
                                let value = FieldValue::String(x.name.clone().into());
                                (context, value)
                            }
                            _ => unreachable!(),
                        }
                    })),
                    "path" => Box::new(contexts.map(|context| {
                        match context.active_vertex::<Self::Vertex>() {
                            None => (context, FieldValue::Null),
                            Some(FilesystemVertex::File(ref x)) => {
                                let value = FieldValue::String(x.path.clone().into());
                                (context, value)
                            }
                            _ => unreachable!(),
                        }
                    })),
                    "extension" => Box::new(contexts.map(|context| {
                        match context.active_vertex::<Self::Vertex>() {
                            None => (context, FieldValue::Null),
                            Some(FilesystemVertex::File(ref x)) => {
                                let value =
                                    x.extension.clone().map(Into::into).unwrap_or(FieldValue::Null);
                                (context, value)
                            }
                            _ => unreachable!(),
                        }
                    })),
                    "__typename" => Box::new(contexts.map(|context| {
                        match context.active_vertex::<Self::Vertex>() {
                            None => (context, FieldValue::Null),
                            Some(_) => (context, "File".into()),
                        }
                    })),
                    _ => todo!(),
                }
            }
            _ => todo!(),
        }
    }

    fn resolve_neighbors<V: AsVertex<Self::Vertex> + 'a>(
        &self,
        contexts: ContextIterator<'a, V>,
        type_name: &Arc<str>,
        edge_name: &Arc<str>,
        parameters: &EdgeParameters,
        resolve_info: &ResolveEdgeInfo,
    ) -> ContextOutcomeIterator<'a, V, VertexIterator<'a, Self::Vertex>> {
        match (type_name.as_ref(), edge_name.as_ref()) {
            ("Directory", "out_Directory_ContainsFile") => {
                let iterator = EdgeResolverIterator::new(
                    self.origin.clone(),
                    contexts,
                    directory_contains_file_handler,
                );
                Box::from(iterator)
            }
            ("Directory", "out_Directory_Subdirectory") => {
                let iterator = EdgeResolverIterator::new(
                    self.origin.clone(),
                    contexts,
                    directory_subdirectory_handler,
                );
                Box::from(iterator)
            }
            _ => unimplemented!(),
        }
    }

    fn resolve_coercion<V: AsVertex<Self::Vertex> + 'a>(
        &self,
        contexts: ContextIterator<'a, V>,
        type_name: &Arc<str>,
        coerce_to_type: &Arc<str>,
        resolve_info: &ResolveInfo,
    ) -> ContextOutcomeIterator<'a, V, bool> {
        todo!()
    }
}