use crate::error::Result;
use crate::msodde::field_parser::{self, DdeField};
use crate::rtfobj::parser::RtfParser;
pub fn process_rtf(data: &[u8]) -> Result<Vec<DdeField>> {
let parse_result = RtfParser::parse(data)?;
let mut fields = Vec::new();
for dest in &parse_result.destinations {
if dest.name != "fldinst" {
continue;
}
let instruction = decode_fldinst_data(&dest.hex_data);
let trimmed = instruction.trim();
if trimmed.is_empty() {
continue;
}
if field_parser::is_dde_field(trimmed)
&& let Some(dde) = field_parser::parse_dde_field(trimmed) {
fields.push(dde);
}
if let Some(decoded) = field_parser::decode_quote_field(trimmed)
&& field_parser::is_dde_field(&decoded)
&& let Some(mut dde) = field_parser::parse_dde_field(&decoded) {
dde.quote_decoded = Some(decoded);
fields.push(dde);
}
}
Ok(fields)
}
fn decode_fldinst_data(hex_data: &str) -> String {
if hex_data.len() >= 2 {
let clean: String = hex_data.chars().filter(|c| c.is_ascii_hexdigit()).collect();
if clean.len() >= 2 && clean.len().is_multiple_of(2)
&& let Ok(bytes) = hex::decode(&clean) {
if bytes.iter().all(|&b| b.is_ascii_graphic() || b.is_ascii_whitespace()) {
return String::from_utf8_lossy(&bytes).to_string();
}
}
}
hex_data.to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_process_rtf_with_fldinst_dde() {
let rtf = br"{\rtf1 {\field {\fldinst DDEAUTO cmd /c calc}}}";
let result = process_rtf(rtf);
assert!(result.is_ok());
}
#[test]
fn test_process_rtf_no_fldinst() {
let rtf = br"{\rtf1 Hello World}";
let fields = process_rtf(rtf).unwrap();
assert!(fields.is_empty());
}
#[test]
fn test_process_rtf_invalid() {
let result = process_rtf(b"Not an RTF");
assert!(result.is_err());
}
#[test]
fn test_decode_fldinst_ascii() {
let hex_str = "44444541555 4f20636d64";
let result = decode_fldinst_data(hex_str);
assert!(!result.is_empty());
}
#[test]
fn test_decode_fldinst_empty() {
let result = decode_fldinst_data("");
assert_eq!(result, "");
}
}