use crate::{tag::Tag, ProjectInfo}; pub fn generate_html_for_tags( tags: Vec, project_info: &ProjectInfo, crate_info: Option, // valid if the file belongs to a crate dependency source: &str, file_name: String, relative_data_path: &str, relative_root_dir: &str, relative_file_path: &str, ) -> String { let mut out = create_header( file_name, relative_data_path, relative_root_dir, relative_file_path, project_info, crate_info, ); // reserve some extra space out.reserve(out.len() + (2 * source.len())); out += "
\n"; out += "\n"; out += "\n"; out += "\n"; let mut linenum = 1; out += ""; out += "\n"; linenum += 1; let linenumstr = linenum.to_string(); out += "\n\n
1"; let mut last_found_tag_idx = 0; let mut current_tag_end = usize::max_value(); let mut stack: Vec<&Tag> = Vec::new(); // manually index because our tags store byte offset as start/end // and chars.enumerate() will iterate over utf8 let mut i = 0; for ch in source.chars() { if i == current_tag_end { while let Some(t) = stack.pop() { out = out + &t.close(); } current_tag_end = usize::max_value(); } // if we already have a tag, then no need to search if current_tag_end == usize::max_value() { for (idx, tag) in tags.iter().skip(last_found_tag_idx).enumerate() { if tag.start as usize == i { out = out + &tag.open(); current_tag_end = tag.end as usize; stack.push(tag); last_found_tag_idx = idx + last_found_tag_idx + 1; break; } else if tag.start as usize > i { // tags are sorted, so if its greater than 'i', we didn't find anything break; } } } if ch == '\n' { let stack_copy = stack.clone(); while let Some(t) = stack.pop() { out = format!("{}{}", out, t.close()) } out += "
"; out = out + &linenumstr; out += ""; // reopen tags for next line for tag in &stack_copy { out = out + &tag.open(); } stack = stack_copy; } else if ch == '<' { out.push_str("<"); } else if ch == '>' { out.push_str(">"); } else if ch == '&' { out.push_str("&"); } else { out.push(ch) } i += ch.len_utf8(); } out += "
\n"; let time = std::time::SystemTime::now(); let datetime: chrono::DateTime = time.into(); let datetime = datetime.format("%Y-%b-%d"); // BEGIN footer let footer = format!( "Generated on {} from project {} revision {}", datetime, project_info.name, project_info.version ); // TODO out += "
\n"; // END footer out += "
\n\n\n"; out } pub fn create_header( file_name: String, data_path: &str, root_path: &str, file_path: &str, project_info: &ProjectInfo, crate_info: Option, ) -> String { let (project_name, mut version): (&str, &str) = if let Some(p) = &crate_info { (&p.name, &p.version) } else { (&project_info.name, &project_info.version) }; if version.len() > 7 { version = &version[..7]; // limit the length to 7 } format!( r#" {file_name} source code [{file_path}] - Codebrowser "# ) }