aldrin-parser 0.4.0

Aldrin schema parser library.
Documentation
use crate::error::DuplicateServiceUuid;
use crate::issues::Issues;
use crate::validate::Validate;
use crate::{Error, Schema, Warning};
use std::collections::hash_map::{Entry, HashMap};
use std::path::{Path, PathBuf};

#[derive(Debug)]
pub struct Parser {
    schema_paths: Vec<PathBuf>,
}

impl Parser {
    pub fn new() -> Self {
        Parser {
            schema_paths: Vec::new(),
        }
    }

    pub fn add_schema_path<P>(&mut self, path: P)
    where
        P: Into<PathBuf>,
    {
        self.schema_paths.push(path.into());
    }

    pub fn parse<P>(&self, schema_path: P) -> Parsed
    where
        P: AsRef<Path>,
    {
        let mut issues = Issues::default();
        let main_schema = Schema::parse(schema_path, &mut issues);

        let mut parsed = Parsed {
            main_schema: main_schema.name().to_owned(),
            schemas: HashMap::new(),
            issues,
        };
        parsed
            .schemas
            .insert(main_schema.name().to_owned(), main_schema);

        let mut imports = parsed
            .main_schema()
            .imports()
            .iter()
            .map(|i| i.schema_name().value().to_owned())
            .collect::<Vec<_>>();
        while let Some(import) = imports.pop() {
            let entry = match parsed.schemas.entry(import) {
                Entry::Occupied(_) => continue,
                Entry::Vacant(entry) => entry,
            };

            let schema_path = match self.find_schema(entry.key()) {
                Some(schema_path) => schema_path,
                None => continue,
            };

            let schema = Schema::parse(schema_path, &mut parsed.issues);
            imports.extend(
                schema
                    .imports()
                    .iter()
                    .map(|i| i.schema_name().value().to_owned()),
            );
            entry.insert(schema);
        }

        parsed.validate(&self.schema_paths);
        parsed
    }

    fn find_schema(&self, schema_name: &str) -> Option<PathBuf> {
        for mut path in self.schema_paths.iter().rev().cloned() {
            path.push(schema_name);
            path.set_extension("aldrin");

            if path.is_file() {
                return Some(path);
            }
        }

        None
    }
}

impl Default for Parser {
    fn default() -> Self {
        Parser::new()
    }
}

#[derive(Debug)]
pub struct Parsed {
    main_schema: String,
    schemas: HashMap<String, Schema>,
    issues: Issues,
}

impl Parsed {
    fn validate(&mut self, schema_paths: &[PathBuf]) {
        DuplicateServiceUuid::validate(self.schemas.values(), &mut self.issues);

        for (schema_name, schema) in &self.schemas {
            let is_main_schema = *schema_name == self.main_schema;
            let mut validate = Validate::new(
                schema_name,
                &mut self.issues,
                &self.schemas,
                is_main_schema,
                schema_paths,
            );
            schema.validate(&mut validate);
        }
    }

    pub fn main_schema(&self) -> &Schema {
        self.get_schema(&self.main_schema).unwrap()
    }

    pub fn get_schema(&self, schema_name: &str) -> Option<&Schema> {
        self.schemas.get(schema_name)
    }

    pub fn errors(&self) -> &[Error] {
        self.issues.errors()
    }

    pub fn warnings(&self) -> &[Warning] {
        self.issues.warnings()
    }

    pub fn other_warnings(&self) -> &[Warning] {
        self.issues.other_warnings()
    }
}