use minicbor::data::Type;
use crate::characterclasses::{PATH_UE, QUERY_UE, FRAGMENT_UE, HOST_UE, AsciiSet, contains_byte};
#[derive(Debug, Clone)]
enum FirstComponent {
Discard(Discard),
Nothing,
JustAuthority(AuthorityIsh),
SchemeAuthority(Scheme, AuthorityIsh),
}
#[derive(Debug, Clone)]
pub enum Scheme {
SchemeName(String),
SchemeId(i8),
}
#[derive(Debug, Clone)]
struct UserInfo(Pet<HOST_UE>);
#[derive(Debug, Clone)]
enum AuthorityIsh {
HostPort(Option<UserInfo>, Host, Option<Port>),
NoAuthority, LeadingSlash, }
#[derive(Debug, Clone)]
pub struct Host(HostImpl);
#[derive(Debug, Clone)]
enum HostImpl {
HostName(PetSequence<'.', HOST_UE>),
HostIp4(no_std_net::Ipv4Addr),
HostIp6(no_std_net::Ipv6Addr, Option<String>),
}
impl<'a> crate::traits::Host for &'a Host {
type HostItem<'b> = &'b Pet<HOST_UE> where Self: 'b;
type HostIter<'b> = &'b [Pet<HOST_UE>] where Self: 'b;
fn as_ref(&self) -> crate::traits::HostRef<'_, Self::HostItem<'_>, Self::HostIter<'_>> {
match &self.0 {
HostImpl::HostName(seq) => crate::traits::HostRef::Hostname(&seq.0[..]),
HostImpl::HostIp4(a) => crate::traits::HostRef::IPv4(a),
HostImpl::HostIp6(address, zone) => crate::traits::HostRef::IPv6 { address, zone: zone.as_ref().map(|a| a.as_str()) },
}
}
}
#[derive(Debug, Clone)]
pub enum Pet<const UNESCAPED: AsciiSet> {
JustText(String),
Alternating(Vec<(String, Box<[u8]>)>),
}
impl<const UNESCAPED: AsciiSet> Pet<UNESCAPED> {
fn load(decoder: &mut minicbor::Decoder) -> Result<Self, LoadError> {
match decoder.datatype()? {
Type::String => Ok(Pet::JustText(decoder.str()?.to_string())),
Type::Array => {
let mut items = decoder.array()?
.ok_or(LoadError(LoadErrorImpl::ParsingError ))?;
let mut vec = Vec::with_capacity(((items + 1) / 2) as _);
loop {
let text = if vec.len() != 0 || decoder.datatype()? == Type::String {
items -= 1;
decoder.str()?.to_string()
} else {
String::new()
};
let pet = if items > 0 {
items -= 1;
Box::from(decoder.bytes()?)
} else {
Box::from([])
};
vec.push((text, pet));
if items == 0 {
break;
}
}
Ok(Pet::Alternating(vec))
}
_ => return Err(LoadError(LoadErrorImpl::UnexpectedType))
}
}
fn load_if_present(decoder: &mut minicbor::Decoder) -> Result<Option<Self>, LoadError> {
match decoder.datatype() {
Err(e) if e.is_end_of_input() => Ok(None),
_ => Ok(Some(Self::load(decoder)?)),
}
}
fn format_str_to_uri(w: &mut core::fmt::Formatter<'_>, part: &str) -> Result<(), core::fmt::Error>{
for b in part.as_bytes() {
if contains_byte(UNESCAPED, *b) {
write!(w, "{}", core::str::from_utf8(&[*b]).expect("All bytes <128 are valid UTF-8"))?
} else {
Self::format_pet_to_uri(w, &[*b])?;
}
}
Ok(())
}
fn format_pet_to_uri(w: &mut core::fmt::Formatter<'_>, pet: &[u8]) -> Result<(), core::fmt::Error>{
for c in pet {
write!(w, "%{:2X}", c)?;
}
Ok(())
}
}
impl<'a, const UNESCAPED: AsciiSet> crate::traits::TextOrPet<UNESCAPED> for &'a Pet<UNESCAPED> {
type UriEncoded<'b> = &'b Self where Self: 'b;
fn to_uri_component(&self) -> Self::UriEncoded<'_> {
&self
}
}
impl<const UNESCAPED: AsciiSet> core::fmt::Display for Pet<UNESCAPED> {
fn fmt(&self, w: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
match self {
Pet::Alternating(v) => {
for (text, bytes) in v {
Self::format_str_to_uri(w, text)?;
Self::format_pet_to_uri(w, bytes)?;
};
},
Pet::JustText(s) => {
Self::format_str_to_uri(w, s)?;
}
};
Ok(())
}
}
type Port = u16;
#[allow(non_camel_case_types)]
type u7 = u8;
#[derive(Debug, Clone)]
enum Discard {
DiscardAll,
Numeric(u7),
}
#[derive(Debug, Clone)]
struct PetSequence<const SEP: char, const UNESCAPED: AsciiSet>(Vec<Pet<UNESCAPED>>);
type Path = Option<PetSequence<'/', PATH_UE>>;
type Query = Option<PetSequence<'?', QUERY_UE>>;
type Fragment = Option<Pet<FRAGMENT_UE>>;
impl<const SEP: char, const UNESCAPED: AsciiSet> PetSequence<SEP, UNESCAPED> {
fn load_optional(decoder: &mut minicbor::Decoder) -> Result<Option<Self>, LoadError> {
Ok(match decoder.datatype() {
Ok(Type::Array) => {
let number = decoder.array()?
.ok_or(LoadError(LoadErrorImpl::ParsingError ))?
as usize;
let mut pathstrings = Vec::with_capacity(number);
for _ in 0..number {
pathstrings.push(Pet::load(decoder)?)
}
Some(PetSequence(pathstrings))
}
Ok(Type::Null) => {
decoder.null().expect("A null was already found");
None
}
Ok(_) => return Err(LoadError(LoadErrorImpl::UnexpectedType)),
Err(e) if e.is_end_of_input() => None,
Err(e) => return Err(e.into()),
})
}
}
#[derive(Debug, Clone)]
pub struct NativeCBORCriRef {
scheme_authority_discard: FirstComponent,
path: Path,
query: Query,
fragment: Fragment,
}
#[derive(Clone, Debug)]
pub struct NativeCBORCri(NativeCBORCriRef);
#[derive(Debug)]
pub struct LoadError(LoadErrorImpl);
#[derive(Debug)]
enum LoadErrorImpl {
ParsingError,
PrematureEnd,
UnexpectedType,
}
trait ErrorExt<T> {
fn eof_as_none(self) -> Result<Option<T>, LoadError>;
}
impl<T> ErrorExt<T> for Result<T, minicbor::decode::Error> {
fn eof_as_none(self) -> Result<Option<T>, LoadError> {
match self {
Ok(s) => Ok(Some(s)),
Err(e) if e.is_end_of_input() => Ok(None),
Err(e) => Err(e.into()),
}
}
}
impl From<minicbor::decode::Error> for LoadError {
fn from(e: minicbor::decode::Error) -> Self {
if e.is_type_mismatch() {
LoadError(LoadErrorImpl::UnexpectedType)
} else if e.is_end_of_input() {
LoadError(LoadErrorImpl::PrematureEnd)
} else {
LoadError(LoadErrorImpl::ParsingError)
}
}
}
impl<'a> TryFrom<&'a [u8]> for NativeCBORCriRef {
type Error = LoadError;
fn try_from(s: &'a [u8]) -> Result<Self, LoadError> {
let mut decoder = minicbor::Decoder::new(s);
let length = decoder.array().unwrap(); load_cri_ref(&mut decoder)
}
}
impl crate::traits::Scheme for &Scheme {
fn to_cri_id(&self) -> Option<i16> {
match self {
Scheme::SchemeName(_) => None,
Scheme::SchemeId(n) => Some((*n).into()),
}
}
fn to_text_scheme(&self) -> &str {
match self {
Scheme::SchemeName(s) => s,
Scheme::SchemeId(-1) => "coap",
Scheme::SchemeId(-2) => "coaps",
Scheme::SchemeId(-3) => "http",
Scheme::SchemeId(-4) => "https",
Scheme::SchemeId(_) => todo!("Check validity earlier"),
}
}
}
impl super::accessor::CriBase for NativeCBORCriRef {
type Scheme<'a> = &'a Scheme where Self: 'a;
type Host<'a> = &'a Host where Self: 'a;
type PathItem<'a> = &'a Pet<PATH_UE> where Self: 'a;
type PathIter<'a> = impl Iterator<Item=Self::PathItem<'a>> + ExactSizeIterator where Self: 'a;
type QueryItem<'a> = &'a Pet<QUERY_UE> where Self: 'a;
type QueryIter<'a> = impl Iterator<Item=Self::QueryItem<'a>> where Self: 'a;
type FragmentItem<'a> = &'a Pet<FRAGMENT_UE> where Self: 'a;
type UserInfoItem<'a> = &'a Pet<HOST_UE> where Self: 'a;
fn path(&self) -> Self::PathIter<'_> {
use super::accessor::CriRef;
match &self.path {
Some(p) => &p.0[..],
None => &[],
}.iter()
}
fn query(&self) -> Self::QueryIter<'_> {
match &self.query {
Some(q) => &q.0[..],
None => &[],
}.iter()
}
fn fragment(&self) -> Option<Self::FragmentItem<'_>> {
match &self.fragment {
Some(s) => Some(&s),
None => None,
}
}
fn userinfo(&self) -> Option<Self::UserInfoItem<'_>> {
match &self.scheme_authority_discard {
FirstComponent::SchemeAuthority(_, AuthorityIsh::HostPort(Some(UserInfo(a)), _, _)) => Some(a),
FirstComponent::JustAuthority(AuthorityIsh::HostPort(Some(UserInfo(a)), _, _)) => Some(a),
FirstComponent::SchemeAuthority(_, AuthorityIsh::HostPort(None, _, _)) => None,
FirstComponent::JustAuthority(AuthorityIsh::HostPort(None, _, _)) => None,
_ => panic!("Used wrong"),
}
}
fn host(&self) -> Self::Host<'_> {
match &self.scheme_authority_discard {
FirstComponent::SchemeAuthority(_, AuthorityIsh::HostPort(_, h, _)) => h.into(),
FirstComponent::JustAuthority(AuthorityIsh::HostPort(_, h, _)) => h.into(),
_ => panic!("Used wrong"),
}
}
fn port(&self) -> Option<u16> {
match self.scheme_authority_discard {
FirstComponent::SchemeAuthority(_, AuthorityIsh::HostPort(_, _, p)) => p,
FirstComponent::JustAuthority(AuthorityIsh::HostPort(_, _, p)) => p,
_ => panic!("Used wrong"),
}
}
}
impl super::accessor::CriRef for NativeCBORCriRef {
fn discard(&self) -> crate::accessor::Discard {
match self.scheme_authority_discard {
FirstComponent::Discard(Discard::DiscardAll) => crate::accessor::Discard::All,
FirstComponent::Discard(Discard::Numeric(n)) => crate::accessor::Discard::Some(n.into()),
FirstComponent::Nothing => crate::accessor::Discard::Some(0), _ => crate::accessor::Discard::All,
}
}
fn scheme(&self) -> Option<Self::Scheme<'_>> {
match &self.scheme_authority_discard {
FirstComponent::SchemeAuthority(s, _) => Some(s),
_ => None,
}
}
fn authority(&self) -> Option<crate::traits::Authority> {
match self.scheme_authority_discard {
FirstComponent::Discard(_) => None,
FirstComponent::JustAuthority(AuthorityIsh::HostPort(_, _, _)) |
FirstComponent::SchemeAuthority(_, AuthorityIsh::HostPort(_, _,_)) => Some(crate::traits::Authority::HostPort),
FirstComponent::JustAuthority(AuthorityIsh::NoAuthority) |
FirstComponent::SchemeAuthority(_, AuthorityIsh::NoAuthority) => Some(crate::traits::Authority::NoAuthoritySlashless),
FirstComponent::JustAuthority(AuthorityIsh::LeadingSlash) |
FirstComponent::SchemeAuthority(_, AuthorityIsh::LeadingSlash) => Some(crate::traits::Authority::NoAuthoritySlashStart),
FirstComponent::Nothing => None, }
}
}
#[derive(Debug)]
pub struct RefIsRelative;
impl<'a> TryFrom<NativeCBORCriRef> for NativeCBORCri {
type Error = RefIsRelative;
fn try_from(s: NativeCBORCriRef) -> Result<Self, RefIsRelative> {
use super::accessor::CriRef;
if s.scheme().is_some() {
Ok(NativeCBORCri(s))
} else {
Err(RefIsRelative)
}
}
}
impl super::accessor::CriBase for NativeCBORCri {
type Scheme<'a> = &'a Scheme where Self: 'a;
type Host<'a> = &'a Host where Self: 'a;
type PathItem<'a> = &'a Pet<PATH_UE> where Self: 'a;
type PathIter<'a> = impl Iterator<Item=Self::PathItem<'a>> + ExactSizeIterator where Self: 'a;
type QueryItem<'a> = &'a Pet<QUERY_UE> where Self: 'a;
type QueryIter<'a> = impl Iterator<Item=Self::QueryItem<'a>> where Self: 'a;
type FragmentItem<'a> = &'a Pet<FRAGMENT_UE> where Self: 'a;
type UserInfoItem<'a> = &'a Pet<HOST_UE> where Self: 'a;
fn path(&self) -> Self::PathIter<'_> {
self.0.path()
}
fn query(&self) -> Self::QueryIter<'_> {
self.0.query()
}
fn fragment(&self) -> Option<Self::FragmentItem<'_>> {
self.0.fragment()
}
fn userinfo(&self) -> Option<Self::UserInfoItem<'_>> {
self.0.userinfo()
}
fn host(&self) -> Self::Host<'_> {
self.0.host()
}
fn port(&self) -> Option<u16> {
self.0.port()
}
}
impl super::accessor::Cri for NativeCBORCri {
fn scheme(&self) -> Self::Scheme<'_> {
use super::accessor::CriRef;
self.0.scheme()
.expect("This was checked at construction time to be present")
}
fn authority(&self) -> crate::traits::Authority {
use super::accessor::CriRef;
self.0.authority()
.expect("This was checked at construction time to be present")
}
}
fn load_host_port(decoder: &mut minicbor::Decoder) -> Result<(Option<UserInfo>, Host, Option<Port>), LoadError> {
let mut elements = decoder.array().expect("I was promised an array, must be badly encoded").expect("No indefinite arrays please");
let mut first_datatype = decoder.datatype().expect("Odd place to stop a CRI");
let mut userinfo = None;
if first_datatype == Type::Bool {
let boolean = decoder.bool().expect("Type was just checked");
if boolean != false {
return Err(LoadError(LoadErrorImpl::UnexpectedType));
}
userinfo = Some(UserInfo(Pet::load(decoder)?));
first_datatype = decoder.datatype().expect("Odd place to stop a CRI (userinfo but no remaining authority)");
elements -= 2;
}
let host = match first_datatype {
Type::Bytes => {
elements -= 1;
let bytes = decoder.bytes()?;
Host(match bytes.len() {
4 => HostImpl::HostIp4(<[u8; 4]>::try_from(bytes).expect("Length was just checked").into()),
16 => {
let zone = match decoder.datatype()? {
Type::String => {
elements -= 1;
Some(decoder.str()?)
}
_ => None,
};
HostImpl::HostIp6(<[u8; 16]>::try_from(bytes).expect("Length was just checked").into(), zone.map(|s| s.to_string()))
}
_ => return Err(LoadError(LoadErrorImpl::UnexpectedType))
})
}
Type::String | Type::Array => {
let mut hoststrings = Vec::with_capacity(elements as _); while elements > 0 && match decoder.datatype()? {
Type::String => true,
Type::Array => true,
_ => false,
} {
elements -= 1;
let string = Pet::load(decoder)?;
hoststrings.push(string);
}
Host(HostImpl::HostName(PetSequence(hoststrings)))
}
_ => return Err(LoadError(LoadErrorImpl::UnexpectedType))
};
let mut port = None;
match elements {
1 => {
port = Some(decoder.u16()?);
}
0 => (),
_ => return Err(LoadError(LoadErrorImpl::UnexpectedType))
}
Ok((userinfo, host, port))
}
fn load_authorityish(decoder: &mut minicbor::Decoder) -> Result<AuthorityIsh, LoadError> {
Ok(match decoder.datatype()
.eof_as_none()?
{
None => {
AuthorityIsh::LeadingSlash
}
Some(Type::Null) => {
decoder.null().expect("A null was already found");
AuthorityIsh::LeadingSlash
}
Some(Type::Bool) => {
let boolean = decoder.bool().expect("Type was just checked");
if boolean == false {
return Err(LoadError(LoadErrorImpl::UnexpectedType));
}
AuthorityIsh::NoAuthority
}
Some(Type::Array) => {
let (authority, host, port) = load_host_port(decoder)?;
AuthorityIsh::HostPort(authority, host, port)
}
_ => return Err(LoadError(LoadErrorImpl::UnexpectedType))
})
}
fn load_first_component(decoder: &mut minicbor::Decoder) -> Result<FirstComponent, LoadError> {
Ok(match decoder.datatype() {
Err(e) if e.is_end_of_input() => {
FirstComponent::Nothing
}
Ok(Type::U8) => {
let discard = decoder.u8()?;
FirstComponent::Discard(Discard::Numeric(discard))
}
Ok(Type::Bool) => {
let discard = decoder.bool().expect("Type was just checked");
if discard == false {
return Err(LoadError(LoadErrorImpl::UnexpectedType));
}
FirstComponent::Discard(Discard::DiscardAll)
}
Ok(Type::I8) => {
let scheme = Scheme::SchemeId(decoder.i8()?);
FirstComponent::SchemeAuthority(scheme, load_authorityish(decoder)?)
}
Ok(Type::String) => {
let scheme = Scheme::SchemeName(decoder.str()?.to_string());
FirstComponent::SchemeAuthority(scheme, load_authorityish(decoder)?)
}
Ok(Type::Null) => {
decoder.null().expect("A null was already found");
FirstComponent::JustAuthority(load_authorityish(decoder)?)
}
Ok(Type::Array) => {
FirstComponent::JustAuthority(AuthorityIsh::LeadingSlash)
}
Ok(_) => return Err(LoadError(LoadErrorImpl::UnexpectedType)),
Err(e) => return Err(e.into()),
})
}
fn load_cri_ref(decoder: &mut minicbor::Decoder) -> Result<NativeCBORCriRef, LoadError> {
let first = load_first_component(decoder)?;
let path = PetSequence::load_optional(decoder)?;
let query = PetSequence::load_optional(decoder)?;
let fragment = Pet::load_if_present(decoder)?;
Ok(NativeCBORCriRef {
scheme_authority_discard: first,
path,
query,
fragment,
})
}
#[test]
fn load() {
let data = hex::decode("85218263666f6f19126782627061627468816571756572796466726167").unwrap();
let _: NativeCBORCriRef = data[..].try_into().expect("Basic example could not be parsed");
}