#![deny(missing_docs, clippy::all)]
use std::collections::BTreeSet;
use std::fmt::Display;
use std::rc::Rc;
use std::str::FromStr;
#[derive(Eq, PartialEq, Clone, Debug, Ord, PartialOrd, Default)]
pub struct SetVersion {
versions: BTreeSet<Rc<SetVersion>>,
}
impl SetVersion {
pub fn setver_compare(&self, other: &SetVersion) -> f32 {
if self.is_subset(other) {
0.0
} else if self == other {
1.0
} else if other.is_subset(self) {
f32::INFINITY
} else {
f32::NAN
}
}
pub fn is_subset(&self, other: &SetVersion) -> bool {
self.versions.is_subset(&other.versions)
}
pub fn is_strict_subset(&self, other: &SetVersion) -> bool {
!other.is_superset(self)
}
pub fn is_superset(&self, other: &SetVersion) -> bool {
self.versions.is_superset(&other.versions)
}
pub fn is_strict_superset(&self, other: &SetVersion) -> bool {
!other.is_subset(self)
}
pub fn add_child_version(&mut self, child: Rc<SetVersion>) -> &mut Self {
self.versions.insert(child);
self
}
pub fn to_integralternative(&self) -> u128 {
self.into()
}
pub fn string_to_integralternative(setver: &str) -> u128 {
let bytes = Self::string_to_integralternative_bytes(setver);
Self::u128_from_vec(bytes)
}
pub fn to_integralternative_bytes(&self) -> Vec<u8> {
let stringified = String::from(self);
Self::string_to_integralternative_bytes(&stringified)
}
pub fn string_to_integralternative_bytes(setver: &str) -> Vec<u8> {
let mut current_byte = 0;
let mut bytes = Vec::new();
let mut bit_count = 0;
for c in setver.chars().rev() {
current_byte = (current_byte >> 1)
| match c {
'{' => 0,
'}' => 1 << 7,
_ => unreachable!(),
};
bit_count += 1;
if bit_count > 7 {
bit_count = 0;
bytes.push(current_byte);
current_byte = 0;
}
}
if bit_count != 0 {
current_byte >>= 8 - bit_count;
bytes.push(current_byte);
}
bytes.reverse();
bytes
}
fn u128_from_vec(vec: Vec<u8>) -> u128 {
if vec.len() > 128 / 8 {
panic!("Input {:?} is too large to be represented in u128", vec);
}
let mut result = 0u128;
for byte in vec {
result = (result << 8) | (byte as u128);
}
result
}
}
impl Display for SetVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{{")?;
for version in &self.versions {
version.fmt(f)?;
}
write!(f, "}}")
}
}
impl From<&SetVersion> for String {
fn from(this: &SetVersion) -> Self {
format!("{}", this)
}
}
impl From<&SetVersion> for u128 {
fn from(this: &SetVersion) -> Self {
let bytes = this.to_integralternative_bytes();
SetVersion::u128_from_vec(bytes)
}
}
impl PartialEq<u128> for SetVersion {
fn eq(&self, other: &u128) -> bool {
self.to_integralternative() == *other
}
}
impl PartialEq<&str> for SetVersion {
fn eq(&self, other: &&str) -> bool {
match other.parse::<SetVersion>() {
Ok(other_setver) => self == &other_setver,
Err(_) => false,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum SetVerParseError {
IllegalCharacter(char),
NonUniqueElements,
UnclosedBrace,
Empty,
TooManySets,
}
impl Display for SetVerParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match &self {
Self::IllegalCharacter(c) => format!("Illegal character '{}'", c),
Self::NonUniqueElements => "Set contains non-unique subsets".to_string(),
Self::UnclosedBrace => "Unclosed set brace".to_string(),
Self::Empty => "Empty string".to_string(),
Self::TooManySets => "Too many sets (more than one)".to_string(),
}
)
}
}
impl FromStr for SetVersion {
type Err = SetVerParseError;
fn from_str(value: &str) -> Result<Self, Self::Err> {
if value.len() < 2 {
return Err(SetVerParseError::Empty);
}
let mut chars = value.chars();
let open_curly = chars.next().unwrap();
if open_curly != '{' {
return Err(SetVerParseError::IllegalCharacter(open_curly));
}
let mut brace_level = 1;
let mut inner_sets = vec!["".to_owned()];
for next_char in &mut chars {
match next_char {
'{' => brace_level += 1,
'}' => brace_level -= 1,
_ => return Err(SetVerParseError::IllegalCharacter(next_char)),
}
if brace_level == 0 {
break;
}
inner_sets.last_mut().unwrap().push(next_char);
if brace_level == 1 {
inner_sets.push("".to_owned());
}
}
if brace_level != 0 {
return Err(SetVerParseError::UnclosedBrace);
}
if chars.next() != None {
return Err(SetVerParseError::TooManySets);
}
inner_sets.remove(inner_sets.len() - 1);
if inner_sets.is_empty() {
return Ok(Self::default());
}
let versions = inner_sets
.iter()
.map(|string_set| string_set.parse::<SetVersion>().map(Rc::new))
.collect::<Result<BTreeSet<Rc<SetVersion>>, SetVerParseError>>()?;
if versions.len() < inner_sets.len() {
return Err(SetVerParseError::NonUniqueElements);
}
Ok(Self { versions })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_correct_setver() -> Result<(), SetVerParseError> {
for test_string in
["{}", "{{}}", "{{}{{}}}", "{{}{{}}{{}{{}}}}", "{{{{{{{}}}}}}}", "{{}{{}}{{{}}}}", "{{}{{{}}{{}{{}}}}}"]
{
assert_eq!(test_string.parse::<SetVersion>()?.to_string(), test_string);
}
Ok(())
}
#[test]
fn parse_incorrect_setver() {
assert_eq!("".parse::<SetVersion>().unwrap_err(), SetVerParseError::Empty);
assert_eq!("asd".parse::<SetVersion>().unwrap_err(), SetVerParseError::IllegalCharacter('a'));
assert_eq!("{{b}}".parse::<SetVersion>().unwrap_err(), SetVerParseError::IllegalCharacter('b'));
"{{}{}".parse::<SetVersion>().unwrap_err();
"}{}".parse::<SetVersion>().unwrap_err();
assert_eq!("{}{}".parse::<SetVersion>().unwrap_err(), SetVerParseError::TooManySets);
assert_eq!("{{}{}}".parse::<SetVersion>().unwrap_err(), SetVerParseError::NonUniqueElements);
assert_eq!("{{{}{}}{}}".parse::<SetVersion>().unwrap_err(), SetVerParseError::NonUniqueElements);
assert_eq!("{{}{{}{{}}}{{}{{}}}}".parse::<SetVersion>().unwrap_err(), SetVerParseError::NonUniqueElements);
}
#[test]
fn equality() {
assert_eq!("{}".parse::<SetVersion>().unwrap(), "{}".parse::<SetVersion>().unwrap());
assert_ne!("{{{}}}".parse::<SetVersion>().unwrap(), "{{}}".parse::<SetVersion>().unwrap());
assert_eq!("{{}{{}}}".parse::<SetVersion>().unwrap(), "{{{}}{}}".parse::<SetVersion>().unwrap());
assert_eq!(
"{{{{}{{}}}{{}}}{}}".parse::<SetVersion>().unwrap(),
"{{}{{{}}{{}{{}}}}}".parse::<SetVersion>().unwrap()
);
assert_eq!("{{{{}{{}}}{{}}}{}}".parse::<SetVersion>().unwrap(), "{{}{{{}}{{}{{}}}}}");
}
#[test]
fn integralternative() {
assert_eq!("{{}{{{}}{{}{{}}}}}".parse::<SetVersion>().unwrap().to_integralternative(), 35999);
assert_eq!("{{}{{{}}{{}{{}}}}}".parse::<SetVersion>().unwrap(), 35999);
assert_eq!(SetVersion::string_to_integralternative("{{{{}}{}}{{}}}"), 871);
assert_eq!(SetVersion::string_to_integralternative("{{{}}{{{}}{}}}"), 1591);
}
}