Skip to main content

google_search_console_api/
types.rs

1//! Common types used across the Search Console API.
2
3use serde::{Deserialize, Serialize};
4
5/// Filter group for dimension filters.
6///
7/// <https://developers.google.com/webmaster-tools/v1/searchanalytics/query#response>
8#[derive(Default, Debug, Serialize, Deserialize, Clone)]
9pub struct DimensionFilterGroup {
10    /// How the filters are combined. Default is "and".
11    #[serde(rename = "groupType")]
12    pub group_type: Option<String>,
13    /// List of filters in this group.
14    pub filters: Vec<DimensionFilterGroupFilter>,
15}
16
17/// Individual filter within a dimension filter group.
18#[derive(Default, Debug, Serialize, Deserialize, Clone)]
19pub struct DimensionFilterGroupFilter {
20    /// The dimension to filter on.
21    pub dimension: Option<String>,
22    /// The operator for the filter (e.g., "equals", "contains", "notContains").
23    pub operator: Option<String>,
24    /// The value to filter by.
25    pub expression: Option<String>,
26}
27
28/// Dimension for grouping search analytics data.
29///
30/// Use dimensions to group your search analytics data by specific criteria.
31///
32/// # Example
33///
34/// ```rust
35/// use google_search_console_api::types::Dimension;
36///
37/// let dimensions = vec![Dimension::Query, Dimension::Page, Dimension::Date];
38/// ```
39#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
40#[serde(rename_all = "camelCase")]
41pub enum Dimension {
42    /// Group by country (ISO 3166-1 alpha-3 country code).
43    Country,
44    /// Group by device type (desktop, mobile, tablet).
45    Device,
46    /// Group by page URL.
47    Page,
48    /// Group by search query.
49    Query,
50    /// Group by search appearance (e.g., rich results, AMP).
51    SearchAppearance,
52    /// Group by date.
53    Date,
54}
55
56#[doc(hidden)]
57#[deprecated(since = "0.2.0", note = "Use `Dimension` instead")]
58pub type DIMENSION = Dimension;
59
60/// Sitemap information.
61///
62/// <https://developers.google.com/webmaster-tools/v1/sitemaps#resource>
63#[derive(Default, Debug, Serialize, Deserialize)]
64pub struct Sitemap {
65    /// The URL of the sitemap.
66    pub path: Option<String>,
67    /// Date & time when this sitemap was last submitted.
68    #[serde(rename = "lastSubmitted")]
69    pub last_submitted: Option<String>,
70    /// Whether the sitemap is pending processing.
71    #[serde(rename = "isPending")]
72    pub is_pending: Option<bool>,
73    /// Whether this is a sitemap index file.
74    #[serde(rename = "isSitemapsIndex")]
75    pub is_sitemaps_index: Option<bool>,
76    /// The type of sitemap (e.g., "sitemap", "rss", "atom", "urlList").
77    #[serde(rename = "type")]
78    pub sitemap_type: Option<String>,
79    /// Date & time when this sitemap was last downloaded.
80    #[serde(rename = "lastDownloaded")]
81    pub last_downloaded: Option<String>,
82    /// Number of warnings for this sitemap.
83    pub warnings: Option<String>,
84    /// Number of errors for this sitemap.
85    pub errors: Option<String>,
86    /// Contents of the sitemap.
87    pub contents: Option<Vec<SitemapContent>>,
88}
89
90/// Content information within a sitemap.
91#[derive(Default, Debug, Serialize, Deserialize)]
92pub struct SitemapContent {
93    /// The type of content (e.g., "web", "image", "video", "news").
94    #[serde(rename = "type")]
95    pub content_type: Option<String>,
96    /// Number of URLs submitted.
97    pub submitted: Option<String>,
98    /// Number of URLs indexed.
99    pub indexed: Option<String>,
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn test_dimension_serialize() {
108        assert_eq!(
109            serde_json::to_string(&Dimension::Country).unwrap(),
110            "\"country\""
111        );
112        assert_eq!(
113            serde_json::to_string(&Dimension::Device).unwrap(),
114            "\"device\""
115        );
116        assert_eq!(
117            serde_json::to_string(&Dimension::Page).unwrap(),
118            "\"page\""
119        );
120        assert_eq!(
121            serde_json::to_string(&Dimension::Query).unwrap(),
122            "\"query\""
123        );
124        assert_eq!(
125            serde_json::to_string(&Dimension::SearchAppearance).unwrap(),
126            "\"searchAppearance\""
127        );
128        assert_eq!(
129            serde_json::to_string(&Dimension::Date).unwrap(),
130            "\"date\""
131        );
132    }
133
134    #[test]
135    fn test_dimension_deserialize() {
136        assert_eq!(
137            serde_json::from_str::<Dimension>("\"country\"").unwrap(),
138            Dimension::Country
139        );
140        assert_eq!(
141            serde_json::from_str::<Dimension>("\"query\"").unwrap(),
142            Dimension::Query
143        );
144        assert_eq!(
145            serde_json::from_str::<Dimension>("\"date\"").unwrap(),
146            Dimension::Date
147        );
148    }
149
150    #[test]
151    fn test_dimension_equality() {
152        assert_eq!(Dimension::Query, Dimension::Query);
153        assert_ne!(Dimension::Query, Dimension::Page);
154    }
155
156    #[test]
157    fn test_sitemap_deserialize() {
158        let json = r#"{
159            "path": "https://example.com/sitemap.xml",
160            "lastSubmitted": "2024-01-01T00:00:00Z",
161            "isPending": false,
162            "isSitemapsIndex": false,
163            "type": "sitemap"
164        }"#;
165
166        let sitemap: Sitemap = serde_json::from_str(json).unwrap();
167        assert_eq!(
168            sitemap.path,
169            Some("https://example.com/sitemap.xml".to_string())
170        );
171        assert_eq!(sitemap.is_pending, Some(false));
172        assert_eq!(sitemap.sitemap_type, Some("sitemap".to_string()));
173    }
174
175    #[test]
176    fn test_sitemap_default() {
177        let sitemap = Sitemap::default();
178        assert!(sitemap.path.is_none());
179        assert!(sitemap.is_pending.is_none());
180    }
181
182    #[test]
183    fn test_dimension_filter_group_serialize() {
184        let filter = DimensionFilterGroupFilter {
185            dimension: Some("query".to_string()),
186            operator: Some("contains".to_string()),
187            expression: Some("test".to_string()),
188        };
189
190        let group = DimensionFilterGroup {
191            group_type: Some("and".to_string()),
192            filters: vec![filter],
193        };
194
195        let json = serde_json::to_string(&group).unwrap();
196        assert!(json.contains("\"groupType\":\"and\""));
197        assert!(json.contains("\"dimension\":\"query\""));
198        assert!(json.contains("\"operator\":\"contains\""));
199        assert!(json.contains("\"expression\":\"test\""));
200    }
201}