Radish alpha
r
rad:z4D5UCArafTzTQpDZNQRuqswh3ury
Radicle desktop app
Radicle
Git
radicle-desktop crates radicle-types src traits patch.rs
use std::collections::BTreeSet;

use radicle::cob::Title;
use radicle::node::device::BoxedSigner;
use radicle::node::Handle;
use radicle::patch::cache::Patches as _;
use radicle::storage::ReadStorage;
use radicle::{cob, git, identity, Node};

use crate::cobs;
use crate::domain::patch::models;
use crate::error::Error;
use crate::traits::Profile;

pub trait Patches: Profile {
    fn get_patch(
        &self,
        rid: identity::RepoId,
        id: git::Oid,
    ) -> Result<Option<models::patch::Patch>, Error> {
        let profile = self.profile();
        let repo = profile.storage.repository(rid)?;
        let patches = profile.patches(&repo)?;
        let patch = patches.get(&id.into())?;
        let aliases = &profile.aliases();
        let patches = patch.map(|patch| models::patch::Patch::new(id.into(), &patch, aliases));

        Ok::<_, Error>(patches)
    }

    fn revisions_by_patch(
        &self,
        rid: identity::RepoId,
        id: git::Oid,
    ) -> Result<Option<Vec<models::patch::Revision>>, Error> {
        let profile = self.profile();
        let repo = profile.storage.repository(rid)?;
        let patches = profile.patches(&repo)?;
        let revisions = patches.get(&id.into())?.map(|patch| {
            let aliases = &profile.aliases();

            patch
                .revisions()
                .map(|(_, r)| models::patch::Revision::new(r.clone(), aliases))
                .collect::<Vec<_>>()
        });

        Ok::<_, Error>(revisions)
    }

    fn revision_by_id(
        &self,
        rid: identity::RepoId,
        id: git::Oid,
        revision_id: git::Oid,
    ) -> Result<Option<models::patch::Revision>, Error> {
        let profile = self.profile();
        let repo = profile.storage.repository(rid)?;
        let patches = profile.patches(&repo)?;
        let revision = patches.get(&id.into())?.and_then(|patch| {
            let aliases = &profile.aliases();

            patch
                .revision(&revision_id.into())
                .map(|r| models::patch::Revision::new(r.clone(), aliases))
        });

        Ok::<_, Error>(revision)
    }

    fn review_by_id(
        &self,
        rid: identity::RepoId,
        id: git::Oid,
        revision_id: git::Oid,
        review_id: cob::patch::ReviewId,
    ) -> Result<Option<models::patch::Review>, Error> {
        let profile = self.profile();
        let repo = profile.storage.repository(rid)?;
        let patches = profile.patches(&repo)?;
        let review = patches.get(&id.into())?.and_then(|patch| {
            let aliases = &profile.aliases();

            patch
                .reviews_of(revision_id.into())
                .find(|(id, _)| *id == &review_id)
                .map(|(_, review)| models::patch::Review::new(review.clone(), aliases))
        });

        Ok::<_, Error>(review)
    }
}

