use super::*;
fn parse_version(version_str: &str) -> Option<Version> {
let parts: Vec<&str> = version_str.split('-').collect();
let version_part: &str = parts.first()?;
let prerelease: Option<String> = parts.get(1).map(|s: &&str| s.to_string());
let nums: Vec<&str> = version_part.split('.').collect();
if nums.len() != 3 {
return None;
}
let major: u64 = nums.first()?.parse().ok()?;
let minor: u64 = nums.get(1)?.parse().ok()?;
let patch: u64 = nums.get(2)?.parse().ok()?;
Some(Version {
major,
minor,
patch,
prerelease,
})
}
fn parse_prerelease(prerelease: &str) -> Option<(&str, u64)> {
let parts: Vec<&str> = prerelease.split('.').collect();
let pre_type: &str = parts.first()?;
let number: u64 = parts
.get(1)
.and_then(|s: &&str| s.parse().ok())
.unwrap_or(0);
Some((pre_type, number))
}
fn get_next_prerelease(current: Option<&String>, target_type: &str) -> String {
match current {
Some(pre) => {
if let Some((pre_type, number)) = parse_prerelease(pre)
&& pre_type == target_type
&& number > 0
{
return format!("{}.{}", target_type, number + 1);
}
format!("{target_type}.1")
}
None => target_type.to_string(),
}
}
fn version_to_string(version: &Version) -> String {
let base: String = format!("{}.{}.{}", version.major, version.minor, version.patch);
match &version.prerelease {
Some(pre) => format!("{base}-{pre}"),
None => base,
}
}
fn bump_version(version: &Version, bump_type: &BumpVersionType) -> Version {
match bump_type {
BumpVersionType::Patch => Version {
major: version.major,
minor: version.minor,
patch: version.patch + 1,
prerelease: None,
},
BumpVersionType::Minor => Version {
major: version.major,
minor: version.minor + 1,
patch: 0,
prerelease: None,
},
BumpVersionType::Major => Version {
major: version.major + 1,
minor: 0,
patch: 0,
prerelease: None,
},
BumpVersionType::Release => Version {
major: version.major,
minor: version.minor,
patch: version.patch,
prerelease: None,
},
BumpVersionType::Alpha => {
let prerelease: String = get_next_prerelease(version.prerelease.as_ref(), "alpha");
Version {
major: version.major,
minor: version.minor,
patch: version.patch,
prerelease: Some(prerelease),
}
}
BumpVersionType::Beta => {
let prerelease: String = get_next_prerelease(version.prerelease.as_ref(), "beta");
Version {
major: version.major,
minor: version.minor,
patch: version.patch,
prerelease: Some(prerelease),
}
}
BumpVersionType::Rc => {
let prerelease: String = get_next_prerelease(version.prerelease.as_ref(), "rc");
Version {
major: version.major,
minor: version.minor,
patch: version.patch,
prerelease: Some(prerelease),
}
}
}
}
pub async fn execute_bump(
manifest_path: &str,
bump_type: &BumpVersionType,
) -> Result<String, Box<dyn std::error::Error>> {
let path: &Path = Path::new(manifest_path);
let content: String = read_to_string(path).await?;
let mut doc: DocumentMut = content
.parse()
.map_err(|e: TomlError| format!("failed to parse {}: {}", manifest_path, e))?;
let version_slot: &mut Item = if doc
.get("workspace")
.and_then(|workspace: &Item| workspace.get("package"))
.is_some()
{
doc.get_mut("workspace")
.and_then(|workspace: &mut Item| workspace.get_mut("package"))
.and_then(|package: &mut Item| package.get_mut("version"))
.ok_or_else(|| -> Box<dyn std::error::Error> {
"workspace.package.version not found".into()
})?
} else if doc.get("package").is_some() {
doc.get_mut("package")
.and_then(|package: &mut Item| package.get_mut("version"))
.ok_or_else(|| -> Box<dyn std::error::Error> { "package.version not found".into() })?
} else {
return Err("neither [package] nor [workspace.package] found in Cargo.toml".into());
};
let version_str: String = version_slot
.as_str()
.ok_or_else(|| -> Box<dyn std::error::Error> { "version field is not a string".into() })?
.to_string();
let version: Version =
parse_version(&version_str).ok_or_else(|| -> Box<dyn std::error::Error> {
format!("failed to parse version: {}", version_str).into()
})?;
let bumped: Version = bump_version(&version, bump_type);
let version_string: String = version_to_string(&bumped);
set_item_string_preserving_decor(version_slot, &version_string);
write(path, doc.to_string()).await?;
Ok(version_string)
}