Radish alpha
r
Radicle terminal user interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
lib: Move out COB modules
Erik Kundt committed 1 year ago
commit eca3b296c32a3af4f6b05a3479e9fece84f216a1
parent cc849ccc3182a202aadf393e80ba8b64cf2d67a4
17 files changed +380 -553
added bin/cob.rs
@@ -0,0 +1,40 @@
+
use std::str::FromStr;
+

+
use anyhow::Result;
+

+
use radicle::cob::Label;
+
use radicle::prelude::Did;
+

+
pub mod inbox;
+
pub mod issue;
+
pub mod patch;
+

+
#[allow(dead_code)]
+
pub fn parse_labels(input: String) -> Result<Vec<Label>> {
+
    let mut labels = vec![];
+
    if !input.is_empty() {
+
        for name in input.split(',') {
+
            match Label::new(name.trim()) {
+
                Ok(label) => labels.push(label),
+
                Err(err) => return Err(anyhow::anyhow!(err).context("Can't parse labels.")),
+
            }
+
        }
+
    }
+

+
    Ok(labels)
+
}
+

+
#[allow(dead_code)]
+
pub fn parse_assignees(input: String) -> Result<Vec<Did>> {
+
    let mut assignees = vec![];
+
    if !input.is_empty() {
+
        for did in input.split(',') {
+
            match Did::from_str(&format!("did:key:{}", did)) {
+
                Ok(did) => assignees.push(did),
+
                Err(err) => return Err(anyhow::anyhow!(err).context("Can't parse assignees.")),
+
            }
+
        }
+
    }
+

+
    Ok(assignees)
+
}
added bin/cob/inbox.rs
@@ -0,0 +1,38 @@
+
use anyhow::Result;
+

+
use radicle::node::notifications::Notification;
+
use radicle::storage::git::Repository;
+
use radicle::Profile;
+

+
#[derive(Clone, Default, Debug, Eq, PartialEq)]
+
pub struct Filter {}
+

+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+
pub struct SortBy {
+
    pub reverse: bool,
+
    pub field: &'static str,
+
}
+

+
impl Default for SortBy {
+
    fn default() -> Self {
+
        Self {
+
            reverse: true,
+
            field: "timestamp",
+
        }
+
    }
+
}
+

+
pub fn all(repository: &Repository, profile: &Profile) -> Result<Vec<Notification>> {
+
    let all = profile
+
        .notifications_mut()?
+
        .by_repo(&repository.id, "timestamp")?
+
        .collect::<Vec<_>>();
+

+
    let mut notifications = vec![];
+
    for n in all {
+
        let n = n?;
+
        notifications.push(n);
+
    }
+

+
    Ok(notifications)
+
}
added bin/cob/issue.rs
@@ -0,0 +1,151 @@
+
use anyhow::Result;
+

+
use radicle::cob::issue::{Issue, IssueId};
+
use radicle::cob::Label;
+
use radicle::issue::cache::Issues;
+
use radicle::issue::State;
+
use radicle::prelude::{Did, Signer};
+
use radicle::storage::git::Repository;
+
use radicle::Profile;
+

+
#[derive(Clone, Debug, Eq, PartialEq)]
+
pub struct Filter {
+
    state: Option<State>,
+
    assigned: bool,
+
    assignees: Vec<Did>,
+
}
+

+
impl Default for Filter {
+
    fn default() -> Self {
+
        Self {
+
            state: Some(State::default()),
+
            assigned: false,
+
            assignees: vec![],
+
        }
+
    }
+
}
+

+
impl Filter {
+
    pub fn with_state(mut self, state: Option<State>) -> Self {
+
        self.state = state;
+
        self
+
    }
+

+
    pub fn with_assgined(mut self, assigned: bool) -> Self {
+
        self.assigned = assigned;
+
        self
+
    }
+

+
    pub fn with_assginee(mut self, assignee: Did) -> Self {
+
        self.assignees.push(assignee);
+
        self
+
    }
+
}
+

