use alloc::format;
use alloc::string::String;
use alloc::vec::Vec;
use core::cmp::Ordering;
pub use standard::Standard;
use crate::component::PartType;
use crate::error::Error;
use crate::{BuildMetadata, Prerelease, Version};
mod standard;
pub type CapturedBytes = Vec<u8>;
pub type RemainingUnparsedBytes = [u8];
pub type NextPartType = Option<PartType>;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Dialect {
Standard,
}
pub trait DialectParser {
fn parse_byte(
byte: u8,
part: (PartType, &CapturedBytes),
remaining_bytes: &RemainingUnparsedBytes,
) -> Result<NextPartType, Error> {
if (part.0 == PartType::Patch || part.0 == PartType::Prerelease) && byte == b'+' {
return Ok(Some(PartType::BuildMetadata));
}
if part.0 == PartType::Patch && byte == b'-' {
return Ok(Some(PartType::Prerelease));
}
if byte == b'.' {
match part.0 {
PartType::Major => return Ok(Some(PartType::Minor)),
PartType::Minor => return Ok(Some(PartType::Patch)),
PartType::Prerelease => return Ok(Some(PartType::Prerelease)),
_ => {}
}
}
match part.0 {
PartType::Major => {
if !byte.is_ascii_digit() {
return Err(Error::InvalidCharacter(part.0));
}
let is_first_digit = part.1.is_empty();
let is_last_digit = remaining_bytes.is_empty() || remaining_bytes[0] == b'.';
if byte == b'0' && is_first_digit && !is_last_digit {
return Err(Error::InvalidPrecedingZero(part.0));
}
}
PartType::Minor => {
if !byte.is_ascii_digit() {
return Err(Error::InvalidCharacter(part.0));
}
let is_first_digit = part.1.is_empty();
let is_last_digit = remaining_bytes.is_empty() || remaining_bytes[0] == b'.';
if byte == b'0' && (is_first_digit && !is_last_digit) {
return Err(Error::InvalidPrecedingZero(part.0));
}
}
PartType::Patch => {
if !byte.is_ascii_digit() {
return Err(Error::InvalidCharacter(part.0));
}
let is_first_digit = part.1.is_empty();
let is_last_digit = remaining_bytes.is_empty()
|| (remaining_bytes[0] == b'+' || remaining_bytes[0] == b'-');
if byte == b'0' && (is_first_digit && !is_last_digit) {
return Err(Error::InvalidPrecedingZero(part.0));
}
}
PartType::Prerelease => {
if !byte.is_ascii_alphanumeric() && byte != b'-' {
return Err(Error::InvalidCharacter(part.0));
}
}
PartType::BuildMetadata => {
if !byte.is_ascii_alphanumeric() && byte != b'-' && byte != b'.' {
return Err(Error::InvalidCharacter(part.0));
}
}
}
Ok(None)
}
fn cmp(a: &Version, b: &Version) -> Ordering {
if a.major != b.major {
return if a.major > b.major {
Ordering::Greater
} else {
Ordering::Less
};
}
if a.minor != b.minor {
return if a.minor > b.minor {
return Ordering::Greater;
} else {
Ordering::Less
};
}
if a.patch != b.patch {
return if a.patch > b.patch {
Ordering::Greater
} else {
Ordering::Less
};
}
if let Prerelease::Identifier(a) = &a.prerelease {
if let Prerelease::Identifier(b) = &b.prerelease {
return if a < b {
Ordering::Less
} else if a.eq(b) {
Ordering::Equal
} else {
Ordering::Greater
};
}
return Ordering::Less;
} else if b.prerelease != Prerelease::Empty {
return Ordering::Greater;
}
Ordering::Equal
}
fn eq(a: &Version, b: &Version) -> bool {
a.major.eq(&b.major)
&& a.minor.eq(&b.minor)
&& a.patch.eq(&b.patch)
&& a.prerelease.eq(&b.prerelease)
}
fn format(version: &Version) -> String {
let mut string = format!("{}.{}.{}", version.major, version.minor, version.patch);
if let Prerelease::Identifier(identifier) = &version.prerelease {
string.push_str(&format!(
"-{}",
identifier
.iter()
.fold(String::new(), |mut str, part| {
str.push_str(&format!(".{part}"));
str
})
.trim_start_matches('.')
));
}
if let BuildMetadata::Identifier(identifier) = &version.build_metadata {
string.push_str(&format!("+{identifier}"));
}
string
}
}