use anyhow::{Result, anyhow};
use log::info;
use rustc_middle::ty::TyCtxt;
use crate::svg_generator::data::ExternalEvent;
use std::collections::{HashMap, BTreeMap};
use std::{path::PathBuf, fs};
use std::env::current_dir;
use crate::rustviz_library::rv::Rustviz;
use std::fs::File;
use crate::expr_visitor::RapData;
pub fn annotate_struct_field (
line_str: &str,
hash_map: & mut HashMap<String, usize>,
a_map: & mut BTreeMap<usize, Vec<String>>,
hashes: & mut usize,
field: & rustc_hir::FieldDef,
m: & TyCtxt
) {
let name:String = field.ident.as_str().to_owned();
let hash = *hash_map.entry(name.clone()).or_insert_with(|| {
let current_hash = *hashes;
*hashes = (*hashes + 1) % 10;
current_hash
});
let line: usize = m.sess.source_map().lookup_char_pos(field.ident.span.lo()).line;
let left: usize = m.sess.source_map().lookup_char_pos(field.ident.span.lo()).col_display;
let right: usize = m.sess.source_map().lookup_char_pos(field.ident.span.hi()).col_display;
let mut line_contents = line_str.to_string();
let replace_with = format!("[_tspan data-hash=\"{}\"_]{}[_/tspan_]", hash, name);
line_contents.replace_range(left..right, &replace_with);
let v = a_map.get_mut(&line).unwrap();
if !v.contains(&line_contents) {
v.push(line_contents);
}
}
pub fn annotate_toplevel_fn (
func_ident: rustc_span::symbol::Ident,
line_str: &str,
raps: & HashMap<String, RapData>,
a_map: & mut BTreeMap<usize, Vec<String>>,
hashes: & mut usize,
m: &TyCtxt) {
let func_name = func_ident.as_str().to_owned();
let line: usize = m.sess.source_map().lookup_char_pos(func_ident.span.lo()).line;
let left: usize = m.sess.source_map().lookup_char_pos(func_ident.span.lo()).col_display;
let right: usize = m.sess.source_map().lookup_char_pos(func_ident.span.hi()).col_display;
let hash = match raps.get(&func_name) {
Some(r) => { *r.rap.hash() }
None => {
let current_hash = *hashes;
*hashes = (*hashes + 1) % 10;
current_hash as u64
}
};
let mut line_contents = line_str.to_string();
let replace_with: String = format!("[_tspan class=\"fn\" data-hash=\"{}\" hash=\"{}\"_]{}[_/tspan_]", 0, hash, func_name);
line_contents.replace_range(left..right, &replace_with);
let v = a_map.get_mut(&line).unwrap();
if !v.contains(&line_contents) {
v.push(line_contents);
}
}
pub fn annotate_enum_variant(
ctor_name: &str,
parent_name: &str,
variant: & rustc_hir::Variant,
rap_map: & HashMap<String, RapData>,
a_map: & mut BTreeMap<usize, Vec<String>>,
m: & TyCtxt
) {
let rap_name = format!("{}::{}", parent_name, ctor_name);
if rap_map.contains_key(&rap_name) {
let span = variant.ident.span;
let hash = rap_map.get(&rap_name).unwrap().rap.hash();
let line: usize = m.sess.source_map().lookup_char_pos(span.lo()).line;
let left: usize = m.sess.source_map().lookup_char_pos(span.lo()).col_display;
let line_str = &a_map[&line][0];
let right = m.sess.source_map().lookup_char_pos(span.hi()).col_display;
let mut line_contents = line_str.to_string();
let replace_with = format!("[_tspan class=\"fn\" data-hash=\"{}\" hash=\"{}\"_]{}[_/tspan_]", 0, hash, ctor_name);
line_contents.replace_range(left..right, &replace_with);
let v = a_map.get_mut(&line).unwrap();
if !v.contains(&line_contents) {
v.push(line_contents);
}
}
}
pub struct RV1Helper {
source_str: String,
source_path: PathBuf
}
impl RV1Helper {
pub fn new () -> RV1Helper {
RV1Helper { source_str: String::new(), source_path: PathBuf::new() }
}
pub fn initialize_line_map(&mut self) -> Result<BTreeMap<usize, String>> {
self.source_path = current_dir()?;
self.source_path = self.source_path.join("src/lib.rs"); info!("source path {:#?}", self.source_path);
let mut line_map: BTreeMap<usize, String> = BTreeMap::new();
match fs::read_to_string(self.source_path.clone()) {
Ok(contents) => {
self.source_str = contents.clone(); let mut res_str: String = String::new();
for line in contents.lines() {
res_str.push_str(line);
res_str.push('\n');
}
let lines: Vec<&str> = res_str.lines().collect();
for (line_num, line_content) in lines.iter().enumerate() {
line_map.insert(line_num + 1, line_content.to_string());
}
}
Err(e) => {
return Err(anyhow!("Error with reading source file : {}", e));
}
}
return Ok(line_map);
}
pub fn generate_vis(& mut self,
mut line_map: BTreeMap<usize, Vec<ExternalEvent>>,
p_events: Vec<(usize, ExternalEvent)>,
a_map: & mut BTreeMap<usize, Vec<String>>,
num_raps: usize,
fn_start_lines: HashMap<u64, usize>,
write_to_cwd: bool) -> Result<()> {
let mut keys_to_remove: Vec<usize> = Vec::new();
for (k, v) in line_map.iter() {
if v.is_empty() {
keys_to_remove.push(*k);
}
}
for k in keys_to_remove.iter() {
line_map.remove(k);
}
let annotated_source_str: String = generate_annotated_src(a_map);
let rv = Rustviz::new(&annotated_source_str, &self.source_str, p_events, line_map, num_raps, fn_start_lines)?;
if write_to_cwd { self.source_path.pop(); let code_panel_path: PathBuf = self.source_path.join("vis_code.svg");
let timeline_panel_path: PathBuf = self.source_path.join("vis_timeline.svg");
if !code_panel_path.exists(){
File::create(code_panel_path.clone())?;
}
if !timeline_panel_path.exists(){
File::create(timeline_panel_path.clone())?;
}
fs::write(code_panel_path, rv.code_panel())?;
fs::write(timeline_panel_path, rv.timeline_panel())?;
}
else {
let res = format!("{}:::{}", rv.code_panel(), rv.timeline_panel());
println!("{res}");
}
Ok(())
}
}
fn union_strings(strings: &Vec<String>) -> String {
if strings.len() == 1 {
return strings[0].clone();
}
if strings.len() == 2 {
return strings[1].clone();
}
let bufs: Vec<Vec<char>> = strings.iter().map(|s| s.chars().collect()).collect();
let mut offsets = vec![0usize; bufs.len()];
let mut res = String::new();
loop {
let mut emitted_marker = true;
while emitted_marker {
emitted_marker = false;
for k in 0..bufs.len() {
if offsets[k] < bufs[k].len() && bufs[k][offsets[k]] == '[' {
while offsets[k] < bufs[k].len() {
let c = bufs[k][offsets[k]];
res.push(c);
offsets[k] += 1;
if c == ']' { break; }
}
emitted_marker = true;
}
}
}
let chosen = bufs.iter().zip(offsets.iter()).find_map(|(buf, &o)| {
if o < buf.len() { Some(buf[o]) } else { None }
});
let Some(ch) = chosen else { break };
res.push(ch);
for k in 0..bufs.len() {
if offsets[k] < bufs[k].len() && bufs[k][offsets[k]] == ch {
offsets[k] += 1;
}
}
}
res
}
pub fn generate_annotated_src(annotated_line_map: & mut BTreeMap<usize, Vec<String>>) -> String {
let mut annotated_str = String::new();
for (_k, v) in annotated_line_map {
annotated_str.push_str(&union_strings(v));
annotated_str.push('\n');
}
annotated_str = annotated_str.replace("&", "&");
annotated_str = annotated_str.replace("<", "<");
annotated_str = annotated_str.replace(">", ">");
annotated_str = annotated_str.replace("[_", "<");
annotated_str = annotated_str.replace("_]", ">");
annotated_str
}