+
impl ToString for Filter {
+
    fn to_string(&self) -> String {
+
        let mut filter = String::new();
+

+
        if let Some(state) = &self.state {
+
            filter.push_str(&format!("is:{}", state));
+
            filter.push(' ');
+
        }
+
        if self.assigned {
+
            filter.push_str("is:assigned");
+
            filter.push(' ');
+
        }
+
        if !self.assignees.is_empty() {
+
            filter.push_str("assignees:");
+
            filter.push('[');
+

+
            let mut assignees = self.assignees.iter().peekable();
+
            while let Some(assignee) = assignees.next() {
+
                filter.push_str(&assignee.encode());
+

+
                if assignees.peek().is_some() {
+
                    filter.push(',');
+
                }
+
            }
+
            filter.push(']');
+
        }
+

+
        filter
+
    }
+
}
+

+
pub fn all(profile: &Profile, repository: &Repository) -> Result<Vec<(IssueId, Issue)>> {
+
    let cache = profile.issues(repository)?;
+
    let issues = cache.list()?;
+

+
    Ok(issues.flatten().collect())
+
}
+

+
#[allow(dead_code)]
+
pub fn find(profile: &Profile, repository: &Repository, id: &IssueId) -> Result<Option<Issue>> {
+
    let cache = profile.issues(repository)?;
+
    Ok(cache.get(id)?)
+
}
+

+
#[allow(dead_code)]
+
pub fn create<G: Signer>(
+
    profile: &Profile,
+
    repository: &Repository,
+
    signer: &G,
+
    title: String,
+
    description: String,
+
    labels: &[Label],
+
    assignees: &[Did],
+
) -> Result<IssueId> {
+
    let mut issues = profile.issues_mut(repository)?;
+
    let issue = issues.create(title, description.trim(), labels, assignees, [], signer)?;
+

+
    Ok(*issue.id())
+
}
+

+
#[cfg(test)]
+
mod tests {
+
    use std::str::FromStr;
+

+
    use anyhow::Result;
+
    use radicle::issue;
+

+
    use super::*;
+

+
    #[test]
+
    fn issue_filter_display_with_state_should_succeed() -> Result<()> {
+
        let actual = Filter::default().with_state(Some(issue::State::Open));
+

+
        assert_eq!(String::from("is:open "), actual.to_string());
+

+
        Ok(())
+
    }
+

+
    #[test]
+
    fn issue_filter_display_with_state_and_assigned_should_succeed() -> Result<()> {
+
        let actual = Filter::default()
+
            .with_state(Some(issue::State::Open))
+
            .with_assgined(true);
+

+
        assert_eq!(String::from("is:open is:assigned "), actual.to_string());
+

+
        Ok(())
+
    }
+

+
    #[test]
+
    fn issue_filter_display_with_status_and_author_should_succeed() -> Result<()> {
+
        let actual = Filter::default()
+
            .with_state(Some(issue::State::Open))
+
            .with_assginee(Did::from_str(
+
                "did:key:z6MkswQE8gwZw924amKatxnNCXA55BMupMmRg7LvJuim2C1V",
+
            )?);
+

+
        assert_eq!(
+
            String::from(
+
                "is:open assignees:[did:key:z6MkswQE8gwZw924amKatxnNCXA55BMupMmRg7LvJuim2C1V]"
+
            ),
+
            actual.to_string()
+
        );
+

+
        Ok(())
+
    }
+
}
added bin/cob/patch.rs
@@ -0,0 +1,134 @@
+
use anyhow::Result;
+

+
use radicle::cob::patch::{Patch, PatchId};
+
use radicle::identity::Did;
+
use radicle::patch::cache::Patches;
+
use radicle::patch::Status;
+
use radicle::storage::git::Repository;
+
use radicle::Profile;
+

+
#[derive(Clone, Debug, Eq, PartialEq)]
+
pub struct Filter {
+
    status: Option<Status>,
+
    authored: bool,
+
    authors: Vec<Did>,
+
}
+

