Skip to content

Commit

Permalink
feat: support GitHub
Browse files Browse the repository at this point in the history
WIP:
  • Loading branch information
cnwangjie committed Mar 24, 2021
1 parent bb7f1d6 commit b4397c3
Show file tree
Hide file tree
Showing 19 changed files with 933 additions and 353 deletions.
9 changes: 8 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ async-trait = "^0.1.40"
clap = "^2.33.3"
log = { version = "^0.4.11", features = ["std"] }
colored = "^2"
base64 = "0.13"

[dev-dependencies]
libc = "0.2"
56 changes: 32 additions & 24 deletions src/command/mod.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,50 @@
mod pr;
mod profile;

use clap::{crate_version, crate_authors, App, Arg};
use crate::logger::Logger;
use anyhow::{Result, anyhow};
use anyhow::Result;
use clap::{crate_authors, crate_version, App, AppSettings, Arg};
use log::debug;


pub fn get_app<'a, 'b>() -> App<'a, 'b> {
App::new("yag")
.version(crate_version!())
.author(crate_authors!())
.arg(
Arg::with_name("v")
.short("v")
.long("verbose")
.help("verbose mode")
.global(true),
)
.subcommand(pr::sub_command())
.subcommand(profile::sub_command())
App::new("yag")
.version(crate_version!())
.author(crate_authors!())
.global_setting(AppSettings::ColoredHelp)
.global_setting(AppSettings::DisableHelpSubcommand)
.global_setting(AppSettings::DontCollapseArgsInUsage)
.global_setting(AppSettings::VersionlessSubcommands)
.arg(
Arg::with_name("v")
.short("v")
.long("verbose")
.help("verbose mode")
.global(true),
)
.subcommand(pr::sub_command().setting(AppSettings::SubcommandRequiredElseHelp))
.subcommand(profile::sub_command().setting(AppSettings::SubcommandRequiredElseHelp))
}


pub async fn run() -> Result<()> {
let mut app = get_app();

let matches = app.clone().get_matches();

Logger::init(matches.is_present("v"))?;
debug!("verbose mode enabled");

let (command, arg_matches) = matches.subcommand();
debug!("command: {}", command);
let arg_matches = arg_matches.ok_or(anyhow!("arg matches is none"))?;
debug!("arg matches: {:#?}", arg_matches);
match command {
"pr" => pr::Command::new(&arg_matches).run().await,
"profile" => profile::Command::new(&arg_matches).run().await,
_ => Ok(app.write_long_help(&mut std::io::stdout())?),
if let (command, Some(arg_matches)) = matches.subcommand() {
debug!("command: {}", command);
debug!("arg matches: {:#?}", arg_matches);
match command {
"pr" => pr::Command::new(&arg_matches).unwrap().run().await?,
"profile" => profile::Command::new(&arg_matches).unwrap().run().await?,
_ => (),
}
} else {
app.print_long_help()?;
println!();
}

Ok(())
}
160 changes: 84 additions & 76 deletions src/command/pr.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use anyhow::{bail, Error, Result};
use clap::{App, Arg, ArgMatches, SubCommand};
use anyhow::{Error, Result, bail};
use colored::Colorize;
use utils::user_input;

Expand All @@ -8,63 +8,71 @@ use crate::utils;

#[inline]
pub fn sub_command<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("pr")
.about("Manage pull requests (aka. merge request for GitLab)")
.alias("mr")
.subcommand(
SubCommand::with_name("get")
.about("Get detail of single pull request")
.arg(Arg::with_name("id").required(true).takes_value(true)),
)
.subcommand(
SubCommand::with_name("close")
.about("Close pull request")
.arg(Arg::with_name("id").required(true).takes_value(true)),
)
.subcommand(
SubCommand::with_name("list").about("List pull requests")
.about("List pull requests of current repository")
.arg(Arg::with_name("author").long("author").takes_value(true))
.arg(Arg::with_name("me").long("me"))
.arg(Arg::with_name("status").long("status").takes_value(true))
.arg(Arg::with_name("page").long("page").takes_value(true)),
)
.subcommand(
SubCommand::with_name("create")
.alias("new")
.about("Create a new pull request")
.arg(Arg::with_name("title").takes_value(true))
.arg(
Arg::with_name("base")
.alias("target")
.long("base")
.short("b")
.takes_value(true),
)
.arg(
Arg::with_name("head")
.alias("source")
.long("head")
.short("h")
.takes_value(true),
),
)
SubCommand::with_name("pr")
.about("Manage pull requests (aka. merge request for GitLab)")
.alias("mr")
.subcommand(
SubCommand::with_name("get")
.about("Get detail of single pull request")
.arg(Arg::with_name("id").required(true).takes_value(true)),
)
.subcommand(
SubCommand::with_name("close")
.about("Close pull request")
.arg(Arg::with_name("id").required(true).takes_value(true)),
)
.subcommand(
SubCommand::with_name("list")
.about("List pull requests")
.about("List pull requests of current repository")
.arg(Arg::with_name("author").long("author").takes_value(true))
.arg(Arg::with_name("me").long("me"))
.arg(Arg::with_name("status").long("status").takes_value(true))
.arg(Arg::with_name("page").long("page").takes_value(true)),
)
.subcommand(
SubCommand::with_name("create")
.alias("new")
.about("Create a new pull request")
.arg(Arg::with_name("title").takes_value(true))
.arg(
Arg::with_name("base")
.alias("target")
.long("base")
.short("b")
.takes_value(true),
)
.arg(
Arg::with_name("head")
.alias("source")
.long("head")
.short("h")
.takes_value(true),
),
)
}

