use md5::{Digest, Md5};
use rand::Rng;
pub fn scan_for_aperture_definitions(content_lines: &[&str]) -> (Vec<String>, Vec<u32>) {
let mut existing_definitions = Vec::new();
let mut existing_aperture_ids = Vec::new();
for line in content_lines.iter().take(200) {
if line.starts_with("%ADD") {
let potential_num_part = &line[4..]; if let Some(num_end_pos) = potential_num_part.find(|c: char| !c.is_ascii_digit()) {
let num_str = &potential_num_part[..num_end_pos];
if (2..=4).contains(&num_str.len()) {
if let Ok(num) = num_str.parse::<u32>() {
existing_definitions.push(line.to_string());
existing_aperture_ids.push(num);
}
}
}
}
}
(existing_definitions, existing_aperture_ids)
}
pub fn select_injection_template(
existing_definitions: &[String],
existing_aperture_ids: &[u32],
) -> (String, u32, u32) {
let mut rng = rand::rng();
let injection_site_index = rng.random_range(5..existing_definitions.len());
let template_definition_line = existing_definitions[injection_site_index].clone();
let injection_aperture_id = existing_aperture_ids[injection_site_index]; let original_template_id = existing_aperture_ids[injection_site_index]; (
template_definition_line,
injection_aperture_id,
original_template_id,
)
}
pub fn renumber_apertures(content: &str, injection_aperture_id: u32) -> String {
let mut renumbered_lines = Vec::new();
for line in content.split('\n') {
let mut prefix: Option<&str> = None;
if line.starts_with("%ADD") {
prefix = Some("%ADD");
} else if line.starts_with("G54D") {
prefix = Some("G54D");
}
let mut line_was_renumbered = false;
if let Some(p) = prefix {
let potential_num_part = &line[p.len()..];
if let Some(num_end_pos) = potential_num_part.find(|c: char| !c.is_ascii_digit()) {
let num_str = &potential_num_part[..num_end_pos];
if (2..=4).contains(&num_str.len()) {
if let Ok(number) = num_str.parse::<u32>() {
if number >= injection_aperture_id {
let rest_of_line = &potential_num_part[num_end_pos..];
renumbered_lines.push(format!("{}{}{}", p, number + 1, rest_of_line));
line_was_renumbered = true;
}
}
}
}
}
if !line_was_renumbered {
renumbered_lines.push(line.to_string());
}
}
renumbered_lines.join("\n")
}
pub fn generate_hashed_dimension(
content_with_shifted_ids: &str,
is_foreign_board_file: bool,
) -> String {
let data_to_hash = if is_foreign_board_file {
format!("494d{}", content_with_shifted_ids)
} else {
content_with_shifted_ids.to_string()
};
let mut md5_hasher = Md5::new();
md5_hasher.update(data_to_hash.as_bytes());
let digest = md5_hasher.finalize();
let hex_digest = format!("{:x}", digest);
let final_hex_chars = &hex_digest[hex_digest.len() - 2..];
let decimal_from_hash = u32::from_str_radix(final_hex_chars, 16).unwrap_or(0) % 100;
let hash_based_suffix = format!("{:02}", decimal_from_hash);
let mut rng = rand::rng();
let random_base_dimension: f64 = rng.random_range(0.0..1.0);
let combined_dimension_str = format!("{:.2}{}", random_base_dimension, hash_based_suffix);
if combined_dimension_str.parse::<f64>().unwrap_or(0.0) == 0.0 {
"0.0100".to_string()
} else {
combined_dimension_str
}
}
pub fn create_fingerprint_aperture_line(
template_definition_line: &str,
injection_aperture_id: u32,
original_template_id: u32,
final_dimension_str: &str,
) -> String {
let new_definition_from_template = if let Some(comma_pos) = template_definition_line.find(',') {
let part_after_comma = &template_definition_line[comma_pos + 1..];
let size_end_pos = part_after_comma
.find(|c: char| !c.is_ascii_digit() && c != '.')
.unwrap_or_else(|| part_after_comma.len());
let original_size_part = &template_definition_line[comma_pos..comma_pos + 1 + size_end_pos];
let new_size_part = format!(",{}", final_dimension_str);
template_definition_line.replace(original_size_part, &new_size_part)
} else {
format!("%ADD{}C,{}*%", injection_aperture_id, final_dimension_str)
};
let old_id_in_template = format!("ADD{}", original_template_id);
let new_id_str = format!("ADD{}", injection_aperture_id);
new_definition_from_template.replace(&old_id_in_template, &new_id_str)
}
pub fn insert_new_aperture_line(
content_with_shifted_ids: &str,
final_fingerprint_line: &str,
injection_aperture_id: u32,
) -> String {
let anchor_for_insertion = format!("%ADD{}", injection_aperture_id - 1);
let mut output_lines: Vec<String> = Vec::new();
let mut fingerprint_was_inserted = false;
for line in content_with_shifted_ids.split('\n') {
output_lines.push(line.to_string());
if line.starts_with(&anchor_for_insertion) {
output_lines.push(final_fingerprint_line.to_string());
fingerprint_was_inserted = true;
}
}
if !fingerprint_was_inserted {
let mut final_lines: Vec<String> = Vec::new();
let mut mo_section_found = false;
for line in content_with_shifted_ids.split('\n') {
if !mo_section_found && line.starts_with("%MO") {
mo_section_found = true;
} else if mo_section_found
&& !fingerprint_was_inserted
&& (line.starts_with("%LP") || line.starts_with('G'))
{
final_lines.push(final_fingerprint_line.to_string());
fingerprint_was_inserted = true;
}
final_lines.push(line.to_string());
}
return final_lines.join("\n");
}
output_lines.join("\n")
}