pub trait PatchesMut: Profile {
    fn edit_patch(
        &self,
        rid: identity::RepoId,
        cob_id: git::Oid,
        action: models::patch::Action,
        opts: cobs::CobOptions,
    ) -> Result<models::patch::Patch, Error> {
        let profile = self.profile();
        let mut node = Node::new(profile.socket());
        let repo = profile.storage.repository(rid)?;
        let signer = profile.signer()?;
        let aliases = profile.aliases();
        let mut patches = profile.patches_mut(&repo)?;
        let mut patch = patches.get_mut(&cob_id.into())?;

        match action {
            models::patch::Action::RevisionEdit {
                revision,
                description,
                embeds,
            } => {
                patch.edit_revision(
                    revision,
                    description,
                    embeds.into_iter().map(Into::into).collect::<Vec<_>>(),
                    &signer,
                )?;
            }
            models::patch::Action::RevisionCommentRedact { revision, comment } => {
                patch.comment_redact(revision, comment, &signer)?;
            }
            models::patch::Action::ReviewCommentRedact { review, comment } => {
                patch.redact_review_comment(review, comment, &signer)?;
            }
            models::patch::Action::ReviewCommentReact {
                review,
                comment,
                reaction,
                active,
            } => {
                patch.react_review_comment(review, comment, reaction, active, &signer)?;
            }
            models::patch::Action::ReviewCommentResolve { review, comment } => {
                patch.resolve_review_comment(review, comment, &signer)?;
            }
            models::patch::Action::ReviewCommentUnresolve { review, comment } => {
                patch.unresolve_review_comment(review, comment, &signer)?;
            }
            models::patch::Action::Edit { title, target } => {
                patch.edit::<BoxedSigner, String>(Title::try_from(title)?, target, &signer)?;
            }
            models::patch::Action::ReviewEdit {
                review,
                summary,
                verdict,
                labels,
                embeds,
            } => {
                patch.review_edit(
                    review,
                    verdict.map(|v| v.into()),
                    summary.unwrap_or_default(),
                    labels,
                    embeds
                        .unwrap_or_default()
                        .into_iter()
                        .map(Into::into)
                        .collect::<Vec<_>>(),
                    &signer,
                )?;
            }
            models::patch::Action::ReviewReact {
                review,
                reaction,
                active,
            } => {
                patch.review_react(review, reaction, active, &signer)?;
            }
            models::patch::Action::Review {
                revision,
                summary,
                verdict,
                labels,
            } => {
                patch.review(
                    revision,
                    verdict.map(|v| v.into()),
                    summary,
                    labels,
                    &signer,
                )?;
            }
            models::patch::Action::ReviewRedact { review } => {
                patch.redact_review(review, &signer)?;
            }
            models::patch::Action::ReviewComment {
                review,
                body,
                location,
                reply_to,
                embeds,
            } => {
                patch.review_comment(
                    review,
                    body,
                    location.map(|l| l.into()),
                    reply_to,
                    embeds.into_iter().map(Into::into).collect::<Vec<_>>(),
                    &signer,
                )?;
            }
            models::patch::Action::ReviewCommentEdit {
                review,
                comment,
                body,
                embeds,
            } => {
                patch.edit_review_comment(
                    review,
                    comment,
                    body,
                    embeds.into_iter().map(Into::into).collect::<Vec<_>>(),
                    &signer,
                )?;
            }
            models::patch::Action::Lifecycle { state } => {
                patch.lifecycle(state, &signer)?;
            }
            models::patch::Action::Assign { assignees } => {
                patch.assign(
                    assignees.iter().map(|a| *a.did()).collect::<BTreeSet<_>>(),
                    &signer,
                )?;
            }
            models::patch::Action::Label { labels } => {
                patch.label(labels, &signer)?;
            }
            models::patch::Action::RevisionReact {
                revision,
                reaction,
                location,
                active,
            } => {
                patch.react(
                    revision,
                    reaction,
                    location.map(|l| l.into()),
                    active,
                    &signer,
                )?;
            }
            models::patch::Action::RevisionComment {
                revision,
                location,
                body,
                reply_to,
                embeds,
            } => {
                patch.comment(
                    revision,
                    body,
                    reply_to,
                    location.map(|l| l.into()),
                    embeds.into_iter().map(Into::into).collect::<Vec<_>>(),
                    &signer,
                )?;
            }
            models::patch::Action::RevisionCommentEdit {
                revision,
                comment,
                body,
                embeds,
            } => {
                patch.comment_edit(
                    revision,
                    comment,
                    body,
                    embeds.into_iter().map(Into::into).collect::<Vec<_>>(),
                    &signer,
                )?;
            }
            models::patch::Action::RevisionCommentReact {
                revision,
                comment,
                reaction,
                active,
            } => {
                patch.comment_react(revision, comment, reaction, active, &signer)?;
            }
            models::patch::Action::RevisionRedact { revision } => {
                patch.redact(revision, &signer)?;
            }
            models::patch::Action::Merge { .. } => {
                unimplemented!("We don't support merging of patches through the desktop")
            }
            models::patch::Action::Revision { .. } => {
                unimplemented!("We don't support creating new revisions through the desktop")
            }
        }

        if opts.announce() {
            if let Err(e) = node.announce_refs_for(rid, [profile.public_key]) {
                log::error!("Not able to announce changes: {}", e)
            }
        }

        Ok::<_, Error>(models::patch::Patch::new(*patch.id(), &patch, &aliases))
    }
}