Skip to main content

sova_meta/
overlay.rs

1//! Per-request meta overlay (`Arc<Mutex<…>>` so middleware sees handler writes).
2
3use chrono::{DateTime, Utc};
4use serde_json::Value;
5use std::sync::{Arc, Mutex};
6
7#[derive(Debug, Clone, Default)]
8struct MetaOverlayData {
9    title: Option<String>,
10    description: Option<String>,
11    image: Option<String>,
12    noindex: Option<bool>,
13    canonical_path: Option<String>,
14    published: Option<DateTime<Utc>>,
15    og_type: Option<String>,
16    jsonld: Vec<Value>,
17    manual: bool,
18}
19
20/// Shared per-request overlay (cheap to clone into middleware after `next`).
21#[derive(Debug, Clone, Default)]
22pub struct MetaOverlay {
23    inner: Arc<Mutex<MetaOverlayData>>,
24}
25
26impl MetaOverlay {
27    pub fn title(&self, t: impl Into<String>) -> &Self {
28        self.inner.lock().unwrap().title = Some(t.into());
29        self
30    }
31
32    pub fn description(&self, d: impl Into<String>) -> &Self {
33        self.inner.lock().unwrap().description = Some(d.into());
34        self
35    }
36
37    pub fn image(&self, i: impl Into<String>) -> &Self {
38        self.inner.lock().unwrap().image = Some(i.into());
39        self
40    }
41
42    pub fn noindex(&self) -> &Self {
43        self.inner.lock().unwrap().noindex = Some(true);
44        self
45    }
46
47    pub fn canonical_path(&self, p: impl Into<String>) -> &Self {
48        self.inner.lock().unwrap().canonical_path = Some(p.into());
49        self
50    }
51
52    pub fn published(&self, t: DateTime<Utc>) -> &Self {
53        self.inner.lock().unwrap().published = Some(t);
54        self
55    }
56
57    pub fn published_opt(&self, t: Option<DateTime<Utc>>) -> &Self {
58        if let Some(t) = t {
59            self.published(t);
60        }
61        self
62    }
63
64    pub fn og_type(&self, t: impl Into<String>) -> &Self {
65        self.inner.lock().unwrap().og_type = Some(t.into());
66        self
67    }
68
69    pub fn jsonld(&self, block: Value) -> &Self {
70        self.inner.lock().unwrap().jsonld.push(block);
71        self
72    }
73
74    pub fn manual(&self) -> &Self {
75        self.inner.lock().unwrap().manual = true;
76        self
77    }
78
79    pub(crate) fn snapshot(&self) -> OverlaySnapshot {
80        let g = self.inner.lock().unwrap();
81        OverlaySnapshot {
82            title: g.title.clone(),
83            description: g.description.clone(),
84            image: g.image.clone(),
85            noindex: g.noindex,
86            canonical_path: g.canonical_path.clone(),
87            published: g.published,
88            og_type: g.og_type.clone(),
89            jsonld: g.jsonld.clone(),
90            manual: g.manual,
91        }
92    }
93}
94
95#[derive(Debug, Clone, Default)]
96pub(crate) struct OverlaySnapshot {
97    pub title: Option<String>,
98    pub description: Option<String>,
99    pub image: Option<String>,
100    pub noindex: Option<bool>,
101    pub canonical_path: Option<String>,
102    pub published: Option<DateTime<Utc>>,
103    pub og_type: Option<String>,
104    pub jsonld: Vec<Value>,
105    pub manual: bool,
106}