use crate::OptDoc;
use crate::doc::{ComponentItem, DocBuilder, TopLevelItem};
use crate::pipeline::{self, AnalyzeOptions};
use log::warn;
use miette::Result;
use std::collections::BTreeMap;
use veryl_analyzer::symbol::{SymbolId, SymbolKind};
use veryl_analyzer::symbol_table;
use veryl_metadata::Metadata;
use veryl_parser::resource_table;
pub struct CmdDoc {
opt: OptDoc,
}
impl CmdDoc {
pub fn new(opt: OptDoc) -> Self {
Self { opt }
}
pub fn exec(&self, metadata: &mut Metadata) -> Result<bool> {
let mut paths = metadata.paths(&self.opt.files, true, true)?;
paths.retain(|x| !x.example);
let options = AnalyzeOptions {
defines: &[],
emit_mode: false,
incremental: false,
fail_fast: true,
};
let _ = pipeline::analyze(metadata, &paths, options, None, None)?;
let mut modules = BTreeMap::new();
let mut proto_modules = BTreeMap::new();
let mut interfaces = BTreeMap::new();
let mut packages = BTreeMap::new();
for symbol in veryl_analyzer::symbol_table::get_all() {
let text = resource_table::get_str_value(symbol.token.text).unwrap();
let file_name = text.clone();
let symbol = symbol.clone();
if format!("{}", symbol.namespace) == metadata.project.name && symbol.public {
match &symbol.kind {
SymbolKind::Module(x) if !x.is_proto => {
let html_name = fmt_generic_parameters(&text, &x.generic_parameters);
let item = TopLevelItem {
file_name,
html_name,
symbol,
};
modules.insert(text, item);
}
SymbolKind::Module(x) if x.is_proto => {
let html_name = file_name.clone();
let item = TopLevelItem {
file_name,
html_name,
symbol,
};
proto_modules.insert(text, item);
}
SymbolKind::Interface(x) if !x.is_proto => {
let html_name = fmt_generic_parameters(&text, &x.generic_parameters);
let item = TopLevelItem {
file_name,
html_name,
symbol,
};
interfaces.insert(text, item);
}
SymbolKind::Package(x) if !x.is_proto => {
let html_name = fmt_generic_parameters(&text, &x.generic_parameters);
let item = TopLevelItem {
file_name,
html_name,
symbol,
};
packages.insert(text, item);
}
_ => (),
}
}
}
let modules: Vec<_> = modules.into_values().collect();
let proto_modules: Vec<_> = proto_modules.into_values().collect();
let interfaces: Vec<_> = interfaces.into_values().collect();
let packages: Vec<_> = packages.into_values().collect();
let components = gather_components(metadata);
let builder = DocBuilder::new(
metadata,
modules,
proto_modules,
interfaces,
packages,
components,
)?;
builder.build()?;
Ok(true)
}
}
fn gather_components(metadata: &Metadata) -> Vec<ComponentItem> {
if metadata.components.is_empty() {
return vec![];
}
let root = metadata.project_path();
let target_dir = root.join("target/veryl-components");
let mut ret = BTreeMap::new();
for def in &metadata.components {
let manifests = def.collect_manifests(&root, &target_dir);
if manifests.is_empty() {
warn!(
"component package ({}) has no interface manifest; its components get no pages — the build failed or cargo is unavailable (a manifest committed by `veryl publish` also works)",
def.path.display()
);
}
for (name, manifest) in manifests {
ret.entry(name.clone()).or_insert_with(|| ComponentItem {
file_name: format!("component_{name}"),
name,
manifest,
});
}
}
ret.into_values().collect()
}
fn fmt_generic_parameters(name: &str, params: &[SymbolId]) -> String {
if params.is_empty() {
name.to_string()
} else {
let mut name = name.to_string();
name.push_str("::<");
for param in params {
let symbol = symbol_table::get(*param).unwrap();
name.push_str(&format!("{}, ", symbol.token.text));
}
format!("{}>", name.strip_suffix(", ").unwrap())
}
}