use anyhow::{anyhow, Context, Result};
use octocrab::Octocrab;
use crate::skills::{parse_skill_meta, SkillMeta, Source};
pub fn build_client(token: Option<&str>) -> Result<Octocrab> {
let mut builder = Octocrab::builder();
if let Some(token) = token {
builder = builder.personal_token(token.to_string());
}
builder.build().context("build octocrab client")
}
pub struct FetchedSkill {
pub meta: SkillMeta,
pub content: String,
}
pub async fn fetch_repo_skills(
client: &Octocrab,
owner: &str,
repo: &str,
) -> Result<Vec<FetchedSkill>> {
let repos = client.repos(owner, repo);
let mut root = repos
.get_content()
.path("")
.send()
.await
.with_context(|| format!("list contents of {owner}/{repo}"))?;
let mut skills = Vec::new();
for item in root.take_items() {
if item.r#type != "dir" {
continue;
}
match fetch_skill_content(client, owner, repo, &item.name).await {
Ok(content) => {
let meta = parse_skill_meta(&item.name, &content, Source::Remote);
skills.push(FetchedSkill { meta, content });
}
Err(_) => continue, }
}
Ok(skills)
}
pub async fn fetch_skill_content(
client: &Octocrab,
owner: &str,
repo: &str,
name: &str,
) -> Result<String> {
let path = format!("{name}/SKILL.md");
let mut resp = client
.repos(owner, repo)
.get_content()
.path(&path)
.send()
.await
.with_context(|| format!("get {path}"))?;
let items = resp.take_items();
let file = items
.into_iter()
.find(|c| c.r#type == "file")
.ok_or_else(|| anyhow!("{path} not found or not a file"))?;
file.decoded_content()
.ok_or_else(|| anyhow!("{path} has no content"))
}
pub async fn push_skill_content(
client: &Octocrab,
owner: &str,
repo: &str,
name: &str,
content: &str,
message: &str,
) -> Result<()> {
let path = format!("{name}/SKILL.md");
let repos = client.repos(owner, repo);
let existing_sha = match repos.get_content().path(&path).send().await {
Ok(mut resp) => resp
.take_items()
.into_iter()
.find(|c| c.r#type == "file")
.map(|c| c.sha),
Err(_) => None,
};
match existing_sha {
Some(sha) => {
repos
.update_file(&path, message, content, sha)
.send()
.await
.with_context(|| format!("update {path}"))?;
}
None => {
repos
.create_file(&path, message, content)
.send()
.await
.with_context(|| format!("create {path}"))?;
}
}
Ok(())
}