Skip to content

Commit

Permalink
Merge pull request #28 from kitsuyui/enhance-mure-issues
Browse files Browse the repository at this point in the history
WIP: Enhance mure issues
  • Loading branch information
kitsuyui authored Aug 28, 2022
2 parents 39dd415 + 8bf742c commit 276014d
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 37 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ Example:

<img width="1023" alt="example-mure-issues" src="https://user-images.githubusercontent.com/2596972/184259022-cb428537-f12e-41b0-8b49-a72565afa167.png">

### Options

`--query` option is available for advanced search like `--query 'user:kitsuyui'`
See this page for more about advanced search: https://docs.github.com/en/search-github/searching-on-github/searching-for-repositories

Default search query is `user:{username} is:public fork:false archived:false`

### mure refresh

`mure refresh` updates the repository.
Expand Down
14 changes: 12 additions & 2 deletions graphql/schema/query.graphql
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
query SearchRepositoryQuery($query: String!) {
repos: search(query: $query, type: REPOSITORY, first: 100) {
query SearchRepositoryQuery($query: String!, $cursor: String, $first: Int!) {
repos: search(
query: $query
type: REPOSITORY
first: $first
after: $cursor
) {
pageInfo {
startCursor
endCursor
hasNextPage
}
edges {
node {
__typename
Expand Down
45 changes: 19 additions & 26 deletions src/app/issues/mod.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,39 @@
use crate::github;
use crate::github::api::search_repository_query::{
ResponseData, SearchRepositoryQueryReposEdgesNode, Variables,
};
use crate::github::api::search_repository_query::SearchRepositoryQueryReposEdgesNodeOnRepository;
use crate::mure_error::Error;

pub struct RepositorySummary {
// | "\(.issues.totalCount)\t\(.pullRequests.totalCount)\t\(.defaultBranchRef.name)\t\(.url)"'
pub name: String,
pub number_of_issues: i64,
pub number_of_pull_requests: i64,
pub default_branch_name: String,
pub default_branch_name: Option<String>,
pub url: String,
}

pub fn repository_summary(result: ResponseData) -> Result<Vec<RepositorySummary>, Error> {
pub fn repository_summary(
repos: Vec<SearchRepositoryQueryReposEdgesNodeOnRepository>,
) -> Result<Vec<RepositorySummary>, Error> {
let mut results: Vec<RepositorySummary> = Vec::new();
if let Some(edge) = result.repos.edges {
for edge_ in edge {
let node = edge_.expect("edge is None").node.expect("node is None");
match node {
SearchRepositoryQueryReposEdgesNode::Repository(repo) => {
results.push(RepositorySummary {
name: repo.name.clone(),
number_of_issues: repo.issues.total_count,
number_of_pull_requests: repo.pull_requests.total_count,
default_branch_name: repo.default_branch_ref.unwrap().name.clone(),
url: repo.url.clone(),
});
}
_ => unreachable!("unreachable!"),
}
}
for repo in repos {
results.push(RepositorySummary {
name: repo.name.clone(),
number_of_issues: repo.issues.total_count,
number_of_pull_requests: repo.pull_requests.total_count,
default_branch_name: repo
.default_branch_ref
.as_ref()
.map(|default_branch_ref| default_branch_ref.name.clone()),
url: repo.url.clone(),
});
}
Ok(results)
}

pub fn show_issues(user: &str) -> Result<(), Error> {
pub fn show_issues(query: &str) -> Result<(), Error> {
// TODO: more flexible search query
let query = format!("user:{} is:public fork:false archived:false", user);
let var = Variables { query };
let token = std::env::var("GH_TOKEN").expect("GH_TOKEN is not set");
match github::api::search_repository(token, var) {
match github::api::search_all_repositories(&token, query) {
Err(e) => println!("{}", e),
Ok(result) => {
match repository_summary(result) {
Expand All @@ -52,7 +45,7 @@ pub fn show_issues(user: &str) -> Result<(), Error> {
"{}\t{}\t{}\t{}",
result.number_of_issues,
result.number_of_pull_requests,
result.default_branch_name,
result.default_branch_name.unwrap_or_else(|| "".to_string()),
result.url
);
}
Expand Down
55 changes: 52 additions & 3 deletions src/github/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,61 @@ type URI = String;
#[graphql(
schema_path = "graphql/schema/schema.docs.graphql",
query_path = "graphql/schema/query.graphql",
response_derives = "Debug,PartialEq,Eq"
response_derives = "Debug,PartialEq,Eq,Clone"
)]
pub struct SearchRepositoryQuery;

pub fn search_repository(
token: String,
pub fn search_all_repositories(
token: &str,
query: &str,
) -> Result<Vec<search_repository_query::SearchRepositoryQueryReposEdgesNodeOnRepository>, Error> {
let mut results =
vec![] as Vec<search_repository_query::SearchRepositoryQueryReposEdgesNodeOnRepository>;

let mut cursor = None as Option<String>;
let mut count = 0;
loop {
let variables = search_repository_query::Variables {
query: query.to_string(),
first: 100,
cursor,
};
let response = search_repositories(token, variables);
match response {
Ok(response) => {
let page_info = response.repos.page_info;
let edges = response.repos.edges;
if let Some(edge) = edges {
for edge_ in edge {
let node = edge_.expect("edge is None").node.expect("node is None");
if let search_repository_query::SearchRepositoryQueryReposEdgesNode::Repository(repo) =
node
{
results.push(repo);
}
}
}
if page_info.has_next_page {
cursor = page_info.end_cursor;
} else {
break;
}
}
Err(err) => {
return Err(err);
}
}
count += 1;
if count > 100 {
// Avoid infinite loop to prevent reaching github api limit.
break;
}
}
Ok(results)
}

fn search_repositories(
token: &str,
variables: search_repository_query::Variables,
) -> Result<search_repository_query::ResponseData, Error> {
let request_body = SearchRepositoryQuery::build_query(variables);
Expand Down
27 changes: 21 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,19 @@ fn main() {
Err(e) => println!("{}", e),
}
}
Some(("issues", _)) => match app::issues::show_issues(&config.github.username) {
Ok(_) => (),
Err(e) => println!("{}", e),
},
Some(("issues", matches)) => {
let query = match matches.get_one::<String>("query") {
Some(query) => query.to_string(),
None => format!(
"user:{} is:public fork:false archived:false",
&config.github.username
),
};
match app::issues::show_issues(&query) {
Ok(_) => (),
Err(e) => println!("{}", e),
}
}
Some(("clone", matches)) => {
let repo_url = matches.get_one::<String>("url").unwrap();
match app::clone::clone(&config, repo_url) {
Expand All @@ -55,7 +64,13 @@ fn parser() -> App<'static> {
.required(false)
.index(1),
);
let subcommand_issues = App::new("issues").about("show issues");
let subcommand_issues = App::new("issues").about("show issues").arg(
clap::Arg::new("query")
.short('q')
.long("query")
.takes_value(true)
.help("query to search issues"),
);
let subcommand_clone = App::new("clone").about("clone repository").arg(
clap::Arg::with_name("url")
.help("repository url")
Expand Down Expand Up @@ -93,7 +108,7 @@ fn test_parser() {
}
}
let cmd = parser();
match cmd.get_matches_from_safe(["mure", "issues"]) {
match cmd.get_matches_from_safe(["mure", "issues", "--query", "test"]) {
Ok(matches) => {
assert_eq!(matches.subcommand_name(), Some("issues"));
}
Expand Down

0 comments on commit 276014d

Please sign in to comment.