use crate::error::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OutputFormat {
Doc,
Json,
}
fn escape_html(s: &str) -> String {
s.replace('&', "&")
.replace('<', "<")
.replace('>', ">")
.replace('"', """)
}
pub fn make_url(url_base: &str, title: &str) -> String {
let encoded_title = title.replace(' ', "_");
format!("{}/{}", url_base, encoded_title)
}
pub fn format_page(
id: u64,
title: &str,
url_base: &str,
text: &str,
format: OutputFormat,
) -> String {
let url = make_url(url_base, title);
match format {
OutputFormat::Doc => {
let escaped_title = escape_html(title);
let escaped_url = escape_html(&url);
format!(
"<doc id=\"{}\" url=\"{}\" title=\"{}\">\n{}\n</doc>\n\n",
id, escaped_url, escaped_title, text
)
}
OutputFormat::Json => {
let obj = serde_json::json!({
"id": id.to_string(),
"url": url,
"title": title,
"text": text,
});
let json_str = serde_json::to_string(&obj).unwrap_or_default();
format!("{}\n", json_str)
}
}
}
pub fn parse_file_size(spec: &str) -> Result<u64, Error> {
let spec = spec.trim();
if spec.is_empty() {
return Err(Error::InvalidFileSize(spec.to_string()));
}
let last_char = spec.chars().last().unwrap_or('0');
let multiplier = match last_char {
'K' => Some(1024u64),
'M' => Some(1024u64 * 1024),
'G' => Some(1024u64 * 1024 * 1024),
_ => None,
};
match multiplier {
Some(mult) => {
let num_part = &spec[..spec.len() - 1];
let num: u64 = num_part
.parse()
.map_err(|_| Error::InvalidFileSize(spec.to_string()))?;
Ok(num * mult)
}
None => {
let num: u64 = spec
.parse()
.map_err(|_| Error::InvalidFileSize(spec.to_string()))?;
Ok(num)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_page_doc() {
let result = format_page(
1,
"Test Page",
"https://en.wikipedia.org/wiki",
"Hello world.",
OutputFormat::Doc,
);
let expected = "<doc id=\"1\" url=\"https://en.wikipedia.org/wiki/Test_Page\" title=\"Test Page\">\nHello world.\n</doc>\n\n";
assert_eq!(result, expected);
}
#[test]
fn test_format_page_json() {
let result = format_page(
42,
"Test Page",
"https://en.wikipedia.org/wiki",
"Some text here.",
OutputFormat::Json,
);
let parsed: serde_json::Value = serde_json::from_str(result.trim()).unwrap();
assert_eq!(parsed["id"], "42");
assert_eq!(parsed["url"], "https://en.wikipedia.org/wiki/Test_Page");
assert_eq!(parsed["title"], "Test Page");
assert_eq!(parsed["text"], "Some text here.");
}
#[test]
fn test_make_url_with_spaces() {
let url = make_url("https://en.wikipedia.org/wiki", "New York City");
assert_eq!(url, "https://en.wikipedia.org/wiki/New_York_City");
}
#[test]
fn test_make_url_japanese_title() {
let url = make_url("https://ja.wikipedia.org/wiki", "東京都");
assert_eq!(url, "https://ja.wikipedia.org/wiki/東京都");
}
#[test]
fn test_make_url_no_spaces() {
let url = make_url("https://en.wikipedia.org/wiki", "Rust");
assert_eq!(url, "https://en.wikipedia.org/wiki/Rust");
}
#[test]
fn test_parse_file_size_kilobytes() {
assert_eq!(parse_file_size("500K").unwrap(), 512000);
assert_eq!(parse_file_size("1K").unwrap(), 1024);
}
#[test]
fn test_parse_file_size_megabytes() {
assert_eq!(parse_file_size("1M").unwrap(), 1048576);
assert_eq!(parse_file_size("10M").unwrap(), 10485760);
}
#[test]
fn test_parse_file_size_gigabytes() {
assert_eq!(parse_file_size("1G").unwrap(), 1073741824);
}
#[test]
fn test_parse_file_size_plain_number() {
assert_eq!(parse_file_size("4096").unwrap(), 4096);
assert_eq!(parse_file_size("0").unwrap(), 0);
}
#[test]
fn test_parse_file_size_invalid() {
assert!(parse_file_size("").is_err());
assert!(parse_file_size("abc").is_err());
assert!(parse_file_size("M").is_err());
assert!(parse_file_size("12X").is_err());
}
#[test]
fn test_escape_html_in_doc_format() {
let result = format_page(
1,
"A <b>bold</b> & \"quoted\" title",
"https://en.wikipedia.org/wiki",
"Some text.",
OutputFormat::Doc,
);
assert!(
result.contains("title=\"A <b>bold</b> & "quoted" title\"")
);
assert!(result.contains("url=\"https://en.wikipedia.org/wiki/A_<b>bold</b>_&_"quoted"_title\""));
}
#[test]
fn test_json_format_trailing_newline() {
let result = format_page(
1,
"Title",
"https://example.com",
"Text",
OutputFormat::Json,
);
assert!(result.ends_with('\n'));
assert!(!result.ends_with("\n\n"));
}
#[test]
fn test_doc_format_trailing_newline() {
let result = format_page(1, "Title", "https://example.com", "Text", OutputFormat::Doc);
assert!(result.ends_with("</doc>\n\n"));
}
}