use encoding_rs::Encoding;
use super::encoding::find;
pub fn extract_title(bytes: &[u8], enc: &'static Encoding) -> Option<String> {
extract_field(bytes, b"{\\title", enc)
}
pub fn extract_author(bytes: &[u8], enc: &'static Encoding) -> Option<String> {
extract_field(bytes, b"{\\author", enc)
}
fn extract_field(bytes: &[u8], opener: &[u8], enc: &'static Encoding) -> Option<String> {
let mut from = 0usize;
loop {
let pos = from + find(&bytes[from..], opener)?;
from = pos + opener.len();
if bytes.get(from).is_some_and(|c| c.is_ascii_alphanumeric()) {
continue;
}
return read_field_value(bytes, from, enc);
}
}
fn read_field_value(bytes: &[u8], start: usize, enc: &'static Encoding) -> Option<String> {
let mut i = start;
if bytes.get(i) == Some(&b' ') {
i += 1;
}
let mut raw: Vec<u8> = Vec::new();
let mut out = String::new();
let mut uc_skip = 1i32;
let mut uc_pending = 0i32;
let flush = |raw: &mut Vec<u8>, out: &mut String| {
if !raw.is_empty() {
let (d, _, _) = enc.decode(raw);
out.push_str(&d);
raw.clear();
}
};
while i < bytes.len() {
match bytes[i] {
b'{' => return None, b'}' => break,
b'\\' if i + 1 < bytes.len() => {
let nx = bytes[i + 1];
if nx == b'\'' && i + 4 <= bytes.len() {
if uc_pending > 0 {
uc_pending -= 1;
} else if let Ok(h) = std::str::from_utf8(&bytes[i + 2..i + 4]) {
if let Ok(b) = u8::from_str_radix(h, 16) {
raw.push(b);
}
}
i += 4;
continue;
} else if nx == b'u'
&& bytes.get(i + 2).is_some_and(|c| c.is_ascii_digit() || *c == b'-')
{
flush(&mut raw, &mut out);
let mut k = i + 2;
let neg = bytes[k] == b'-';
if neg {
k += 1;
}
let st = k;
while k < bytes.len() && bytes[k].is_ascii_digit() {
k += 1;
}
let v: i32 = std::str::from_utf8(&bytes[st..k])
.unwrap_or("0")
.parse()
.unwrap_or(0);
let code = if neg { (-v + 65536) as u32 } else { v as u32 };
if let Some(ch) = char::from_u32(code) {
out.push(ch);
}
uc_pending = uc_skip;
i = k;
continue;
} else if matches!(nx, b'\\' | b'{' | b'}') {
flush(&mut raw, &mut out);
out.push(nx as char);
i += 2;
continue;
}
let mut k = i + 1;
let wstart = k;
while k < bytes.len() && bytes[k].is_ascii_alphabetic() {
k += 1;
}
let wend = k;
while k < bytes.len() && (bytes[k].is_ascii_digit() || bytes[k] == b'-') {
k += 1;
}
if &bytes[wstart..wend] == b"uc" {
uc_skip = std::str::from_utf8(&bytes[wend..k])
.ok()
.and_then(|s| s.parse::<i32>().ok())
.unwrap_or(uc_skip)
.max(0);
}
if k < bytes.len() && bytes[k] == b' ' {
k += 1;
}
flush(&mut raw, &mut out);
i = k;
}
c => {
if uc_pending > 0 {
uc_pending -= 1;
} else {
raw.push(c);
}
i += 1;
}
}
}
flush(&mut raw, &mut out);
let t = out.trim().to_string();
if t.is_empty() || t.chars().all(|c| c == '?' || c.is_whitespace()) {
None
} else {
Some(t)
}
}