radicle-feed 0.5.3

A feed service for Radicle
Documentation
use radicle::cob::{Op, TypeName};
use radicle::node::AliasStore;
use radicle::profile::Aliases;
use snafu::ResultExt;

use crate::feed::cobs;
use crate::models::entry::OperationEntry;
use crate::storage::FeedStorage;

// Builds OperationEntry objects
pub struct OperationBuilder<'a> {
    aliases: &'a Aliases,
    rid: &'a radicle::prelude::RepoId,
    typename: &'a TypeName,
    repo_alias: Option<String>,
}

impl<'a> OperationBuilder<'a> {
    pub fn new(
        aliases: &'a Aliases,
        rid: &'a radicle::prelude::RepoId,
        typename: &'a TypeName,
        repo_alias: Option<String>,
    ) -> Self {
        Self {
            aliases,
            rid,
            typename,
            repo_alias,
        }
    }

    pub fn build_operations<A, S: FeedStorage>(
        &self,
        stream_entries: Vec<Op<A>>,
        mut last_processed_id: Option<radicle::git::Oid>,
        storage: &mut S,
    ) -> Result<(Vec<OperationEntry>, Option<radicle::git::Oid>), snafu::Whatever>
    where
        A: serde::Serialize + for<'de> serde::Deserialize<'de> + Clone,
    {
        let mut operations = Vec::new();
        let mut state_accumulator: cobs::CobStateAccumulator = match last_processed_id {
            Some(ref last) => {
                let last_operation = storage
                    .get_operation_by_id(last)
                    .whatever_context("context")?;

                last_operation.into()
            }
            None => cobs::CobStateAccumulator::new(),
        };

        for stream_entry in stream_entries {
            // Update state from actions
            for action in &stream_entry.actions {
                let action_json = serde_json::to_value(action.clone())
                    .whatever_context("Failed to serialize action")?;
                state_accumulator.update_from_action(&action_json);
            }

            // Skip if operation already exists
            if storage
                .operation_exists(&stream_entry.id())
                .whatever_context("Unable to query for existing operation")?
            {
                tracing::debug!("Operation already exists in storage, skipping");
                continue;
            }

            operations.push(OperationEntry {
                operation_id: stream_entry.id().to_string(),
                rid: self.rid.to_string(),
                cob_title: state_accumulator.title(),
                cob_status: state_accumulator.state(),
                repo_alias: self.repo_alias.clone(),
                created_at: stream_entry.timestamp.as_secs() as i32,
                actions: stream_entry
                    .actions
                    .clone()
                    .into_iter()
                    .filter_map(|action| serde_json::to_value(action).ok())
                    .collect(),
                author: stream_entry.author.to_string(),
                author_alias: self.aliases.alias(&stream_entry.author).map(Into::into),
                typename: self.typename.to_string(),
            });

            last_processed_id = Some(stream_entry.id());
        }

        Ok((operations, last_processed_id))
    }
}