use semver::Version;
use std::collections::btree_map::{Iter, Range};
use std::collections::BTreeMap;
use std::fmt;
use std::iter::Rev;
use std::ops::RangeBounds;
use super::commits::Commits;
use super::error::{Error, Result};
use super::id::Id;
use super::Repository;
pub struct Versions<'a> {
repository: &'a Repository,
tags: BTreeMap<Version, Id>,
}
impl Repository {
pub fn versions(&self) -> Result<Versions<'_>> {
let tags = self.inner.tag_names(Some("v[0-9]*.[0-9]*.[0-9]**"))?;
let iter = tags.iter().flatten().map(|name| {
let version = name.trim_start_matches('v').parse()?;
Ok((version, self.find(name)?.id()))
});
let tags = iter.collect::<Result<_>>()?;
Ok(Versions { repository: self, tags })
}
}
impl Versions<'_> {
#[inline]
#[must_use]
pub fn get(&self, version: &Version) -> Option<&Id> {
self.tags.get(version)
}
#[inline]
#[must_use]
pub fn contains(&self, version: &Version) -> bool {
self.tags.contains_key(version)
}
#[inline]
pub fn range<R>(&self, range: R) -> Range<'_, Version, Id>
where
R: RangeBounds<Version>,
{
self.tags.range(range)
}
pub fn commits(&self, version: Option<&Version>) -> Result<Commits<'_>> {
if let Some(version) = version {
if !self.tags.contains_key(version) {
return Err(Error::Version);
}
let mut iter = self.tags.range(..=version).rev();
if let Some((_, start)) = iter.next() {
if let Some((_, end)) = iter.next() {
self.repository.commits(start..end)
} else {
self.repository.commits(start..)
}
} else {
Err(Error::Version)
}
} else {
let mut iter = self.tags.range(..).rev();
if let Some((_, end)) = iter.next() {
self.repository.commits(..end)
} else {
self.repository.commits(..)
}
}
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = (&Version, &Id)> {
self.into_iter()
}
}
#[allow(clippy::must_use_candidate)]
impl Versions<'_> {
#[inline]
pub fn len(&self) -> usize {
self.tags.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.tags.is_empty()
}
}
impl<'a> IntoIterator for &'a Versions<'a> {
type Item = (&'a Version, &'a Id);
type IntoIter = Rev<Iter<'a, Version, Id>>;
fn into_iter(self) -> Self::IntoIter {
self.tags.iter().rev()
}
}
impl fmt::Debug for Versions<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Versions")
.field("tags", &self.tags)
.finish()
}
}