Skip to main content

schemaorg_rs/profiles/google/
event.rs

1//! Google Rich Results profile for Event.
2//!
3//! Source: <https://developers.google.com/search/docs/appearance/structured-data/event>
4//! Verified: 2026-04-01
5
6use crate::types::SchemaNode;
7use crate::validation::ValidationDiagnostic as VD;
8
9use super::common::{recommend_property, require_property, validate_nested};
10use crate::profiles::{NodeProfileResult, Profile, TypeEligibility};
11
12/// Google Rich Results profile for Event structured data.
13pub struct GoogleEventProfile;
14
15impl Profile for GoogleEventProfile {
16    fn name(&self) -> &'static str {
17        "google"
18    }
19
20    fn version(&self) -> &'static str {
21        "2026-04-01"
22    }
23
24    fn source_url(&self) -> &'static str {
25        "https://developers.google.com/search/docs/appearance/structured-data/event"
26    }
27
28    fn supported_types(&self) -> &[&str] {
29        &["Event"]
30    }
31
32    fn evaluate_node(&self, node: &SchemaNode, _vocab_diagnostics: &[VD]) -> NodeProfileResult {
33        let type_name = node.types.first().map_or("Event", |t| t.as_str());
34        let path = type_name;
35        let mut diagnostics = Vec::new();
36        let mut required_missing = Vec::new();
37        let mut recommended_missing = Vec::new();
38
39        // Required fields
40        for prop in &["name", "startDate", "location"] {
41            if let Some(d) = require_property(node, prop, path) {
42                required_missing.push((*prop).to_string());
43                diagnostics.push(d);
44            }
45        }
46
47        // Recommended fields
48        for prop in &[
49            "image",
50            "description",
51            "endDate",
52            "offers",
53            "performer",
54            "organizer",
55            "eventStatus",
56            "eventAttendanceMode",
57        ] {
58            if let Some(d) = recommend_property(node, prop, path) {
59                recommended_missing.push((*prop).to_string());
60                diagnostics.push(d);
61            }
62        }
63
64        // Nested Place validation
65        let place_diags =
66            validate_nested(node, "location", "Place", &["name", "address"], &[], path);
67        diagnostics.extend(place_diags);
68
69        // Nested VirtualLocation validation
70        let virtual_diags =
71            validate_nested(node, "location", "VirtualLocation", &["url"], &[], path);
72        diagnostics.extend(virtual_diags);
73
74        let eligible = required_missing.is_empty();
75
76        NodeProfileResult {
77            type_eligibility: TypeEligibility {
78                schema_type: type_name.to_string(),
79                eligible,
80                required_missing,
81                recommended_missing,
82                field_diagnostics: diagnostics,
83            },
84        }
85    }
86}