186 lines
5.8 KiB
Rust
186 lines
5.8 KiB
Rust
use std::{collections::HashMap, ops::ControlFlow};
|
|
|
|
use anyhow::{Context, anyhow};
|
|
use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
|
|
use tracing::{error, info_span, instrument};
|
|
|
|
use crate::{
|
|
config::Config,
|
|
dirs::Dirs,
|
|
doc::Doc,
|
|
html::navmap::NavigationMap,
|
|
import_map::ImportMap,
|
|
parse::parse_tree_with_diagnostics,
|
|
state::{Source, Treehouse, report_diagnostics},
|
|
tree::SemaRoots,
|
|
vfs::{self, Cd, Content, VPath, VPathBuf},
|
|
};
|
|
|
|
pub struct Sources {
|
|
pub config: Config,
|
|
pub treehouse: Treehouse,
|
|
pub navigation_map: NavigationMap,
|
|
pub import_map: ImportMap,
|
|
}
|
|
|
|
impl Sources {
|
|
pub fn load(dirs: &Dirs) -> anyhow::Result<Self> {
|
|
let config = {
|
|
let _span = info_span!("load_config").entered();
|
|
let mut config: Config = toml_edit::de::from_str(
|
|
&vfs::query::<Content>(&dirs.root, VPath::new_const("treehouse.toml"))
|
|
.map(Content::string)
|
|
.ok_or_else(|| anyhow!("config file does not exist"))??,
|
|
)
|
|
.context("failed to deserialize config")?;
|
|
config.site = std::env::var("TREEHOUSE_SITE").unwrap_or(config.site);
|
|
config.autopopulate_emoji(&*dirs.emoji)?;
|
|
config.autopopulate_pics(&*dirs.pic)?;
|
|
config.load_syntaxes(dirs.syntax.clone())?;
|
|
config
|
|
};
|
|
|
|
let treehouse = load_trees(&config, dirs)?;
|
|
let navigation_map = NavigationMap::build(
|
|
&treehouse,
|
|
treehouse.files_by_tree_path[VPath::new("index")],
|
|
);
|
|
let import_map = ImportMap::generate(
|
|
&config.site,
|
|
&Cd::new(dirs.static_.clone(), VPathBuf::new("js")),
|
|
&config.build.javascript.import_roots,
|
|
);
|
|
|
|
Ok(Sources {
|
|
config,
|
|
treehouse,
|
|
navigation_map,
|
|
import_map,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[instrument(skip(config, dirs))]
|
|
fn load_trees(config: &Config, dirs: &Dirs) -> anyhow::Result<Treehouse> {
|
|
let mut treehouse = Treehouse::new();
|
|
let mut diagnostics = vec![];
|
|
let mut parsed_trees = HashMap::new();
|
|
|
|
let mut paths = vec![];
|
|
let mut doc_paths = vec![];
|
|
|
|
vfs::walk_dir_rec(&*dirs.content, VPath::ROOT, &mut |path| {
|
|
match path.extension() {
|
|
Some("tree") => paths.push(path.to_owned()),
|
|
Some("dj") => doc_paths.push(path.to_owned()),
|
|
_ => (),
|
|
}
|
|
ControlFlow::Continue(())
|
|
});
|
|
|
|
// Trees
|
|
|
|
// NOTE: Sources are filled in later; they can be left out until a call to report_diagnostics.
|
|
let file_ids: Vec<_> = paths
|
|
.iter()
|
|
.map(|path| treehouse.add_file(path.clone(), Source::Other(String::new())))
|
|
.collect();
|
|
|
|
let parse_results: Vec<_> = {
|
|
let _span = info_span!("load_trees::parse").entered();
|
|
paths
|
|
.into_par_iter()
|
|
.zip(&file_ids)
|
|
.flat_map(|(path, &file_id)| {
|
|
vfs::query::<Content>(&dirs.content, &path)
|
|
.and_then(|c| c.string().ok())
|
|
.map(|input| {
|
|
let parse_result = parse_tree_with_diagnostics(file_id, &input);
|
|
(path, file_id, input, parse_result)
|
|
})
|
|
})
|
|
.collect()
|
|
};
|
|
|
|
for (path, file_id, input, _) in &parse_results {
|
|
let tree_path = path.with_extension("");
|
|
treehouse
|
|
.files_by_tree_path
|
|
.insert(tree_path.clone(), *file_id);
|
|
treehouse.set_source(
|
|
*file_id,
|
|
Source::Tree {
|
|
input: input.clone(),
|
|
tree_path,
|
|
},
|
|
);
|
|
}
|
|
|
|
{
|
|
let _span = info_span!("load_trees::sema").entered();
|
|
for (path, file_id, _, result) in parse_results {
|
|
match result {
|
|
Ok(roots) => {
|
|
let roots = SemaRoots::from_roots(
|
|
&mut treehouse,
|
|
&mut diagnostics,
|
|
config,
|
|
file_id,
|
|
roots,
|
|
);
|
|
treehouse.roots.insert(file_id, roots);
|
|
parsed_trees.insert(path, file_id);
|
|
}
|
|
Err(mut parse_diagnostics) => diagnostics.append(&mut parse_diagnostics),
|
|
}
|
|
}
|
|
}
|
|
|
|
// Docs
|
|
|
|
let mut doc_file_ids = vec![];
|
|
|
|
for path in &doc_paths {
|
|
if let Some(input) =
|
|
vfs::query::<Content>(&dirs.content, path).and_then(|c| c.string().ok())
|
|
{
|
|
let file_id = treehouse.add_file(path.clone(), Source::Other(input));
|
|
treehouse.files_by_doc_path.insert(path.clone(), file_id);
|
|
doc_file_ids.push(file_id);
|
|
} else {
|
|
error!(
|
|
"doc {path} does not exist in content directory even though it was enumerated via walk_dir_rec"
|
|
);
|
|
}
|
|
}
|
|
|
|
for file_id in doc_file_ids {
|
|
let (doc, mut doc_diagnostics) = Doc::parse(&mut treehouse, config, file_id);
|
|
treehouse.docs.insert(file_id, doc);
|
|
diagnostics.append(&mut doc_diagnostics);
|
|
}
|
|
|
|
// Tags
|
|
|
|
for file_id in treehouse.files_by_tree_path.values() {
|
|
let roots = &treehouse.roots[file_id];
|
|
for tag_name in &roots.attributes.tags {
|
|
let tag = treehouse.tags.entry(tag_name.clone()).or_default();
|
|
tag.files.push(*file_id);
|
|
}
|
|
}
|
|
|
|
for file_id in treehouse.files_by_doc_path.values() {
|
|
let doc = &treehouse.docs[file_id];
|
|
for tag_name in &doc.attributes.tags {
|
|
let tag = treehouse.tags.entry(tag_name.clone()).or_default();
|
|
tag.files.push(*file_id);
|
|
}
|
|
}
|
|
|
|
// Diagnostics
|
|
|
|
report_diagnostics(&treehouse, &diagnostics)?;
|
|
|
|
Ok(treehouse)
|
|
}
|