use chrono::{DateTime, NaiveDate, TimeZone, Utc};
use error::ListError;
use list::{self, Status};
use minidom::Element;
use request::ListType;
use SeriesInfo;
use super::{ChangeTracker, EntryValues, ListEntry, UserInfo};
#[derive(Debug, Clone)]
pub struct MangaInfo {
pub id: u32,
pub title: String,
pub english_title: Option<String>,
pub synonyms: Vec<String>,
pub chapters: u32,
pub volumes: u32,
pub series_type: MangaType,
pub publishing_status: PublishingStatus,
pub start_date: Option<NaiveDate>,
pub end_date: Option<NaiveDate>,
pub synopsis: Option<String>,
pub image_url: String,
}
impl SeriesInfo for MangaInfo {
#[doc(hidden)]
fn parse_search_result(xml: &Element) -> Result<MangaInfo, ListError> {
let entry = MangaInfo {
id: list::parse_xml_child(xml, "id")?,
title: list::parse_xml_child(xml, "title")?,
english_title: match list::parse_xml_child::<String>(xml, "english") {
Ok(ref title) if title.is_empty() => None,
Ok(title) => Some(title),
Err(e) => return Err(e),
},
synonyms: {
list::split_by_delim(&list::parse_xml_child::<String>(xml, "synonyms")?, "; ")
},
chapters: list::parse_xml_child(xml, "chapters")?,
volumes: list::parse_xml_child(xml, "volumes")?,
series_type: {
let s_type = list::parse_xml_child(xml, "type")?;
MangaType::from_str(&s_type).ok_or_else(|| ListError::UnknownSeriesType(s_type))?
},
publishing_status: {
let status = list::parse_xml_child(xml, "status")?;
PublishingStatus::from_str(&status).ok_or_else(|| ListError::UnknownStatus(status))?
},
start_date: list::parse_str_date(&list::parse_xml_child::<String>(xml, "start_date")?),
end_date: list::parse_str_date(&list::parse_xml_child::<String>(xml, "end_date")?),
synopsis: Some(list::parse_xml_child(xml, "synopsis")?),
image_url: list::parse_xml_child(xml, "image")?,
};
Ok(entry)
}
}
impl PartialEq for MangaInfo {
#[inline]
fn eq(&self, other: &MangaInfo) -> bool {
self.id == other.id
}
}
gen_list_field_enum!(MangaType,
["A traditional manga series."]
Manga = [1, "manga"],
["A type of manga that usually has less than 500 pages and few illustrations."]
Novel = [2, "novel"],
["A manga series with a single chapter."]
OneShot = [3, "one-shot"],
["A South Korean manga series."]
Manhwa = [4, "manhwa"],
["A Chinese / Taiwanese manga series."]
Manhua = [5, "manhua"],
);
gen_list_field_enum!(PublishingStatus,
["A manga that is currently publishing."]
Publishing = [1, "publishing"],
["A manga series that has finished publishing."]
Finished = [2, "finished"],
["A manga series that hasn't begun being published yet."]
NotYetPublished = [3, "not yet published"],
);
#[derive(Debug, Clone)]
pub struct MangaEntry {
pub series_info: MangaInfo,
pub last_updated_time: DateTime<Utc>,
pub values: MangaValues,
}
impl MangaEntry {
#[inline]
pub fn new(info: MangaInfo) -> MangaEntry {
MangaEntry {
series_info: info,
last_updated_time: Utc::now(),
values: MangaValues::new(),
}
}
}
impl ListEntry for MangaEntry {
type Info = MangaInfo;
type Values = MangaValues;
type UserInfo = MangaUserInfo;
#[doc(hidden)]
fn from_xml(xml: &Element) -> Result<MangaEntry, ListError> {
let info = MangaInfo {
id: list::parse_xml_child(xml, "series_mangadb_id")?,
title: list::parse_xml_child(xml, "series_title")?,
english_title: None,
synonyms: {
list::split_by_delim(
&list::parse_xml_child::<String>(xml, "series_synonyms")?,
"; ",
)
},
chapters: list::parse_xml_child(xml, "series_chapters")?,
volumes: list::parse_xml_child(xml, "series_volumes")?,
series_type: {
let s_type = list::parse_xml_child(xml, "series_type")?;
MangaType::from_i32(s_type)
.ok_or_else(|| ListError::UnknownSeriesType(s_type.to_string()))?
},
publishing_status: {
let status = list::parse_xml_child(xml, "series_status")?;
PublishingStatus::from_i32(status)
.ok_or_else(|| ListError::UnknownStatus(status.to_string()))?
},
start_date: {
list::parse_str_date(&list::parse_xml_child::<String>(xml, "series_start")?)
},
end_date: list::parse_str_date(&list::parse_xml_child::<String>(xml, "series_end")?),
synopsis: None,
image_url: list::parse_xml_child(xml, "series_image")?,
};
let entry = MangaEntry {
series_info: info,
last_updated_time: Utc.timestamp(list::parse_xml_child(xml, "my_last_updated")?, 0),
values: MangaValues::from_xml(xml)?,
};
Ok(entry)
}
#[doc(hidden)]
#[inline]
fn values_mut(&mut self) -> &mut MangaValues {
&mut self.values
}
#[doc(hidden)]
#[inline]
fn set_last_updated_time(&mut self) {
self.last_updated_time = Utc::now();
}
#[doc(hidden)]
#[inline]
fn id(&self) -> u32 {
self.series_info.id
}
#[doc(hidden)]
#[inline]
fn list_type() -> ListType {
ListType::Manga
}
}
#[derive(Debug, Default, Clone)]
pub struct MangaValues {
chapter: ChangeTracker<u32>,
volume: ChangeTracker<u32>,
status: ChangeTracker<Status>,
score: ChangeTracker<u8>,
start_date: ChangeTracker<Option<NaiveDate>>,
finish_date: ChangeTracker<Option<NaiveDate>>,
rereading: ChangeTracker<bool>,
tags: ChangeTracker<Vec<String>>,
}
impl MangaValues {
#[inline]
pub fn new() -> MangaValues {
MangaValues::default()
}
fn from_xml(xml: &Element) -> Result<MangaValues, ListError> {
let values = MangaValues {
chapter: list::parse_xml_child::<u32>(xml, "my_read_chapters")?.into(),
volume: list::parse_xml_child::<u32>(xml, "my_read_volumes")?.into(),
status: {
let status_num = list::parse_xml_child(xml, "my_status")?;
Status::from_i32(status_num)
.ok_or_else(|| ListError::UnknownStatus(status_num.to_string()))?
.into()
},
score: list::parse_xml_child::<u8>(xml, "my_score")?.into(),
start_date: {
list::parse_str_date(&list::parse_xml_child::<String>(xml, "my_start_date")?).into()
},
finish_date: {
list::parse_str_date(&list::parse_xml_child::<String>(xml, "my_finish_date")?)
.into()
},
rereading: {
list::parse_xml_child::<u8>(xml, "my_rereadingg")
.map(|v| v == 1)
.unwrap_or(false)
.into()
},
tags: {
list::split_by_delim(&list::parse_xml_child::<String>(xml, "my_tags")?, ",").into()
},
};
Ok(values)
}
#[inline]
pub fn tags(&self) -> &Vec<String> {
&self.tags.value
}
#[inline]
pub fn tags_mut(&mut self) -> &mut Vec<String> {
self.tags.changed = true;
&mut self.tags.value
}
}
impl_tracker_getset!(MangaValues,
[chapter, set_read_chapters, "number of read chapters"]: u32,
[volume, set_read_volumes, "number of read volumes"]: u32,
[status, set_status, "current reading status of the series"]: Status,
[score, set_score, "user's rating of the series"]: u8,
[start_date, set_start_date, "date the user started reading the series"]: Option<NaiveDate>,
[finish_date, set_finish_date, "date the user finished reading the series"]: Option<NaiveDate>,
[rereading, set_rereading, "current re-reading status of the series"]: bool,
);
impl_entryvalues!(MangaValues,
chapter(num): "chapter" => num.to_string(),
volume(vol): "volume" => vol.to_string(),
status(status): "status" => (*status as i32).to_string(),
score(score): "score" => score.to_string(),
start_date(date): "date_start" => list::date_to_str(*date),
finish_date(date): "date_finish" => list::date_to_str(*date),
rereading(v): "enable_rereading" => (*v as u8).to_string(),
tags(t): "tags" => list::concat_by_delim(t, ','),
);
#[derive(Debug, Clone)]
pub struct MangaUserInfo {
pub user_id: u32,
pub reading: u32,
pub completed: u32,
pub on_hold: u32,
pub dropped: u32,
pub plan_to_read: u32,
pub days_spent_watching: f32,
}
impl UserInfo for MangaUserInfo {
#[doc(hidden)]
fn from_xml(xml: &Element) -> Result<MangaUserInfo, ListError> {
let info = MangaUserInfo {
user_id: list::parse_xml_child(xml, "user_id")?,
reading: list::parse_xml_child(xml, "user_reading")?,
completed: list::parse_xml_child(xml, "user_completed")?,
on_hold: list::parse_xml_child(xml, "user_onhold")?,
dropped: list::parse_xml_child(xml, "user_dropped")?,
plan_to_read: list::parse_xml_child(xml, "user_plantoread")?,
days_spent_watching: list::parse_xml_child(xml, "user_days_spent_watching")?,
};
Ok(info)
}
}