use crate::review_backend::{models::PullRequest, DefaultReviewBackend, DynReviewBackend}; use comfy_table::{presets::UTF8_HORIZONTAL_ONLY, Cell, Row, Table}; #[cfg(test)] use mockall::{automock, mock, predicate::*}; pub struct Review { backend: DynReviewBackend, } impl Default for Review { fn default() -> Self { Self::new(std::sync::Arc::new(DefaultReviewBackend::new())) } } impl Review { fn new(backend: DynReviewBackend) -> Self { Self { backend } } // Workflow // 1. Fetch list of repos // 2. Present menu // 3. Choose begin quick review // 4. Present pr and use delta to view changes // 5. Approve, open, skip or quit // 6. Repeat from 4 fn run(&self, review_requested: Option) -> eyre::Result<()> { let prs = self.backend.get_prs(review_requested)?; let prs_table = Self::generate_prs_table(&prs); self.backend.present_prs(prs_table)?; Ok(()) } fn generate_prs_table(prs: &[PullRequest]) -> String { let mut table = Table::new(); let table = table .load_preset(UTF8_HORIZONTAL_ONLY) .set_content_arrangement(comfy_table::ContentArrangement::Dynamic) .set_header(vec![ Cell::new("repo"), Cell::new("title"), Cell::new("number"), ]) .add_rows(prs.iter().map(|pr| { let pr = pr.clone(); vec![pr.repository.name, pr.title, pr.number.to_string()] })); table.to_string() } } impl util::Cmd for Review { fn cmd() -> eyre::Result { Ok(clap::Command::new("review")) } fn exec(_: &clap::ArgMatches) -> eyre::Result<()> { Self::default().run(Some("lunarway/squad-aura".into())) } } #[cfg(test)] mod tests { use crate::review_backend::{models::Repository, MockReviewBackend}; use super::*; use pretty_assertions::{assert_eq, assert_ne}; #[test] fn can_fetch_prs() { let mut backend = MockReviewBackend::new(); let prs = vec![ PullRequest { title: "some-title".into(), number: 0, repository: Repository { name: "some-name".into(), }, }, PullRequest { title: "some-other-title".into(), number: 1, repository: Repository { name: "some-other-name".into(), }, }, ]; let backendprs = prs.clone(); backend .expect_get_prs() .with(eq(Some("kjuulh".into()))) .times(1) .returning(move |_| Ok(backendprs.clone())); backend.expect_present_prs().times(1).returning(|_| Ok(())); let review = Review::new(std::sync::Arc::new(backend)); review .run(Some("kjuulh".into())) .expect("to return a list of pull requests"); } #[test] fn can_generate_table() { let prs = vec![ PullRequest { title: "some-title".into(), number: 0, repository: Repository { name: "some-name".into(), }, }, PullRequest { title: "some-other-title".into(), number: 1, repository: Repository { name: "some-other-name".into(), }, }, ]; let expected_table = "───────────────────────────────────────────── repo title number ═════════════════════════════════════════════ some-name some-title 0 ───────────────────────────────────────────── some-other-name some-other-title 1 ─────────────────────────────────────────────"; let output = Review::generate_prs_table(&prs); assert_eq!(output, expected_table.to_string()) } }