adnv 1.5.0

CLI that tracks, checks and auto-updates infrastructure dependency versions across Terraform, Helm, Docker, Git and npm
Documentation
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)
}