use super::{Cli, Result};
pub const CLI: Cli = Cli {
name: "gh",
hint: "Install from https://cli.github.com and run `gh auth login`",
};
pub fn require() -> Result<Gh> {
CLI.require()?;
Ok(Gh)
}
pub struct Gh;
impl Gh {
pub fn run(&self, args: &[&str]) -> Result<String> {
run(args)
}
pub fn star_repo(&self, repo: &str) -> Result<()> {
star_repo(repo)
}
pub fn search_code(&self, query: &str) -> SearchCode {
search_code(query)
}
pub fn search_repos(&self, query: &str) -> SearchRepos {
search_repos(query)
}
pub fn api(&self, endpoint: impl Into<String>) -> Api {
api(endpoint)
}
}
pub fn available() -> bool {
CLI.available()
}
pub fn run(args: &[&str]) -> Result<String> {
log::debug!("gh: running gh {}", args.join(" "));
let output = CLI.run_command(CLI.command().args(args))?;
log::debug!("gh: returned {} bytes", output.len());
Ok(output)
}
pub fn star_repo(repo: &str) -> Result<()> {
CLI.run_status(
CLI.command()
.args(["repo", "star", repo])
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null()),
)
}
pub fn search_code(query: &str) -> SearchCode {
SearchCode {
query: query.to_string(),
filename: None,
match_on: None,
repo: None,
json_fields: None,
limit: None,
}
}
pub struct SearchCode {
query: String,
filename: Option<String>,
match_on: Option<String>,
repo: Option<String>,
json_fields: Option<String>,
limit: Option<usize>,
}
impl SearchCode {
pub fn filename(mut self, name: impl Into<String>) -> Self {
self.filename = Some(name.into());
self
}
pub fn match_on(mut self, field: impl Into<String>) -> Self {
self.match_on = Some(field.into());
self
}
pub fn repo(mut self, repo: impl Into<String>) -> Self {
self.repo = Some(repo.into());
self
}
pub fn json(mut self, fields: &[&str]) -> Self {
self.json_fields = Some(fields.join(","));
self
}
pub fn limit(mut self, n: usize) -> Self {
self.limit = Some(n);
self
}
pub fn run(self) -> Result<String> {
let mut args = vec!["search".to_string(), "code".to_string()];
if let Some(f) = &self.filename {
args.push("--filename".to_string());
args.push(f.clone());
}
if let Some(m) = &self.match_on {
args.push("--match".to_string());
args.push(m.clone());
}
if let Some(r) = &self.repo {
args.push("--repo".to_string());
args.push(r.clone());
}
args.push(self.query);
if let Some(j) = &self.json_fields {
args.push("--json".to_string());
args.push(j.clone());
}
if let Some(l) = self.limit {
args.push("--limit".to_string());
args.push(l.to_string());
}
let refs: Vec<&str> = args.iter().map(|s| s.as_str()).collect();
crate::shell::gh::run(&refs)
}
}
pub fn search_repos(query: &str) -> SearchRepos {
SearchRepos {
query: query.to_string(),
json_fields: None,
limit: None,
}
}
pub struct SearchRepos {
query: String,
json_fields: Option<String>,
limit: Option<usize>,
}
impl SearchRepos {
pub fn json(mut self, fields: &[&str]) -> Self {
self.json_fields = Some(fields.join(","));
self
}
pub fn limit(mut self, n: usize) -> Self {
self.limit = Some(n);
self
}
pub fn run(self) -> Result<String> {
let mut args = vec!["search".to_string(), "repos".to_string(), self.query];
if let Some(j) = &self.json_fields {
args.push("--json".to_string());
args.push(j.clone());
}
if let Some(l) = self.limit {
args.push("--limit".to_string());
args.push(l.to_string());
}
let refs: Vec<&str> = args.iter().map(|s| s.as_str()).collect();
crate::shell::gh::run(&refs)
}
}
pub fn api(endpoint: impl Into<String>) -> Api {
Api {
endpoint: endpoint.into(),
jq: None,
}
}
pub struct Api {
endpoint: String,
jq: Option<String>,
}
impl Api {
pub fn jq(mut self, expr: impl Into<String>) -> Self {
self.jq = Some(expr.into());
self
}
pub fn run(self) -> Result<String> {
let mut args = vec!["api".to_string(), self.endpoint];
if let Some(j) = &self.jq {
args.push("--jq".to_string());
args.push(j.clone());
}
let refs: Vec<&str> = args.iter().map(|s| s.as_str()).collect();
crate::shell::gh::run(&refs)
}
}