#![deny(missing_docs)]
#![allow(clippy::result_large_err)]
use errors::{TagError, TagResult};
pub use semver;
pub use semver::Version;
pub mod errors;
#[cfg(test)]
mod tests;
pub struct Package {
pub name: String,
pub version: Option<Version>,
}
pub struct PartialAnnouncementTag {
pub tag: String,
pub release: ReleaseType,
pub prerelease: bool,
}
impl Default for PartialAnnouncementTag {
fn default() -> PartialAnnouncementTag {
PartialAnnouncementTag {
tag: String::new(),
release: ReleaseType::None,
prerelease: false,
}
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum ReleaseType {
None,
Version(Version),
Package {
idx: usize,
version: Version,
},
}
pub fn parse_tag(packages: &[Package], tag: &str) -> TagResult<PartialAnnouncementTag> {
let mut announcing_package = None;
let announcing_version;
let announcing_prerelease;
let announcement_tag = tag.to_owned();
let mut tag_suffix;
if let Some((prefix, suffix)) = announcement_tag.rsplit_once('/') {
let maybe_package = if let Some((_prefix, package)) = prefix.rsplit_once('/') {
package
} else {
prefix
};
if let Some((package, "")) = strip_prefix_package(maybe_package, packages) {
announcing_package = Some(package);
}
tag_suffix = suffix;
} else {
tag_suffix = &announcement_tag;
};
if announcing_package.is_none() {
if let Some((package, suffix)) = strip_prefix_package(tag_suffix, packages) {
if let Some(suffix) = suffix.strip_prefix('-') {
tag_suffix = suffix;
announcing_package = Some(package);
}
}
}
if let Some(suffix) = tag_suffix.strip_prefix('v') {
tag_suffix = suffix;
}
match tag_suffix.parse::<Version>() {
Ok(version) => {
announcing_prerelease = !version.pre.is_empty();
announcing_version = version;
if let Some(pkg_idx) = announcing_package {
if let Some(package) = packages.get(pkg_idx) {
if let Some(real_version) = &package.version {
if real_version != &announcing_version {
return Err(TagError::ContradictoryTagVersion {
tag: tag.to_owned(),
package_name: package.name.clone(),
tag_version: announcing_version,
real_version: real_version.clone(),
});
}
}
}
}
}
Err(e) => {
return Err(TagError::TagVersionParse {
tag: tag.to_owned(),
details: e,
})
}
}
let release = if let Some(idx) = announcing_package {
ReleaseType::Package {
idx,
version: announcing_version,
}
} else {
ReleaseType::Version(announcing_version)
};
Ok(PartialAnnouncementTag {
tag: announcement_tag,
prerelease: announcing_prerelease,
release,
})
}
fn strip_prefix_package<'a>(input: &'a str, packages: &[Package]) -> Option<(usize, &'a str)> {
let mut result: Option<(usize, &'a str)> = None;
for (pkg_id, package) in packages.iter().enumerate() {
if let Some(rest) = input.strip_prefix(&package.name) {
if let Some((_, best)) = result {
if best.len() <= rest.len() {
continue;
}
}
result = Some((pkg_id, rest))
}
}
result
}