use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
pub struct HreflangItem {
pub lang: String,
pub href: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
pub struct OpenGraphMeta {
pub title: Option<String>,
pub description: Option<String>,
pub image: Option<String>,
pub og_type: Option<String>,
pub url: Option<String>,
pub site_name: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
pub struct TwitterCardMeta {
pub card: Option<String>,
pub title: Option<String>,
pub description: Option<String>,
pub image: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
pub struct AiBotDirectives {
pub gptbot_blocked: bool,
pub perplexity_blocked: bool,
pub claudebot_blocked: bool,
pub google_extended_blocked: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
pub struct JsonLdBlock {
pub schema_type: Option<String>,
pub raw_json: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SeoAuditResult {
pub http_status: Option<i32>,
pub response_time_ms: Option<i32>,
pub payload_bytes: Option<i32>,
pub page_title: Option<String>,
pub meta_description: Option<String>,
pub h1_content: Option<String>,
pub h1_count: usize,
pub canonical_url: Option<String>,
pub has_canonical: bool,
pub has_noindex: bool,
pub has_nofollow: bool,
pub robots_directive: Option<String>,
pub hreflang: Vec<HreflangItem>,
pub opengraph: OpenGraphMeta,
pub twitter_card: TwitterCardMeta,
pub json_ld: Vec<JsonLdBlock>,
pub ai_directives: AiBotDirectives,
pub passed: bool,
pub block_reason: Option<String>,
}
impl SeoAuditResult {
pub fn hreflang_json(&self) -> Option<String> {
if self.hreflang.is_empty() {
None
} else {
serde_json::to_string(&self.hreflang).ok()
}
}
pub fn schema_types(&self) -> Vec<String> {
self.json_ld
.iter()
.filter_map(|b| b.schema_type.clone())
.collect()
}
}