+
impl Default for Filter {
+
    fn default() -> Self {
+
        Self {
+
            status: Some(Status::default()),
+
            authored: false,
+
            authors: vec![],
+
        }
+
    }
+
}
+

+
impl Filter {
+
    pub fn with_status(mut self, status: Option<Status>) -> Self {
+
        self.status = status;
+
        self
+
    }
+

+
    pub fn with_authored(mut self, authored: bool) -> Self {
+
        self.authored = authored;
+
        self
+
    }
+

+
    pub fn with_author(mut self, author: Did) -> Self {
+
        self.authors.push(author);
+
        self
+
    }
+
}
+

+
impl ToString for Filter {
+
    fn to_string(&self) -> String {
+
        let mut filter = String::new();
+

+
        if let Some(state) = &self.status {
+
            filter.push_str(&format!("is:{}", state));
+
            filter.push(' ');
+
        }
+
        if self.authored {
+
            filter.push_str("is:authored");
+
            filter.push(' ');
+
        }
+
        if !self.authors.is_empty() {
+
            filter.push_str("authors:");
+
            filter.push('[');
+

+
            let mut authors = self.authors.iter().peekable();
+
            while let Some(author) = authors.next() {
+
                filter.push_str(&author.encode());
+

+
                if authors.peek().is_some() {
+
                    filter.push(',');
+
                }
+
            }
+
            filter.push(']');
+
        }
+

+
        filter
+
    }
+
}
+

+
pub fn all(profile: &Profile, repository: &Repository) -> Result<Vec<(PatchId, Patch)>> {
+
    let cache = profile.patches(repository)?;
+
    let patches = cache.list()?;
+

+
    Ok(patches.flatten().collect())
+
}
+

+
#[allow(dead_code)]
+
pub fn find(profile: &Profile, repository: &Repository, id: &PatchId) -> Result<Option<Patch>> {
+
    let cache = profile.patches(repository)?;
+
    Ok(cache.get(id)?)
+
}
+

+
#[cfg(test)]
+
mod tests {
+
    use std::str::FromStr;
+

+
    use anyhow::Result;
+
    use radicle::patch;
+

+
    use super::*;
+

+
    #[test]
+
    fn patch_filter_display_with_status_should_succeed() -> Result<()> {
+
        let actual = Filter::default().with_status(Some(patch::Status::Open));
+

+
        assert_eq!(String::from("is:open "), actual.to_string());
+

+
        Ok(())
+
    }
+

+
    #[test]
+
    fn patch_filter_display_with_status_and_authored_should_succeed() -> Result<()> {
+
        let actual = Filter::default()
+
            .with_status(Some(patch::Status::Open))
+
            .with_authored(true);
+

+
        assert_eq!(String::from("is:open is:authored "), actual.to_string());
+

+
        Ok(())
+
    }
+

+
    #[test]
+
    fn patch_filter_display_with_status_and_author_should_succeed() -> Result<()> {
+
        let actual = Filter::default()
+
            .with_status(Some(patch::Status::Open))
+
            .with_author(Did::from_str(
+
                "did:key:z6MkswQE8gwZw924amKatxnNCXA55BMupMmRg7LvJuim2C1V",
+
            )?);
+

+
        assert_eq!(
+
            String::from(
+
                "is:open authors:[did:key:z6MkswQE8gwZw924amKatxnNCXA55BMupMmRg7LvJuim2C1V]"
+
            ),
+
            actual.to_string()
+
        );
+

+
        Ok(())
+
    }
+
}
modified bin/commands/inbox.rs
@@ -9,13 +9,13 @@ use anyhow::anyhow;

use radicle_tui as tui;

-
use tui::cob::inbox::{self};
-

use radicle_cli::terminal;
use radicle_cli::terminal::{Args, Error, Help};

use self::common::{Mode, RepositoryMode, SelectionMode};

+
use crate::cob::inbox;
+

