fix: fixed a bug where incorrect characters are replaced from the format string

This commit is contained in:
Djairo Hougee 2024-04-23 20:50:15 +02:00
parent d34e842f3e
commit 2ea0e57338
1 changed files with 90 additions and 69 deletions

View File

@ -1,15 +1,17 @@
//! This file deals with formatting and outputting to stdout. //! This file deals with formatting and outputting to stdout.
use log::{error, info};
use std::collections::HashMap; use std::collections::HashMap;
use log::{info, error};
use string_builder::Builder; use string_builder::Builder;
use dyn_fmt::AsStrFormatExt;
use crate::structs::{config::{Field, Config}, data::Data}; use crate::structs::{
config::{Config, Field},
data::Data,
};
/// This function finds the last whitespace in a string and returns its' index. /// This function finds the last whitespace in a string and returns its' index.
/// If there is no whitespace it returns usize::MAX instead. /// If there is no whitespace it returns usize::MAX instead.
fn fuzzy_cutoff(str: &str) -> usize { fn fuzzy_cutoff(str: &str) -> usize {
str.rfind(char::is_whitespace).unwrap_or_else( || usize::MAX) str.rfind(char::is_whitespace).unwrap_or_else(|| usize::MAX)
} }
/// This function helps deal with non-UTF8 strings. /// This function helps deal with non-UTF8 strings.
@ -23,16 +25,16 @@ fn fuzzy_cutoff(str: &str) -> usize {
/// Returns: /// Returns:
/// usize indicatheing the index of the character boundary nearest max_len. /// usize indicatheing the index of the character boundary nearest max_len.
fn get_char_boundary(str: &str, max_len: usize) -> usize { fn get_char_boundary(str: &str, max_len: usize) -> usize {
match max_len > str.len() || str.is_char_boundary(max_len) { match max_len > str.len() || str.is_char_boundary(max_len) {
true => max_len, true => max_len,
false => { false => {
let mut idx = max_len; let mut idx = max_len;
while !str.is_char_boundary(idx) { while !str.is_char_boundary(idx) {
idx -= 1; idx -= 1;
} }
idx idx
}, }
} }
} }
/// This function applies truncation to each string in the given hashmap, as dictated by the values in the given Fields. /// This function applies truncation to each string in the given hashmap, as dictated by the values in the given Fields.
@ -43,18 +45,25 @@ fn get_char_boundary(str: &str, max_len: usize) -> usize {
/// brk: Optional, character to insert when a string is truncated. /// brk: Optional, character to insert when a string is truncated.
/// fuzzy: Whether to apply the fuzzy truncation function or not. /// fuzzy: Whether to apply the fuzzy truncation function or not.
/// strings: Hashmap containing the strings to be truncated. Key values should match with the names of the Fields Vec. /// strings: Hashmap containing the strings to be truncated. Key values should match with the names of the Fields Vec.
fn cutoff(fields: &Vec<Field>, brk: Option<char>, fuzzy: bool, strings: &mut HashMap<String, String>) { fn cutoff(
for field in fields { fields: &Vec<Field>,
if let Some(str) = strings.get_mut(&field.field) { brk: Option<char>,
if str.len() >= field.num_chars as usize { fuzzy: bool,
str.truncate(get_char_boundary(str, field.num_chars as usize)); strings: &mut HashMap<String, String>,
if fuzzy {str.truncate(fuzzy_cutoff(str))} ) {
if let Some(c) = brk { for field in fields {
str.push(c); if let Some(str) = strings.get_mut(&field.field) {
if str.len() >= field.num_chars as usize {
str.truncate(get_char_boundary(str, field.num_chars as usize));
if fuzzy {
str.truncate(fuzzy_cutoff(str))
}
if let Some(c) = brk {
str.push(c);
}
}
} }
}
} }
}
} }
/// This function appends the prefix character to the given string builder. /// This function appends the prefix character to the given string builder.
@ -63,8 +72,8 @@ fn cutoff(fields: &Vec<Field>, brk: Option<char>, fuzzy: bool, strings: &mut Has
/// b: mutable String builder to append to. /// b: mutable String builder to append to.
/// data: Data struct containing the current prefix character. /// data: Data struct containing the current prefix character.
fn append_prefix(b: &mut Builder, data: &Data) { fn append_prefix(b: &mut Builder, data: &Data) {
b.append(data.prefix); b.append(data.prefix);
b.append(" "); b.append(" ");
} }
/// This function appends each field in the Data.field_text Hashmap to the given String builder. /// This function appends each field in the Data.field_text Hashmap to the given String builder.
@ -75,29 +84,33 @@ fn append_prefix(b: &mut Builder, data: &Data) {
/// cfg: Config struct for the program. /// cfg: Config struct for the program.
/// data: Data struct containing the field-text HashMap. /// data: Data struct containing the field-text HashMap.
fn append_fields(b: &mut Builder, cfg: &Config, data: &Data) { fn append_fields(b: &mut Builder, cfg: &Config, data: &Data) {
let mut idx = 0; let mut idx = 0;
let len = data.field_text.len() as i32; let len = data.field_text.len() as i32;
for field in &cfg.metadata_fields { for field in &cfg.metadata_fields {
if let Some(string) = data.field_text.get(&field.field) { if let Some(string) = data.field_text.get(&field.field) {
idx += 1; idx += 1;
if cfg.escape_chars { if cfg.escape_chars {
let s: &String = &string.chars() let s: &String = &string
.map(|x| match x { .chars()
'&' => "&amp;".to_owned(), .map(|x| match x {
_ => x.to_string(), '&' => "&amp;".to_owned(),
}).collect(); _ => x.to_string(),
})
.collect();
b.append(field.format.format(&[s.as_str()])); b.append(field.format.replace("{}", s.as_str()));
} else { } else {
b.append(field.format.format(&[string.as_str()])); b.append(field.format.replace("{}", string.as_str()));
} }
if idx < len {b.append(cfg.metadata_separator.as_str())}; if idx < len {
} else { b.append(cfg.metadata_separator.as_str())
info!("failed to get {} value!", field.field); };
} else {
info!("failed to get {} value!", field.field);
}
} }
}
} }
/// This higher level function formats and appends the entire output to a string builder. /// This higher level function formats and appends the entire output to a string builder.
@ -109,17 +122,17 @@ fn append_fields(b: &mut Builder, cfg: &Config, data: &Data) {
/// Returns: /// Returns:
/// String to be outputted. /// String to be outputted.
fn build_string(cfg: &Config, data: &Data) -> String { fn build_string(cfg: &Config, data: &Data) -> String {
let mut b = Builder::default(); let mut b = Builder::default();
if cfg.render_prefix { if cfg.render_prefix {
append_prefix(&mut b, data); append_prefix(&mut b, data);
} }
append_fields(&mut b, cfg, data); append_fields(&mut b, cfg, data);
b.string().unwrap_or_else(|e| { b.string().unwrap_or_else(|e| {
error!("{e}"); error!("{e}");
"Failed to unwrap string!".to_owned() "Failed to unwrap string!".to_owned()
}) })
} }
/// This higher level function calls the appropriate string building function depending on a few settings: /// This higher level function calls the appropriate string building function depending on a few settings:
@ -131,10 +144,18 @@ fn build_string(cfg: &Config, data: &Data) -> String {
/// cfg: Config struct for the program. /// cfg: Config struct for the program.
/// data: mutable Data struct containing the state of the program. /// data: mutable Data struct containing the state of the program.
pub fn print_text(cfg: &Config, data: &mut Data) { pub fn print_text(cfg: &Config, data: &mut Data) {
if (cfg.hide_output && data.current_player.is_none()) || data.field_text.is_empty() || cfg.metadata_fields.is_empty() { if (cfg.hide_output && data.current_player.is_none())
println!(""); || data.field_text.is_empty()
} else { || cfg.metadata_fields.is_empty()
cutoff(&cfg.metadata_fields, cfg.break_character, cfg.fuzzy, &mut data.field_text); {
println!("{}", build_string(cfg, data)); println!("");
} } else {
cutoff(
&cfg.metadata_fields,
cfg.break_character,
cfg.fuzzy,
&mut data.field_text,
);
println!("{}", build_string(cfg, data));
}
} }