use dev_prefix::*;
use super::types::*;
use core;
pub fn get_subcommand<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("check")
.about("check for any errors in the project")
.settings(&[AS::DeriveDisplayOrder, COLOR])
}
fn paint_it<W: Write>(w: &mut W, msg: &str) {
if COLOR_IF_POSSIBLE {
write!(w, "{}", Red.paint(msg)).unwrap();
} else {
write!(w, "{}", msg).unwrap();
}
}
fn paint_it_bold<W: Write>(w: &mut W, msg: &str) {
if COLOR_IF_POSSIBLE {
write!(w, "{}", Red.bold().paint(msg)).unwrap();
} else {
write!(w, "{}", msg).unwrap();
}
}
fn display_invalid_partof<W: Write>(w: &mut W, cwd: &Path, project: &Project) -> u64 {
let mut error: u64 = 0;
let mut invalid_partof = ArtNames::new();
let mut displayed_header = false;
for (name, artifact) in &project.artifacts {
invalid_partof.clear();
for p in &artifact.partof {
if !project.artifacts.contains_key(p) {
invalid_partof.insert(p.clone());
}
}
if !invalid_partof.is_empty() {
error += 1;
let mut msg = String::new();
if !displayed_header {
displayed_header = true;
paint_it_bold(w, "\nFound partof names that do not exist:\n");
}
write!(msg,
" {} [{}]: {:?}\n",
name,
utils::relative_path(&artifact.path, cwd).display(),
invalid_partof)
.unwrap();
paint_it(w, &msg);
}
}
error
}
fn display_unresolvable<W: Write>(w: &mut W, project: &Project) -> u64 {
let mut error: u64 = 0;
let unresolved: Vec<(ArtNameRc, &Artifact)> = Vec::from_iter(project.artifacts
.iter()
.filter(|a| a.1.completed < 0. || a.1.tested < 0.)
.map(|n| (n.0.clone(), n.1)));
let unknown_names: HashSet<ArtNameRc> = HashSet::from_iter(unresolved.iter()
.map(|u| u.0.clone()));
if !unresolved.is_empty() {
error += 1;
let mut unresolved_partof: HashMap<ArtNameRc, HashSet<ArtNameRc>> = HashMap::new();
for &(ref name, artifact) in &unresolved {
let partof: HashSet<_> = artifact.partof
.iter()
.filter(|n| {
!project.artifacts.contains_key(n.as_ref()) ||
unknown_names.contains(n.as_ref())
})
.cloned()
.collect();
unresolved_partof.insert(name.clone(), partof);
}
let mut resolved_names: HashSet<ArtNameRc> = HashSet::new();
let mut remove_names = Vec::new();
let mut just_resolved = Vec::new();
loop {
just_resolved.clear();
let mut did_something = false;
for (name, partof) in &mut unresolved_partof {
remove_names.clear();
for p in partof.iter() {
if resolved_names.contains(p) {
remove_names.push(p.clone());
}
}
if !remove_names.is_empty() {
did_something = true;
}
for p in &remove_names {
partof.remove(p);
}
if partof.is_empty() {
resolved_names.insert(name.clone());
just_resolved.push(name.clone());
}
}
if !just_resolved.is_empty() {
did_something = true;
}
for r in &just_resolved {
unresolved_partof.remove(r);
}
if !did_something {
break;
}
}
paint_it_bold(w,
"\nArtifacts partof contains at least one recursive reference:\n");
let mut unresolved_partof: Vec<_> = unresolved_partof.drain()
.map(|mut v| (v.0, v.1.drain().collect::<Vec<_>>()))
.collect();
unresolved_partof.sort_by(|a, b| a.0.cmp(&b.0));
for (name, partof) in unresolved_partof.drain(0..) {
let mut msg = String::new();
write!(msg, " {:<30}: {:?}\n", name.to_string(), partof).unwrap();
write!(w, "{}", msg).unwrap();
}
}
error
}
fn display_invalid_locs<W: Write>(w: &mut W, cwd: &Path, project: &Project) -> u64 {
let mut error: u64 = 0;
if !project.dne_locs.is_empty() {
error += 1;
let mut invalid_locs: HashMap<PathBuf, Vec<(ArtName, Loc)>> = HashMap::new();
for (name, loc) in &project.dne_locs {
if !invalid_locs.contains_key(&loc.path) {
invalid_locs.insert(loc.path.clone(), Vec::new());
}
invalid_locs.get_mut(&loc.path).unwrap().push((name.clone(), loc.clone()));
}
let header = "\nFound implementation links in the code that do not exist:\n";
paint_it_bold(w, header);
let mut invalid_locs: Vec<(PathBuf, Vec<(ArtName, Loc)>)> =
Vec::from_iter(invalid_locs.drain());
invalid_locs.sort_by(|a, b| a.0.cmp(&b.0));
for (path, mut locs) in invalid_locs.drain(0..) {
let mut pathstr = String::new();
write!(pathstr,
" {}:\n",
utils::relative_path(&path, cwd).display())
.unwrap();
paint_it(w, &pathstr);
locs.sort_by(|a, b| a.1.line.cmp(&b.1.line));
for (name, loc) in locs {
let mut loc_str = String::new();
write!(loc_str, " - [{}]", loc.line).unwrap();
paint_it(w, &loc_str);
write!(w, " {}\n", name).unwrap();
}
}
}
error
}
fn display_hanging_artifacts<W: Write>(w: &mut W, cwd: &Path, project: &Project) -> u64 {
let mut error: u64 = 0;
fn partof_types(a: &Artifact, types: &HashSet<ArtType>) -> bool {
for p in &a.partof {
if types.contains(&p.ty) {
return true;
}
}
false
}
let rsk_spc_types = HashSet::from_iter(vec![ArtType::RSK, ArtType::SPC]);
let req_types = HashSet::from_iter(vec![ArtType::REQ]);
let mut hanging: Vec<(ArtNameRc, &Path)> = Vec::new();
for (name, artifact) in &project.artifacts {
let ty = name.ty;
if (ty != ArtType::REQ) && !artifact.is_parent() && !name.is_root() &&
name.parent().unwrap().is_root() &&
match ty {
ArtType::TST => !partof_types(artifact, &rsk_spc_types),
ArtType::SPC | ArtType::RSK => !partof_types(artifact, &req_types),
_ => unreachable!(),
} {
hanging.push((name.clone(), &artifact.path));
}
}
hanging.sort_by(|a, b| a.1.cmp(b.1));
if !hanging.is_empty() {
error += 1;
let msg = "\nHanging artifacts found (top-level but not partof a higher type):\n";
paint_it_bold(w, msg);
for (h, p) in hanging {
let mut msg = String::new();
write!(msg,
" {:<30}: {}\n",
utils::relative_path(p, cwd).display(),
h)
.unwrap();
write!(w, "{}", msg).unwrap();
}
}
error
}
fn display_hanging_references<W: Write>(w: &mut W, cwd: &Path, project: &Project) -> u64 {
let mut error: u64 = 0;
let regexp = Regex::new(&format!(r"(?i)\[\[({})\]\]", core::types::ART_VALID_STR))
.expect("tested regexp");
let mut hanging: HashMap<ArtNameRc, Vec<ArtName>> = HashMap::new();
for (name, artifact) in &project.artifacts {
let mut found = vec![];
for cap in regexp.captures_iter(&artifact.text) {
let raw = cap.get(1).expect("regexp definition");
let tname = ArtName::from_str(raw.as_str()).expect("regexp validatd");
if !project.artifacts.contains_key(&tname) {
error += 1;
found.push(tname);
}
}
if !found.is_empty() {
hanging.insert(name.clone(), found);
}
}
if !hanging.is_empty() {
paint_it_bold(w,
"\nArtifacts text contains invalid [[ART-name]] references:\n");
let mut hanging: Vec<_> = hanging.drain().collect();
hanging.sort();
for &(ref name, ref found) in &hanging {
let artifact = project.artifacts.get(name).expect("inserted from");
paint_it(w,
&format!(" {} ({}):\n",
name,
utils::relative_path(&artifact.path, cwd).display()));
for f in found {
write!(w, " - {}", f).unwrap();
}
}
}
error
}
#[allow(cyclomatic_complexity)] pub fn run_cmd<W: Write>(w: &mut W, cwd: &Path, project: &Project) -> Result<()> {
let mut error: u64 = 0;
error += display_invalid_partof(w, cwd, project);
error += display_unresolvable(w, project);
error += display_invalid_locs(w, cwd, project);
error += display_hanging_artifacts(w, cwd, project);
error += display_hanging_references(w, cwd, project);
if error == 0 {
let mut msg = String::new();
write!(msg, "art check: no errors found in {}\n", cwd.display()).unwrap();
if COLOR_IF_POSSIBLE {
write!(w, "{}", Green.paint(msg)).unwrap();
} else {
write!(w, "{}", msg).unwrap();
}
} else {
write!(w, "\n").unwrap();
}
if error != 0 {
Err(ErrorKind::CmdError("errors found during ls, see logs".to_string()).into())
} else {
Ok(())
}
}