use std::collections::HashMap;
use anyhow::Context;
use reqwest::Url;
use crate::utils::{patterns, receiver::Reciever};
use super::thread::Thread;
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[allow(non_camel_case_types)]
pub enum BoardSettingType {
BBS_TITLE,
BBS_TITLE_ORIG,
BBS_NONAME_NAME,
BBS_SUBJECT_COUNT,
BBS_NAME_COUNT,
BBS_MAIL_COUNT,
BBS_MESSAGE_COUNT,
BBS_THREAD_TATESUGI,
}
fn normalize_board_setting(
html: &str,
) -> anyhow::Result<HashMap<BoardSettingType, Option<String>>> {
let row = html.lines();
let mut settings = HashMap::new();
for (i, col) in row.enumerate() {
if i == 0 {
continue;
}
let key = col.split('=').next().unwrap();
let value = col.split('=').skip(1).next().map(String::from);
match key {
"BBS_TITLE" => {
settings.insert(BoardSettingType::BBS_TITLE, value);
}
"BBS_TITLE_ORIG" => {
settings.insert(BoardSettingType::BBS_TITLE_ORIG, value);
}
"BBS_NONAME_NAME" => {
settings.insert(BoardSettingType::BBS_NONAME_NAME, value);
}
"BBS_SUBJECT_COUNT" => {
settings.insert(BoardSettingType::BBS_SUBJECT_COUNT, value);
}
"BBS_NAME_COUNT" => {
settings.insert(BoardSettingType::BBS_NAME_COUNT, value);
}
"BBS_MAIL_COUNT" => {
settings.insert(BoardSettingType::BBS_MAIL_COUNT, value);
}
"BBS_MESSAGE_COUNT" => {
settings.insert(BoardSettingType::BBS_MESSAGE_COUNT, value);
}
"BBS_THREAD_TATESUGI" => {
settings.insert(BoardSettingType::BBS_THREAD_TATESUGI, value);
}
_ => {}
}
}
Ok(settings)
}
fn normalize_board(html: &str, url: String) -> anyhow::Result<Vec<Thread>> {
let url = Url::parse(&url).context("failed to parse url")?;
let server_name = url.host_str().unwrap().to_string();
let board_key = url.path().split("/").nth(1).unwrap().to_string();
let mut threads = Vec::new();
for c in patterns::parse_thread_list(&html) {
let group = (
c.name("id").context("")?.as_str(),
c.name("title").context("")?.as_str(),
c.name("count").context("")?.as_str().parse::<usize>()?,
);
match group {
(id, title, count) => {
let thread = Thread::new(&server_name, &board_key, id, title, count);
threads.push(thread);
}
}
}
Ok(threads)
}
#[derive(Debug)]
pub struct Board {
pub url: String,
}
impl Board {
pub fn new(url: String) -> Self {
let mut url = url.clone();
if url.ends_with("subback.html") {
let pos = url.rfind("/").unwrap();
url.truncate(pos);
}
Board { url }
}
pub async fn load(&self) -> anyhow::Result<Vec<Thread>> {
let url = format!("{}{}", self.url, "subback.html");
let html = Reciever::get(&url).await?.html();
anyhow::Ok(normalize_board(html.as_str(), self.url.clone())?)
}
pub async fn load_settings(&self) -> anyhow::Result<HashMap<BoardSettingType, Option<String>>> {
let url = format!("{}{}", self.url, "/SETTING.TXT");
println!("{}", url);
let html = Reciever::get(&url).await?.html();
anyhow::Ok(normalize_board_setting(html.as_str())?)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_substr_to_subback_url() {
let url = "https://mi.termchan.net/termchan/subback.html";
let board = Board::new(url.to_string());
assert_eq!(board.url, "https://termchan.net/termchan");
}
#[tokio::test]
async fn test_board_load() {
let url = "https://mi.termchan.net/news4vip";
let board = Board::new(url.to_string());
println!("{:?}", board.load().await.unwrap());
let threads = board.load().await.unwrap();
println!("{:#?}", threads);
}
#[tokio::test]
async fn test_setting_load() {
let url = "https://mi.termchan.net/news4vip";
let board = Board::new(url.to_string());
let resp = board.load_settings().await.unwrap();
println!("{:?}", resp);
}
}