use crate::Error;
use url::Url;
use url;
use std::convert::From;
use std::convert::Into;
use chrono_utils;
use chrono::DateTime;
use chrono::FixedOffset;
use chrono_utils::parser::parse_w3c_datetime;
use std::fmt;
use std::num;
#[derive(Clone,Debug)]
pub struct UrlEntry {
pub loc: Location,
pub lastmod: LastMod,
pub changefreq: ChangeFreq,
pub priority: Priority,
}
impl UrlEntry {
pub fn new() -> UrlEntry {
UrlEntry {
loc: Location::None,
lastmod: LastMod::None,
changefreq: ChangeFreq::None,
priority: Priority::None,
}
}
pub fn builder() -> UrlEntryBuilder {
UrlEntryBuilder { url_entry: UrlEntry::new() }
}
}
#[derive(Clone,Debug)]
pub struct UrlEntryBuilder {
url_entry: UrlEntry,
}
impl UrlEntryBuilder {
pub fn loc<S: Into<String>>(mut self, url: S) -> UrlEntryBuilder {
let url = url.into();
self.url_entry.loc = Location::from(url);
return self;
}
pub fn lastmod(mut self, date: DateTime<FixedOffset>) -> UrlEntryBuilder {
self.url_entry.lastmod = LastMod::DateTime(date);
return self;
}
pub fn changefreq(mut self, changefreq: ChangeFreq) -> UrlEntryBuilder {
self.url_entry.changefreq = changefreq;
return self;
}
pub fn priority(mut self, val: f32) -> UrlEntryBuilder {
self.url_entry.priority = Priority::Value(val);
return self;
}
pub fn build(self) -> Result<UrlEntry, Error> {
if !self.url_entry.loc.is_url() {
return Err(Error::Invalid("Required a location in the Url".to_string()));
}
if let Priority::Value(val) = self.url_entry.priority {
if val > 1.0 || val < 0.0 {
return Err(Error::Invalid("priority should be betwheen 0 and 1".to_string()))
}
}
return Ok(self.url_entry);
}
}
impl Into<UrlEntry> for UrlEntryBuilder {
fn into(self) -> UrlEntry {
return self.build().unwrap();
}
}
impl Into<UrlEntry> for Url {
fn into( self ) -> UrlEntry {
UrlEntry {
loc: Location::from(self),
lastmod: LastMod::None,
changefreq: ChangeFreq::None,
priority: Priority::None,
}
}
}
impl Into<UrlEntry> for String {
fn into(self) -> UrlEntry {
let location = Location::from(self);
if let Location::ParseErr(error) = location {
panic!("Unable to parse location: {}", error);
}
UrlEntry {
loc: location,
lastmod: LastMod::None,
changefreq: ChangeFreq::None,
priority: Priority::None,
}
}
}
impl Into<UrlEntry> for &'static str {
fn into(self) -> UrlEntry {
let location: String = self.into();
return location.into();
}
}
#[derive(Clone,Debug)]
pub struct SiteMapEntry {
pub loc: Location,
pub lastmod: LastMod,
}
impl SiteMapEntry {
pub fn new() -> SiteMapEntry {
SiteMapEntry {
loc: Location::None,
lastmod: LastMod::None,
}
}
pub fn builder() -> SiteMapEntryBuilder {
SiteMapEntryBuilder { sitemap_entry: SiteMapEntry::new() }
}
}
#[derive(Debug,Clone)]
pub struct SiteMapEntryBuilder {
sitemap_entry: SiteMapEntry,
}
impl SiteMapEntryBuilder {
pub fn loc<S: Into<String>>(mut self, url: S) -> SiteMapEntryBuilder {
let url = url.into();
self.sitemap_entry.loc = Location::from(url);
return self;
}
pub fn lastmod(mut self, date: DateTime<FixedOffset>) -> SiteMapEntryBuilder {
self.sitemap_entry.lastmod = LastMod::DateTime(date);
return self;
}
pub fn build(self) -> Result<SiteMapEntry, Error> {
if let Location::Url(_) = self.sitemap_entry.loc {
Ok(self.sitemap_entry)
} else {
Err(Error::Invalid("Required a location in the sitemap".to_string()))
}
}
}
impl Into<SiteMapEntry> for SiteMapEntryBuilder {
fn into(self) -> SiteMapEntry {
return self.build().unwrap();
}
}
impl Into<SiteMapEntry> for Url {
fn into( self ) -> SiteMapEntry {
SiteMapEntry {
loc: Location::from( self ),
lastmod: LastMod::None,
}
}
}
impl Into<SiteMapEntry> for String {
fn into(self) -> SiteMapEntry {
let location = Location::from(self);
if let Location::ParseErr(error) = location {
panic!("Unable to parse location: {}", error);
}
SiteMapEntry {
loc: location,
lastmod: LastMod::None,
}
}
}
impl Into<SiteMapEntry> for &'static str {
fn into(self) -> SiteMapEntry {
let location: String = self.into();
return location.into();
}
}
#[derive(Debug,Clone)]
pub enum Location {
None,
Url(Url),
ParseErr(url::ParseError),
}
impl Location {
pub fn get_url(&self) -> Option<Url> {
match *self {
Location::Url(ref url) => {
return Some(url.clone());
}
_ => {
return None;
}
}
}
pub fn is_url(&self) -> bool {
return match *self {
Location::Url(_) => true,
_ => false,
}
}
pub fn is_none(&self) -> bool {
return match *self {
Location::None => true,
_ => false,
}
}
pub fn is_parse_error(&self) -> bool {
return match *self {
Location::ParseErr(_) => true,
_ => false,
}
}
}
impl From<Url> for Location {
fn from( url: Url ) -> Self {
Location::Url( url )
}
}
impl From<String> for Location {
fn from(url: String) -> Self {
match Url::parse(&url) {
Ok(url) => {
return Location::Url(url);
}
Err(error) => {
return Location::ParseErr(error);
}
}
}
}
#[derive(Debug,Clone)]
pub enum LastMod {
None,
DateTime(DateTime<FixedOffset>),
ParseErr(chrono_utils::parser::error::ParseError),
}
impl LastMod {
pub fn get_time(&self) -> Option<DateTime<FixedOffset>> {
match *self {
LastMod::DateTime(ref time) => {
return Some(time.clone());
}
_ => {
return None;
}
}
}
}
impl From<String> for LastMod {
fn from(time: String) -> Self {
match parse_w3c_datetime(&time) {
Ok(time) => {
return LastMod::DateTime(time);
}
Err(error) => {
return LastMod::ParseErr(error);
}
}
}
}
#[derive(PartialEq,Debug,Clone)]
pub struct ChangeFreqParseError {
pub description: String,
}
impl ChangeFreqParseError {
pub fn new(description: String) -> ChangeFreqParseError {
ChangeFreqParseError { description: description }
}
}
impl fmt::Display for ChangeFreqParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Not recognized string '{}'", self.description)
}
}
#[derive(PartialEq,Debug,Clone)]
pub enum ChangeFreq {
None,
Always,
Hourly,
Daily,
Weekly,
Monthly,
Yearly,
Never,
ParseErr(ChangeFreqParseError),
}
impl ChangeFreq {
pub fn as_str(&self) -> &str {
match *self {
ChangeFreq::None => "",
ChangeFreq::Always => "always",
ChangeFreq::Hourly => "hourly",
ChangeFreq::Daily => "daily",
ChangeFreq::Weekly => "weekly",
ChangeFreq::Monthly => "monthly",
ChangeFreq::Yearly => "yearly",
ChangeFreq::Never => "never",
ChangeFreq::ParseErr(_) => "",
}
}
}
impl From<String> for ChangeFreq {
fn from(time: String) -> Self {
let lowercase_time = time.to_lowercase();
match lowercase_time.as_ref() {
"always" => {
return ChangeFreq::Always;
}
"hourly" => {
return ChangeFreq::Hourly;
}
"daily" => {
return ChangeFreq::Daily;
}
"weekly" => {
return ChangeFreq::Weekly;
}
"monthly" => {
return ChangeFreq::Monthly;
}
"yearly" => {
return ChangeFreq::Yearly;
}
"never" => {
return ChangeFreq::Never;
}
_ => {
return ChangeFreq::ParseErr(ChangeFreqParseError::new(time));
}
}
}
}
#[derive(Debug,Clone)]
pub enum Priority {
None,
Value(f32),
ParseErr(num::ParseFloatError),
ErrValueLesserZero(f32),
ErrValueGreaterOne(f32),
}
impl Priority {
pub fn get_priority(&self) -> Option<f32> {
match *self {
Priority::Value(value) => {
return Some(value);
}
_ => {
return None;
}
}
}
}
impl From<String> for Priority {
fn from(priority: String) -> Self {
let value = priority.parse::<f32>();
match value {
Ok(value) => {
if value > 1.0 {
return Priority::ErrValueGreaterOne(value);
} else if value < 0.0 {
return Priority::ErrValueLesserZero(value);
} else {
return Priority::Value(value);
}
}
Err(error) => {
return Priority::ParseErr(error);
}
}
}
}