apollo-compiler 1.31.1

A compiler for the GraphQL query language.
Documentation
use anyhow::anyhow;
use anyhow::Result;
use apollo_compiler::parser::Parser;
use apollo_compiler::validation::Valid;
use apollo_compiler::ExecutableDocument;
use apollo_compiler::Schema;
use notify::Config;
use notify::EventKind;
use notify::PollWatcher;
use notify::RecursiveMode;
use notify::Watcher;
use std::collections::HashMap;
use std::fs;
use std::fs::DirEntry;
use std::path::Path;
use std::path::PathBuf;
use std::sync::mpsc::channel;
use std::time::Duration;

fn main() -> Result<()> {
    let dir = Path::new("crates/apollo-compiler/examples/documents");
    let mut watcher = FileWatcher::new();
    watcher.watch(dir)
}

pub struct FileWatcher {
    manifest: HashMap<PathBuf, (Valid<Schema>, Valid<ExecutableDocument>)>,
}

#[allow(clippy::new_without_default)]
impl FileWatcher {
    pub fn new() -> Self {
        Self {
            manifest: HashMap::new(),
        }
    }

    // The `watch` fn first goes over every document in a given directory, and
    // parse them all, and validate them all.
    //
    // We then watch for file changes, and reparse and revalidates relevant files.
    pub fn watch(&mut self, dir: impl AsRef<Path>) -> Result<()> {
        for entry in fs::read_dir(&dir)? {
            let (proposed_document, src_path) = get_schema_and_maybe_path(entry?)?;
            self.add_document(proposed_document, src_path)?;
        }

        self.watch_broadcast(dir.as_ref())
    }

    fn watch_broadcast(&mut self, dir: &Path) -> Result<()> {
        let (broadcaster, listener) = channel();
        let mut watcher = PollWatcher::new(
            move |res| {
                broadcaster.send(res).unwrap();
            },
            Config::default().with_poll_interval(Duration::from_secs(1)),
        )?;
        watcher.watch(dir, RecursiveMode::NonRecursive)?;
        println!("watching {} for changes", dir.display());
        loop {
            match listener.recv() {
                Ok(Ok(event)) => {
                    for path in event.paths {
                        if path.is_dir() {
                            continue;
                        }

                        match event.kind {
                            EventKind::Any => {
                                println!("changes detected in {}", path.display())
                            }
                            EventKind::Create(_) | EventKind::Modify(_) => {
                                match fs::read_to_string(&path) {
                                    Ok(contents) => {
                                        println!("changes detected in {}", path.display());
                                        self.add_document(contents, path)?;
                                    }
                                    Err(e) => {
                                        println!(
                                            "could not read {} from disk, {:?}",
                                            dir.display(),
                                            Some(anyhow!("{}", e)),
                                        );
                                    }
                                }
                            }
                            _ => {}
                        }
                    }
                }
                Ok(Err(e)) => {
                    println!(
                        "unknown error while watching {},  {:?}",
                        &dir.display(),
                        Some(anyhow!("{}", e)),
                    );
                }
                Err(e) => {
                    println!(
                        "unknown error while watching {},  {:?}",
                        &dir.display(),
                        Some(anyhow!("{}", e)),
                    );
                }
            }
        }
    }

    fn add_document(
        &mut self,
        proposed_document: String,
        src_path: PathBuf,
    ) -> Result<PathBuf, anyhow::Error> {
        let full_path = fs::canonicalize(&src_path)?;
        let doc = Parser::new()
            .parse_mixed_validate(proposed_document, src_path)
            .unwrap();
        self.manifest.insert(full_path.clone(), doc);
        Ok(full_path)
    }
}

fn get_schema_and_maybe_path(entry: DirEntry) -> Result<(String, PathBuf)> {
    let src = fs::read_to_string(entry.path()).expect("Could not read document file.");
    Ok((src, entry.path()))
}