nostralink 0.2.1

Linked data library for nostr
Documentation
//! Bookmarks API

use super::manager::{RdfEventsStore, RdfStoreError};
use nostr::event::EventId;
use oxigraph::model::*;
use std::str::FromStr;
use std::time::{SystemTime, UNIX_EPOCH};
use urn::Urn;

pub trait EventBookmarks {
    fn bookmark(
        &self,
        event_uri: NamedNode,
        title: String,
    ) -> Result<(), RdfStoreError>;
    fn get_bookmarks(
        &self,
    ) -> Result<Vec<(NamedNode, EventId, String)>, RdfStoreError>;
}

fn bookmarks_graph1() -> NamedNode {
    NamedNode::new("urn:nostr-bookmarks:1").unwrap()
}

fn bookmarks_root() -> NamedNode {
    NamedNode::new("urn:nostr-bookmarks:root").unwrap()
}

fn pred_bookmark_event() -> NamedNode {
    NamedNode::new("https://w3id.org/nostr#eventBookmark").unwrap()
}

fn pred_bookmark_title() -> NamedNode {
    NamedNode::new("https://w3id.org/nostr#bookmarkTitle").unwrap()
}

fn pred_bookmark_dateadded() -> NamedNode {
    NamedNode::new("https://w3id.org/nostr#bookmarkDateAdded").unwrap()
}

fn evuri_to_eventid(node: NamedNode) -> Result<EventId, RdfStoreError> {
    let urn = Urn::from_str(node.into_string().as_str())
        .map_err(|_| RdfStoreError::URNError)?;

    Ok(EventId::parse(urn.nss()).map_err(|_| RdfStoreError::URNError)?)
}

const BQUERY: &str = "
SELECT ?root ?event ?title ?date_added
FROM <urn:nostr-bookmarks:1>
WHERE {
?root nostr:eventBookmark ?event .
?event nostr:bookmarkTitle ?title .
?event nostr:bookmarkDateAdded ?date_added .
}
ORDER BY DESC(?date_added)
";

fn get_epoch_ms() -> u64 {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_secs()
}

impl EventBookmarks for RdfEventsStore {
    fn bookmark(
        &self,
        event_uri: NamedNode,
        title: String,
    ) -> Result<(), RdfStoreError> {
        let pred_b = pred_bookmark_event();
        let pred_title = pred_bookmark_title();
        let root = bookmarks_root();
        let bnode = bookmarks_graph1();

        let _ = self
            .store
            .insert(QuadRef::new(&root, &pred_b, &event_uri, &bnode));
        let _ = self.store.insert(QuadRef::new(
            &event_uri,
            &pred_title,
            &Literal::from(title),
            &bnode,
        ));
        let _ = self.store.insert(QuadRef::new(
            &event_uri,
            &pred_bookmark_dateadded(),
            &Literal::from(get_epoch_ms()),
            &bnode,
        ));
        Ok(())
    }

    fn get_bookmarks(
        &self,
    ) -> Result<Vec<(NamedNode, EventId, String)>, RdfStoreError> {
        let subs = [(
            Variable::new("root").unwrap(),
            Term::NamedNode(bookmarks_root()),
        )];

        let results = self
            .run_query(&BQUERY.to_string(), subs, None)
            .map_err(|_| RdfStoreError::QueryError)?;

        let mut v = Vec::new();

        for row in &results.rows {
            let event = row.get("event").unwrap();
            let title = row.get("title").unwrap();

            let event_uri = event
                .value
                .to_named_node()
                .map_err(|_| RdfStoreError::NamedNodeError)?;
            let evid = evuri_to_eventid(event_uri.clone())
                .map_err(|_| RdfStoreError::URNError)
                .unwrap();

            v.push((event_uri.clone(), evid, title.to_string()));
        }

        Ok(v)
    }
}