use alloc::string::String;
use core::fmt::{Display, Formatter};
use enumflags2::{bitflags, BitFlags};
use serde::{Deserialize, Serialize};
use strum_macros::IntoStaticStr;
use crate::common::cbor_values::ByteString;
#[cfg(test)]
mod tests;
pub type AifRestMethodSet = BitFlags<AifRestMethod>;
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize)]
pub struct TextEncodedScope(String);
impl Display for TextEncodedScope {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize)]
pub struct BinaryEncodedScope(ByteString);
#[bitflags]
#[derive(Serialize, Deserialize, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[repr(u64)]
pub enum AifRestMethod {
Get = u64::pow(2, 0),
Post = u64::pow(2, 1),
Put = u64::pow(2, 2),
Delete = u64::pow(2, 3),
Fetch = u64::pow(2, 4),
Patch = u64::pow(2, 5),
IPatch = u64::pow(2, 6),
DynamicGet = u64::pow(2, 32),
DynamicPost = u64::pow(2, 33),
DynamicPut = u64::pow(2, 34),
DynamicDelete = u64::pow(2, 35),
DynamicFetch = u64::pow(2, 36),
DynamicPatch = u64::pow(2, 37),
DynamicIPatch = u64::pow(2, 38),
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct AifEncodedScopeElement {
pub path: String,
pub permissions: BitFlags<AifRestMethod>,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize)]
pub struct AifEncodedScope(Vec<AifEncodedScopeElement>);
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize)]
pub struct LibdcafEncodedScope(AifEncodedScopeElement);
#[derive(Debug, PartialEq, Eq, Clone, Hash, IntoStaticStr)]
pub enum Scope {
TextEncoded(TextEncodedScope),
BinaryEncoded(BinaryEncodedScope),
AifEncoded(AifEncodedScope),
LibdcafEncoded(LibdcafEncodedScope),
}
mod conversion {
use ciborium::value::{Integer, Value};
use serde::{Deserializer, Serializer};
use serde::de::Error;
use crate::error::{
InvalidAifEncodedScopeError, InvalidBinaryEncodedScopeError, InvalidTextEncodedScopeError,
ScopeFromValueError, WrongSourceTypeError,
};
use super::*;
impl TextEncodedScope {
pub fn elements(&self) -> impl Iterator<Item = &str> {
self.0.split(' ')
}
}
impl TryFrom<&str> for TextEncodedScope {
type Error = InvalidTextEncodedScopeError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
if value.ends_with(' ') {
Err(InvalidTextEncodedScopeError::EndsWithSeparator)
} else if value.starts_with(' ') {
Err(InvalidTextEncodedScopeError::StartsWithSeparator)
} else if value.contains(['"', '\\']) {
Err(InvalidTextEncodedScopeError::IllegalCharacters)
} else if value.contains(" ") {
Err(InvalidTextEncodedScopeError::ConsecutiveSeparators)
} else if value.is_empty() {
Err(InvalidTextEncodedScopeError::EmptyScope)
} else {
Ok(TextEncodedScope(value.into()))
}
}
}
impl TryFrom<Vec<&str>> for TextEncodedScope {
type Error = InvalidTextEncodedScopeError;
fn try_from(value: Vec<&str>) -> Result<Self, Self::Error> {
if value.iter().any(|x| x.contains([' ', '\\', '"'])) {
Err(InvalidTextEncodedScopeError::IllegalCharacters)
} else if value.iter().any(|x| x.is_empty()) {
Err(InvalidTextEncodedScopeError::EmptyElement)
} else if value.is_empty() {
Err(InvalidTextEncodedScopeError::EmptyScope)
} else {
Ok(TextEncodedScope(value.join(" ")))
}
}
}
impl BinaryEncodedScope {
pub fn elements(
&self,
separator: Option<u8>,
) -> Result<Vec<&[u8]>, InvalidBinaryEncodedScopeError> {
assert!(
!self.0.is_empty(),
"Invariant violated: Scope may not be empty"
);
if let Some(separator) = separator {
let split = self.0.split(move |x| x == &separator);
if self.0.first().filter(|x| **x != separator).is_none() {
Err(InvalidBinaryEncodedScopeError::StartsWithSeparator(
separator,
))
} else if self.0.last().filter(|x| **x != separator).is_none() {
Err(InvalidBinaryEncodedScopeError::EndsWithSeparator(separator))
} else if self.0.windows(2).any(|x| x[0] == x[1] && x[1] == separator) {
Err(InvalidBinaryEncodedScopeError::ConsecutiveSeparators(
separator,
))
} else {
debug_assert!(
split.clone().all(|x| !x.is_empty()),
"Post-condition violated: Result may not contain empty slices"
);
Ok(split.collect())
}
} else {
Ok(vec![self.0.as_slice()])
}
}
}
impl TryFrom<&[u8]> for BinaryEncodedScope {
type Error = InvalidBinaryEncodedScopeError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
let vec = value.to_vec();
if vec.is_empty() {
Err(InvalidBinaryEncodedScopeError::EmptyScope)
} else {
Ok(BinaryEncodedScope(vec))
}
}
}
impl AifEncodedScopeElement {
#[must_use]
pub fn new<T, U>(path: T, permissions: U) -> AifEncodedScopeElement
where
T: Into<String>,
U: Into<BitFlags<AifRestMethod>>,
{
AifEncodedScopeElement {
path: path.into(),
permissions: permissions.into(),
}
}
pub fn try_from_bits<T>(
path: T,
permissions: u64,
) -> Result<AifEncodedScopeElement, InvalidAifEncodedScopeError>
where
T: Into<String>,
{
BitFlags::<AifRestMethod>::from_bits(permissions)
.map_err(|_| InvalidAifEncodedScopeError::InvalidRestMethodSet)
.map(|permissions| AifEncodedScopeElement {
path: path.into(),
permissions,
})
}
fn into_cbor_value(self) -> Value {
Value::Array(vec![
Value::Text(self.path),
Value::Integer(Integer::from(self.permissions.bits())),
])
}
}
impl AifEncodedScope {
#[must_use]
pub fn new(elements: Vec<AifEncodedScopeElement>) -> AifEncodedScope {
AifEncodedScope(elements)
}
#[must_use]
pub fn elements(&self) -> &Vec<AifEncodedScopeElement> {
&self.0
}
#[must_use]
pub fn to_elements(self) -> Vec<AifEncodedScopeElement> {
self.0
}
}
impl Serialize for AifEncodedScopeElement {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
Value::Array(vec![
Value::Text(self.path.clone()),
Value::Integer(Integer::from(self.permissions.bits())),
])
.serialize(serializer)
}
}
impl<T> From<Vec<(T, BitFlags<AifRestMethod>)>> for AifEncodedScope
where
T: Into<String>,
{
fn from(value: Vec<(T, BitFlags<AifRestMethod>)>) -> Self {
AifEncodedScope::new(
value
.into_iter()
.map(|(path, set)| AifEncodedScopeElement::new(path, set))
.collect(),
)
}
}
impl TryFrom<Vec<(String, u64)>> for AifEncodedScope {
type Error = InvalidAifEncodedScopeError;
fn try_from(value: Vec<(String, u64)>) -> Result<Self, Self::Error> {
Ok(AifEncodedScope::new(
value
.into_iter()
.map(|(path, rest)| AifEncodedScopeElement::try_from_bits(path, rest))
.collect::<Result<Vec<AifEncodedScopeElement>, InvalidAifEncodedScopeError>>(
)?,
))
}
}
impl LibdcafEncodedScope {
#[must_use]
pub fn new<T>(path: T, permissions: BitFlags<AifRestMethod>) -> LibdcafEncodedScope
where
T: Into<String>,
{
LibdcafEncodedScope(AifEncodedScopeElement::new(path, permissions))
}
#[must_use]
pub fn from_element(element: AifEncodedScopeElement) -> LibdcafEncodedScope {
LibdcafEncodedScope(element)
}
pub fn try_from_bits<T>(
path: T,
permissions: u64,
) -> Result<LibdcafEncodedScope, InvalidAifEncodedScopeError>
where
T: Into<String>,
{
Ok(LibdcafEncodedScope::from_element(
AifEncodedScopeElement::try_from_bits(path, permissions)?,
))
}
#[must_use]
pub fn element(&self) -> &AifEncodedScopeElement {
&self.0
}
#[must_use]
pub fn to_element(self) -> AifEncodedScopeElement {
self.0
}
#[must_use]
pub fn elements(&self) -> Vec<&AifEncodedScopeElement> {
vec![self.element()]
}
#[must_use]
pub fn to_elements(self) -> Vec<AifEncodedScopeElement> {
vec![self.to_element()]
}
}
impl From<LibdcafEncodedScope> for Scope {
fn from(value: LibdcafEncodedScope) -> Self {
Scope::LibdcafEncoded(value)
}
}
impl From<AifEncodedScope> for Scope {
fn from(value: AifEncodedScope) -> Self {
Scope::AifEncoded(value)
}
}
impl From<TextEncodedScope> for Scope {
fn from(value: TextEncodedScope) -> Self {
Scope::TextEncoded(value)
}
}
impl From<BinaryEncodedScope> for Scope {
fn from(value: BinaryEncodedScope) -> Self {
Scope::BinaryEncoded(value)
}
}
impl TryFrom<Vec<&str>> for Scope {
type Error = InvalidTextEncodedScopeError;
fn try_from(value: Vec<&str>) -> Result<Self, InvalidTextEncodedScopeError> {
Ok(Scope::from(TextEncodedScope::try_from(value)?))
}
}
impl TryFrom<&[u8]> for Scope {
type Error = InvalidBinaryEncodedScopeError;
fn try_from(value: &[u8]) -> Result<Self, InvalidBinaryEncodedScopeError> {
Ok(Scope::from(BinaryEncodedScope::try_from(value)?))
}
}
impl TryFrom<Vec<(String, u64)>> for Scope {
type Error = InvalidAifEncodedScopeError;
fn try_from(value: Vec<(String, u64)>) -> Result<Self, Self::Error> {
Ok(Scope::from(AifEncodedScope::try_from(value)?))
}
}
impl TryFrom<Scope> for BinaryEncodedScope {
type Error = WrongSourceTypeError<Scope>;
fn try_from(value: Scope) -> Result<Self, Self::Error> {
if let Scope::BinaryEncoded(scope) = value {
Ok(scope)
} else {
Err(WrongSourceTypeError::new("BinaryEncoded", value.into()))
}
}
}
impl TryFrom<Scope> for TextEncodedScope {
type Error = WrongSourceTypeError<Scope>;
fn try_from(value: Scope) -> Result<Self, Self::Error> {
if let Scope::TextEncoded(scope) = value {
Ok(scope)
} else {
Err(WrongSourceTypeError::new("TextEncoded", value.into()))
}
}
}
impl TryFrom<Scope> for AifEncodedScope {
type Error = WrongSourceTypeError<Scope>;
fn try_from(value: Scope) -> Result<Self, Self::Error> {
if let Scope::AifEncoded(scope) = value {
Ok(scope)
} else {
Err(WrongSourceTypeError::new("AifEncoded", value.into()))
}
}
}
impl TryFrom<Scope> for LibdcafEncodedScope {
type Error = WrongSourceTypeError<Scope>;
fn try_from(value: Scope) -> Result<Self, Self::Error> {
if let Scope::LibdcafEncoded(scope) = value {
Ok(scope)
} else {
Err(WrongSourceTypeError::new("LibdcafEncoded", value.into()))
}
}
}
impl From<Scope> for Value {
fn from(scope: Scope) -> Self {
match scope {
Scope::TextEncoded(text) => Value::Text(text.0),
Scope::BinaryEncoded(binary) => Value::Bytes(binary.0),
Scope::AifEncoded(aif) => Value::Array(
aif.to_elements()
.into_iter()
.map(AifEncodedScopeElement::into_cbor_value)
.collect(),
),
Scope::LibdcafEncoded(lib) => lib.0.into_cbor_value(),
}
}
}
impl TryFrom<Value> for Scope {
type Error = ScopeFromValueError;
fn try_from(value: Value) -> Result<Self, Self::Error> {
#[allow(clippy::needless_pass_by_value)] fn value_to_aif_element(
value: Value,
) -> Result<AifEncodedScopeElement, InvalidAifEncodedScopeError> {
let values = value
.as_array()
.ok_or(InvalidAifEncodedScopeError::MalformedArray)?;
let path = values
.first()
.and_then(Value::as_text)
.ok_or(InvalidAifEncodedScopeError::MalformedArray)?
.to_string();
let permissions = values
.get(1)
.and_then(|x| {
x.as_integer().map(|x| {
u64::try_from(x)
.map(BitFlags::<AifRestMethod>::from_bits)
.map(Result::ok)
.ok()
})
})
.flatten()
.flatten() .ok_or(InvalidAifEncodedScopeError::MalformedArray)?;
Ok(AifEncodedScopeElement::new(path, permissions))
}
match value {
Value::Bytes(b) => Ok(Scope::BinaryEncoded(BinaryEncodedScope::try_from(
b.as_slice(),
)?)),
Value::Text(t) => Ok(Scope::TextEncoded(TextEncodedScope::try_from(t.as_str())?)),
Value::Array(a) => {
if a.first().filter(|x| x.is_text()).is_some() {
Ok(Scope::LibdcafEncoded(LibdcafEncodedScope(
value_to_aif_element(Value::Array(a))?,
)))
} else {
a.into_iter()
.map(value_to_aif_element)
.collect::<Result<Vec<AifEncodedScopeElement>, InvalidAifEncodedScopeError>>()
.map(|x| Scope::AifEncoded(AifEncodedScope::new(x)))
.map_err(ScopeFromValueError::InvalidAifEncodedScope)
}
}
v => Err(ScopeFromValueError::invalid_type(&v)),
}
}
}
impl Serialize for Scope {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
Value::from(self.clone()).serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Scope {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Scope::try_from(Value::deserialize(deserializer)?)
.map_err(|x| D::Error::custom(x.to_string()))
}
}
}