use std::borrow::Cow;
use std::convert::TryFrom;
use std::fmt::Display;
use std::hash::Hash;
use std::str::{self, FromStr};
use std::{io, mem, num};
use thiserror::Error;
#[derive(Debug, Clone, PartialEq, PartialOrd, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Value {
String(String),
Float(f64),
Int(i64),
Buffer(Box<[u8]>),
Boolean(bool),
#[default]
Empty,
List(Box<[Value]>),
}
impl Eq for Value {}
impl From<String> for Value {
fn from(value: String) -> Self {
Value::new(value)
}
}
impl From<&str> for Value {
fn from(value: &str) -> Self {
Value::wrap(value)
}
}
impl From<Cow<'_, str>> for Value {
fn from(value: Cow<'_, str>) -> Self {
Value::wrap(&value)
}
}
pub trait ParamValue {
fn is_empty(&self) -> bool;
fn is_i64(&self) -> bool;
fn is_f64(&self) -> bool;
fn is_buffer(&self) -> bool;
fn is_str(&self) -> bool;
fn is_numeric(&self) -> bool {
self.is_i64() | self.is_f64()
}
fn is_list(&self) -> bool;
fn is_boolean(&self) -> bool;
fn to_f64(&self) -> Result<f64, ParamValueParseError>;
fn to_f32(&self) -> Result<f32, ParamValueParseError> {
let v = self.to_f64()?;
Ok(v as f32)
}
fn to_bool(&self) -> Result<bool, ParamValueParseError>;
fn to_i64(&self) -> Result<i64, ParamValueParseError>;
fn to_i32(&self) -> Result<i32, ParamValueParseError> {
let v = self.to_i64()?;
Ok(v as i32)
}
fn to_u64(&self) -> Result<u64, ParamValueParseError> {
let v = self.to_i64()?;
Ok(v as u64)
}
fn to_str(&self) -> Cow<'_, str>;
fn as_str(&self) -> Cow<'_, str> {
self.to_str()
}
fn to_buffer(&self) -> Result<Cow<'_, [u8]>, ParamValueParseError>;
fn as_slice(&self) -> Cow<'_, [Value]>;
fn parse<T: FromStr>(&self) -> Result<T, T::Err>;
fn as_bytes(&self) -> Cow<'_, [u8]>;
fn as_ref(&self) -> ValueRef<'_>;
fn data_len(&self) -> usize;
}
#[derive(Debug, Clone, Error, PartialEq)]
pub enum ParamValueParseError {
#[error("Failed to extract a float from {0:?}")]
FailedToExtractFloat(Option<String>),
#[error("Failed to extract a int from {0:?}")]
FailedToExtractInt(Option<String>),
#[error("Failed to extract a string")]
FailedToExtractString,
#[error("Failed to extract a buffer")]
FailedToExtractBuffer,
}
impl FromStr for Value {
type Err = ParamValueParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_empty() {
return Ok(Self::Empty);
}
if let Ok(value) = s.parse::<i64>() {
Ok(Self::Int(value))
} else if let Ok(value) = s.parse::<f64>() {
Ok(Self::Float(value))
} else if let Ok(value) = s.parse::<bool>() {
Ok(Self::Boolean(value))
} else {
Ok(Self::String(s.to_string()))
}
}
}
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::String(v) => f.write_str(v),
Value::Float(v) => v.fmt(f),
Value::Int(v) => v.fmt(f),
Value::Buffer(v) => f.write_str(&String::from_utf8_lossy(v)),
Value::Empty => f.write_str(""),
Value::Boolean(v) => v.fmt(f),
Value::List(v) => {
f.write_str("[ ")?;
if let Some(vi) = v.first() {
vi.fmt(f)?;
}
for vi in v.iter().skip(1) {
f.write_str(", ")?;
vi.fmt(f)?;
}
f.write_str(" ]")
}
}
}
}
impl From<ParamValueParseError> for io::Error {
fn from(value: ParamValueParseError) -> Self {
Self::new(io::ErrorKind::InvalidData, value)
}
}
impl Value {
pub fn new(s: String) -> Self {
if s.is_empty() {
Self::Empty
} else if let Ok(value) = s.parse::<i64>() {
Self::Int(value)
} else if let Ok(value) = s.parse::<f64>() {
Self::Float(value)
} else if let Ok(value) = s.parse::<bool>() {
Self::Boolean(value)
} else {
Self::String(s)
}
}
pub fn wrap(s: &str) -> Self {
if s.is_empty() {
Self::Empty
} else if let Ok(value) = s.parse::<i64>() {
Self::Int(value)
} else if let Ok(value) = s.parse::<f64>() {
Self::Float(value)
} else {
Self::String(s.to_string())
}
}
fn is_empty(&self) -> bool {
matches!(self, Self::Empty)
}
fn is_i64(&self) -> bool {
matches!(self, Self::Int(_))
}
fn is_f64(&self) -> bool {
matches!(self, Self::Float(_))
}
fn is_buffer(&self) -> bool {
matches!(self, Self::Buffer(_))
}
fn is_str(&self) -> bool {
matches!(self, Self::String(_))
}
fn is_list(&self) -> bool {
matches!(self, Self::List(_))
}
pub fn coerce_f64(&mut self) -> Result<(), ParamValueParseError> {
let value = self.to_f64()?;
*self = Self::Float(value);
Ok(())
}
pub fn coerce_i64(&mut self) -> Result<(), ParamValueParseError> {
let value = self.to_i64()?;
*self = Self::Int(value);
Ok(())
}
pub fn coerce_str(&mut self) -> Result<(), ParamValueParseError> {
let value = self.to_string();
*self = Self::String(value);
Ok(())
}
pub fn coerce_empty(&mut self) {
*self = Self::Empty;
}
pub fn coerce_buffer(&mut self) -> Result<(), ParamValueParseError> {
let buffer = self.to_buffer()?;
*self = Self::Buffer(buffer.into());
Ok(())
}
pub fn coerce_bool(&mut self) -> Result<(), ParamValueParseError> {
let value = self.to_bool()?;
*self = Self::Boolean(value);
Ok(())
}
pub fn coerce_list(&mut self) -> Result<(), ParamValueParseError> {
if !self.is_list() {
let mut tmp = Self::Empty;
core::mem::swap(&mut tmp, self);
*self = Self::List([tmp].into());
}
Ok(())
}
pub fn parse<T: FromStr>(&self) -> Result<T, T::Err> {
match self {
Value::String(s) => s.parse(),
Value::Float(v) => v.to_string().parse(),
Value::Int(i) => i.to_string().parse(),
Value::Buffer(b) => String::from_utf8_lossy(b).parse(),
Value::Empty => "".parse(),
Value::Boolean(b) => b.to_string().parse(),
Value::List(_) => self.to_string().parse(),
}
}
fn to_bool(&self) -> Result<bool, ParamValueParseError> {
if let Self::Boolean(val) = self {
Ok(*val)
} else if self.is_numeric() {
Ok(self.to_i64()? != 0)
} else if let Self::Empty = self {
Ok(false)
} else if let Ok(v) = self.parse() {
Ok(v)
} else {
Err(ParamValueParseError::FailedToExtractInt(Some(
self.to_string(),
)))
}
}
fn to_f64(&self) -> Result<f64, ParamValueParseError> {
if let Self::Float(val) = self {
return Ok(*val);
} else if let Self::Int(val) = self {
return Ok(*val as f64);
} else if let Self::String(val) = self {
if let Ok(v) = val.parse() {
return Ok(v);
}
}
Err(ParamValueParseError::FailedToExtractFloat(Some(
self.to_string(),
)))
}
fn to_i64(&self) -> Result<i64, ParamValueParseError> {
if let Self::Int(val) = self {
return Ok(*val);
} else if let Self::Float(val) = self {
return Ok(*val as i64);
} else if let Self::String(val) = self {
if let Ok(v) = val.parse() {
return Ok(v);
}
}
Err(ParamValueParseError::FailedToExtractInt(Some(
self.to_string(),
)))
}
fn to_str(&self) -> Cow<'_, str> {
if let Self::String(val) = self {
Cow::Borrowed(val)
} else {
Cow::Owned(self.to_string())
}
}
fn to_buffer(&self) -> Result<Cow<'_, [u8]>, ParamValueParseError> {
if let Self::Buffer(val) = self {
Ok(Cow::Borrowed(val))
} else if let Self::String(val) = self {
Ok(Cow::Borrowed(val.as_bytes()))
} else {
Err(ParamValueParseError::FailedToExtractBuffer)
}
}
fn as_ref(&self) -> ValueRef<'_> {
self.into()
}
pub fn as_slice(&self) -> &[Self] {
if let Self::List(val) = self {
val.as_ref()
} else {
core::slice::from_ref(self)
}
}
}
impl ParamValue for Value {
fn is_empty(&self) -> bool {
self.is_empty()
}
fn is_i64(&self) -> bool {
self.is_i64()
}
fn is_f64(&self) -> bool {
self.is_f64()
}
fn is_buffer(&self) -> bool {
self.is_buffer()
}
fn is_str(&self) -> bool {
self.is_str()
}
fn to_f64(&self) -> Result<f64, ParamValueParseError> {
self.to_f64()
}
fn to_i64(&self) -> Result<i64, ParamValueParseError> {
self.to_i64()
}
fn to_str(&self) -> Cow<'_, str> {
self.to_str()
}
fn to_buffer(&self) -> Result<Cow<'_, [u8]>, ParamValueParseError> {
self.to_buffer()
}
fn parse<T: FromStr>(&self) -> Result<T, T::Err> {
self.parse()
}
fn as_bytes(&self) -> Cow<'_, [u8]> {
match self {
Self::String(v) => Cow::Borrowed(v.as_bytes()),
Self::Buffer(v) => Cow::Borrowed(v.as_ref()),
Self::Float(v) => Cow::Owned(v.to_string().into_bytes()),
Self::Int(v) => Cow::Owned(v.to_string().into_bytes()),
Self::Empty => Cow::Borrowed(b""),
Self::Boolean(v) => Cow::Owned(v.to_string().into_bytes()),
Self::List(_) => Cow::Owned(self.to_string().into_bytes()),
}
}
fn as_ref(&self) -> ValueRef<'_> {
self.into()
}
fn data_len(&self) -> usize {
match self {
Self::String(v) => v.len(),
Self::Buffer(v) => v.len(),
Self::Float(_) => 8,
Self::Int(_) => 8,
Self::Empty => 0,
Self::Boolean(_) => mem::size_of::<bool>(),
Self::List(v) => v.iter().map(|vi| vi.data_len()).sum()
}
}
fn is_boolean(&self) -> bool {
matches!(self, Self::Boolean(_))
}
fn to_bool(&self) -> Result<bool, ParamValueParseError> {
self.to_bool()
}
fn is_list(&self) -> bool {
self.is_list()
}
fn as_slice(&self) -> Cow<'_, [Value]> {
Cow::Borrowed(self.as_slice())
}
}
impl PartialEq<String> for Value {
fn eq(&self, other: &String) -> bool {
self.as_str() == other.as_str()
}
}
impl PartialEq<str> for Value {
fn eq(&self, other: &str) -> bool {
self.as_str() == other
}
}
impl PartialEq<&str> for Value {
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
}
}
impl PartialEq<i64> for Value {
fn eq(&self, other: &i64) -> bool {
if let Self::Int(val) = self {
val == other
} else {
false
}
}
}
impl PartialEq<f64> for Value {
fn eq(&self, other: &f64) -> bool {
if let Self::Float(val) = self {
val == other
} else {
false
}
}
}
impl PartialEq<bool> for Value {
fn eq(&self, other: &bool) -> bool {
if let Self::Boolean(val) = self {
val == other
} else {
false
}
}
}
impl Hash for Value {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
core::mem::discriminant(self).hash(state);
match self {
Self::String(s) => s.hash(state),
Self::Float(v) => v.to_bits().hash(state),
Self::Int(v) => (*v).hash(state),
Self::Buffer(v) => v.hash(state),
Self::Empty => 0u8.hash(state),
Self::Boolean(v) => v.hash(state),
Self::List(v) => {
v.iter().for_each(|vi| vi.hash(state));
}
}
}
}
macro_rules! param_value_int {
($val:ty) => {
impl From<$val> for Value {
fn from(value: $val) -> Self {
Self::Int(value as i64)
}
}
impl From<&$val> for Value {
fn from(value: &$val) -> Self {
Self::Int(*value as i64)
}
}
impl From<Option<$val>> for Value {
fn from(value: Option<$val>) -> Self {
if let Some(v) = value {
Self::Int(v as i64)
} else {
Self::Empty
}
}
}
};
}
macro_rules! param_value_float {
($val:ty) => {
impl From<$val> for Value {
fn from(value: $val) -> Self {
Self::Float(value as f64)
}
}
impl From<&$val> for Value {
fn from(value: &$val) -> Self {
Self::Float(*value as f64)
}
}
impl From<Option<$val>> for Value {
fn from(value: Option<$val>) -> Self {
if let Some(v) = value {
Self::Float(v as f64)
} else {
Self::Empty
}
}
}
};
}
param_value_int!(i8);
param_value_int!(i16);
param_value_int!(i32);
param_value_int!(i64);
param_value_int!(u8);
param_value_int!(u16);
param_value_int!(u32);
param_value_int!(u64);
param_value_int!(usize);
param_value_float!(f32);
param_value_float!(f64);
#[derive(Debug, Clone, PartialEq, PartialOrd, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub enum ValueRef<'a> {
String(Cow<'a, str>),
Float(f64),
Int(i64),
Buffer(Cow<'a, [u8]>),
#[default]
Empty,
Boolean(bool),
List(Cow<'a, [Value]>),
}
impl Eq for ValueRef<'_> {}
impl From<String> for ValueRef<'_> {
fn from(value: String) -> Self {
value.parse().unwrap()
}
}
impl<'a> From<&'a str> for ValueRef<'a> {
fn from(value: &'a str) -> Self {
ValueRef::new(value)
}
}
impl<'a> From<Cow<'a, str>> for ValueRef<'a> {
fn from(value: Cow<'a, str>) -> Self {
match value {
Cow::Borrowed(s) => Self::new(s),
Cow::Owned(s) => s.parse().unwrap(),
}
}
}
impl PartialEq<String> for ValueRef<'_> {
fn eq(&self, other: &String) -> bool {
self.as_str() == other.as_str()
}
}
impl PartialEq<str> for ValueRef<'_> {
fn eq(&self, other: &str) -> bool {
self.as_str() == other
}
}
impl PartialEq<&str> for ValueRef<'_> {
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
}
}
impl PartialEq<i64> for ValueRef<'_> {
fn eq(&self, other: &i64) -> bool {
if let Self::Int(val) = self {
val == other
} else {
false
}
}
}
impl PartialEq<f64> for ValueRef<'_> {
fn eq(&self, other: &f64) -> bool {
if let Self::Float(val) = self {
val == other
} else {
false
}
}
}
impl PartialEq<bool> for ValueRef<'_> {
fn eq(&self, other: &bool) -> bool {
if let Self::Boolean(val) = self {
val == other
} else {
false
}
}
}
impl FromStr for ValueRef<'_> {
type Err = ParamValueParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_empty() {
return Ok(Self::Empty);
}
if let Ok(value) = s.parse() {
Ok(Self::Int(value))
} else if let Ok(value) = s.parse() {
Ok(Self::Float(value))
} else if let Ok(value) = s.parse() {
Ok(Self::Boolean(value))
} else {
Ok(Self::String(Cow::Owned(s.to_string())))
}
}
}
impl Display for ValueRef<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::String(v) => f.write_str(v),
Self::Float(v) => v.fmt(f),
Self::Int(v) => v.fmt(f),
Self::Buffer(v) => f.write_str(&String::from_utf8_lossy(v)),
Self::Empty => f.write_str(""),
Self::Boolean(v) => v.fmt(f),
Self::List(v) => {
f.write_str("[ ")?;
if let Some(vi) = v.first() {
vi.fmt(f)?;
}
for vi in v.iter().skip(1) {
f.write_str(", ")?;
vi.fmt(f)?;
}
f.write_str(" ]")
}
}
}
}
impl<'a> ValueRef<'a> {
pub fn new(s: &'a str) -> Self {
if s.is_empty() {
return Self::Empty;
}
if let Ok(value) = s.parse::<i64>() {
Self::Int(value)
} else if let Ok(value) = s.parse::<f64>() {
Self::Float(value)
} else if let Ok(value) = s.parse() {
Self::Boolean(value)
} else {
Self::String(Cow::Borrowed(s))
}
}
pub const fn wrap(s: &'a str) -> Self {
Self::String(Cow::Borrowed(s))
}
fn is_empty(&self) -> bool {
matches!(self, Self::Empty)
}
fn is_i64(&self) -> bool {
matches!(self, Self::Int(_))
}
fn is_f64(&self) -> bool {
matches!(self, Self::Float(_))
}
fn is_buffer(&self) -> bool {
matches!(self, Self::Buffer(_))
}
fn is_str(&self) -> bool {
matches!(self, Self::String(_))
}
fn is_list(&self) -> bool {
matches!(self, Self::List(_))
}
fn to_bool(&self) -> Result<bool, ParamValueParseError> {
if let Self::Boolean(val) = self {
Ok(*val)
} else if self.is_numeric() {
Ok(self.to_i64()? != 0)
} else if let Self::Empty = self {
Ok(false)
} else if let Ok(v) = self.parse() {
Ok(v)
} else {
Err(ParamValueParseError::FailedToExtractInt(Some(
self.to_string(),
)))
}
}
pub fn coerce_bool(&mut self) -> Result<(), ParamValueParseError> {
let value = self.to_bool()?;
*self = Self::Boolean(value);
Ok(())
}
pub fn coerce_f64(&mut self) -> Result<(), ParamValueParseError> {
let value = self.to_f64()?;
*self = Self::Float(value);
Ok(())
}
pub fn coerce_i64(&mut self) -> Result<(), ParamValueParseError> {
let value = self.to_i64()?;
*self = Self::Int(value);
Ok(())
}
pub fn coerce_str(&mut self) -> Result<(), ParamValueParseError> {
if self.is_str() {
} else {
let value = self.to_string();
*self = Self::String(Cow::Owned(value));
}
Ok(())
}
pub fn coerce_empty(&mut self) {
*self = Self::Empty;
}
pub fn coerce_buffer(&mut self) -> Result<(), ParamValueParseError> {
if self.is_buffer() {
Ok(())
} else {
let buffer = Cow::Owned(self.to_buffer()?.to_vec());
*self = Self::Buffer(buffer);
Ok(())
}
}
pub fn coerce_list(&mut self) -> Result<(), ParamValueParseError> {
if !self.is_list() {
let dup = match self {
Self::Boolean(v) => Value::Boolean(*v),
Self::Empty => Value::Empty,
Self::Float(v) => Value::Float(*v),
Self::Int(v) => Value::Int(*v),
Self::Buffer(v) => Value::Buffer(v.to_vec().into()),
Self::String(v) => {
Value::String(v.to_string())
},
Self::List(_) => unimplemented!(),
};
*self = Self::List(Cow::Owned([dup].into()));
}
Ok(())
}
fn parse<T: FromStr>(&self) -> Result<T, T::Err> {
match self {
Self::String(s) => s.parse(),
Self::Float(v) => v.to_string().parse(),
Self::Int(i) => i.to_string().parse(),
Self::Buffer(b) => String::from_utf8_lossy(b).parse(),
Self::Empty => "".parse(),
Self::Boolean(v) => v.to_string().parse(),
Self::List(_) => self.to_string().parse(),
}
}
fn to_f64(&self) -> Result<f64, ParamValueParseError> {
if let Self::Float(val) = self {
return Ok(*val);
} else if let Self::Int(val) = self {
return Ok(*val as f64);
} else if let Self::String(val) = self {
if let Ok(v) = val.parse() {
return Ok(v);
}
}
Err(ParamValueParseError::FailedToExtractFloat(Some(
self.to_string(),
)))
}
fn to_i64(&self) -> Result<i64, ParamValueParseError> {
if let Self::Int(val) = self {
return Ok(*val);
} else if let Self::Float(val) = self {
return Ok(*val as i64);
} else if let Self::String(val) = self {
if let Ok(v) = val.parse() {
return Ok(v);
}
}
Err(ParamValueParseError::FailedToExtractInt(Some(
self.to_string(),
)))
}
fn to_str(&self) -> Cow<'_, str> {
if let Self::String(val) = self {
Cow::Borrowed(val)
} else {
Cow::Owned(self.to_string())
}
}
fn to_buffer(&self) -> Result<Cow<'_, [u8]>, ParamValueParseError> {
if let Self::Buffer(val) = self {
match val {
Cow::Borrowed(v) => Ok(Cow::Borrowed(*v)),
Cow::Owned(v) => Ok(Cow::Borrowed(v)),
}
} else if let Self::String(val) = self {
Ok(Cow::Borrowed(val.as_bytes()))
} else {
Err(ParamValueParseError::FailedToExtractBuffer)
}
}
}
impl ParamValue for ValueRef<'_> {
fn is_empty(&self) -> bool {
self.is_empty()
}
fn is_i64(&self) -> bool {
self.is_i64()
}
fn is_f64(&self) -> bool {
self.is_f64()
}
fn is_buffer(&self) -> bool {
self.is_buffer()
}
fn is_str(&self) -> bool {
self.is_str()
}
fn is_boolean(&self) -> bool {
matches!(self, Self::Boolean(_))
}
fn to_bool(&self) -> Result<bool, ParamValueParseError> {
self.to_bool()
}
fn to_f64(&self) -> Result<f64, ParamValueParseError> {
self.to_f64()
}
fn to_i64(&self) -> Result<i64, ParamValueParseError> {
self.to_i64()
}
fn to_str(&self) -> Cow<'_, str> {
self.to_str()
}
fn to_buffer(&self) -> Result<Cow<'_, [u8]>, ParamValueParseError> {
self.to_buffer()
}
fn parse<T: FromStr>(&self) -> Result<T, T::Err> {
self.parse()
}
fn as_bytes(&self) -> Cow<'_, [u8]> {
match self {
Self::String(v) => Cow::Borrowed(v.as_bytes()),
Self::Buffer(v) => Cow::Borrowed(v.as_ref()),
Self::Float(v) => Cow::Owned(v.to_string().into_bytes()),
Self::Int(v) => Cow::Owned(v.to_string().into_bytes()),
Self::Empty => Cow::Borrowed(b""),
Self::Boolean(v) => Cow::Owned(v.to_string().into_bytes()),
Self::List(_) => Cow::Owned(self.to_string().into_bytes())
}
}
fn as_ref(&self) -> ValueRef<'_> {
self.clone()
}
fn data_len(&self) -> usize {
match self {
Self::String(v) => v.len(),
Self::Buffer(v) => v.len(),
Self::Float(_) => 8,
Self::Int(_) => 8,
Self::Empty => 0,
Self::Boolean(_) => mem::size_of::<bool>(),
Self::List(v) => v.iter().map(|vi| vi.data_len()).sum()
}
}
fn is_list(&self) -> bool {
self.is_list()
}
fn as_slice(&self) -> Cow<'_, [Value]> {
match self {
Self::List(v) => {
Cow::Borrowed(v)
},
_ => {
let dup = match self {
Self::Boolean(v) => Value::Boolean(*v),
Self::Empty => Value::Empty,
Self::Float(v) => Value::Float(*v),
Self::Int(v) => Value::Int(*v),
Self::Buffer(v) => Value::Buffer(v.to_vec().into()),
Self::String(v) => {
Value::String(v.to_string())
},
Self::List(_) => unimplemented!(),
};
Cow::Owned([dup].into())
}
}
}
}
impl<'a> From<&'a Value> for ValueRef<'a> {
fn from(value: &'a Value) -> Self {
match value {
Value::String(s) => Self::String(Cow::Borrowed(s)),
Value::Float(v) => Self::Float(*v),
Value::Int(v) => Self::Int(*v),
Value::Buffer(v) => Self::Buffer(Cow::Borrowed(v)),
Value::Empty => Self::Empty,
Value::Boolean(v) => Self::Boolean(*v),
Value::List(v) => Self::List(Cow::Borrowed(v))
}
}
}
impl PartialEq<Value> for ValueRef<'_> {
fn eq(&self, other: &Value) -> bool {
*self == other.as_ref()
}
}
impl<'a> PartialEq<ValueRef<'a>> for Value {
fn eq(&self, other: &ValueRef<'a>) -> bool {
self.as_ref() == *other
}
}
impl<'a> From<ValueRef<'a>> for Value {
fn from(value: ValueRef<'a>) -> Self {
match value {
ValueRef::String(s) => match s {
Cow::Borrowed(s) => Self::String(s.to_string()),
Cow::Owned(s) => Self::String(s),
},
ValueRef::Float(v) => Self::Float(v),
ValueRef::Int(v) => Self::Int(v),
ValueRef::Buffer(v) => Self::Buffer(v.to_vec().into_boxed_slice()),
ValueRef::Empty => Self::Empty,
ValueRef::Boolean(v) => Self::Boolean(v),
ValueRef::List(v) => {
let mut ve = Vec::with_capacity(v.len());
for vi in v.iter() {
ve.push(vi.clone())
}
Self::List(ve.into_boxed_slice())
}
}
}
}
impl Hash for ValueRef<'_> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
core::mem::discriminant(self).hash(state);
match self {
Self::String(s) => s.hash(state),
Self::Float(v) => v.to_bits().hash(state),
Self::Int(v) => (*v).hash(state),
Self::Buffer(v) => v.hash(state),
Self::Empty => 0u8.hash(state),
Self::Boolean(v) => (*v).hash(state),
Self::List(v) => v.iter().for_each(|vi| vi.hash(state)),
}
}
}
macro_rules! param_value_ref_int {
($val:ty) => {
impl<'a> From<$val> for ValueRef<'a> {
fn from(value: $val) -> Self {
Self::Int(value as i64)
}
}
impl<'a> From<&$val> for ValueRef<'a> {
fn from(value: &$val) -> Self {
Self::Int(*value as i64)
}
}
impl<'a> From<Option<$val>> for ValueRef<'a> {
fn from(value: Option<$val>) -> Self {
if let Some(v) = value {
Self::Int(v as i64)
} else {
Self::Empty
}
}
}
};
}
macro_rules! param_value_ref_float {
($val:ty) => {
impl<'a> From<$val> for ValueRef<'a> {
fn from(value: $val) -> Self {
Self::Float(value as f64)
}
}
impl<'a> From<&$val> for ValueRef<'a> {
fn from(value: &$val) -> Self {
Self::Float(*value as f64)
}
}
impl<'a> From<Option<$val>> for ValueRef<'a> {
fn from(value: Option<$val>) -> Self {
if let Some(v) = value {
Self::Float(v as f64)
} else {
Self::Empty
}
}
}
};
}
impl From<ValueRef<'_>> for f32 {
fn from(value: ValueRef<'_>) -> Self {
value.to_f32().unwrap()
}
}
impl From<ValueRef<'_>> for f64 {
fn from(value: ValueRef<'_>) -> Self {
value.to_f64().unwrap()
}
}
impl From<ValueRef<'_>> for i32 {
fn from(value: ValueRef<'_>) -> Self {
value.to_i32().unwrap()
}
}
impl From<ValueRef<'_>> for i64 {
fn from(value: ValueRef<'_>) -> Self {
value.to_i64().unwrap()
}
}
impl From<bool> for Value {
fn from(value: bool) -> Self {
Self::Boolean(value)
}
}
impl From<bool> for ValueRef<'_> {
fn from(value: bool) -> Self {
Self::Boolean(value)
}
}
param_value_ref_int!(i8);
param_value_ref_int!(i16);
param_value_ref_int!(i32);
param_value_ref_int!(i64);
param_value_ref_int!(u8);
param_value_ref_int!(u16);
param_value_ref_int!(u32);
param_value_ref_int!(u64);
param_value_ref_int!(usize);
param_value_ref_float!(f32);
param_value_ref_float!(f64);
#[cfg(feature = "serde")]
impl From<Value> for serde_json::Value {
fn from(value: Value) -> Self {
match value {
Value::Boolean(val) => serde_json::Value::Bool(val),
Value::Float(val) => {
serde_json::Value::Number(serde_json::Number::from_f64(val).unwrap())
}
Value::Int(val) => {
serde_json::Value::Number(serde_json::Number::from_i128(val as i128).unwrap())
}
Value::String(val) => serde_json::Value::String(val),
Value::Buffer(val) => serde_json::to_value(&val).unwrap(),
Value::Empty => serde_json::Value::Null,
Value::List(val) => {
let mut ve = Vec::new();
for vi in val {
ve.push(vi.into());
}
serde_json::Value::Array(ve)
}
}
}
}
pub type AccessionIntCode = u32;
pub type AccessionByteCode7 = [u8; 7];
#[allow(unused)]
#[derive(Debug)]
#[repr(u8)]
enum AccessionCode {
Int(AccessionIntCode),
Byte7(AccessionByteCode7),
}
impl Display for AccessionCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AccessionCode::Int(v) => write!(f, "{v:07}"),
AccessionCode::Byte7(v) => write!(
f,
"{}",
core::str::from_utf8(v)
.map_err(|e| format!("ERROR:{e}"))
.unwrap()
),
}
}
}
#[derive(Debug, thiserror::Error, Clone, PartialEq)]
pub enum AccessionCodeParseError {
#[error("The acccession code was too long: {0}")]
AccessionCodeTooLong(String),
#[error("The acccession code was not in range: {0}")]
AccessionCodeNotInRange(String),
}
impl FromStr for AccessionCode {
type Err = AccessionCodeParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() > 7 {
return Err(AccessionCodeParseError::AccessionCodeTooLong(s.to_string()));
}
if !s.is_ascii() {
return Err(AccessionCodeParseError::AccessionCodeNotInRange(
s.to_string(),
));
}
if let Ok(u) = s.parse::<AccessionIntCode>() {
Ok(Self::Int(u))
} else {
let mut bytes = AccessionByteCode7::default();
for (byte_from, byte_to) in s.as_bytes().iter().rev().zip(bytes.iter_mut().rev()) {
*byte_to = *byte_from;
}
Ok(Self::Byte7(bytes))
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CURIE {
pub controlled_vocabulary: ControlledVocabulary,
pub accession: AccessionIntCode,
}
#[allow(unused)]
macro_rules! accessioncode {
($acc:literal) => {
AccessionCode::Int($acc)
};
($acc:tt) => {
match stringify!($acc).as_bytes() {
[a, b, c, d, e, f, g] => AccessionCode::Byte7([*a, *b, *c, *d, *e, *f, *g]),
_ => panic!(concat!(
"Cannot convert ",
stringify!($acc),
" to accession code. Expected exactly 7 bytes"
)),
}
};
}
#[macro_export]
macro_rules! find_param_method {
($meth:ident, $curie:expr) => {
$crate::find_param_method!($meth, $curie, "Find a parameter by its CURIE");
};
($meth:ident, $curie:expr, $desc:literal) => {
#[doc=$desc]
pub fn $meth(&self) -> Option<$crate::params::ValueRef<'_>> {
self.get_param_by_curie($curie)
.map(|p| $crate::params::ParamLike::value(p))
}
};
($meth:ident, $curie:expr, $conv:expr, $result:ty) => {
$crate::find_param_method!(
$meth,
$curie,
$conv,
$result,
"Find a parameter by its CURIE"
);
};
($meth:ident, $curie:expr, $conv:expr, $result:ty, $desc:literal) => {
#[doc=$desc]
pub fn $meth(&self) -> $result {
self.get_param_by_curie($curie).map($conv)
}
};
}
#[macro_export]
macro_rules! curie {
($ns:ident:$acc:literal) => {
$crate::params::CURIE {
controlled_vocabulary: $crate::params::ControlledVocabulary::$ns,
accession: $acc,
}
};
(IMZML:$acc:literal) => {
$crate::params::CURIE { controlled_vocabulary: $crate::params::ControlledVocabulary::IMZML, accession: $acc }
};
}
impl CURIE {
pub const fn new(cv_id: ControlledVocabulary, accession: AccessionIntCode) -> Self {
Self {
controlled_vocabulary: cv_id,
accession,
}
}
pub fn as_param(&self) -> Param {
let mut param = Param::new();
param.controlled_vocabulary = Some(self.controlled_vocabulary);
param.accession = Some(self.accession);
param
}
#[inline(always)]
pub fn accession_int(&self) -> u32 {
self.accession
}
#[inline(always)]
pub fn controlled_vocabulary(&self) -> ControlledVocabulary {
self.controlled_vocabulary
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct PackedCURIE(u64);
#[allow(unused)]
impl PackedCURIE {
pub fn from_curie(curie: CURIE) -> Self {
let cv = curie.controlled_vocabulary();
let cv_id = cv as u64;
let acc_code = curie.accession_int();
let code = acc_code as u64;
Self(cv_id << 56 | code)
}
pub fn accession(&self) -> u64 {
self.0 & 0x00ffffffffffffff
}
pub fn controlled_vocabulary(&self) -> ControlledVocabulary {
((self.0 & 0xff00000000000000) as u8).try_into().unwrap()
}
}
impl Display for CURIE {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}:{:07}",
self.controlled_vocabulary.prefix(),
self.accession
)
}
}
impl<T: ParamLike> PartialEq<T> for CURIE {
fn eq(&self, other: &T) -> bool {
if !other.is_controlled()
|| other
.controlled_vocabulary()
.map(|c| c != self.controlled_vocabulary)
.unwrap_or_default()
{
false
} else {
other
.accession()
.map(|a| a == self.accession)
.unwrap_or_default()
}
}
}
#[derive(Debug, Error)]
pub enum CURIEParsingError {
#[error("{0} is not a recognized controlled vocabulary")]
UnknownControlledVocabulary(
#[from]
#[source]
ControlledVocabularyResolutionError,
),
#[error("Failed to parse accession number {0}")]
AccessionParsingError(
#[from]
#[source]
num::ParseIntError,
),
#[error("Did not detect a namespace separator ':' token")]
MissingNamespaceSeparator,
}
impl FromStr for CURIE {
type Err = CURIEParsingError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut tokens = s.split(':');
let cv = tokens
.next()
.ok_or(CURIEParsingError::MissingNamespaceSeparator)?;
let accession = tokens.next();
if accession.is_none() {
Err(CURIEParsingError::MissingNamespaceSeparator)
} else {
let cv: ControlledVocabulary = cv.parse::<ControlledVocabulary>()?;
let accession = accession.unwrap().parse()?;
Ok(CURIE::new(cv, accession))
}
}
}
impl TryFrom<&Param> for CURIE {
type Error = String;
fn try_from(value: &Param) -> Result<Self, Self::Error> {
match (value.controlled_vocabulary, value.accession) {
(Some(cv), Some(acc)) => Ok(CURIE::new(cv, acc)),
_ => Err(format!(
"{} is missing controlled vocabulary or accession",
value.name()
)),
}
}
}
impl<'a> TryFrom<&ParamCow<'a>> for CURIE {
type Error = String;
fn try_from(value: &ParamCow<'a>) -> Result<Self, Self::Error> {
match (value.controlled_vocabulary, value.accession) {
(Some(cv), Some(acc)) => Ok(CURIE::new(cv, acc)),
_ => Err(format!(
"{} is missing controlled vocabulary or accession",
value.name()
)),
}
}
}
pub fn curie_to_num(curie: &str) -> (Option<ControlledVocabulary>, Option<AccessionIntCode>) {
let mut parts = curie.split(':');
let prefix = match parts.next() {
Some(v) => v.parse::<ControlledVocabulary>().unwrap().as_option(),
None => None,
};
if let Some(k) = curie.split(':').nth(1) {
match k.parse() {
Ok(v) => (prefix, Some(v)),
Err(_) => (prefix, None),
}
} else {
(prefix, None)
}
}
pub trait ParamLike {
fn name(&self) -> &str;
fn value(&self) -> ValueRef<'_>;
fn accession(&self) -> Option<AccessionIntCode>;
fn controlled_vocabulary(&self) -> Option<ControlledVocabulary>;
fn unit(&self) -> Unit;
fn is_ms(&self) -> bool {
if let Some(cv) = self.controlled_vocabulary() {
cv == ControlledVocabulary::MS
} else {
false
}
}
fn parse<T: str::FromStr>(&self) -> Result<T, T::Err> {
self.value().parse::<T>()
}
fn is_controlled(&self) -> bool {
self.accession().is_some()
}
fn curie(&self) -> Option<CURIE> {
if !self.is_controlled() {
None
} else {
let cv = self.controlled_vocabulary().unwrap();
let acc = self.accession().unwrap();
Some(CURIE::new(cv, acc))
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ParamCow<'a> {
pub name: Cow<'a, str>,
pub value: ValueRef<'a>,
pub accession: Option<AccessionIntCode>,
pub controlled_vocabulary: Option<ControlledVocabulary>,
pub unit: Unit,
}
impl<'a> ParamValue for ParamCow<'a> {
fn is_empty(&self) -> bool {
<ValueRef<'a> as ParamValue>::is_empty(&self.value)
}
fn is_i64(&self) -> bool {
<ValueRef<'a> as ParamValue>::is_i64(&self.value)
}
fn is_f64(&self) -> bool {
<ValueRef<'a> as ParamValue>::is_f64(&self.value)
}
fn is_buffer(&self) -> bool {
<ValueRef<'a> as ParamValue>::is_buffer(&self.value)
}
fn is_str(&self) -> bool {
<ValueRef<'a> as ParamValue>::is_str(&self.value)
}
fn to_f64(&self) -> Result<f64, ParamValueParseError> {
<ValueRef<'a> as ParamValue>::to_f64(&self.value)
}
fn to_i64(&self) -> Result<i64, ParamValueParseError> {
<ValueRef<'a> as ParamValue>::to_i64(&self.value)
}
fn to_str(&self) -> Cow<'_, str> {
<ValueRef<'a> as ParamValue>::to_str(&self.value)
}
fn to_buffer(&self) -> Result<Cow<'_, [u8]>, ParamValueParseError> {
<ValueRef<'a> as ParamValue>::to_buffer(&self.value)
}
fn parse<T: FromStr>(&self) -> Result<T, T::Err> {
<ValueRef<'a> as ParamValue>::parse(&self.value)
}
fn as_bytes(&self) -> Cow<'_, [u8]> {
<ValueRef<'a> as ParamValue>::as_bytes(&self.value)
}
fn as_ref(&self) -> ValueRef<'_> {
<ValueRef<'a> as ParamValue>::as_ref(&self.value)
}
fn data_len(&self) -> usize {
<ValueRef<'a> as ParamValue>::data_len(&self.value)
}
fn is_boolean(&self) -> bool {
<ValueRef<'a> as ParamValue>::is_boolean(&self.value)
}
fn to_bool(&self) -> Result<bool, ParamValueParseError> {
<ValueRef<'a> as ParamValue>::to_bool(&self.value)
}
fn is_list(&self) -> bool {
<ValueRef<'a> as ParamValue>::is_list(&self.value)
}
fn as_slice(&self) -> Cow<'_, [Value]> {
<ValueRef<'a> as ParamValue>::as_slice(&self.value)
}
}
impl ParamCow<'static> {
pub const fn const_new(
name: &'static str,
value: ValueRef<'static>,
accession: Option<AccessionIntCode>,
controlled_vocabulary: Option<ControlledVocabulary>,
unit: Unit,
) -> Self {
Self {
name: Cow::Borrowed(name),
value,
accession,
controlled_vocabulary,
unit,
}
}
}
impl<'a> ParamCow<'a> {
pub fn new(
name: Cow<'a, str>,
value: ValueRef<'a>,
accession: Option<AccessionIntCode>,
controlled_vocabulary: Option<ControlledVocabulary>,
unit: Unit,
) -> Self {
Self {
name,
value,
accession,
controlled_vocabulary,
unit,
}
}
pub fn parse<T: str::FromStr>(&self) -> Result<T, T::Err> {
self.value.parse::<T>()
}
pub const fn is_controlled(&self) -> bool {
self.accession.is_some()
}
pub const fn curie(&self) -> Option<CURIE> {
match (self.controlled_vocabulary, self.accession) {
(Some(cv), Some(acc)) => Some(CURIE::new(cv, acc)),
_ => None,
}
}
}
impl<'a> ParamLike for ParamCow<'a> {
fn name(&self) -> &str {
&self.name
}
fn value(&self) -> ValueRef<'a> {
self.value.clone()
}
fn accession(&self) -> Option<AccessionIntCode> {
self.accession
}
fn controlled_vocabulary(&self) -> Option<ControlledVocabulary> {
self.controlled_vocabulary
}
fn unit(&self) -> Unit {
self.unit
}
}
impl<'a> From<ParamCow<'a>> for Param {
fn from(value: ParamCow<'a>) -> Self {
Param {
name: value.name.into_owned(),
value: value.value.into(),
accession: value.accession,
controlled_vocabulary: value.controlled_vocabulary,
unit: value.unit,
}
}
}
impl PartialEq<CURIE> for ParamCow<'_> {
fn eq(&self, other: &CURIE) -> bool {
other.eq(self)
}
}
impl<'a> AsRef<ValueRef<'a>> for ParamCow<'a> {
fn as_ref(&self) -> &ValueRef<'a> {
&self.value
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Param {
pub name: String,
pub value: Value,
pub accession: Option<AccessionIntCode>,
pub controlled_vocabulary: Option<ControlledVocabulary>,
pub unit: Unit,
}
impl AsRef<Value> for Param {
fn as_ref(&self) -> &Value {
&self.value
}
}
impl ParamValue for Param {
fn is_empty(&self) -> bool {
<Value as ParamValue>::is_empty(&self.value)
}
fn is_i64(&self) -> bool {
<Value as ParamValue>::is_i64(&self.value)
}
fn is_f64(&self) -> bool {
<Value as ParamValue>::is_f64(&self.value)
}
fn is_buffer(&self) -> bool {
<Value as ParamValue>::is_buffer(&self.value)
}
fn is_str(&self) -> bool {
<Value as ParamValue>::is_str(&self.value)
}
fn to_f64(&self) -> Result<f64, ParamValueParseError> {
<Value as ParamValue>::to_f64(&self.value)
}
fn to_i64(&self) -> Result<i64, ParamValueParseError> {
<Value as ParamValue>::to_i64(&self.value)
}
fn to_str(&self) -> Cow<'_, str> {
<Value as ParamValue>::to_str(&self.value)
}
fn to_buffer(&self) -> Result<Cow<'_, [u8]>, ParamValueParseError> {
<Value as ParamValue>::to_buffer(&self.value)
}
fn parse<T: FromStr>(&self) -> Result<T, T::Err> {
<Value as ParamValue>::parse(&self.value)
}
fn as_bytes(&self) -> Cow<'_, [u8]> {
<Value as ParamValue>::as_bytes(&self.value)
}
fn as_ref(&self) -> ValueRef<'_> {
<Value as ParamValue>::as_ref(&self.value)
}
fn data_len(&self) -> usize {
<Value as ParamValue>::data_len(&self.value)
}
fn is_boolean(&self) -> bool {
<Value as ParamValue>::is_boolean(&self.value)
}
fn to_bool(&self) -> Result<bool, ParamValueParseError> {
<Value as ParamValue>::to_bool(&self.value)
}
fn is_list(&self) -> bool {
<Value as ParamValue>::is_list(&self.value)
}
fn as_slice(&self) -> Cow<'_, [Value]> {
<Value as ParamValue>::as_slice(&self.value)
}
}
impl Display for Param {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut body = if self.is_controlled() {
format!(
"{}:{}|{}={}",
String::from_utf8_lossy(self.controlled_vocabulary.unwrap().as_bytes()),
self.accession.unwrap(),
self.name,
self.value
)
} else {
format!("{}={}", self.name, self.value)
};
if self.unit != Unit::Unknown {
body.extend(format!(" {}", self.unit).chars());
};
f.write_str(body.as_str())
}
}
#[derive(Default, Debug, Clone)]
pub struct ParamBuilder {
name: String,
value: Value,
accession: Option<AccessionIntCode>,
controlled_vocabulary: Option<ControlledVocabulary>,
unit: Unit,
}
impl ParamBuilder {
pub fn name<S: ToString>(mut self, name: S) -> Self {
self.name = name.to_string();
self
}
pub fn value<V: Into<Value>>(mut self, value: V) -> Self {
self.value = value.into();
self
}
pub fn controlled_vocabulary(mut self, cv: ControlledVocabulary) -> Self {
self.controlled_vocabulary = Some(cv);
self
}
pub fn accession(mut self, accession: AccessionIntCode) -> Self {
self.accession = Some(accession);
self
}
pub fn curie(mut self, curie: CURIE) -> Self {
self.controlled_vocabulary = Some(curie.controlled_vocabulary);
self.accession = Some(curie.accession);
self
}
pub fn unit(mut self, unit: Unit) -> Self {
self.unit = unit;
self
}
pub fn build(self) -> Param {
let mut this = Param::new();
this.name = self.name;
this.value = self.value;
this.controlled_vocabulary = self.controlled_vocabulary;
this.accession = self.accession;
this.unit = self.unit;
this
}
}
impl Param {
pub fn new() -> Param {
Param {
..Default::default()
}
}
pub fn builder() -> ParamBuilder {
ParamBuilder::default()
}
pub fn new_key_value<K: Into<String>, V: Into<Value>>(name: K, value: V) -> Param {
let mut inst = Self::new();
inst.name = name.into();
inst.value = value.into();
inst
}
pub fn parse<T: str::FromStr>(&self) -> Result<T, T::Err> {
self.value.parse::<T>()
}
pub const fn is_controlled(&self) -> bool {
self.accession.is_some()
}
pub const fn curie(&self) -> Option<CURIE> {
match (self.controlled_vocabulary, self.accession) {
(Some(cv), Some(acc)) => Some(CURIE::new(cv, acc)),
_ => None,
}
}
pub fn curie_str(&self) -> Option<String> {
self.curie().map(|c| c.to_string())
}
pub fn with_unit<S: AsRef<str>, A: AsRef<str>>(mut self, accession: S, name: A) -> Param {
self.unit = Unit::from_accession(accession.as_ref());
if matches!(self.unit, Unit::Unknown) {
self.unit = Unit::from_name(name.as_ref());
}
self
}
pub fn with_unit_t(mut self, unit: &Unit) -> Param {
self.unit = *unit;
self
}
}
impl ParamLike for Param {
fn name(&self) -> &str {
&self.name
}
fn value(&self) -> ValueRef<'_> {
self.value.as_ref()
}
fn accession(&self) -> Option<AccessionIntCode> {
self.accession
}
fn controlled_vocabulary(&self) -> Option<ControlledVocabulary> {
self.controlled_vocabulary
}
fn unit(&self) -> Unit {
self.unit
}
}
impl PartialEq<CURIE> for Param {
fn eq(&self, other: &CURIE) -> bool {
other.eq(self)
}
}
impl<'a> PartialEq<ParamCow<'a>> for Param {
fn eq(&self, other: &ParamCow<'a>) -> bool {
self.controlled_vocabulary == other.controlled_vocabulary
&& self.accession == other.accession
&& self.name == other.name
&& self.value == other.value
&& self.unit == other.unit
}
}
impl PartialEq<Param> for ParamCow<'_> {
fn eq(&self, other: &Param) -> bool {
self.controlled_vocabulary == other.controlled_vocabulary
&& self.accession == other.accession
&& self.name == other.name
&& self.value == other.value
&& self.unit == other.unit
}
}
impl Hash for Param {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
self.value.hash(state);
self.accession.hash(state);
self.controlled_vocabulary.hash(state);
self.unit.hash(state);
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)]
pub enum ControlledVocabulary {
MS = 1,
UO,
EFO,
OBI,
HANCESTRO,
BFO,
NCIT,
BTO,
PRIDE,
#[cfg(feature = "imzml")]
IMS,
Unknown,
}
const MS_CV: &str = "MS";
const UO_CV: &str = "UO";
const EFO_CV: &str = "EFO";
const OBI_CV: &str = "OBI";
const HANCESTRO_CV: &str = "HANCESTRO";
const BFO_CV: &str = "BFO";
const BTO_CV: &str = "BTO";
const NCIT_CV: &str = "NCIT";
const PRIDE_CV: &str = "PRIDE";
#[cfg(feature = "imzml")]
const IMS_CV: &str = "IMS";
const MS_CV_BYTES: &[u8] = MS_CV.as_bytes();
const UO_CV_BYTES: &[u8] = UO_CV.as_bytes();
const EFO_CV_BYTES: &[u8] = EFO_CV.as_bytes();
const OBI_CV_BYTES: &[u8] = OBI_CV.as_bytes();
const HANCESTRO_CV_BYTES: &[u8] = HANCESTRO_CV.as_bytes();
const BFO_CV_BYTES: &[u8] = BFO_CV.as_bytes();
const BTO_CV_BYTES: &[u8] = BTO_CV.as_bytes();
const NCIT_CV_BYTES: &[u8] = NCIT_CV.as_bytes();
const PRIDE_CV_BYTES: &[u8] = PRIDE_CV.as_bytes();
#[cfg(feature = "imzml")]
const IMS_CV_BYTES: &[u8] = IMS_CV.as_bytes();
impl TryFrom<u8> for ControlledVocabulary {
type Error = ControlledVocabularyResolutionError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
1 => Ok(Self::MS),
2 => Ok(Self::UO),
3 => Ok(Self::EFO),
4 => Ok(Self::OBI),
5 => Ok(Self::HANCESTRO),
6 => Ok(Self::BFO),
7 => Ok(Self::NCIT),
8 => Ok(Self::BTO),
9 => Ok(Self::PRIDE),
#[cfg(feature = "imzml")]
10 => Ok(Self::IMS),
_ => Err(ControlledVocabularyResolutionError::UnknownControlledVocabularyCode(value)),
}
}
}
#[derive(Debug, Clone)]
pub enum AccessionLike<'a> {
Text(Cow<'a, str>),
Number(AccessionIntCode),
CURIE(CURIE),
}
impl From<AccessionIntCode> for AccessionLike<'_> {
fn from(value: AccessionIntCode) -> Self {
Self::Number(value)
}
}
impl<'a> From<&'a str> for AccessionLike<'a> {
fn from(value: &'a str) -> Self {
Self::Text(Cow::Borrowed(value))
}
}
impl From<String> for AccessionLike<'_> {
fn from(value: String) -> Self {
Self::Text(Cow::Owned(value))
}
}
impl<'a> ControlledVocabulary {
pub const fn prefix(&self) -> Cow<'static, str> {
match &self {
Self::MS => Cow::Borrowed(MS_CV),
Self::UO => Cow::Borrowed(UO_CV),
Self::EFO => Cow::Borrowed(EFO_CV),
Self::OBI => Cow::Borrowed(OBI_CV),
Self::HANCESTRO => Cow::Borrowed(HANCESTRO_CV),
Self::BFO => Cow::Borrowed(BFO_CV),
Self::NCIT => Cow::Borrowed(NCIT_CV),
Self::BTO => Cow::Borrowed(BTO_CV),
Self::PRIDE => Cow::Borrowed(PRIDE_CV),
#[cfg(feature = "imzml")]
Self::IMS => Cow::Borrowed(IMS_CV),
Self::Unknown => panic!("Cannot encode unknown CV"),
}
}
pub const fn as_bytes(&self) -> &'static [u8] {
match &self {
Self::MS => MS_CV_BYTES,
Self::UO => UO_CV_BYTES,
Self::EFO => EFO_CV_BYTES,
Self::OBI => OBI_CV_BYTES,
Self::HANCESTRO => HANCESTRO_CV_BYTES,
Self::BFO => BFO_CV_BYTES,
Self::NCIT => NCIT_CV_BYTES,
Self::BTO => BTO_CV_BYTES,
Self::PRIDE => PRIDE_CV_BYTES,
#[cfg(feature = "imzml")]
Self::IMS => IMS_CV_BYTES,
Self::Unknown => panic!("Cannot encode unknown CV"),
}
}
pub const fn as_option(&self) -> Option<Self> {
match self {
Self::Unknown => None,
_ => Some(*self),
}
}
pub fn param<A: Into<AccessionLike<'a>>, S: Into<String>>(
&self,
accession: A,
name: S,
) -> Param {
let mut param = Param::new();
param.controlled_vocabulary = Some(*self);
param.name = name.into();
let accession: AccessionLike = accession.into();
match accession {
AccessionLike::Text(s) => {
if let Some(nb) = s.split(':').next_back() {
param.accession =
Some(nb.parse().unwrap_or_else(|_| {
panic!("Expected accession to be numeric, got {}", s)
}))
}
}
AccessionLike::Number(n) => param.accession = Some(n),
AccessionLike::CURIE(c) => param.accession = Some(c.accession),
}
param
}
pub const fn curie(&self, accession: AccessionIntCode) -> CURIE {
CURIE::new(*self, accession)
}
pub const fn const_param(
&self,
name: &'static str,
value: ValueRef<'static>,
accession: AccessionIntCode,
unit: Unit,
) -> ParamCow<'static> {
ParamCow {
name: Cow::Borrowed(name),
value,
accession: Some(accession),
controlled_vocabulary: Some(*self),
unit,
}
}
pub const fn const_param_ident(
&self,
name: &'static str,
accession: AccessionIntCode,
) -> ParamCow<'static> {
self.const_param(name, ValueRef::Empty, accession, Unit::Unknown)
}
pub const fn const_param_ident_unit(
&self,
name: &'static str,
accession: AccessionIntCode,
unit: Unit,
) -> ParamCow<'static> {
self.const_param(name, ValueRef::Empty, accession, unit)
}
pub fn param_val<S: Into<String>, A: Into<AccessionLike<'a>>, V: Into<Value>>(
&self,
accession: A,
name: S,
value: V,
) -> Param {
let mut param = self.param(accession, name);
param.value = value.into();
param
}
}
#[derive(Debug, Clone, Error)]
pub enum ControlledVocabularyResolutionError {
#[error("Unrecognized controlled vocabulary {0}")]
UnknownControlledVocabulary(String),
#[error("Unrecognized controlled vocabulary code {0}")]
UnknownControlledVocabularyCode(u8),
}
impl FromStr for ControlledVocabulary {
type Err = ControlledVocabularyResolutionError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"MS" | "PSI-MS" => Ok(Self::MS),
"UO" => Ok(Self::UO),
EFO_CV => Ok(Self::EFO),
OBI_CV => Ok(Self::OBI),
BFO_CV => Ok(Self::BFO),
HANCESTRO_CV => Ok(Self::HANCESTRO),
#[cfg(feature = "imzml")]
IMS_CV => Ok(Self::IMS),
_ => Ok(Self::Unknown),
}
}
}
pub type ParamList = Vec<Param>;
pub trait ParamDescribedRead {
fn params(&self) -> &[Param];
fn get_param_by_name(&self, name: &str) -> Option<&Param> {
self.params().iter().find(|¶m| param.name == name)
}
fn get_param_by_curie(&self, curie: &CURIE) -> Option<&Param> {
self.params().iter().find(|¶m| curie == param)
}
fn get_param_by_accession(&self, accession: &str) -> Option<&Param> {
let (cv, acc_num) = curie_to_num(accession);
self.params()
.iter()
.find(|¶m| param.accession == acc_num && param.controlled_vocabulary == cv)
}
fn iter_params(&self) -> std::slice::Iter<'_, Param> {
self.params().iter()
}
}
pub trait ParamDescribedMut {
fn params_mut(&mut self) -> &mut ParamList;
fn add_param(&mut self, param: Param) {
self.params_mut().push(param);
}
fn extend_params(&mut self, it: impl IntoIterator<Item = Param>) {
self.params_mut().extend(it)
}
fn remove_param(&mut self, index: usize) -> Param {
self.params_mut().remove(index)
}
fn iter_params_mut(&mut self) -> std::slice::IterMut<'_, Param> {
self.params_mut().iter_mut()
}
}
impl ParamDescribedRead for &[Param] {
fn params(&self) -> &[Param] {
self
}
}
impl ParamDescribedRead for Vec<Param> {
fn params(&self) -> &[Param] {
self.as_ref()
}
}
pub trait ParamDescribed {
fn params(&self) -> &[Param];
fn params_mut(&mut self) -> &mut ParamList;
fn add_param(&mut self, param: Param) {
self.params_mut().push(param);
}
fn extend_params(&mut self, it: impl IntoIterator<Item = Param>) {
self.params_mut().extend(it)
}
fn remove_param(&mut self, index: usize) -> Param {
self.params_mut().remove(index)
}
fn get_param_by_name(&self, name: &str) -> Option<&Param> {
self.params().iter().find(|¶m| param.name == name)
}
fn get_param_by_curie(&self, curie: &CURIE) -> Option<&Param> {
self.params().iter().find(|¶m| curie == param)
}
fn get_param_by_accession(&self, accession: &str) -> Option<&Param> {
let (cv, acc_num) = curie_to_num(accession);
self.params()
.iter()
.find(|¶m| param.accession == acc_num && param.controlled_vocabulary == cv)
}
fn iter_params(&self) -> std::slice::Iter<'_, Param> {
self.params().iter()
}
fn iter_params_mut(&mut self) -> std::slice::IterMut<'_, Param> {
self.params_mut().iter_mut()
}
}
impl ParamDescribed for ParamList {
fn params(&self) -> &[Param] {
self
}
fn params_mut(&mut self) -> &mut ParamList {
self
}
}
#[macro_export]
macro_rules! impl_param_described {
($($t:ty), +) => {$(
impl $crate::params::ParamDescribed for $t {
fn params(&self) -> &[$crate::params::Param] {
return &self.params
}
fn params_mut(&mut self) -> &mut $crate::params::ParamList {
return &mut self.params
}
}
)+};
}
#[doc(hidden)]
pub const _EMPTY_PARAM: &[Param] = &[];
#[macro_export]
macro_rules! impl_param_described_deferred {
($($t:ty), +) => {$(
impl $crate::params::ParamDescribed for $t {
fn params(&self) -> &[$crate::params::Param] {
match &self.params {
Some(val) => &val,
None => {
$crate::params::_EMPTY_PARAM
}
}
}
fn params_mut(&mut self) -> &mut $crate::params::ParamList {
let val = &mut self.params;
if val.is_some() {
return val.as_deref_mut().unwrap()
} else {
*val = Some(Box::default());
return val.as_deref_mut().unwrap()
}
}
}
)+};
}
macro_rules! units {
[$($unit:ident, $accession:literal, $baccession:literal, $name:literal, $bname:literal, $cv:ident, $id:literal);*;] => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Unit {
Unknown,
$($unit,)*
}
impl Unit {
pub const fn for_param(&self) -> (&'static str, &'static str) {
match self {
$(Self::$unit => ($accession, $name),)*
Self::Unknown => ("","")
}
}
pub const fn from_name(name: &str) -> Unit {
match name.as_bytes() {
$($bname => Self::$unit,)*
_ => Self::Unknown
}
}
pub const fn from_accession(acc: &str) -> Unit {
match acc.as_bytes() {
$($baccession => Self::$unit,)*
_ => Self::Unknown
}
}
pub const fn from_curie(acc: &CURIE) -> Unit {
match acc {
$(CURIE {
controlled_vocabulary: ControlledVocabulary::$cv,
accession: $id,
} => Self::$unit,)*
_ => Self::Unknown
}
}
pub const fn to_curie(&self) -> Option<CURIE> {
match self {
$(Self::$unit => Some(CURIE {
controlled_vocabulary: ControlledVocabulary::$cv,
accession: $id,
}),)*
Self::Unknown => None
}
}
pub const fn from_param(param: &Param) -> Unit {
param.unit
}
pub const fn is_unknown(&self) -> bool {
matches!(self, Self::Unknown)
}
}
};
}
units![
AbsorbanceUnit, "UO:0000269", b"UO:0000269", "absorbance unit", b"absorbance unit", UO, 269;
Celsius, "UO:0000027", b"UO:0000027", "degree Celsius", b"degree Celsius", UO, 27;
CountsPerSecond, "MS:1000814", b"MS:1000814", "counts per second", b"counts per second", MS, 1000814;
DetectorCounts, "MS:1000131", b"MS:1000131", "number of detector counts", b"number of detector counts", MS, 1000131;
Dimensionless, "UO:0000186", b"UO:0000186", "dimensionless unit", b"dimensionless unit", UO, 186;
Electronvolt, "UO:0000266", b"UO:0000266", "electronvolt", b"electronvolt", UO, 266;
Kelvin, "UO:0000012", b"UO:0000012", "kelvin", b"kelvin", UO, 12;
Mass, "UO:000221", b"UO:000221", "dalton", b"dalton", UO, 221;
MicrolitersPerMinute, "UO:0000271", b"UO:0000271", "microliters per minute", b"microliters per minute", UO, 271;
Millisecond, "UO:0000028", b"UO:0000028", "millisecond", b"millisecond", UO, 28;
Minute, "UO:0000031", b"UO:0000031", "minute", b"minute", UO, 31;
MZ, "MS:1000040", b"MS:1000040", "m/z", b"m/z", MS, 1000040;
Nanometer, "UO:0000018", b"UO:0000018", "nanometer", b"nanometer", UO, 18;
Micrometer, "UO:0000017", b"UO:0000017", "micrometer", b"micrometer", UO, 17;
Millimeter, "UO:0000016", b"UO:0000016", "millimeter", b"millimeter", UO, 16;
Centimeter, "UO:0000015", b"UO:0000015", "centimeter", b"centimeter", UO, 15;
PartsPerMillion, "UO:0000169", b"UO:0000169", "parts per million ", b"parts per million ", UO, 169;
Pascal, "UO:0000110", b"UO:0000110", "pascal", b"pascal", UO, 110;
Percent, "UO:0000187", b"UO:0000187", "percent", b"percent", UO, 187;
PercentBasePeak, "MS:1000132", b"MS:1000132", "percent of base peak", b"percent of base peak", MS, 1000132;
PercentBasePeakTimes100, "MS:1000905", b"MS:1000905", "percent of base peak times 100", b"percent of base peak times 100", MS, 1000905;
Psi, "UO:0010052", b"UO:0010052", "pounds per square inch", b"pounds per square inch", UO, 10052;
Second, "UO:0000010", b"UO:0000010", "second", b"second", UO, 10;
Volt, "UO:0000218", b"UO:0000218", "volt", b"volt", UO, 218;
VoltSecondPerSquareCentimeter, "MS:1002814", b"MS:1002814", "volt-second per square centimeter", b"volt-second per square centimeter", MS, 1002814;
Hertz, "UO:000106", b"UO:000106", "hertz", b"hertz", UO, 106;
Liter, "UO:0000099", b"UO:0000099", "liter", b"liter", UO, 99;
Milliliter, "UO:0000098", b"UO:0000098", "milliliter", b"milliliter", UO, 98;
Microliter, "UO:0000101", b"UO:0000101", "microliter", b"microliter", UO, 101;
];
impl Default for Unit {
fn default() -> Self {
Self::Unknown
}
}
impl Display for Unit {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(format!("{:?}", self).as_str())
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_build_param() {
assert_eq!(
ParamBuilder::default()
.name("dalton")
.curie(curie!(UO:221))
.build(),
ControlledVocabulary::UO.param("UO:000221", "dalton")
);
let p = ParamBuilder::default()
.controlled_vocabulary(ControlledVocabulary::MS)
.accession(1000529)
.name("instrument serial number")
.value("FSN10375")
.unit(Unit::Unknown)
.build();
assert_eq!(p.value(), "FSN10375");
assert_eq!(p.unit(), Unit::Unknown);
}
#[test]
fn test_value() {
let x = 42;
let mut val: Value = x.into();
let mut val_ref: ValueRef = x.into();
let mut val_ref2: ValueRef = (&x).into();
assert_eq!(val, x);
assert_eq!(val_ref, x);
assert_eq!(val_ref, val);
assert_eq!(val_ref2, val_ref);
assert!(val.to_bool().unwrap());
assert!(val_ref.to_bool().unwrap());
assert_eq!(val.to_str(), x.to_string());
assert_eq!(val_ref.to_str(), x.to_string());
val = x.to_string().parse().unwrap();
val_ref = x.to_string().parse().unwrap();
assert_eq!(val, x);
assert_eq!(val_ref, x);
assert_eq!(val_ref, val);
let x2 = Some(x);
val = x2.into();
val_ref = x2.into();
val_ref2 = (&x).into();
assert_eq!(val, x);
assert_eq!(val_ref, x);
assert_eq!(val_ref, val);
assert_eq!(val_ref2, val_ref);
assert!(val.to_bool().unwrap());
assert!(val_ref.to_bool().unwrap());
assert_eq!(val.to_str(), x.to_string());
assert_eq!(val_ref.to_str(), x.to_string());
val = x.to_string().parse().unwrap();
val_ref = x.to_string().parse().unwrap();
assert_eq!(val, x);
assert_eq!(val_ref, x);
assert_eq!(val_ref, val);
let x = 42.01;
val = x.into();
val_ref = x.into();
val_ref2 = (&x).into();
assert_eq!(val, x);
assert_eq!(val_ref, x);
assert_eq!(val_ref, val);
assert_eq!(val_ref2, val_ref);
assert!(val.to_bool().unwrap());
assert!(val_ref.to_bool().unwrap());
assert_eq!(val.to_str(), x.to_string());
assert_eq!(val_ref.to_str(), x.to_string());
val = x.to_string().parse().unwrap();
val_ref = x.to_string().parse().unwrap();
assert_eq!(val, x);
assert_eq!(val_ref, x);
assert_eq!(val_ref, val);
let x2 = Some(x);
val = x2.into();
val_ref = x2.into();
assert_eq!(val, x);
assert_eq!(val_ref, x);
assert_eq!(val_ref, val);
assert!(val.to_bool().unwrap());
assert!(val_ref.to_bool().unwrap());
assert_eq!(val.to_str(), x.to_string());
assert_eq!(val_ref.to_str(), x.to_string());
val = x.to_string().parse().unwrap();
val_ref = x.to_string().parse().unwrap();
assert_eq!(val, x);
assert_eq!(val_ref, x);
assert_eq!(val_ref, val);
let x = true;
val = x.into();
val_ref = x.into();
assert_eq!(val, x);
assert_eq!(val_ref, x);
assert_eq!(val_ref, val);
assert!(val.to_bool().unwrap());
assert!(val_ref.to_bool().unwrap());
assert_eq!(val.to_str(), x.to_string());
assert_eq!(val_ref.to_str(), x.to_string());
val = x.to_string().parse().unwrap();
val_ref = x.to_string().parse().unwrap();
assert_eq!(val, x);
assert_eq!(val_ref, x);
assert_eq!(val_ref, val);
let x = "Foobar".to_string();
val = x.clone().into();
val_ref = x.clone().into();
val_ref2 = x.as_str().into();
assert_eq!(val, x);
assert_eq!(val_ref, x);
assert_eq!(val_ref, val);
assert_eq!(val_ref2, val_ref);
assert_eq!(val.to_str(), x.to_string());
assert_eq!(val_ref.to_str(), x.to_string());
val = x.to_string().parse().unwrap();
val_ref = x.to_string().parse().unwrap();
assert_eq!(val, x);
assert_eq!(val_ref, x);
assert_eq!(val_ref, val);
assert_eq!(val.to_buffer().unwrap(), x.as_bytes());
assert_eq!(val_ref.to_buffer().unwrap(), x.as_bytes());
assert_eq!(val_ref.to_buffer().unwrap(), val.to_buffer().unwrap());
}
}