pub struct Command<'a> {
command: &'a str,
matches: &'a ArgMatches<'a>,
}

impl<'a> Command<'a> {
pub fn new(matches: &'a ArgMatches<'a>) -> Self {
Command {
matches: matches,
pub fn new(matches: &'a ArgMatches<'a>) -> Option<Self> {
match matches.subcommand() {
(command, Some(arg_matches)) => Some(Command {
command: command,
matches: arg_matches,
}),
_ => {
println!("{}", matches.usage());
None
}
}
}

pub async fn run(&self) -> Result<()> {
let (command, _) = self.matches.subcommand();
match command {
match self.command {
"get" => self.get().await,
"list" => self.list().await,
"create" => self.create().await,
Expand All @@ -74,13 +82,13 @@ impl<'a> Command<'a> {
}

async fn get(&self) -> Result<()> {
let id = self.matches
.value_of("id")
.and_then(|s| s.parse::<usize>().ok())
.unwrap();
let id = self
.matches
.value_of("id")
.and_then(|s| s.parse::<usize>().ok())
.unwrap();

let pr = get_repo().await?
.get_pull_request(id).await?;
let pr = get_repo().await?.get_pull_request(id).await?;

println!("{:#}", pr);

Expand All @@ -89,8 +97,7 @@ impl<'a> Command<'a> {

async fn list(&self) -> Result<()> {
let opt = ListPullRequestOpt::from(self.matches.clone());
let result = get_repo().await?
.list_pull_requests(opt).await?;
let result = get_repo().await?.list_pull_requests(opt).await?;

print!("{:#}", result);
Ok(())
Expand All @@ -102,50 +109,51 @@ impl<'a> Command<'a> {
_ => utils::get_current_branch()?,
};

let target_branch = self.matches.value_of("base")
let target_branch = self
.matches
.value_of("base")
.map(|base| base.to_string())
.or_else(|| {
utils::get_git_config("yag.pr.target").ok()
})
.or_else(|| utils::get_git_config("yag.pr.target").ok())
.unwrap_or("master".to_string());

if source_branch == target_branch {
bail!("head branch and base branch are same: {}", source_branch)
}

if utils::get_rev(&source_branch)? == utils::get_rev(&target_branch)? {
let ok = user_input(&format!("{} head is same as base. still create pr? (Y/n) ", "warning".yellow().bold()))?;
let ok = user_input(&format!(
"{} head is same as base. still create pr? (Y/n) ",
"warning".yellow().bold()
))?;
if ok == "n" {
return Ok(())
return Ok(());
}
}

let title = self.matches
let title = self
.matches
.value_of("title")
.map(|title| title.to_string())
.or_else(|| {
utils::get_latest_commit_message().ok()
})
.ok_or(Error::msg("Cannot get latest commit message. Please specify title manually."))?;

let pr = get_repo().await?
.create_pull_request(
&source_branch,
&target_branch,
&title,
)
.or_else(|| utils::get_latest_commit_message().ok())
.ok_or(Error::msg(
"Cannot get latest commit message. Please specify title manually.",
))?;

let pr = get_repo()
.await?
.create_pull_request(&source_branch, &target_branch, &title)
.await?;
print!("{:#}", pr);
Ok(())
}

async fn close(&self) -> Result<()> {
let id = self.matches
let id = self
.matches
.value_of("id")
.and_then(|s| s.parse::<usize>().ok())
.unwrap();
let pr = get_repo().await?
.close_pull_request(id).await?;
let pr = get_repo().await?.close_pull_request(id).await?;

print!("{:#}", pr);
Ok(())
Expand Down
27 changes: 16 additions & 11 deletions src/command/profile.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,43 @@
use clap::{App, ArgMatches, SubCommand};
use crate::profile::{load_profile, prompt_add_profile, write_profile};
use anyhow::Result;
use crate::profile::{load_profile, prompt_add_profile};
use clap::{App, ArgMatches, SubCommand};
use log::debug;

#[inline]
pub fn sub_command<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("profile")
.about("Manage profiles")
.subcommand(
SubCommand::with_name("add")
.about("Add profile config interactively"),
)
.subcommand(SubCommand::with_name("add").about("Add profile config interactively"))
}
pub struct Command<'a> {
command: &'a str,
matches: &'a ArgMatches<'a>,
}

impl<'a> Command<'a> {
pub fn new(matches: &'a ArgMatches<'a>) -> Self {
Command {
matches: matches,
pub fn new(matches: &'a ArgMatches<'a>) -> Option<Self> {
match matches.subcommand() {
(command, Some(arg_matches)) => Some(Command {
command: command,
matches: arg_matches,
}),
_ => None,
}
}

pub async fn run(&self) -> Result<()> {
let (command, _) = self.matches.subcommand();
match command {
match self.command {
"add" => self.add().await,
_ => Ok(println!("{}", self.matches.usage())),
}
}

async fn add(&self) -> Result<()> {
let mut profile = load_profile().await?;
debug!("profile loaded: {:#?}", profile);
prompt_add_profile(&mut profile).await?;
debug!("profile added: {:#?}", profile);
write_profile(&profile).await?;
Ok(())
}
}
Loading

0 comments on commit b4397c3

Please sign in to comment.