pub const HELP: Help = Help {
    name: "inbox",
    description: "Terminal interfaces for notifications",
modified bin/commands/inbox/select.rs
@@ -22,7 +22,6 @@ use radicle::Profile;

use radicle_tui as tui;

-
use tui::cob::inbox;
use tui::store;
use tui::store::StateValue;
use tui::ui::items::{Filter, NotificationItem, NotificationItemFilter};
@@ -33,6 +32,8 @@ use tui::ui::widget::window::{Page, PageProps, Shortcuts, ShortcutsProps, Window
use tui::ui::widget::{ToWidget, Widget};
use tui::{BoxedAny, Channel, Exit, PageStack};

+
use crate::cob::inbox;
+

use self::ui::Browser;
use self::ui::BrowserProps;

modified bin/commands/issue.rs
@@ -15,9 +15,10 @@ use radicle_cli::terminal::{Args, Error, Help};

use radicle_tui as tui;

-
use tui::cob;
use tui::log;

+
use crate::cob;
+

pub const HELP: Help = Help {
    name: "issue",
    description: "Terminal interfaces for issues",
modified bin/commands/issue/select.rs
@@ -17,7 +17,6 @@ use radicle::Profile;

use radicle_tui as tui;

-
use tui::cob::issue;
use tui::store;
use tui::store::StateValue;
use tui::ui::items::{Filter, IssueItem, IssueItemFilter};
@@ -29,6 +28,8 @@ use tui::ui::widget::{ToWidget, Widget};

use tui::{BoxedAny, Channel, Exit, PageStack};

+
use crate::cob::issue;
+

use self::ui::{Browser, BrowserProps};

use super::common::Mode;
modified bin/commands/patch.rs
@@ -9,13 +9,16 @@ use anyhow::anyhow;

use radicle::identity::RepoId;
use radicle::patch::Status;
+

+
use radicle_cli::terminal;
+
use radicle_cli::terminal::args::{Args, Error, Help};
+

use radicle_tui as tui;

-
use tui::cob::patch::{self, Filter};
use tui::log;

-
use radicle_cli::terminal;
-
use radicle_cli::terminal::args::{Args, Error, Help};
+
use crate::cob::patch;
+
use crate::cob::patch::Filter;

pub const HELP: Help = Help {
    name: "patch",
modified bin/commands/patch/select.rs
@@ -16,7 +16,6 @@ use ratatui::style::Stylize;
use ratatui::text::{Line, Span, Text};

use termion::event::Key;
-
use tui::cob::patch;
use tui::store;
use tui::ui::items::{Filter, PatchItem, PatchItemFilter};
use tui::ui::span;
@@ -31,6 +30,8 @@ use self::ui::{Browser, BrowserProps};

use super::common::Mode;

+
use crate::cob::patch;
+

type Selection = tui::Selection<PatchId>;

pub struct Context {
modified bin/main.rs
@@ -1,3 +1,4 @@
+
mod cob;
mod commands;

use std::ffi::OsString;
deleted src/cob.rs
@@ -1,38 +0,0 @@
-
use std::str::FromStr;
-

-
use anyhow::Result;
-

-
use radicle::cob::Label;
-
use radicle::prelude::Did;
-

-
pub mod inbox;
-
pub mod issue;
-
pub mod patch;
-

-
pub fn parse_labels(input: String) -> Result<Vec<Label>> {
-
    let mut labels = vec![];
-
    if !input.is_empty() {
-
        for name in input.split(',') {
-
            match Label::new(name.trim()) {
-
                Ok(label) => labels.push(label),
-
                Err(err) => return Err(anyhow::anyhow!(err).context("Can't parse labels.")),
-
            }
-
        }
-
    }
-

-
    Ok(labels)
-
}
-

-
pub fn parse_assignees(input: String) -> Result<Vec<Did>> {
-
    let mut assignees = vec![];
-
    if !input.is_empty() {
-
        for did in input.split(',') {
-
            match Did::from_str(&format!("did:key:{}", did)) {
-
                Ok(did) => assignees.push(did),
-
                Err(err) => return Err(anyhow::anyhow!(err).context("Can't parse assignees.")),
-
            }
-
        }
-
    }
-

-
    Ok(assignees)
-
}
deleted src/cob/inbox.rs
@@ -1,38 +0,0 @@
-
use anyhow::Result;
-

-
use radicle::node::notifications::Notification;
-
use radicle::storage::git::Repository;
-
use radicle::Profile;
-

-
#[derive(Clone, Default, Debug, Eq, PartialEq)]
-
pub struct Filter {}
-

-
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-
pub struct SortBy {
-
    pub reverse: bool,
-
    pub field: &'static str,
-
}
-

-
impl Default for SortBy {
-
    fn default() -> Self {
-
        Self {
-
            reverse: true,
-
            field: "timestamp",
-
        }
-
    }
-
}
-

-
pub fn all(repository: &Repository, profile: &Profile) -> Result<Vec<Notification>> {
-
    let all = profile
-
        .notifications_mut()?
-
        .by_repo(&repository.id, "timestamp")?
-
        .collect::<Vec<_>>();
-

-
    let mut notifications = vec![];
-
    for n in all {
-
        let n = n?;
-
        notifications.push(n);
-
    }
-

-
    Ok(notifications)
-
}
deleted src/cob/issue.rs
@@ -1,149 +0,0 @@
-
use anyhow::Result;
-

-
use radicle::cob::issue::{Issue, IssueId};
-
use radicle::cob::Label;
-
use radicle::issue::cache::Issues;
-
use radicle::issue::State;
-
use radicle::prelude::{Did, Signer};
-
use radicle::storage::git::Repository;
-
use radicle::Profile;
-

-
#[derive(Clone, Debug, Eq, PartialEq)]
-
pub struct Filter {
-
    state: Option<State>,
-
    assigned: bool,
-
    assignees: Vec<Did>,
-
}
-

-
impl Default for Filter {
-
    fn default() -> Self {
-
        Self {
-
            state: Some(State::default()),
-
            assigned: false,
-
            assignees: vec![],
-
        }
-
    }
-
}
-

-
impl Filter {
-
    pub fn with_state(mut self, state: Option<State>) -> Self {
-
        self.state = state;
-
        self
-
    }
-

-
    pub fn with_assgined(mut self, assigned: bool) -> Self {
-
        self.assigned = assigned;
-
        self
-
    }
-

-
    pub fn with_assginee(mut self, assignee: Did) -> Self {
-
        self.assignees.push(assignee);
-
        self
-
    }
-
}
-

-
impl ToString for Filter {
-
    fn to_string(&self) -> String {
-
        let mut filter = String::new();
-

-
        if let Some(state) = &self.state {
-
            filter.push_str(&format!("is:{}", state));
-
            filter.push(' ');
-
        }
-
        if self.assigned {
-
            filter.push_str("is:assigned");
-
            filter.push(' ');
-
        }
-
        if !self.assignees.is_empty() {
-
            filter.push_str("assignees:");
-
            filter.push('[');
-

-
            let mut assignees = self.assignees.iter().peekable();
-
            while let Some(assignee) = assignees.next() {
-
                filter.push_str(&assignee.encode());
-

-
                if assignees.peek().is_some() {
-
                    filter.push(',');
-
                }
-
            }
-
            filter.push(']');
-
        }
-

-
        filter
-
    }
-
}
-

-
pub fn all(profile: &Profile, repository: &Repository) -> Result<Vec<(IssueId, Issue)>> {
-
    let cache = profile.issues(repository)?;
-
    let issues = cache.list()?;
-

-
    Ok(issues.flatten().collect())
-
}
-

-
pub fn find(profile: &Profile, repository: &Repository, id: &IssueId) -> Result<Option<Issue>> {
-
    let cache = profile.issues(repository)?;
-
    Ok(cache.get(id)?)
-
}
-

-
pub fn create<G: Signer>(
-
    profile: &Profile,
-
    repository: &Repository,
-
    signer: &G,
-
    title: String,
-
    description: String,
-
    labels: &[Label],
-
    assignees: &[Did],
-
) -> Result<IssueId> {
-
    let mut issues = profile.issues_mut(repository)?;
-
    let issue = issues.create(title, description.trim(), labels, assignees, [], signer)?;
-

-
    Ok(*issue.id())
-
}
-

-
#[cfg(test)]
-
mod tests {
-
    use std::str::FromStr;
-

-
    use anyhow::Result;
-
    use radicle::issue;
-

-
    use super::*;
-

-
    #[test]
-
    fn issue_filter_display_with_state_should_succeed() -> Result<()> {
-
        let actual = Filter::default().with_state(Some(issue::State::Open));
-

-
        assert_eq!(String::from("is:open "), actual.to_string());
-

-
        Ok(())
-
    }
-

-
    #[test]
-
    fn issue_filter_display_with_state_and_assigned_should_succeed() -> Result<()> {
-
        let actual = Filter::default()
-
            .with_state(Some(issue::State::Open))
-
            .with_assgined(true);
-

-
        assert_eq!(String::from("is:open is:assigned "), actual.to_string());
-

-
        Ok(())
-
    }
-

-
    #[test]
-
    fn issue_filter_display_with_status_and_author_should_succeed() -> Result<()> {
-
        let actual = Filter::default()
-
            .with_state(Some(issue::State::Open))
-
            .with_assginee(Did::from_str(
-
                "did:key:z6MkswQE8gwZw924amKatxnNCXA55BMupMmRg7LvJuim2C1V",
-
            )?);
-

-
        assert_eq!(
-
            String::from(
-
                "is:open assignees:[did:key:z6MkswQE8gwZw924amKatxnNCXA55BMupMmRg7LvJuim2C1V]"
-
            ),
-
            actual.to_string()
-
        );
-

-
        Ok(())
-
    }
-
}
deleted src/cob/patch.rs
@@ -1,133 +0,0 @@
-
use anyhow::Result;
-

-
use radicle::cob::patch::{Patch, PatchId};
-
use radicle::identity::Did;
-
use radicle::patch::cache::Patches;
-
use radicle::patch::Status;
-
use radicle::storage::git::Repository;
-
use radicle::Profile;
-

-
#[derive(Clone, Debug, Eq, PartialEq)]
-
pub struct Filter {
-
    status: Option<Status>,
-
    authored: bool,
-
    authors: Vec<Did>,
-
}
-

-
impl Default for Filter {
-
    fn default() -> Self {
-
        Self {
-
            status: Some(Status::default()),
-
            authored: false,
-
            authors: vec![],
-
        }
-
    }
-
}
-

-
impl Filter {
-
    pub fn with_status(mut self, status: Option<Status>) -> Self {
-
        self.status = status;
-
        self
-
    }
-

-
    pub fn with_authored(mut self, authored: bool) -> Self {
-
        self.authored = authored;
-
        self
-
    }
-

-
    pub fn with_author(mut self, author: Did) -> Self {
-
        self.authors.push(author);
-
        self
-
    }
-
}
-

-
impl ToString for Filter {
-
    fn to_string(&self) -> String {
-
        let mut filter = String::new();
-

-
        if let Some(state) = &self.status {
-
            filter.push_str(&format!("is:{}", state));
-
            filter.push(' ');
-
        }
-
        if self.authored {
-
            filter.push_str("is:authored");
-
            filter.push(' ');
-
        }
-
        if !self.authors.is_empty() {
-
            filter.push_str("authors:");
-
            filter.push('[');
-

-
            let mut authors = self.authors.iter().peekable();
-
            while let Some(author) = authors.next() {
-
                filter.push_str(&author.encode());
-

-
                if authors.peek().is_some() {
-
                    filter.push(',');
-
                }
-
            }
-
            filter.push(']');
-
        }
-

-
        filter
-
    }
-
}
-

-
pub fn all(profile: &Profile, repository: &Repository) -> Result<Vec<(PatchId, Patch)>> {
-
    let cache = profile.patches(repository)?;
-
    let patches = cache.list()?;
-

-
    Ok(patches.flatten().collect())
-
}
-

-
pub fn find(profile: &Profile, repository: &Repository, id: &PatchId) -> Result<Option<Patch>> {
-
    let cache = profile.patches(repository)?;
-
    Ok(cache.get(id)?)
-
}
-

-
#[cfg(test)]
-
mod tests {
-
    use std::str::FromStr;
-

-
    use anyhow::Result;
-
    use radicle::patch;
-

-
    use super::*;
-

-
    #[test]
-
    fn patch_filter_display_with_status_should_succeed() -> Result<()> {
-
        let actual = Filter::default().with_status(Some(patch::Status::Open));
-

-
        assert_eq!(String::from("is:open "), actual.to_string());
-

-
        Ok(())
-
    }
-

-
    #[test]
-
    fn patch_filter_display_with_status_and_authored_should_succeed() -> Result<()> {
-
        let actual = Filter::default()
-
            .with_status(Some(patch::Status::Open))
-
            .with_authored(true);
-

-
        assert_eq!(String::from("is:open is:authored "), actual.to_string());
-

-
        Ok(())
-
    }
-

-
    #[test]
-
    fn patch_filter_display_with_status_and_author_should_succeed() -> Result<()> {
-
        let actual = Filter::default()
-
            .with_status(Some(patch::Status::Open))
-
            .with_author(Did::from_str(
-
                "did:key:z6MkswQE8gwZw924amKatxnNCXA55BMupMmRg7LvJuim2C1V",
-
            )?);
-

-
        assert_eq!(
-
            String::from(
-
                "is:open authors:[did:key:z6MkswQE8gwZw924amKatxnNCXA55BMupMmRg7LvJuim2C1V]"
-
            ),
-
            actual.to_string()
-
        );
-

-
        Ok(())
-
    }
-
}
deleted src/context.rs
@@ -1,184 +0,0 @@
-
use std::fmt::Display;
-

-
use radicle::cob::issue::{Issue, IssueId};
-
use radicle::cob::patch::{Patch, PatchId};
-
use radicle::crypto::ssh::keystore::MemorySigner;
-
use radicle::crypto::Signer;
-
use radicle::identity::{Project, RepoId};
-
use radicle::node::notifications::Notification;
-
use radicle::profile::env::RAD_PASSPHRASE;
-
use radicle::storage::git::Repository;
-
use radicle::storage::{ReadRepository, ReadStorage};
-
use radicle::Profile;
-

-
use radicle_cli::terminal::io::PassphraseValidator;
-
use radicle_term as term;
-
use term::{passphrase, spinner};
-

-
use super::cob::inbox;
-

-
/// Git revision parameter. Supports extended SHA-1 syntax.
-
#[derive(Debug, Clone, PartialEq, Eq)]
-

-
pub struct Rev(String);
-

-
impl From<String> for Rev {
-
    fn from(value: String) -> Self {
-
        Rev(value)
-
    }
-
}
-

-
impl Display for Rev {
-
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-
        write!(f, "{}", self.0)
-
    }
-
}
-

-
/// Application context that holds all the project data that are
-
/// needed to render it.
-
pub struct Context {
-
    profile: Profile,
-
    rid: RepoId,
-
    project: Project,
-
    repository: Repository,
-
    issues: Option<Vec<(IssueId, Issue)>>,
-
    patches: Option<Vec<(PatchId, Patch)>>,
-
    notifications: Vec<Notification>,
-
    signer: Option<Box<dyn Signer>>,
-
}
-

-
impl Context {
-
    pub fn new(profile: Profile, rid: RepoId) -> Result<Self, anyhow::Error> {
-
        let repository = profile.storage.repository(rid).unwrap();
-
        let project = repository.identity_doc()?.project()?;
-
        let notifications = inbox::all(&repository, &profile)?;
-

-
        let issues = None;
-
        let patches = None;
-
        let signer = None;
-

-
        Ok(Self {
-
            profile,
-
            rid,
-
            project,
-
            repository,
-
            issues,
-
            patches,
-
            notifications,
-
            signer,
-
        })
-
    }
-

-
    pub fn with_issues(mut self) -> Self {
-
        use super::cob::issue;
-
        self.issues = Some(issue::all(&self.profile, &self.repository).unwrap_or_default());
-
        self
-
    }
-

-
    pub fn with_patches(mut self) -> Self {
-
        use super::cob::patch;
-
        self.patches = Some(patch::all(&self.profile, &self.repository).unwrap_or_default());
-
        self
-
    }
-

-
    pub fn with_signer(mut self) -> Self {
-
        self.signer = signer(&self.profile).ok();
-
        self
-
    }
-

-
    pub fn profile(&self) -> &Profile {
-
        &self.profile
-
    }
-

-
    pub fn rid(&self) -> &RepoId {
-
        &self.rid
-
    }
-

-
    pub fn project(&self) -> &Project {
-
        &self.project
-
    }
-

-
    pub fn repository(&self) -> &Repository {
-
        &self.repository
-
    }
-

-
    pub fn issues(&self) -> &Option<Vec<(IssueId, Issue)>> {
-
        &self.issues
-
    }
-

-
    pub fn patches(&self) -> &Option<Vec<(PatchId, Patch)>> {
-
        &self.patches
-
    }
-

-
    pub fn notifications(&self) -> &Vec<Notification> {
-
        &self.notifications
-
    }
-

-
    #[allow(clippy::borrowed_box)]
-
    pub fn signer(&self) -> &Option<Box<dyn Signer>> {
-
        &self.signer
-
    }
-

-
    pub fn reload_patches(&mut self) {
-
        use super::cob::patch;
-
        self.patches = Some(patch::all(&self.profile, &self.repository).unwrap_or_default());
-
    }
-

-
    pub fn reload_issues(&mut self) {
-
        use super::cob::issue;
-
        self.issues = Some(issue::all(&self.profile, &self.repository).unwrap_or_default());
-
    }
-
}
-

-
// /// Validates secret key passphrases.
-
// #[derive(Clone)]
-
// pub struct PassphraseValidator {
-
//     keystore: Keystore,
-
// }
-

-
// impl PassphraseValidator {
-
//     /// Create a new validator.
-
//     pub fn new(keystore: Keystore) -> Self {
-
//         Self { keystore }
-
//     }
-
// }
-

-
// impl inquire::validator::StringValidator for PassphraseValidator {
-
//     fn validate(
-
//         &self,
-
//         input: &str,
-
//     ) -> Result<validator::Validation, inquire::error::CustomUserError> {
-
//         let passphrase = Passphrase::from(input.to_owned());
-
//         if self.keystore.is_valid_passphrase(&passphrase)? {
-
//             Ok(validator::Validation::Valid)
-
//         } else {
-
//             Ok(validator::Validation::Invalid(
-
//                 validator::ErrorMessage::from("Invalid passphrase, please try again"),
-
//             ))
-
//         }
-
//     }
-
// }
-

-
/// Get the signer. First we try getting it from ssh-agent, otherwise we prompt the user,
-
/// if we're connected to a TTY.
-
pub fn signer(profile: &Profile) -> anyhow::Result<Box<dyn Signer>> {
-
    if let Ok(signer) = profile.signer() {
-
        return Ok(signer);
-
    }
-
    let validator = PassphraseValidator::new(profile.keystore.clone());
-
    let passphrase = match passphrase(validator) {
-
        Ok(p) => p,
-
        Err(inquire::InquireError::NotTTY) => {
-
            return Err(anyhow::anyhow!(
-
                "running in non-interactive mode, please set `{RAD_PASSPHRASE}` to unseal your key",
-
            ));
-
        }
-
        Err(e) => return Err(e.into()),
-
    };
-
    let spinner = spinner("Unsealing key...");
-
    let signer = MemorySigner::load(&profile.keystore, Some(passphrase))?;
-

-
    spinner.finish();
-

-
    Ok(signer.boxed())
-
}
modified src/lib.rs
@@ -1,5 +1,3 @@
-
pub mod cob;
-
pub mod context;
pub mod event;
pub mod git;
pub mod log;