use super::{ListOptions, Tag};
use crate::{Error, Result};
mod queries {
pub use self::fetch_tags::{FetchTagsQuery, fetch_tags_query};
mod fetch_tags;
}
pub async fn list_tags(source: &url::Url, options: &ListOptions<'_>) -> Result<Vec<Tag>> {
let Some((owner, repo)) = source.path().trim_start_matches('/').split_once('/') else {
return Err(Error::invalid_source(
source.clone(),
"Invalid path (expected format <OWNER>/<REPO>)",
));
};
let github_token = std::env::var("ADNV_GITHUB_TOKEN").map_err(|err| {
Error::env(
err,
"ADNV_GITHUB_TOKEN",
"A GitHub token is needed for GitHub sources",
)
})?;
let client = octocrab::Octocrab::builder()
.personal_token(github_token)
.build()
.map_err(|err| Error::github(err, "Could not build GitHub client"))?;
let mut cursor = None;
let mut tags = Vec::new();
let mut page_number = 0usize;
'tags: loop {
page_number += 1;
if page_number > super::super::MAX_PAGES {
return Err(super::super::too_many_pages("GitHub"));
}
use graphql_client::{GraphQLQuery, Response};
use queries::{
FetchTagsQuery,
fetch_tags_query::{ResponseData, Variables},
};
let query = FetchTagsQuery::build_query(Variables {
owner: owner.into(),
name: repo.into(),
after: cursor.clone(),
});
let res: Response<ResponseData> = client.graphql(&query).await.map_err(|err| {
Error::github(
err,
format!("Could not fetch repository tags from {:?}", source.as_str()),
)
})?;
let data = res.data.ok_or_else(|| {
Error::invalid_source(source.clone(), "GitHub GraphQL returned no data")
})?;
let refs = data.repository.refs;
cursor = refs.page_info.end_cursor;
if cursor.is_none() {
break;
}
let nodes = refs.nodes;
for node in nodes {
use crate::util::iter::FilterWhileAction;
use queries::fetch_tags_query::FetchTagsQueryRepositoryRefsNodesTarget as Target;
let name = node.name;
let target = node.target;
let date = match target {
Target::Commit(data) => data.commit_date,
Target::Tag(data) => data.tagger.tag_date,
_ => continue,
};
match options.consider(&name, &date) {
FilterWhileAction::Pick => tags.push(Tag::new(name, date)),
FilterWhileAction::Skip => {}
FilterWhileAction::Break => break 'tags,
}
}
}
Ok(tags)
}