use super::types::*;
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, settings: &Settings, msg: &str) {
if settings.color {
write!(w, "{}", Red.paint(msg)).unwrap();
} else {
write!(w, "{}", msg).unwrap();
}
}
fn paint_it_bold<W: Write>(w: &mut W, settings: &Settings, msg: &str) {
if settings.color {
write!(w, "{}", Red.bold().paint(msg)).unwrap();
} else {
write!(w, "{}", msg).unwrap();
}
}
#[allow(cyclomatic_complexity)] pub fn do_check<W: Write>(w: &mut W,
cwd: &Path,
artifacts: &Artifacts,
dne_locs: &HashMap<ArtName, Loc>,
settings: &Settings) -> i32 {
let mut error: i32 = 0;
let mut invalid_partof = ArtNames::new();
let mut displayed_header = false;
for (name, artifact) in artifacts.iter() {
invalid_partof.clear();
for p in &artifact.partof {
if !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, settings, "\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, settings, &msg);
}
}
let unresolved: Vec<(ArtNameRc, &Artifact)> = Vec::from_iter(
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| !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, settings, "\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();
}
}
if !dne_locs.is_empty() {
error = 1;
let mut invalid_locs: HashMap<PathBuf, Vec<(ArtName, Loc)>> = HashMap::new();
for (name, loc) in 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, settings, 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, settings, &pathstr);
locs.sort_by(|a, b| a.1.line_col.cmp(&b.1.line_col));
for (name, loc) in locs {
let mut loc_str = String::new();
write!(loc_str, " - ({}:{})", loc.line_col.0, loc.line_col.1).unwrap();
paint_it(w, settings, &loc_str);
write!(w, " {}\n", name).unwrap();
}
}
}
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 artifacts.iter() {
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, settings, 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();
}
}
if error == 0 {
let mut msg = String::new();
write!(msg, "rst check: no errors found in {}\n", cwd.display()).unwrap();
if settings.color {
write!(w, "{}", Green.paint(msg)).unwrap();
} else {
write!(w, "{}", msg).unwrap();
}
} else {
write!(w, "\n").unwrap();
}
error
}