diff --git a/README.md b/README.md
index f7b85a6..bf99ee6 100644
--- a/README.md
+++ b/README.md
@@ -53,6 +53,13 @@ Example:
+### 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.
diff --git a/graphql/schema/query.graphql b/graphql/schema/query.graphql
index 60b7ad3..0803339 100644
--- a/graphql/schema/query.graphql
+++ b/graphql/schema/query.graphql
@@ -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
diff --git a/src/app/issues/mod.rs b/src/app/issues/mod.rs
index afeb8b0..4997ab8 100644
--- a/src/app/issues/mod.rs
+++ b/src/app/issues/mod.rs
@@ -1,7 +1,5 @@
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 {
@@ -9,38 +7,33 @@ pub struct RepositorySummary {
pub name: String,
pub number_of_issues: i64,
pub number_of_pull_requests: i64,
- pub default_branch_name: String,
+ pub default_branch_name: Option,
pub url: String,
}
-pub fn repository_summary(result: ResponseData) -> Result, Error> {
+pub fn repository_summary(
+ repos: Vec,
+) -> Result, Error> {
let mut results: Vec = 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) {
@@ -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
);
}
diff --git a/src/github/api.rs b/src/github/api.rs
index 4e3c85d..22ffa47 100644
--- a/src/github/api.rs
+++ b/src/github/api.rs
@@ -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, Error> {
+ let mut results =
+ vec![] as Vec;
+
+ let mut cursor = None as Option;
+ 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 {
let request_body = SearchRepositoryQuery::build_query(variables);
diff --git a/src/main.rs b/src/main.rs
index 81af9b2..f68561f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -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::("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::("url").unwrap();
match app::clone::clone(&config, repo_url) {
@@ -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")
@@ -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"));
}