Skip to main content

cbf_chrome/data/
navigation.rs

1//! Chrome transport models for navigation history data.
2
3use cbf::data::navigation::{
4    NavigationEntryId, NavigationHistoryEntry, NavigationHistorySnapshot, NavigationKind,
5};
6
7/// Chrome-transport history entry identifier.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
9pub struct ChromeNavigationEntryId(pub u64);
10
11impl ChromeNavigationEntryId {
12    /// Create a new identifier from a raw numeric value.
13    pub const fn new(raw: u64) -> Self {
14        Self(raw)
15    }
16
17    /// Get the raw numeric value of this identifier.
18    pub const fn get(self) -> u64 {
19        self.0
20    }
21}
22
23impl From<NavigationEntryId> for ChromeNavigationEntryId {
24    fn from(value: NavigationEntryId) -> Self {
25        Self::new(value.get())
26    }
27}
28
29impl From<ChromeNavigationEntryId> for NavigationEntryId {
30    fn from(value: ChromeNavigationEntryId) -> Self {
31        NavigationEntryId::new(value.get())
32    }
33}
34
35/// Chrome-transport navigation classification.
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum ChromeNavigationKind {
38    NewDocument,
39    SameDocument,
40    SameDocumentReplace,
41    HistoryTraversal,
42    Reload,
43}
44
45impl From<NavigationKind> for ChromeNavigationKind {
46    fn from(value: NavigationKind) -> Self {
47        match value {
48            NavigationKind::NewDocument => Self::NewDocument,
49            NavigationKind::SameDocument => Self::SameDocument,
50            NavigationKind::SameDocumentReplace => Self::SameDocumentReplace,
51            NavigationKind::HistoryTraversal => Self::HistoryTraversal,
52            NavigationKind::Reload => Self::Reload,
53        }
54    }
55}
56
57impl From<ChromeNavigationKind> for NavigationKind {
58    fn from(value: ChromeNavigationKind) -> Self {
59        match value {
60            ChromeNavigationKind::NewDocument => Self::NewDocument,
61            ChromeNavigationKind::SameDocument => Self::SameDocument,
62            ChromeNavigationKind::SameDocumentReplace => Self::SameDocumentReplace,
63            ChromeNavigationKind::HistoryTraversal => Self::HistoryTraversal,
64            ChromeNavigationKind::Reload => Self::Reload,
65        }
66    }
67}
68
69/// Chrome-transport history entry metadata.
70#[derive(Debug, Clone, PartialEq, Eq)]
71pub struct ChromeNavigationHistoryEntry {
72    pub entry_id: ChromeNavigationEntryId,
73    pub url: String,
74    pub title: Option<String>,
75    pub index: usize,
76    pub same_document_with_previous: bool,
77}
78
79impl From<NavigationHistoryEntry> for ChromeNavigationHistoryEntry {
80    fn from(value: NavigationHistoryEntry) -> Self {
81        Self {
82            entry_id: value.entry_id.into(),
83            url: value.url,
84            title: value.title,
85            index: value.index,
86            same_document_with_previous: value.same_document_with_previous,
87        }
88    }
89}
90
91impl From<ChromeNavigationHistoryEntry> for NavigationHistoryEntry {
92    fn from(value: ChromeNavigationHistoryEntry) -> Self {
93        Self {
94            entry_id: value.entry_id.into(),
95            url: value.url,
96            title: value.title,
97            index: value.index,
98            same_document_with_previous: value.same_document_with_previous,
99        }
100    }
101}
102
103/// Chrome-transport history snapshot.
104#[derive(Debug, Clone, PartialEq, Eq)]
105pub struct ChromeNavigationHistorySnapshot {
106    pub request_id: u64,
107    pub current_entry_id: ChromeNavigationEntryId,
108    pub current_index: usize,
109    pub entries: Vec<ChromeNavigationHistoryEntry>,
110}
111
112impl From<NavigationHistorySnapshot> for ChromeNavigationHistorySnapshot {
113    fn from(value: NavigationHistorySnapshot) -> Self {
114        Self {
115            request_id: value.request_id,
116            current_entry_id: value.current_entry_id.into(),
117            current_index: value.current_index,
118            entries: value.entries.into_iter().map(Into::into).collect(),
119        }
120    }
121}
122
123impl From<ChromeNavigationHistorySnapshot> for NavigationHistorySnapshot {
124    fn from(value: ChromeNavigationHistorySnapshot) -> Self {
125        Self {
126            request_id: value.request_id,
127            current_entry_id: value.current_entry_id.into(),
128            current_index: value.current_index,
129            entries: value.entries.into_iter().map(Into::into).collect(),
130        }
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::{
137        ChromeNavigationEntryId, ChromeNavigationHistoryEntry, ChromeNavigationHistorySnapshot,
138        ChromeNavigationKind,
139    };
140
141    #[test]
142    fn navigation_entry_id_round_trip_preserves_raw_value() {
143        let original = cbf::data::navigation::NavigationEntryId::new(77);
144
145        let chrome = ChromeNavigationEntryId::from(original);
146        let round_trip = cbf::data::navigation::NavigationEntryId::from(chrome);
147
148        assert_eq!(chrome, ChromeNavigationEntryId::new(77));
149        assert_eq!(round_trip, original);
150    }
151
152    #[test]
153    fn history_snapshot_round_trip_with_generic_preserves_payload() {
154        let snapshot = ChromeNavigationHistorySnapshot {
155            request_id: 1,
156            current_entry_id: ChromeNavigationEntryId::new(9),
157            current_index: 1,
158            entries: vec![ChromeNavigationHistoryEntry {
159                entry_id: ChromeNavigationEntryId::new(9),
160                url: "https://example.com".to_string(),
161                title: Some("Example".to_string()),
162                index: 1,
163                same_document_with_previous: false,
164            }],
165        };
166
167        let generic: cbf::data::navigation::NavigationHistorySnapshot = snapshot.clone().into();
168        let round_trip = ChromeNavigationHistorySnapshot::from(generic);
169
170        assert_eq!(round_trip, snapshot);
171        assert_eq!(
172            cbf::data::navigation::NavigationKind::from(ChromeNavigationKind::Reload),
173            cbf::data::navigation::NavigationKind::Reload
174        );
175    }
176}