use chrono::NaiveDate;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
mod dotnet_date_format {
use chrono::{DateTime, NaiveDate, TimeZone, Utc};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S>(date: &NaiveDate, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let timestamp_ms = date
.and_hms_opt(0, 0, 0)
.unwrap()
.and_utc()
.timestamp_millis();
let formatted = format!("/Date({})/", timestamp_ms);
formatted.serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<NaiveDate, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
if s.starts_with("/Date(-") {
return Err(serde::de::Error::custom("Null date value"));
}
if s.starts_with("/Date(") && s.ends_with(")/") {
let inner = &s[6..s.len() - 2];
let (hyph_pos, is_negative) = if let Some(pos) = inner.rfind('-') {
(Some(pos), true)
} else if let Some(pos) = inner.rfind('+') {
(Some(pos), false)
} else {
(None, true)
};
if let Some(hyph) = hyph_pos {
let timestamp_str = &inner[..hyph];
let mut timestamp_ms = timestamp_str
.parse::<f64>()
.map_err(|_| serde::de::Error::custom("Failed to parse timestamp"))?;
if hyph + 3 <= inner.len() {
let hours_str = &inner[hyph + 1..hyph + 3];
let hours = hours_str
.parse::<f64>()
.map_err(|_| serde::de::Error::custom("Failed to parse hours"))?;
let mins = if hyph + 5 <= inner.len() {
let mins_str = &inner[hyph + 3..hyph + 5];
mins_str.parse::<f64>().unwrap_or(0.0)
} else {
0.0
};
let offset_ms = (hours * 60.0 * 60.0 * 1000.0) + (mins * 60.0 * 1000.0);
if is_negative {
timestamp_ms -= offset_ms;
} else {
timestamp_ms += offset_ms;
}
}
match Utc.timestamp_millis_opt(timestamp_ms as i64) {
chrono::LocalResult::Single(dt) => Ok(dt.date_naive()),
_ => Err(serde::de::Error::custom("Invalid timestamp")),
}
} else {
let timestamp_ms = inner
.parse::<i64>()
.map_err(|_| serde::de::Error::custom("Failed to parse timestamp"))?;
match Utc.timestamp_millis_opt(timestamp_ms) {
chrono::LocalResult::Single(dt) => {
let date = dt.date_naive();
Ok(date)
}
_ => Err(serde::de::Error::custom("Invalid timestamp")),
}
}
} else {
s.parse::<DateTime<Utc>>()
.map(|s| s.date_naive())
.map_err(serde::de::Error::custom)
}
}
}
mod dotnet_date_format_opt {
use chrono::{DateTime, NaiveDate, TimeZone, Utc};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S>(date: &Option<NaiveDate>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match date {
None => "/Date(-0)/".serialize(serializer),
Some(date) => {
let timestamp_ms = date
.and_hms_opt(0, 0, 0)
.unwrap()
.and_utc()
.timestamp_millis();
let formatted = format!("/Date({})/", timestamp_ms);
formatted.serialize(serializer)
}
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<NaiveDate>, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
if s.starts_with("/Date(-") {
return Ok(None);
}
if s.starts_with("/Date(") && s.ends_with(")/") {
let inner = &s[6..s.len() - 2];
let (hyph_pos, is_negative) = if let Some(pos) = inner.rfind('-') {
(Some(pos), true)
} else if let Some(pos) = inner.rfind('+') {
(Some(pos), false)
} else {
(None, true)
};
if let Some(hyph) = hyph_pos {
let timestamp_str = &inner[..hyph];
let mut timestamp_ms = timestamp_str
.parse::<f64>()
.map_err(|_| serde::de::Error::custom("Failed to parse timestamp"))?;
if hyph + 3 <= inner.len() {
let hours_str = &inner[hyph + 1..hyph + 3];
let hours = hours_str
.parse::<f64>()
.map_err(|_| serde::de::Error::custom("Failed to parse hours"))?;
let mins = if hyph + 5 <= inner.len() {
let mins_str = &inner[hyph + 3..hyph + 5];
mins_str.parse::<f64>().unwrap_or(0.0)
} else {
0.0
};
let offset_ms = (hours * 60.0 * 60.0 * 1000.0) + (mins * 60.0 * 1000.0);
if is_negative {
timestamp_ms -= offset_ms;
} else {
timestamp_ms += offset_ms;
}
}
match Utc.timestamp_millis_opt(timestamp_ms as i64) {
chrono::LocalResult::Single(dt) => Ok(Some(dt.date_naive())),
_ => Err(serde::de::Error::custom("Invalid timestamp")),
}
} else {
let timestamp_ms = inner
.parse::<i64>()
.map_err(|_| serde::de::Error::custom("Failed to parse timestamp"))?;
match Utc.timestamp_millis_opt(timestamp_ms) {
chrono::LocalResult::Single(dt) => {
let date = dt.date_naive();
Ok(Some(date))
}
_ => Err(serde::de::Error::custom("Invalid timestamp")),
}
}
} else {
s.parse::<DateTime<Utc>>()
.map(|s| Some(s.date_naive()))
.map_err(serde::de::Error::custom)
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResponseWrapper<T> {
pub d: T,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlockedUrl {
#[serde(rename = "Url")]
pub url: String,
#[serde(rename = "Date", with = "dotnet_date_format")]
pub date: NaiveDate,
#[serde(rename = "DaysToExpire", skip_serializing_if = "Option::is_none")]
pub days_to_expire: Option<i32>,
#[serde(rename = "EntityType")]
pub entity_type: BlockedUrlEntityType,
#[serde(rename = "RequestType")]
pub request_type: BlockedUrlRequestType,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum BlockedUrlEntityType {
Page = 0,
Directory = 1,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum BlockedUrlRequestType {
CacheOnly = 0,
FullRemoval = 1,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum BlockReason {
NoPreview,
NoCache,
NoSnippet,
NoIndex,
NoArchive,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CountryRegionSettings {
#[serde(rename = "Date", with = "dotnet_date_format")]
pub date: NaiveDate,
#[serde(rename = "TwoLetterIsoCountryCode")]
pub two_letter_iso_country_code: String,
#[serde(rename = "Type")]
pub r#type: CountryRegionSettingsType,
#[serde(rename = "Url")]
pub url: String,
}
#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr)]
#[repr(i32)]
pub enum CountryRegionSettingsType {
Page = 0,
Directory = 1,
Domain = 2,
Subdomain = 3,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CrawlStats {
#[serde(rename = "AllOtherCodes")]
pub all_other_codes: i64,
#[serde(rename = "BlockedByRobotsTxt")]
pub blocked_by_robots_txt: i64,
#[serde(rename = "Code2xx")]
pub code_2xx: i64,
#[serde(rename = "Code301")]
pub code_301: i64,
#[serde(rename = "Code302")]
pub code_302: i64,
#[serde(rename = "Code4xx")]
pub code_4xx: i64,
#[serde(rename = "Code5xx")]
pub code_5xx: i64,
#[serde(rename = "ConnectionTimeout")]
pub connection_timeout: i64,
#[serde(rename = "CrawledPages")]
pub crawled_pages: i64,
#[serde(rename = "CrawlErrors")]
pub crawl_errors: i64,
#[serde(rename = "Date", with = "dotnet_date_format")]
pub date: NaiveDate,
#[serde(rename = "DnsFailures")]
pub dns_failures: i64,
#[serde(rename = "InIndex")]
pub in_index: i64,
#[serde(rename = "InLinks")]
pub in_links: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeepLink {
#[serde(rename = "Position")]
pub position: i32,
#[serde(rename = "Title")]
pub title: String,
#[serde(rename = "Url")]
pub url: String,
#[serde(rename = "Weight")]
pub weight: DeepLinkWeight,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum DeepLinkWeight {
Disabled = 0,
Low = 1,
Normal = 2,
High = 3,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeepLinkAlgoUrl {
#[serde(rename = "DeepLinkCount")]
pub deep_link_count: i32,
#[serde(rename = "Impressions")]
pub impressions: i32,
#[serde(rename = "Url")]
pub url: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeepLinkBlock {
pub source_url: String,
pub target_url: String,
pub block_type: String,
pub reason: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PagePreviewBlock {
pub url: String,
pub block_reason: BlockReason,
pub blocked_date: NaiveDate,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContentSubmissionQuota {
#[serde(rename = "DailyQuota")]
pub daily_quota: i64,
#[serde(rename = "MonthlyQuota")]
pub monthly_quota: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CrawlSettings {
#[serde(rename = "CrawlBoostAvailable")]
pub crawl_boost_available: bool,
#[serde(rename = "CrawlBoostEnabled")]
pub crawl_boost_enabled: bool,
#[serde(rename = "CrawlRate")]
pub crawl_rate: Vec<u8>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetailedQueryStats {
#[serde(rename = "Clicks")]
pub clicks: i64,
#[serde(rename = "Date", with = "dotnet_date_format")]
pub date: NaiveDate,
#[serde(rename = "Impressions")]
pub impressions: i64,
#[serde(rename = "Position")]
pub position: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryStats {
#[serde(rename = "AvgClickPosition")]
pub avg_click_position: f64,
#[serde(rename = "AvgImpressionPosition")]
pub avg_impression_position: f64,
#[serde(rename = "Clicks")]
pub clicks: i64,
#[serde(rename = "Date", with = "dotnet_date_format")]
pub date: NaiveDate,
#[serde(rename = "Impressions")]
pub impressions: i64,
#[serde(rename = "Query")]
pub query: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
#[repr(i32)]
pub enum CrawlIssues {
None = 0,
Code301 = 1,
Code302 = 2,
Code4xx = 4,
Code5xx = 8,
BlockedByRobotsTxt = 16,
ContainsMalware = 32,
ImportantUrlBlockedByRobotsTxt = 64,
DnsErrors = 128,
TimeOutErrors = 256,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UrlWithCrawlIssues {
#[serde(rename = "HttpCode")]
pub http_code: i32,
#[serde(rename = "Issues")]
pub issues: CrawlIssues,
#[serde(rename = "Url")]
pub url: String,
#[serde(rename = "InLinks")]
pub in_links: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Feed {
#[serde(rename = "Compressed")]
pub compressed: bool,
#[serde(rename = "FileSize")]
pub file_size: i64,
#[serde(rename = "LastCrawled", with = "dotnet_date_format_opt")]
pub last_crawled: Option<NaiveDate>,
#[serde(rename = "Status")]
pub status: String,
#[serde(rename = "Submitted", with = "dotnet_date_format_opt")]
pub submitted: Option<NaiveDate>,
#[serde(rename = "Type")]
pub r#type: String,
#[serde(rename = "Url")]
pub url: String,
#[serde(rename = "UrlCount")]
pub url_count: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Site {
#[serde(rename = "AuthenticationCode")]
pub authentication_code: String,
#[serde(rename = "DnsVerificationCode")]
pub dns_verification_code: String,
#[serde(rename = "IsVerified")]
pub is_verified: bool,
#[serde(rename = "Url")]
pub url: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SiteRoles {
#[serde(rename = "Date", with = "dotnet_date_format")]
pub date: NaiveDate,
#[serde(rename = "DelegatedCode")]
pub delegated_code: Option<String>,
#[serde(rename = "DelegatedCodeOwnerEmail")]
pub delegated_code_owner_email: Option<String>,
#[serde(rename = "DelegatorEmail")]
pub delegator_email: Option<String>,
#[serde(rename = "Email")]
pub email: String,
#[serde(rename = "Expired")]
pub expired: bool,
#[serde(rename = "Role")]
pub role: UserRole,
#[serde(rename = "Site")]
pub site: String,
#[serde(rename = "VerificationSite")]
pub verification_site: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
#[repr(i32)]
pub enum UserRole {
Administrator = 0,
ReadOnly = 1,
ReadWrite = 2,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UrlInfo {
#[serde(rename = "AnchorCount")]
pub anchor_count: i32,
#[serde(rename = "DiscoveryDate", with = "dotnet_date_format")]
pub discovery_date: NaiveDate,
#[serde(rename = "DocumentSize")]
pub document_size: i64,
#[serde(rename = "HttpStatus")]
pub http_status: i32,
#[serde(rename = "IsPage")]
pub is_page: bool,
#[serde(rename = "LastCrawledDate", with = "dotnet_date_format")]
pub last_crawled_date: NaiveDate,
#[serde(rename = "TotalChildUrlCount")]
pub total_child_url_count: i32,
#[serde(rename = "Url")]
pub url: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UrlTrafficInfo {
#[serde(rename = "Clicks")]
pub clicks: i32,
#[serde(rename = "Impressions")]
pub impressions: i32,
#[serde(rename = "IsPage")]
pub is_page: bool,
#[serde(rename = "Url")]
pub url: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UrlSubmissionQuota {
#[serde(rename = "DailyQuota")]
pub daily_quota: i32,
#[serde(rename = "MonthlyQuota")]
pub monthly_quota: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LinkCounts {
#[serde(rename = "Links")]
pub links: Vec<LinkCount>,
#[serde(rename = "TotalPages")]
pub total_pages: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LinkCount {
#[serde(rename = "Count")]
pub count: i32,
#[serde(rename = "Url")]
pub url: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LinkDetails {
#[serde(rename = "Details")]
pub details: Vec<LinkDetail>,
#[serde(rename = "TotalPages")]
pub total_pages: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LinkDetail {
#[serde(rename = "AnchorText")]
pub anchor_text: String,
#[serde(rename = "Url")]
pub url: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryParameter {
#[serde(rename = "Date", with = "dotnet_date_format")]
pub date: NaiveDate,
#[serde(rename = "IsEnabled")]
pub is_enabled: bool,
#[serde(rename = "Parameter")]
pub parameter: String,
#[serde(rename = "Source")]
pub source: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RankAndTrafficStats {
#[serde(rename = "Clicks")]
pub clicks: i64,
#[serde(rename = "Date", with = "dotnet_date_format")]
pub date: NaiveDate,
#[serde(rename = "Impressions")]
pub impressions: i64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
#[repr(i32)]
pub enum CrawlDateFilter {
Any = 0,
LastWeek = 1,
LastTwoWeeks = 2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
#[repr(i32)]
pub enum DiscoveredDateFilter {
Any = 0,
LastWeek = 1,
LastMonth = 2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
#[repr(i32)]
pub enum DocFlagsFilters {
Any = 0,
IsBlockedByRobotsTxt = 1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
#[repr(i32)]
pub enum HttpCodeFilters {
Any = 0,
Code2xx = 1,
Code3xx = 2,
Code301 = 4,
Code302 = 8,
Code4xx = 16,
Code5xx = 32,
AllOthers = 64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FilterProperties {
#[serde(rename = "CrawlDateFilter")]
pub crawl_date_filter: CrawlDateFilter,
#[serde(rename = "DiscoveredDateFilter")]
pub discovered_date_filter: DiscoveredDateFilter,
#[serde(rename = "DocFlagsFilters")]
pub doc_flags_filters: DocFlagsFilters,
#[serde(rename = "HttpCodeFilters")]
pub http_code_filters: HttpCodeFilters,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FetchedUrl {
#[serde(rename = "Url")]
pub url: String,
#[serde(rename = "Date", with = "dotnet_date_format")]
pub date: NaiveDate,
#[serde(rename = "Expired")]
pub expired: bool,
#[serde(rename = "Fetched")]
pub fetched: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FetchedUrlDetails {
#[serde(rename = "Url")]
pub url: String,
#[serde(rename = "Date", with = "dotnet_date_format")]
pub date: NaiveDate,
#[serde(rename = "Document")]
pub document: String,
#[serde(rename = "Headers")]
pub headers: String,
#[serde(rename = "Status")]
pub status: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Keyword {
#[serde(rename = "Query")]
pub query: String,
#[serde(rename = "Impressions")]
pub impressions: i64,
#[serde(rename = "BroadImpressions")]
pub broad_impressions: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KeywordStats {
#[serde(rename = "Query")]
pub query: String,
#[serde(rename = "Impressions")]
pub impressions: i64,
#[serde(rename = "BroadImpressions")]
pub broad_impressions: i64,
#[serde(rename = "Date", with = "dotnet_date_format")]
pub date: NaiveDate,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SiteMove {
#[serde(rename = "SourceSite")]
pub source_site: String,
#[serde(rename = "TargetSite")]
pub target_site: String,
#[serde(rename = "Date", with = "dotnet_date_format")]
pub date: NaiveDate,
#[serde(rename = "Status")]
pub status: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SiteMoveSettings {
#[serde(rename = "TargetSite")]
pub target_site: String,
#[serde(rename = "ValidationTag")]
pub validation_tag: String,
}