Skip to main content

cratestack_parser/load/
diagnostics.rs

1//! Orchestration: graph, part rules and collisions are a hard stop; then
2//! merge, expand mixins and validate the merged schema, attributing every
3//! error to its file.
4
5use std::path::{Path, PathBuf};
6
7use super::attribute::attribute;
8use super::graph::Graph;
9use super::sources::{FsSources, SchemaSources};
10use super::{LoadDiagnostics, LoadOptions, LoadedSchema, SchemaFile, collisions, merge};
11use crate::diagnostics::SchemaError;
12use crate::parse::{ParsedFile, expand_mixins};
13use crate::validate::validate_schema_collecting;
14
15/// Loads `path` and reports every independent problem, from several files
16/// in one run (docs/design/multi-file-schemas.md section 9).
17pub fn load_schema_diagnostics(
18    path: impl AsRef<Path>,
19    options: &LoadOptions<'_>,
20) -> LoadDiagnostics {
21    let fs = FsSources;
22    let sources: &dyn SchemaSources = options.sources.unwrap_or(&fs);
23    let mut graph = Graph::new(sources);
24    let loaded = graph.load(path.as_ref(), options.follow_part_of);
25    let Graph {
26        files, attempted, ..
27    } = graph;
28    let named: Vec<(String, PathBuf)> = files
29        .iter()
30        .map(|f| (f.file.display.clone(), f.file.path.clone()))
31        .collect();
32    let stop = |errors: Vec<SchemaError>| LoadDiagnostics {
33        schema: None,
34        errors,
35        files: attempted.clone(),
36        named: named.clone(),
37    };
38    if let Err(error) = loaded {
39        return stop(vec![error]);
40    }
41    let attribute_all = |error: SchemaError, files: &[SchemaFile]| attribute(error, files.iter());
42    if let Err(error) = collisions::check(&files) {
43        let infos: Vec<SchemaFile> = files.iter().map(|f| f.file.clone()).collect();
44        return stop(vec![attribute_all(error, &infos)]);
45    }
46    let mut infos = Vec::with_capacity(files.len());
47    let mut parsed: Vec<ParsedFile> = Vec::with_capacity(files.len());
48    for loaded in files {
49        infos.push(loaded.file);
50        parsed.push(
51            loaded
52                .parsed
53                .expect("every file parsed once the graph loaded"),
54        );
55    }
56    let mut schema = merge::merge(parsed);
57    if let Err(error) = expand_mixins(&mut schema) {
58        return stop(vec![attribute_all(error, &infos)]);
59    }
60    let errors: Vec<SchemaError> = validate_schema_collecting(&infos[0].display, "", &schema)
61        .into_iter()
62        .map(|error| attribute_all(error, &infos))
63        .collect();
64    if !errors.is_empty() {
65        return stop(errors);
66    }
67    LoadDiagnostics {
68        schema: Some(LoadedSchema {
69            schema,
70            files: infos,
71        }),
72        errors: Vec::new(),
73        files: attempted,
74        named,
75    }
76}