use std::path::PathBuf;
use super::{DepTarget, Manifest, RawDep, Scope};
fn entries<'a>(source: &'a str, keyword: &'a str) -> Vec<&'a str> {
let mut found = Vec::new();
let mut in_block = false;
for line in source.lines() {
let line = line.split("//").next().unwrap_or_default().trim();
if in_block {
match line {
")" => in_block = false,
"" => {}
entry => found.push(entry),
}
continue;
}
let Some(rest) = line
.strip_prefix(keyword)
.filter(|r| r.starts_with([' ', '\t', '(']))
else {
continue;
};
match rest.trim() {
"(" => in_block = true,
entry => found.push(entry),
}
}
found
}
fn dep(target: DepTarget) -> RawDep {
RawDep {
target,
scope: Scope::Runtime,
}
}
fn replaced_by_directory(entry: &str) -> Option<PathBuf> {
let (_, new) = entry.split_once("=>")?;
let target = new.split_whitespace().next()?;
target
.starts_with(['.', '/'])
.then(|| PathBuf::from(target))
}
pub fn read(source: &str) -> Manifest {
let required = entries(source, "require")
.into_iter()
.filter_map(|entry| entry.split_whitespace().next())
.map(|module| dep(DepTarget::Name(module.to_string())));
let replaced = entries(source, "replace")
.into_iter()
.filter_map(replaced_by_directory)
.map(|dir| dep(DepTarget::Path(dir)));
Manifest {
name: entries(source, "module")
.first()
.and_then(|entry| entry.split_whitespace().next())
.map(|path| path.trim_matches('"').to_string()),
deps: required.chain(replaced).collect(),
..Manifest::default()
}
}
pub fn read_work(_source: &str) -> Manifest {
Manifest {
is_workspace_root: true,
..Manifest::default()
}
}
#[cfg(test)]
#[path = "gomod_test.rs"]
mod tests;