link_preview/providers/
og.rs

1//! Open Graph Protocol bindings
2//!
3//! The Open Graph protocol enables any web page to become a rich object in a
4//! social graph. For instance, this is used on Facebook to allow any web page
5//! to have the same functionality as any other object on Facebook.
6//!
7//!
8//!
9//! # References
10//! - [Official Documentation](https://ogp.me)
11use scraper::{Html, Selector};
12use std::fmt;
13
14/// OpenGraphTag meta tags collection
15pub enum OpenGraphTag {
16    /// Represents the "og:title" OpenGraph meta tag.
17    ///
18    /// The title of your object as it should appear within
19    /// the graph, e.g., "The Rock".
20    Title,
21    /// Represents the "og:url" OpenGraph meta tag
22    Url,
23    /// Represents the "og:image" OpenGraph meta tag
24    Image,
25    /// Represents the "og:type" OpenGraph meta tag
26    ///
27    /// The type of your object, e.g., "video.movie". Depending on the type
28    /// you specify, other properties may also be required.
29    Type,
30    /// Represents the "og:description" OpenGraph meta tag
31    Description,
32    /// Represents the "og:locale" OpenGraph meta tag
33    Locale,
34    /// Represents the "og:image:height" OpenGraph meta tag
35    ImageHeight,
36    /// Represents the "og:image:width" OpenGraph meta tag
37    ImageWidth,
38    /// Represents the "og:site_name" OpenGraph meta tag
39    SiteName,
40}
41
42impl fmt::Debug for OpenGraphTag {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        f.write_str(self.str())
45    }
46}
47
48impl OpenGraphTag {
49    fn str(&self) -> &str {
50        match self {
51            OpenGraphTag::Title => "title",
52            OpenGraphTag::Url => "url",
53            OpenGraphTag::Image => "image",
54            OpenGraphTag::Type => "type",
55            OpenGraphTag::Description => "description",
56            OpenGraphTag::Locale => "locale",
57            OpenGraphTag::ImageHeight => "image:height",
58            OpenGraphTag::ImageWidth => "image:width",
59            OpenGraphTag::SiteName => "site_name",
60        }
61    }
62}
63
64/// Finds the OpenGraphTag tag specified in the provided `Html` instance
65pub fn find_og_tag(html: &Html, tag: OpenGraphTag) -> Option<String> {
66    let selector = Selector::parse(&format!("meta[property=\"og:{}\"]", tag.str())).unwrap();
67
68    if let Some(element) = html.select(&selector).next() {
69        if let Some(value) = element.value().attr("content") {
70            return Some(value.to_string());
71        }
72    }
73
74    None
75}
76
77#[cfg(test)]
78mod tests {
79    use crate::html_from_bytes;
80    use crate::tests::OG_COMPLIANT_HTML;
81
82    use super::{find_og_tag, OpenGraphTag};
83
84    #[test]
85    fn retrieves_title_from_og_compliant_html() {
86        let html = html_from_bytes(OG_COMPLIANT_HTML).unwrap();
87        let title = find_og_tag(&html, OpenGraphTag::Title).unwrap();
88
89        assert_eq!(title, "SEO Strategies for a better web");
90    }
91
92    #[test]
93    fn retrieves_description_from_og_compliant_html() {
94        let html = html_from_bytes(OG_COMPLIANT_HTML).unwrap();
95        let description = find_og_tag(&html, OpenGraphTag::Description).unwrap();
96
97        assert_eq!(description, "John Appleseed tells you his secrets on SEO for a better web experience by taking advantage of OpenGraph\'s Tags!");
98    }
99
100    #[test]
101    fn retrieves_url_from_og_compliant_html() {
102        let html = html_from_bytes(OG_COMPLIANT_HTML).unwrap();
103        let url = find_og_tag(&html, OpenGraphTag::Url).unwrap();
104
105        assert_eq!(url, "https://abetterweb.com");
106    }
107
108    #[test]
109    fn retrieves_locale_from_og_compliant_html() {
110        let html = html_from_bytes(OG_COMPLIANT_HTML).unwrap();
111        let locale = find_og_tag(&html, OpenGraphTag::Locale).unwrap();
112
113        assert_eq!(locale, "en_US");
114    }
115
116    #[test]
117    fn retrieves_image_from_og_compliant_html() {
118        let html = html_from_bytes(OG_COMPLIANT_HTML).unwrap();
119        let image = find_og_tag(&html, OpenGraphTag::Image).unwrap();
120
121        assert_eq!(
122            image,
123            "https://www.apple.com/ac/structured-data/images/open_graph_logo.png?201809210816"
124        );
125    }
126
127    #[test]
128    fn retrieves_type_from_og_compliant_html() {
129        let html = html_from_bytes(OG_COMPLIANT_HTML).unwrap();
130        let r#type = find_og_tag(&html, OpenGraphTag::Type).unwrap();
131
132        assert_eq!(r#type, "website");
133    }
134
135    #[test]
136    fn retrieves_image_height_from_og_compliant_html() {
137        let html = html_from_bytes(OG_COMPLIANT_HTML).unwrap();
138        let height = find_og_tag(&html, OpenGraphTag::ImageHeight).unwrap();
139
140        assert_eq!(height, "600");
141    }
142
143    #[test]
144    fn retrieves_image_width_from_og_compliant_html() {
145        let html = html_from_bytes(OG_COMPLIANT_HTML).unwrap();
146        let width = find_og_tag(&html, OpenGraphTag::ImageWidth).unwrap();
147
148        assert_eq!(width, "1200");
149    }
150
151    #[test]
152    fn retrieves_site_name_from_og_compliant_html() {
153        let html = html_from_bytes(OG_COMPLIANT_HTML).unwrap();
154        let site_name = find_og_tag(&html, OpenGraphTag::SiteName).unwrap();
155
156        assert_eq!(site_name, "TechPro");
157    }
158}