use crate::clean;
use crate::config::{ExtractorConfig, OutputFormat};
use crate::dump::{Page, SiteInfo};
use crate::expand::TemplateSource;
pub fn render_page(
page: &Page,
site: &SiteInfo,
config: &ExtractorConfig,
templates: &dyn TemplateSource,
) -> String {
let paragraphs = clean::extract_paragraphs(page, config, templates);
let url = site.page_url(page.id);
let text = paragraphs.join("\n");
match config.format {
OutputFormat::Doc => format!(
"<doc id=\"{}\" url=\"{}\" title=\"{}\">\n{}\n\n{}\n\n</doc>\n",
page.id, url, page.title, page.title, text
),
OutputFormat::Json => format!(
"{{\"id\": {}, \"revid\": {}, \"url\": {}, \"title\": {}, \"text\": {}}}\n",
json_string(&page.id.to_string()),
json_string(&page.revid.to_string()),
json_string(&url),
json_string(&page.title),
json_string(&text),
),
}
}
fn json_string(s: &str) -> String {
let mut out = String::with_capacity(s.len() + 2);
out.push('"');
for c in s.chars() {
match c {
'"' => out.push_str("\\\""),
'\\' => out.push_str("\\\\"),
'\n' => out.push_str("\\n"),
'\r' => out.push_str("\\r"),
'\t' => out.push_str("\\t"),
'\u{8}' => out.push_str("\\b"),
'\u{c}' => out.push_str("\\f"),
c if (c as u32) < 0x20 => {
out.push_str(&format!("\\u{:04x}", c as u32));
}
c if (c as u32) < 0x7f => out.push(c),
c => {
let cp = c as u32;
if cp > 0xFFFF {
let v = cp - 0x10000;
out.push_str(&format!(
"\\u{:04x}\\u{:04x}",
0xD800 + (v >> 10),
0xDC00 + (v & 0x3FF)
));
} else {
out.push_str(&format!("\\u{cp:04x}"));
}
}
}
}
out.push('"');
out
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use crate::expand::TemplateDb;
use super::*;
fn fixtures() -> (Page, SiteInfo) {
let page = Page {
id: 12,
revid: 34,
ns: 0,
title: "Anarchism".to_string(),
redirect: None,
text: "'''Anarchism''' is a [[political philosophy]].".to_string(),
};
let site = SiteInfo {
site_name: "Wikipedia".to_string(),
base: "https://en.wikipedia.org/wiki/Main_Page".to_string(),
namespaces: HashMap::new(),
};
(page, site)
}
#[test]
fn doc_format_matches_wikiextractor() {
let (page, site) = fixtures();
let doc = render_page(
&page,
&site,
&ExtractorConfig::default(),
&TemplateDb::default(),
);
assert_eq!(
doc,
"<doc id=\"12\" url=\"https://en.wikipedia.org/wiki?curid=12\" title=\"Anarchism\">\n\
Anarchism\n\n\
Anarchism is a political philosophy.\n\n\
</doc>\n"
);
}
#[test]
fn json_format_matches_python_dumps() {
let (mut page, site) = fixtures();
page.text = "Beta follows Alpha \u{2014} naturally.".to_string();
let config = ExtractorConfig {
format: OutputFormat::Json,
..Default::default()
};
let line = render_page(&page, &site, &config, &TemplateDb::default());
assert_eq!(
line,
"{\"id\": \"12\", \"revid\": \"34\", \"url\": \"https://en.wikipedia.org/wiki?curid=12\", \
\"title\": \"Anarchism\", \"text\": \"Beta follows Alpha \\u2014 naturally.\"}\n"
);
}
#[test]
fn json_string_escapes_like_python() {
assert_eq!(json_string("a\"b\\c\nd"), "\"a\\\"b\\\\c\\nd\"");
assert_eq!(json_string("caf\u{e9}"), "\"caf\\u00e9\"");
assert_eq!(json_string("\u{1F600}"), "\"\\ud83d\\ude00\""); assert_eq!(json_string("\u{7f}"), "\"\\u007f\"");
}
}