bing_webmaster_api/dto.rs
1//! Data Transfer Objects (DTOs) for Bing Webmaster API
2//!
3//! This module contains all data structures used for communication with the Bing Webmaster API.
4//! All structures mirror the .NET API definitions from `Microsoft.Bing.Webmaster.Api.Interfaces`.
5//!
6//! # Field Naming
7//!
8//! All fields use `#[serde(rename = "...")]` to match the PascalCase naming convention
9//! used by the .NET API, while providing idiomatic snake_case Rust field names.
10
11use chrono::NaiveDate;
12use serde::{Deserialize, Serialize};
13use serde_repr::{Deserialize_repr, Serialize_repr};
14
15/// .NET DateTime serialization format used by Bing API
16///
17/// The Bing API uses .NET's JSON date format: `/Date(timestamp-offset)/`
18/// where timestamp is milliseconds since Unix epoch.
19///
20/// # Format
21/// `/Date(1316156400000-0700)/`
22/// - `1316156400000` - milliseconds since Unix epoch
23/// - `-0700` - timezone offset (optional)
24mod dotnet_date_format {
25 use chrono::{DateTime, NaiveDate, TimeZone, Utc};
26 use serde::{Deserialize, Deserializer, Serialize, Serializer};
27
28 pub fn serialize<S>(date: &NaiveDate, serializer: S) -> Result<S::Ok, S::Error>
29 where
30 S: Serializer,
31 {
32 let timestamp_ms = date
33 .and_hms_opt(0, 0, 0)
34 .unwrap()
35 .and_utc()
36 .timestamp_millis();
37 let formatted = format!("/Date({})/", timestamp_ms);
38 formatted.serialize(serializer)
39 }
40
41 pub fn deserialize<'de, D>(deserializer: D) -> Result<NaiveDate, D::Error>
42 where
43 D: Deserializer<'de>,
44 {
45 let s = String::deserialize(deserializer)?;
46
47 // Handle special case: dates starting with /Date(- are considered null
48 if s.starts_with("/Date(-") {
49 return Err(serde::de::Error::custom("Null date value"));
50 }
51
52 // Handle .NET date format: "/Date(1316156400000-0700)/"
53 if s.starts_with("/Date(") && s.ends_with(")/") {
54 let inner = &s[6..s.len() - 2]; // Remove "/Date(" and ")/"
55
56 // Find last - or + for timezone offset
57 let (hyph_pos, is_negative) = if let Some(pos) = inner.rfind('-') {
58 (Some(pos), true)
59 } else if let Some(pos) = inner.rfind('+') {
60 (Some(pos), false)
61 } else {
62 (None, true)
63 };
64
65 if let Some(hyph) = hyph_pos {
66 // Parse timestamp before timezone offset
67 let timestamp_str = &inner[..hyph];
68 let mut timestamp_ms = timestamp_str
69 .parse::<f64>()
70 .map_err(|_| serde::de::Error::custom("Failed to parse timestamp"))?;
71
72 // Parse timezone offset hours (2 digits)
73 if hyph + 3 <= inner.len() {
74 let hours_str = &inner[hyph + 1..hyph + 3];
75 let hours = hours_str
76 .parse::<f64>()
77 .map_err(|_| serde::de::Error::custom("Failed to parse hours"))?;
78
79 // Parse timezone offset minutes (2 digits)
80 let mins = if hyph + 5 <= inner.len() {
81 let mins_str = &inner[hyph + 3..hyph + 5];
82 mins_str.parse::<f64>().unwrap_or(0.0)
83 } else {
84 0.0
85 };
86
87 // Apply timezone offset to convert to UTC
88 let offset_ms = (hours * 60.0 * 60.0 * 1000.0) + (mins * 60.0 * 1000.0);
89 if is_negative {
90 timestamp_ms -= offset_ms;
91 } else {
92 timestamp_ms += offset_ms;
93 }
94 }
95
96 // Create DateTime from adjusted timestamp
97 match Utc.timestamp_millis_opt(timestamp_ms as i64) {
98 chrono::LocalResult::Single(dt) => Ok(dt.date_naive()),
99 _ => Err(serde::de::Error::custom("Invalid timestamp")),
100 }
101 } else {
102 // No timezone offset - parse as timestamp and use date only (no time component)
103 let timestamp_ms = inner
104 .parse::<i64>()
105 .map_err(|_| serde::de::Error::custom("Failed to parse timestamp"))?;
106
107 match Utc.timestamp_millis_opt(timestamp_ms) {
108 chrono::LocalResult::Single(dt) => {
109 // Return date only (time set to 00:00:00)
110 let date = dt.date_naive();
111 Ok(date)
112 }
113 _ => Err(serde::de::Error::custom("Invalid timestamp")),
114 }
115 }
116 } else {
117 // Fallback to standard ISO format
118 s.parse::<DateTime<Utc>>()
119 .map(|s| s.date_naive())
120 .map_err(serde::de::Error::custom)
121 }
122 }
123}
124
125mod dotnet_date_format_opt {
126 use chrono::{DateTime, NaiveDate, TimeZone, Utc};
127 use serde::{Deserialize, Deserializer, Serialize, Serializer};
128
129 pub fn serialize<S>(date: &Option<NaiveDate>, serializer: S) -> Result<S::Ok, S::Error>
130 where
131 S: Serializer,
132 {
133 match date {
134 None => "/Date(-0)/".serialize(serializer),
135 Some(date) => {
136 let timestamp_ms = date
137 .and_hms_opt(0, 0, 0)
138 .unwrap()
139 .and_utc()
140 .timestamp_millis();
141 let formatted = format!("/Date({})/", timestamp_ms);
142 formatted.serialize(serializer)
143 }
144 }
145 }
146
147 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<NaiveDate>, D::Error>
148 where
149 D: Deserializer<'de>,
150 {
151 let s = String::deserialize(deserializer)?;
152
153 // Handle special case: dates starting with /Date(- are considered null
154 if s.starts_with("/Date(-") {
155 return Ok(None);
156 }
157
158 // Handle .NET date format: "/Date(1316156400000-0700)/"
159 if s.starts_with("/Date(") && s.ends_with(")/") {
160 let inner = &s[6..s.len() - 2]; // Remove "/Date(" and ")/"
161
162 // Find last - or + for timezone offset
163 let (hyph_pos, is_negative) = if let Some(pos) = inner.rfind('-') {
164 (Some(pos), true)
165 } else if let Some(pos) = inner.rfind('+') {
166 (Some(pos), false)
167 } else {
168 (None, true)
169 };
170
171 if let Some(hyph) = hyph_pos {
172 // Parse timestamp before timezone offset
173 let timestamp_str = &inner[..hyph];
174 let mut timestamp_ms = timestamp_str
175 .parse::<f64>()
176 .map_err(|_| serde::de::Error::custom("Failed to parse timestamp"))?;
177
178 // Parse timezone offset hours (2 digits)
179 if hyph + 3 <= inner.len() {
180 let hours_str = &inner[hyph + 1..hyph + 3];
181 let hours = hours_str
182 .parse::<f64>()
183 .map_err(|_| serde::de::Error::custom("Failed to parse hours"))?;
184
185 // Parse timezone offset minutes (2 digits)
186 let mins = if hyph + 5 <= inner.len() {
187 let mins_str = &inner[hyph + 3..hyph + 5];
188 mins_str.parse::<f64>().unwrap_or(0.0)
189 } else {
190 0.0
191 };
192
193 // Apply timezone offset to convert to UTC
194 let offset_ms = (hours * 60.0 * 60.0 * 1000.0) + (mins * 60.0 * 1000.0);
195 if is_negative {
196 timestamp_ms -= offset_ms;
197 } else {
198 timestamp_ms += offset_ms;
199 }
200 }
201
202 // Create DateTime from adjusted timestamp
203 match Utc.timestamp_millis_opt(timestamp_ms as i64) {
204 chrono::LocalResult::Single(dt) => Ok(Some(dt.date_naive())),
205 _ => Err(serde::de::Error::custom("Invalid timestamp")),
206 }
207 } else {
208 // No timezone offset - parse as timestamp and use date only (no time component)
209 let timestamp_ms = inner
210 .parse::<i64>()
211 .map_err(|_| serde::de::Error::custom("Failed to parse timestamp"))?;
212
213 match Utc.timestamp_millis_opt(timestamp_ms) {
214 chrono::LocalResult::Single(dt) => {
215 // Return date only (time set to 00:00:00)
216 let date = dt.date_naive();
217 Ok(Some(date))
218 }
219 _ => Err(serde::de::Error::custom("Invalid timestamp")),
220 }
221 }
222 } else {
223 // Fallback to standard ISO format
224 s.parse::<DateTime<Utc>>()
225 .map(|s| Some(s.date_naive()))
226 .map_err(serde::de::Error::custom)
227 }
228 }
229}
230
231/// Response wrapper for Bing Webmaster API JSON responses
232///
233/// All JSON responses from the Bing API are wrapped in a `{"d": data}` structure,
234/// following the .NET WCF JSON serialization format.
235#[derive(Debug, Clone, Serialize, Deserialize)]
236pub struct ResponseWrapper<T> {
237 /// The wrapped response data
238 pub d: T,
239}
240
241/// Represents a URL that has been blocked from Bing's search index
242///
243/// Used to request temporary removal of content from Bing search results.
244/// This can be for a single page or an entire directory.
245#[derive(Debug, Clone, Serialize, Deserialize)]
246pub struct BlockedUrl {
247 /// The URL to be blocked (e.g., `https://example.com/page`)
248 #[serde(rename = "Url")]
249 pub url: String,
250
251 /// The date when the block was requested
252 #[serde(rename = "Date", with = "dotnet_date_format")]
253 pub date: NaiveDate,
254
255 /// Number of days until the block expires (if applicable)
256 #[serde(rename = "DaysToExpire", skip_serializing_if = "Option::is_none")]
257 pub days_to_expire: Option<i32>,
258
259 /// Whether this blocks a single page or entire directory
260 #[serde(rename = "EntityType")]
261 pub entity_type: BlockedUrlEntityType,
262
263 /// Type of removal requested (cache only or full removal)
264 #[serde(rename = "RequestType")]
265 pub request_type: BlockedUrlRequestType,
266}
267
268/// Specifies whether a block applies to a single page or directory
269#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
270pub enum BlockedUrlEntityType {
271 /// Block a single page
272 Page = 0,
273 /// Block an entire directory and all its contents
274 Directory = 1,
275}
276
277/// Type of content removal requested
278#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
279pub enum BlockedUrlRequestType {
280 /// Remove from cache only, keep in search results
281 CacheOnly = 0,
282 /// Remove completely from search results and cache
283 FullRemoval = 1,
284}
285
286/// Reasons for blocking page previews
287#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
288pub enum BlockReason {
289 /// Don't show preview
290 NoPreview,
291 /// Don't cache the page
292 NoCache,
293 /// Don't show snippet in search results
294 NoSnippet,
295 /// Don't index the page
296 NoIndex,
297 /// Don't show archived version
298 NoArchive,
299}
300
301/// Geographic targeting settings for content
302///
303/// Allows you to specify which country or region specific content is targeted towards.
304/// This helps Bing show the right content to users in different geographic locations.
305#[derive(Debug, Clone, Serialize, Deserialize)]
306pub struct CountryRegionSettings {
307 /// The date when this setting was configured
308 #[serde(rename = "Date", with = "dotnet_date_format")]
309 pub date: NaiveDate,
310
311 /// Two-letter ISO country code (e.g., "US", "GB", "DE")
312 #[serde(rename = "TwoLetterIsoCountryCode")]
313 pub two_letter_iso_country_code: String,
314
315 /// The scope of this geographic targeting setting
316 #[serde(rename = "Type")]
317 pub r#type: CountryRegionSettingsType,
318
319 /// The URL or URL pattern this setting applies to
320 #[serde(rename = "Url")]
321 pub url: String,
322}
323
324/// Scope of geographic targeting
325#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr)]
326#[repr(i32)]
327pub enum CountryRegionSettingsType {
328 /// Target a single page
329 Page = 0,
330 /// Target a directory and all its contents
331 Directory = 1,
332 /// Target the entire domain
333 Domain = 2,
334 /// Target a subdomain
335 Subdomain = 3,
336}
337
338/// Crawl statistics for a website
339///
340/// Provides detailed metrics about how Bingbot crawls your website,
341/// including HTTP response codes, errors, and index status.
342#[derive(Debug, Clone, Serialize, Deserialize)]
343pub struct CrawlStats {
344 /// Count of pages returning other HTTP status codes not categorized below
345 #[serde(rename = "AllOtherCodes")]
346 pub all_other_codes: i64,
347
348 /// Count of pages blocked by robots.txt
349 #[serde(rename = "BlockedByRobotsTxt")]
350 pub blocked_by_robots_txt: i64,
351
352 /// Count of pages returning 2xx success codes
353 #[serde(rename = "Code2xx")]
354 pub code_2xx: i64,
355
356 /// Count of pages returning 301 permanent redirect
357 #[serde(rename = "Code301")]
358 pub code_301: i64,
359
360 /// Count of pages returning 302 temporary redirect
361 #[serde(rename = "Code302")]
362 pub code_302: i64,
363
364 /// Count of pages returning 4xx client error codes
365 #[serde(rename = "Code4xx")]
366 pub code_4xx: i64,
367
368 /// Count of pages returning 5xx server error codes
369 #[serde(rename = "Code5xx")]
370 pub code_5xx: i64,
371
372 /// Count of connection timeouts
373 #[serde(rename = "ConnectionTimeout")]
374 pub connection_timeout: i64,
375
376 /// Total number of pages crawled
377 #[serde(rename = "CrawledPages")]
378 pub crawled_pages: i64,
379
380 /// Total number of crawl errors encountered
381 #[serde(rename = "CrawlErrors")]
382 pub crawl_errors: i64,
383
384 /// Date of these statistics
385 #[serde(rename = "Date", with = "dotnet_date_format")]
386 pub date: NaiveDate,
387
388 /// Count of DNS resolution failures
389 #[serde(rename = "DnsFailures")]
390 pub dns_failures: i64,
391
392 /// Number of pages currently in Bing's index
393 #[serde(rename = "InIndex")]
394 pub in_index: i64,
395
396 /// Number of inbound links to the site
397 #[serde(rename = "InLinks")]
398 pub in_links: i64,
399}
400
401/// Deep link information for search results
402///
403/// Deep links are additional links shown below a main search result,
404/// helping users navigate directly to specific pages within your site.
405#[derive(Debug, Clone, Serialize, Deserialize)]
406pub struct DeepLink {
407 /// Position of this deep link in the search results
408 #[serde(rename = "Position")]
409 pub position: i32,
410
411 /// Display title for the deep link
412 #[serde(rename = "Title")]
413 pub title: String,
414
415 /// URL of the deep link
416 #[serde(rename = "Url")]
417 pub url: String,
418
419 /// Weight/priority of this deep link
420 #[serde(rename = "Weight")]
421 pub weight: DeepLinkWeight,
422}
423
424/// Priority weight for deep links
425///
426/// Controls how prominently a deep link should be displayed in search results.
427#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
428pub enum DeepLinkWeight {
429 /// Deep link is disabled
430 Disabled = 0,
431 /// Low priority
432 Low = 1,
433 /// Normal priority
434 Normal = 2,
435 /// High priority
436 High = 3,
437}
438
439/// Algorithm-suggested deep link URL
440///
441/// URLs that Bing's algorithm suggests as good candidates for deep links.
442#[derive(Debug, Clone, Serialize, Deserialize)]
443pub struct DeepLinkAlgoUrl {
444 /// Number of deep links for this URL
445 #[serde(rename = "DeepLinkCount")]
446 pub deep_link_count: i32,
447
448 /// Number of impressions this URL receives
449 #[serde(rename = "Impressions")]
450 pub impressions: i32,
451
452 /// The suggested URL
453 #[serde(rename = "Url")]
454 pub url: String,
455}
456
457/// Blocked deep link
458///
459/// Represents a deep link that has been explicitly blocked from appearing in search results.
460#[derive(Debug, Clone, Serialize, Deserialize)]
461pub struct DeepLinkBlock {
462 /// Source URL (the main search result)
463 pub source_url: String,
464
465 /// Target URL (the deep link being blocked)
466 pub target_url: String,
467
468 /// Type of block applied
469 pub block_type: String,
470
471 /// Reason for blocking this deep link
472 pub reason: String,
473}
474
475/// Page preview block
476///
477/// Represents a page where preview features (snippet, cache, etc.) have been blocked.
478#[derive(Debug, Clone, Serialize, Deserialize)]
479pub struct PagePreviewBlock {
480 /// URL of the page with blocked preview
481 pub url: String,
482
483 /// Reason for blocking the preview
484 pub block_reason: BlockReason,
485
486 /// Date when the block was applied
487 pub blocked_date: NaiveDate,
488}
489
490/// Content submission API quota
491///
492/// Daily and monthly limits for the content submission API.
493/// Content submission allows submitting page content directly to Bing (up to 10MB per request).
494#[derive(Debug, Clone, Serialize, Deserialize)]
495pub struct ContentSubmissionQuota {
496 /// Daily submission quota remaining
497 #[serde(rename = "DailyQuota")]
498 pub daily_quota: i64,
499
500 /// Monthly submission quota remaining
501 #[serde(rename = "MonthlyQuota")]
502 pub monthly_quota: i64,
503}
504
505/// Crawl rate settings for a site
506///
507/// Controls how frequently Bingbot crawls your site.
508#[derive(Debug, Clone, Serialize, Deserialize)]
509pub struct CrawlSettings {
510 /// Whether crawl boost feature is available for this site
511 #[serde(rename = "CrawlBoostAvailable")]
512 pub crawl_boost_available: bool,
513
514 /// Whether crawl boost is currently enabled
515 #[serde(rename = "CrawlBoostEnabled")]
516 pub crawl_boost_enabled: bool,
517
518 /// Crawl rate configuration data
519 #[serde(rename = "CrawlRate")]
520 pub crawl_rate: Vec<u8>,
521}
522
523/// Detailed query statistics for a specific date
524///
525/// More granular version of `QueryStats` with position data.
526#[derive(Debug, Clone, Serialize, Deserialize)]
527pub struct DetailedQueryStats {
528 /// Number of clicks for this query
529 #[serde(rename = "Clicks")]
530 pub clicks: i64,
531
532 /// Date of these statistics
533 #[serde(rename = "Date", with = "dotnet_date_format")]
534 pub date: NaiveDate,
535
536 /// Number of impressions for this query
537 #[serde(rename = "Impressions")]
538 pub impressions: i64,
539
540 /// Average position in search results
541 #[serde(rename = "Position")]
542 pub position: f64,
543}
544
545/// Search query performance statistics
546///
547/// Contains metrics about how a specific search query performs for your site,
548/// including clicks, impressions, and ranking positions.
549#[derive(Debug, Clone, Serialize, Deserialize)]
550pub struct QueryStats {
551 /// Average position in search results when the page is clicked
552 #[serde(rename = "AvgClickPosition")]
553 pub avg_click_position: f64,
554
555 /// Average position in search results when the page is shown (impression)
556 #[serde(rename = "AvgImpressionPosition")]
557 pub avg_impression_position: f64,
558
559 /// Number of times users clicked through to your site from search results
560 #[serde(rename = "Clicks")]
561 pub clicks: i64,
562
563 /// Date of these statistics
564 #[serde(rename = "Date", with = "dotnet_date_format")]
565 pub date: NaiveDate,
566
567 /// Number of times your site appeared in search results (impressions)
568 #[serde(rename = "Impressions")]
569 pub impressions: i64,
570
571 /// The search query string
572 #[serde(rename = "Query")]
573 pub query: String,
574}
575
576/// Crawl issues encountered for a URL
577///
578/// This is a flags enumeration that supports bitwise combination of values.
579///
580/// Reference: Microsoft.Bing.Webmaster.Api.Interfaces.UrlWithCrawlIssues.CrawlIssues
581#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
582#[repr(i32)]
583pub enum CrawlIssues {
584 /// No issues
585 None = 0,
586 /// HTTP 301 permanent redirect
587 Code301 = 1,
588 /// HTTP 302 temporary redirect
589 Code302 = 2,
590 /// HTTP 4xx client error
591 Code4xx = 4,
592 /// HTTP 5xx server error
593 Code5xx = 8,
594 /// URL blocked by robots.txt
595 BlockedByRobotsTxt = 16,
596 /// Page contains malware
597 ContainsMalware = 32,
598 /// Important URL blocked by robots.txt
599 ImportantUrlBlockedByRobotsTxt = 64,
600 /// DNS resolution errors
601 DnsErrors = 128,
602 /// Request timeout errors
603 TimeOutErrors = 256,
604}
605
606/// URL with crawl issues
607///
608/// Represents a URL that Bingbot encountered problems crawling,
609/// along with information about the type of issues.
610///
611/// Reference: Microsoft.Bing.Webmaster.Api.Interfaces.UrlWithCrawlIssues
612#[derive(Debug, Clone, Serialize, Deserialize)]
613pub struct UrlWithCrawlIssues {
614 /// HTTP status code returned when crawling this URL
615 #[serde(rename = "HttpCode")]
616 pub http_code: i32,
617
618 /// Crawl issues encountered (flags enum, can be combined)
619 #[serde(rename = "Issues")]
620 pub issues: CrawlIssues,
621
622 /// The URL that has crawl issues
623 #[serde(rename = "Url")]
624 pub url: String,
625
626 /// Number of inbound links pointing to this URL
627 #[serde(rename = "InLinks")]
628 pub in_links: i64,
629}
630
631/// RSS or Atom feed information
632///
633/// Contains details about a submitted feed, including its status and crawl information.
634#[derive(Debug, Clone, Serialize, Deserialize)]
635pub struct Feed {
636 /// Whether the feed is compressed (gzipped)
637 #[serde(rename = "Compressed")]
638 pub compressed: bool,
639
640 /// Size of the feed file in bytes
641 #[serde(rename = "FileSize")]
642 pub file_size: i64,
643
644 /// When the feed was last crawled by Bing
645 #[serde(rename = "LastCrawled", with = "dotnet_date_format_opt")]
646 pub last_crawled: Option<NaiveDate>,
647
648 /// Current status of the feed (e.g., "Active", "Pending")
649 #[serde(rename = "Status")]
650 pub status: String,
651
652 /// When the feed was submitted
653 #[serde(rename = "Submitted", with = "dotnet_date_format_opt")]
654 pub submitted: Option<NaiveDate>,
655
656 /// Feed type (e.g., "RSS", "Atom")
657 #[serde(rename = "Type")]
658 pub r#type: String,
659
660 /// URL of the feed
661 #[serde(rename = "Url")]
662 pub url: String,
663
664 /// Number of URLs contained in the feed
665 #[serde(rename = "UrlCount")]
666 pub url_count: i32,
667}
668
669/// Website information and verification status
670///
671/// Contains verification codes and status for a site in Bing Webmaster Tools.
672#[derive(Debug, Clone, Serialize, Deserialize)]
673pub struct Site {
674 /// Authentication code for meta tag verification
675 #[serde(rename = "AuthenticationCode")]
676 pub authentication_code: String,
677
678 /// DNS TXT record code for DNS verification
679 #[serde(rename = "DnsVerificationCode")]
680 pub dns_verification_code: String,
681
682 /// Whether the site ownership has been verified
683 #[serde(rename = "IsVerified")]
684 pub is_verified: bool,
685
686 /// The site URL (e.g., `https://example.com`)
687 #[serde(rename = "Url")]
688 pub url: String,
689}
690
691/// User roles and permissions for a site
692///
693/// Represents a user's access permissions for a specific site in Bing Webmaster Tools.
694#[derive(Debug, Clone, Serialize, Deserialize)]
695pub struct SiteRoles {
696 /// Date when this role was assigned
697 #[serde(rename = "Date", with = "dotnet_date_format")]
698 pub date: NaiveDate,
699
700 /// Delegation code for transferring site ownership
701 #[serde(rename = "DelegatedCode")]
702 pub delegated_code: Option<String>,
703
704 /// Email of the user who owns the delegation code
705 #[serde(rename = "DelegatedCodeOwnerEmail")]
706 pub delegated_code_owner_email: Option<String>,
707
708 /// Email of the user who delegated access
709 #[serde(rename = "DelegatorEmail")]
710 pub delegator_email: Option<String>,
711
712 /// Email of the user with this role
713 #[serde(rename = "Email")]
714 pub email: String,
715
716 /// Whether this role assignment has expired
717 #[serde(rename = "Expired")]
718 pub expired: bool,
719
720 /// The role assigned to the user
721 #[serde(rename = "Role")]
722 pub role: UserRole,
723
724 /// The site URL this role applies to
725 #[serde(rename = "Site")]
726 pub site: String,
727
728 /// The verification site URL
729 #[serde(rename = "VerificationSite")]
730 pub verification_site: String,
731}
732
733/// User role permissions for site access
734///
735/// Defines the level of access a user has to a site in Bing Webmaster Tools.
736///
737/// Reference: Microsoft.Bing.Webmaster.Api.Interfaces.SiteRoles.UserRole
738#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
739#[repr(i32)]
740pub enum UserRole {
741 /// User has full administrative permissions
742 Administrator = 0,
743 /// User has read-only permissions
744 ReadOnly = 1,
745 /// User has read and write permissions
746 ReadWrite = 2,
747}
748
749/// Detailed information about a specific URL
750///
751/// Contains comprehensive metadata about a URL including crawl status,
752/// size, and link information.
753#[derive(Debug, Clone, Serialize, Deserialize)]
754pub struct UrlInfo {
755 /// Number of anchor links pointing to this URL
756 #[serde(rename = "AnchorCount")]
757 pub anchor_count: i32,
758
759 /// When Bing first discovered this URL
760 #[serde(rename = "DiscoveryDate", with = "dotnet_date_format")]
761 pub discovery_date: NaiveDate,
762
763 /// Size of the document in bytes
764 #[serde(rename = "DocumentSize")]
765 pub document_size: i64,
766
767 /// HTTP status code returned when crawling
768 #[serde(rename = "HttpStatus")]
769 pub http_status: i32,
770
771 /// Whether this is a page (true) or directory (false)
772 #[serde(rename = "IsPage")]
773 pub is_page: bool,
774
775 /// When Bing last crawled this URL
776 #[serde(rename = "LastCrawledDate", with = "dotnet_date_format")]
777 pub last_crawled_date: NaiveDate,
778
779 /// Total number of child URLs under this URL
780 #[serde(rename = "TotalChildUrlCount")]
781 pub total_child_url_count: i32,
782
783 /// The URL
784 #[serde(rename = "Url")]
785 pub url: String,
786}
787
788/// Traffic statistics for a specific URL
789///
790/// Contains click and impression data for a URL in search results.
791#[derive(Debug, Clone, Serialize, Deserialize)]
792pub struct UrlTrafficInfo {
793 /// Number of clicks this URL received
794 #[serde(rename = "Clicks")]
795 pub clicks: i32,
796
797 /// Number of times this URL appeared in search results
798 #[serde(rename = "Impressions")]
799 pub impressions: i32,
800
801 /// Whether this is a page (true) or directory (false)
802 #[serde(rename = "IsPage")]
803 pub is_page: bool,
804
805 /// The URL
806 #[serde(rename = "Url")]
807 pub url: String,
808}
809
810/// URL submission API quota
811///
812/// Daily and monthly limits for the URL submission API.
813/// Allows submitting up to 10,000 URLs per day for crawling.
814#[derive(Debug, Clone, Serialize, Deserialize)]
815pub struct UrlSubmissionQuota {
816 /// Daily URL submission quota remaining
817 #[serde(rename = "DailyQuota")]
818 pub daily_quota: i32,
819
820 /// Monthly URL submission quota remaining
821 #[serde(rename = "MonthlyQuota")]
822 pub monthly_quota: i32,
823}
824
825/// Inbound link counts for URLs
826///
827/// Contains a list of URLs and how many inbound links each has.
828#[derive(Debug, Clone, Serialize, Deserialize)]
829pub struct LinkCounts {
830 /// List of URLs with their link counts
831 #[serde(rename = "Links")]
832 pub links: Vec<LinkCount>,
833
834 /// Total number of pages in the result set
835 #[serde(rename = "TotalPages")]
836 pub total_pages: i32,
837}
838
839/// Link count for a specific URL
840#[derive(Debug, Clone, Serialize, Deserialize)]
841pub struct LinkCount {
842 /// Number of inbound links to this URL
843 #[serde(rename = "Count")]
844 pub count: i32,
845
846 /// The URL
847 #[serde(rename = "Url")]
848 pub url: String,
849}
850
851/// Detailed inbound link information
852///
853/// Contains specific details about inbound links including anchor text.
854#[derive(Debug, Clone, Serialize, Deserialize)]
855pub struct LinkDetails {
856 /// List of detailed link information
857 #[serde(rename = "Details")]
858 pub details: Vec<LinkDetail>,
859
860 /// Total number of pages in the result set
861 #[serde(rename = "TotalPages")]
862 pub total_pages: i32,
863}
864
865/// Detail about a specific inbound link
866#[derive(Debug, Clone, Serialize, Deserialize)]
867pub struct LinkDetail {
868 /// The anchor text used for the link
869 #[serde(rename = "AnchorText")]
870 pub anchor_text: String,
871
872 /// The source URL of the link
873 #[serde(rename = "Url")]
874 pub url: String,
875}
876
877/// Query parameter configuration
878///
879/// Represents a URL query parameter that should be ignored or included during crawling.
880/// This helps prevent duplicate content issues from URL parameters.
881#[derive(Debug, Clone, Serialize, Deserialize)]
882pub struct QueryParameter {
883 /// Date when this parameter configuration was set
884 #[serde(rename = "Date", with = "dotnet_date_format")]
885 pub date: NaiveDate,
886
887 /// Whether this parameter is enabled (should be ignored)
888 #[serde(rename = "IsEnabled")]
889 pub is_enabled: bool,
890
891 /// The query parameter name (e.g., "sessionid", "ref")
892 #[serde(rename = "Parameter")]
893 pub parameter: String,
894
895 /// Source of this parameter configuration
896 #[serde(rename = "Source")]
897 pub source: i32,
898}
899
900/// Combined ranking and traffic statistics
901///
902/// Aggregated metrics for site performance in search results.
903#[derive(Debug, Clone, Serialize, Deserialize)]
904pub struct RankAndTrafficStats {
905 /// Total number of clicks
906 #[serde(rename = "Clicks")]
907 pub clicks: i64,
908
909 /// Date of these statistics
910 #[serde(rename = "Date", with = "dotnet_date_format")]
911 pub date: NaiveDate,
912
913 /// Total number of impressions
914 #[serde(rename = "Impressions")]
915 pub impressions: i64,
916}
917
918/// Crawl date filter options
919///
920/// Reference: Microsoft.Bing.Webmaster.Api.Interfaces.CrawlDateFilter
921#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
922#[repr(i32)]
923pub enum CrawlDateFilter {
924 /// Any crawl date
925 Any = 0,
926 /// Last week
927 LastWeek = 1,
928 /// Last two weeks
929 LastTwoWeeks = 2,
930}
931
932/// Discovered date filter options
933///
934/// Reference: Microsoft.Bing.Webmaster.Api.Interfaces.DiscoveredDateFilter
935#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
936#[repr(i32)]
937pub enum DiscoveredDateFilter {
938 /// Any discovered date
939 Any = 0,
940 /// Last week
941 LastWeek = 1,
942 /// Last month
943 LastMonth = 2,
944}
945
946/// Document flags filters
947///
948/// Flags enumeration for filtering by document properties.
949///
950/// Reference: Microsoft.Bing.Webmaster.Api.Interfaces.DocFlagsFilters
951#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
952#[repr(i32)]
953pub enum DocFlagsFilters {
954 /// Any document flags
955 Any = 0,
956 /// Document is blocked by robots.txt
957 IsBlockedByRobotsTxt = 1,
958}
959
960/// HTTP code filters
961///
962/// Flags enumeration for filtering by HTTP status codes.
963///
964/// Reference: Microsoft.Bing.Webmaster.Api.Interfaces.HttpCodeFilters
965#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
966#[repr(i32)]
967pub enum HttpCodeFilters {
968 /// Any HTTP code
969 Any = 0,
970 /// 2xx success codes
971 Code2xx = 1,
972 /// 3xx redirect codes
973 Code3xx = 2,
974 /// 301 permanent redirect
975 Code301 = 4,
976 /// 302 temporary redirect
977 Code302 = 8,
978 /// 4xx client error codes
979 Code4xx = 16,
980 /// 5xx server error codes
981 Code5xx = 32,
982 /// All other codes
983 AllOthers = 64,
984}
985
986/// Filter properties for queries
987///
988/// Used to filter URL information queries by various criteria.
989///
990/// Reference: Microsoft.Bing.Webmaster.Api.Interfaces.FilterProperties
991#[derive(Debug, Clone, Serialize, Deserialize)]
992pub struct FilterProperties {
993 /// Filter by crawl date range
994 #[serde(rename = "CrawlDateFilter")]
995 pub crawl_date_filter: CrawlDateFilter,
996
997 /// Filter by discovered date range
998 #[serde(rename = "DiscoveredDateFilter")]
999 pub discovered_date_filter: DiscoveredDateFilter,
1000
1001 /// Filter by document flags
1002 #[serde(rename = "DocFlagsFilters")]
1003 pub doc_flags_filters: DocFlagsFilters,
1004
1005 /// Filter by HTTP status codes
1006 #[serde(rename = "HttpCodeFilters")]
1007 pub http_code_filters: HttpCodeFilters,
1008}
1009
1010/// URL fetched on demand
1011///
1012/// Result of requesting Bing to fetch a specific URL.
1013///
1014/// Reference: Microsoft.Bing.Webmaster.Api.Interfaces.FetchedUrl
1015#[derive(Debug, Clone, Serialize, Deserialize)]
1016pub struct FetchedUrl {
1017 /// The URL that was fetched
1018 #[serde(rename = "Url")]
1019 pub url: String,
1020
1021 /// When the URL was fetched
1022 #[serde(rename = "Date", with = "dotnet_date_format")]
1023 pub date: NaiveDate,
1024
1025 /// Whether the fetch request has expired
1026 #[serde(rename = "Expired")]
1027 pub expired: bool,
1028
1029 /// Whether the URL was successfully fetched
1030 #[serde(rename = "Fetched")]
1031 pub fetched: bool,
1032}
1033
1034/// Detailed information about a fetched URL
1035///
1036/// Extended version of `FetchedUrl` with document content and HTTP headers.
1037///
1038/// Reference: Microsoft.Bing.Webmaster.Api.Interfaces.FetchedUrlDetails
1039#[derive(Debug, Clone, Serialize, Deserialize)]
1040pub struct FetchedUrlDetails {
1041 /// The URL that was fetched
1042 #[serde(rename = "Url")]
1043 pub url: String,
1044
1045 /// When the URL was fetched
1046 #[serde(rename = "Date", with = "dotnet_date_format")]
1047 pub date: NaiveDate,
1048
1049 /// The document content returned
1050 #[serde(rename = "Document")]
1051 pub document: String,
1052
1053 /// HTTP response headers
1054 #[serde(rename = "Headers")]
1055 pub headers: String,
1056
1057 /// HTTP status message
1058 #[serde(rename = "Status")]
1059 pub status: String,
1060}
1061
1062/// Keyword search statistics
1063///
1064/// Performance metrics for a specific search keyword.
1065///
1066/// Reference: Microsoft.Bing.Webmaster.Api.Interfaces.Keyword
1067#[derive(Debug, Clone, Serialize, Deserialize)]
1068pub struct Keyword {
1069 /// The search query/keyword
1070 #[serde(rename = "Query")]
1071 pub query: String,
1072
1073 /// Number of exact match impressions for this keyword
1074 #[serde(rename = "Impressions")]
1075 pub impressions: i64,
1076
1077 /// Number of broad match impressions for this keyword
1078 #[serde(rename = "BroadImpressions")]
1079 pub broad_impressions: i64,
1080}
1081
1082/// Keyword statistics for a specific date
1083///
1084/// Performance metrics for a keyword on a particular date.
1085///
1086/// Reference: Microsoft.Bing.Webmaster.Api.Interfaces.KeywordStats
1087#[derive(Debug, Clone, Serialize, Deserialize)]
1088pub struct KeywordStats {
1089 /// The search query/keyword
1090 #[serde(rename = "Query")]
1091 pub query: String,
1092
1093 /// Number of exact match impressions
1094 #[serde(rename = "Impressions")]
1095 pub impressions: i64,
1096
1097 /// Number of broad match impressions
1098 #[serde(rename = "BroadImpressions")]
1099 pub broad_impressions: i64,
1100
1101 /// Date of these statistics
1102 #[serde(rename = "Date", with = "dotnet_date_format")]
1103 pub date: NaiveDate,
1104}
1105
1106/// Site migration information
1107///
1108/// Represents a site move/migration from one URL to another.
1109/// Used to inform Bing about domain changes or HTTPS migrations.
1110#[derive(Debug, Clone, Serialize, Deserialize)]
1111pub struct SiteMove {
1112 /// Original site URL (source)
1113 #[serde(rename = "SourceSite")]
1114 pub source_site: String,
1115
1116 /// New site URL (target/destination)
1117 #[serde(rename = "TargetSite")]
1118 pub target_site: String,
1119
1120 /// Date when the site move was registered
1121 #[serde(rename = "Date", with = "dotnet_date_format")]
1122 pub date: NaiveDate,
1123
1124 /// Current status of the site move (e.g., "InProgress", "Complete")
1125 #[serde(rename = "Status")]
1126 pub status: String,
1127}
1128
1129/// Site move configuration settings
1130///
1131/// Settings required to configure a site migration.
1132#[derive(Debug, Clone, Serialize, Deserialize)]
1133pub struct SiteMoveSettings {
1134 /// Target site URL (new location)
1135 #[serde(rename = "TargetSite")]
1136 pub target_site: String,
1137
1138 /// Validation tag to verify ownership of target site
1139 #[serde(rename = "ValidationTag")]
1140 pub validation_tag: String,
1141}