cbf-chrome 0.1.0-alpha.7

Chromium-specific safe API layer for CBF.
Documentation
//! Chrome transport models for navigation history data.

use cbf::data::navigation::{
    NavigationEntryId, NavigationHistoryEntry, NavigationHistorySnapshot, NavigationKind,
};

/// Chrome-transport history entry identifier.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ChromeNavigationEntryId(pub u64);

impl ChromeNavigationEntryId {
    /// Create a new identifier from a raw numeric value.
    pub const fn new(raw: u64) -> Self {
        Self(raw)
    }

    /// Get the raw numeric value of this identifier.
    pub const fn get(self) -> u64 {
        self.0
    }
}

impl From<NavigationEntryId> for ChromeNavigationEntryId {
    fn from(value: NavigationEntryId) -> Self {
        Self::new(value.get())
    }
}

impl From<ChromeNavigationEntryId> for NavigationEntryId {
    fn from(value: ChromeNavigationEntryId) -> Self {
        NavigationEntryId::new(value.get())
    }
}

/// Chrome-transport navigation classification.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ChromeNavigationKind {
    NewDocument,
    SameDocument,
    SameDocumentReplace,
    HistoryTraversal,
    Reload,
}

impl From<NavigationKind> for ChromeNavigationKind {
    fn from(value: NavigationKind) -> Self {
        match value {
            NavigationKind::NewDocument => Self::NewDocument,
            NavigationKind::SameDocument => Self::SameDocument,
            NavigationKind::SameDocumentReplace => Self::SameDocumentReplace,
            NavigationKind::HistoryTraversal => Self::HistoryTraversal,
            NavigationKind::Reload => Self::Reload,
        }
    }
}

impl From<ChromeNavigationKind> for NavigationKind {
    fn from(value: ChromeNavigationKind) -> Self {
        match value {
            ChromeNavigationKind::NewDocument => Self::NewDocument,
            ChromeNavigationKind::SameDocument => Self::SameDocument,
            ChromeNavigationKind::SameDocumentReplace => Self::SameDocumentReplace,
            ChromeNavigationKind::HistoryTraversal => Self::HistoryTraversal,
            ChromeNavigationKind::Reload => Self::Reload,
        }
    }
}

/// Chrome-transport history entry metadata.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ChromeNavigationHistoryEntry {
    pub entry_id: ChromeNavigationEntryId,
    pub url: String,
    pub title: Option<String>,
    pub index: usize,
    pub same_document_with_previous: bool,
}

impl From<NavigationHistoryEntry> for ChromeNavigationHistoryEntry {
    fn from(value: NavigationHistoryEntry) -> Self {
        Self {
            entry_id: value.entry_id.into(),
            url: value.url,
            title: value.title,
            index: value.index,
            same_document_with_previous: value.same_document_with_previous,
        }
    }
}

impl From<ChromeNavigationHistoryEntry> for NavigationHistoryEntry {
    fn from(value: ChromeNavigationHistoryEntry) -> Self {
        Self {
            entry_id: value.entry_id.into(),
            url: value.url,
            title: value.title,
            index: value.index,
            same_document_with_previous: value.same_document_with_previous,
        }
    }
}

/// Chrome-transport history snapshot.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ChromeNavigationHistorySnapshot {
    pub request_id: u64,
    pub current_entry_id: ChromeNavigationEntryId,
    pub current_index: usize,
    pub entries: Vec<ChromeNavigationHistoryEntry>,
}

impl From<NavigationHistorySnapshot> for ChromeNavigationHistorySnapshot {
    fn from(value: NavigationHistorySnapshot) -> Self {
        Self {
            request_id: value.request_id,
            current_entry_id: value.current_entry_id.into(),
            current_index: value.current_index,
            entries: value.entries.into_iter().map(Into::into).collect(),
        }
    }
}

impl From<ChromeNavigationHistorySnapshot> for NavigationHistorySnapshot {
    fn from(value: ChromeNavigationHistorySnapshot) -> Self {
        Self {
            request_id: value.request_id,
            current_entry_id: value.current_entry_id.into(),
            current_index: value.current_index,
            entries: value.entries.into_iter().map(Into::into).collect(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{
        ChromeNavigationEntryId, ChromeNavigationHistoryEntry, ChromeNavigationHistorySnapshot,
        ChromeNavigationKind,
    };

    #[test]
    fn navigation_entry_id_round_trip_preserves_raw_value() {
        let original = cbf::data::navigation::NavigationEntryId::new(77);

        let chrome = ChromeNavigationEntryId::from(original);
        let round_trip = cbf::data::navigation::NavigationEntryId::from(chrome);

        assert_eq!(chrome, ChromeNavigationEntryId::new(77));
        assert_eq!(round_trip, original);
    }

    #[test]
    fn history_snapshot_round_trip_with_generic_preserves_payload() {
        let snapshot = ChromeNavigationHistorySnapshot {
            request_id: 1,
            current_entry_id: ChromeNavigationEntryId::new(9),
            current_index: 1,
            entries: vec![ChromeNavigationHistoryEntry {
                entry_id: ChromeNavigationEntryId::new(9),
                url: "https://example.com".to_string(),
                title: Some("Example".to_string()),
                index: 1,
                same_document_with_previous: false,
            }],
        };

        let generic: cbf::data::navigation::NavigationHistorySnapshot = snapshot.clone().into();
        let round_trip = ChromeNavigationHistorySnapshot::from(generic);

        assert_eq!(round_trip, snapshot);
        assert_eq!(
            cbf::data::navigation::NavigationKind::from(ChromeNavigationKind::Reload),
            cbf::data::navigation::NavigationKind::Reload
        );
    }
}