Cow tools

This commit is contained in:
Aode (lion) 2021-10-06 14:51:31 -05:00
parent 3dfc69dd0c
commit df9dd2ddc5

View file

@ -43,21 +43,21 @@ pub struct PathParser<Inner> {
phantom: PhantomData<Inner>,
}
pub trait PathItem {
pub trait PathItem<'a> {
type Error: std::error::Error;
fn parse<'a>(bytes: &'a [u8]) -> Result<Self, Self::Error>
fn parse(bytes: Cow<'a, [u8]>) -> Result<Self, Self::Error>
where
Self: Sized + 'a;
fn to_bytes(&self) -> Vec<u8>;
}
pub trait PathNode: PathItem {
pub trait PathNode<'a>: PathItem<'a> {
const NAME: &'static [u8];
}
pub trait PathField: PathItem {}
pub trait PathField<'a>: PathItem<'a> {}
pub trait IntoOwned {
type Owned: 'static;
@ -88,7 +88,7 @@ impl<'a> PathGen<'a> {
pub fn push<N>(self, node: N) -> Node<Self, N>
where
N: PathNode,
N: PathNode<'a>,
{
Node {
inner: self,
@ -98,7 +98,7 @@ impl<'a> PathGen<'a> {
pub fn prefix<N>(self) -> Prefix<Self, N>
where
N: PathNode,
N: PathNode<'a>,
{
Prefix {
inner: self,
@ -108,9 +108,9 @@ impl<'a> PathGen<'a> {
}
impl<Inner, Segment> Node<Inner, Segment> {
pub fn push<N>(self, node: N) -> Node<Self, N>
pub fn push<'a, N>(self, node: N) -> Node<Self, N>
where
N: PathNode,
N: PathNode<'a>,
{
Node {
inner: self,
@ -122,9 +122,9 @@ impl<Inner, Segment> Node<Inner, Segment> {
(self.inner, self.segment)
}
pub fn field<F>(self, field: F) -> Field<Self, F>
pub fn field<'a, F>(self, field: F) -> Field<Self, F>
where
F: PathField,
F: PathField<'a>,
{
Field {
inner: self,
@ -132,9 +132,9 @@ impl<Inner, Segment> Node<Inner, Segment> {
}
}
pub fn prefix<N>(self) -> Prefix<Self, N>
pub fn prefix<'a, N>(self) -> Prefix<Self, N>
where
N: PathNode,
N: PathNode<'a>,
{
Prefix {
inner: self,
@ -174,27 +174,27 @@ where
}
impl<Inner> PathParser<Inner> {
pub fn push<N>(self) -> PathParser<Node<Inner, N>>
pub fn push<'a, N>(self) -> PathParser<Node<Inner, N>>
where
N: PathNode,
N: PathNode<'a>,
{
PathParser {
phantom: PhantomData,
}
}
pub fn prefix<N>(self) -> PathParser<Prefix<Inner, N>>
pub fn prefix<'a, N>(self) -> PathParser<Prefix<Inner, N>>
where
N: PathNode,
N: PathNode<'a>,
{
PathParser {
phantom: PhantomData,
}
}
pub fn field<F>(self) -> PathParser<Field<Inner, F>>
pub fn field<'a, F>(self) -> PathParser<Field<Inner, F>>
where
F: PathField,
F: PathField<'a>,
{
PathParser {
phantom: PhantomData,
@ -322,8 +322,8 @@ where
impl<'a, E, Inner, N> Deconstruct<'a, E> for Node<Inner, N>
where
Inner: Deconstruct<'a, E>,
N: PathNode,
E: From<<N as PathItem>::Error>,
N: PathNode<'a>,
E: From<<N as PathItem<'a>>::Error>,
E: From<DeconstructError>,
{
fn deconstruct(s: &'a [u8]) -> Result<Self, E>
@ -334,7 +334,8 @@ where
if let Some((rest, name)) = rsplit_once_escaped(rest) {
if unescape(name) == N::NAME {
let inner = Inner::deconstruct(rest)?;
let segment = N::parse(&unescape(value))?;
let unescaped = unescape(value);
let segment = N::parse(unescaped)?;
return Ok(Node { inner, segment });
}
}
@ -347,8 +348,8 @@ where
impl<'a, E, Inner, F> Deconstruct<'a, E> for Field<Inner, F>
where
Inner: Deconstruct<'a, E>,
F: PathField,
E: From<<F as PathItem>::Error>,
F: PathField<'a>,
E: From<<F as PathItem<'a>>::Error>,
E: From<DeconstructError>,
{
fn deconstruct(s: &'a [u8]) -> Result<Self, E>
@ -357,7 +358,7 @@ where
{
if let Some((rest, value)) = rsplit_once_escaped(s) {
let inner = Inner::deconstruct(rest)?;
let segment = F::parse(&unescape(value))?;
let segment = F::parse(unescape(value))?;
return Ok(Field { inner, segment });
} else {
Err(DeconstructError.into())
@ -368,8 +369,8 @@ where
impl<'a, E, Inner, N> Deconstruct<'a, E> for Prefix<Inner, N>
where
Inner: Deconstruct<'a, E>,
N: PathNode,
E: From<<N as PathItem>::Error>,
N: PathNode<'a>,
E: From<<N as PathItem<'a>>::Error>,
E: From<DeconstructError>,
{
fn deconstruct(s: &'a [u8]) -> Result<Self, E>
@ -390,10 +391,10 @@ where
}
}
impl<Inner, N> Construct for Node<Inner, N>
impl<'a, Inner, N> Construct for Node<Inner, N>
where
Inner: Construct,
N: PathNode,
N: PathNode<'a>,
{
fn construct(&self) -> Vec<u8> {
let mut vec = self.inner.construct();
@ -405,10 +406,10 @@ where
}
}
impl<Inner, F> Construct for Field<Inner, F>
impl<'a, Inner, F> Construct for Field<Inner, F>
where
Inner: Construct,
F: PathField,
F: PathField<'a>,
{
fn construct(&self) -> Vec<u8> {
let mut vec = self.inner.construct();
@ -418,10 +419,10 @@ where
}
}
impl<Inner, N> Construct for Prefix<Inner, N>
impl<'a, Inner, N> Construct for Prefix<Inner, N>
where
Inner: Construct,
N: PathNode,
N: PathNode<'a>,
{
fn construct(&self) -> Vec<u8> {
let mut vec = self.inner.construct();
@ -551,31 +552,34 @@ fn unescape(s: &[u8]) -> Cow<'_, [u8]> {
#[cfg(feature = "test")]
pub mod test {
use super::{PathField, PathItem, PathNode};
use std::fmt::Debug;
use std::{borrow::Cow, fmt::Debug};
pub fn test_pathnode<N>(path_node: N)
pub fn test_pathnode<'a, 'b, N, N2, E>(path_node: N)
where
N: PathNode + Debug + Eq,
<N as PathItem>::Error: Debug,
N: PathNode<'a, Error = E> + Debug,
N2: PathNode<'b, Error = E> + Debug + PartialEq<N> + 'b,
E: Debug,
{
test_roundtrip(path_node);
test_roundtrip::<'a, 'b, N, N2, E>(path_node);
}
pub fn test_field<F>(path_field: F)
pub fn test_field<'a, 'b, F, F2, E>(path_field: F)
where
F: PathField + Debug + Eq,
<F as PathItem>::Error: Debug,
F: PathField<'a, Error = E> + Debug,
F2: PathField<'b, Error = E> + Debug + PartialEq<F> + 'b,
E: Debug,
{
test_roundtrip(path_field)
test_roundtrip::<'a, 'b, F, F2, E>(path_field)
}
fn test_roundtrip<T>(item: T)
fn test_roundtrip<'a, 'b, T, T2, E>(item: T)
where
T: PathItem + Debug + Eq,
<T as PathItem>::Error: Debug,
T: PathItem<'a, Error = E> + Debug,
T2: PathItem<'b, Error = E> + Debug + PartialEq<T> + 'b,
E: Debug,
{
let vec = item.to_bytes();
let round_trip = T::parse(&vec).unwrap();
let round_trip = T2::parse(Cow::Owned(vec.clone())).unwrap();
let plus_one = round_trip.to_bytes();
assert_eq!(round_trip, item);
@ -586,6 +590,7 @@ pub mod test {
#[cfg(test)]
mod tests {
use std::{
borrow::Cow,
fmt::Display,
num::ParseIntError,
str::{from_utf8, Utf8Error},
@ -694,7 +699,7 @@ mod tests {
#[test]
fn construct_field() {
let s = ROOT
.push(Deck("deck-id".to_owned()))
.push(Deck("deck-id".into()))
.push(Key(5))
.field(CommandField)
.to_bytes();
@ -704,20 +709,14 @@ mod tests {
#[test]
fn construct_prefix() {
let s = ROOT
.push(Deck("deck-id".to_owned()))
.prefix::<Key>()
.to_bytes();
let s = ROOT.push(Deck("deck-id".into())).prefix::<Key>().to_bytes();
assert_eq!(&s, b"test-root.deck.deck-id.key")
}
#[test]
fn construct_escaped_prefix() {
let s = ROOT
.push(Deck("deck.id".to_owned()))
.prefix::<Key>()
.to_bytes();
let s = ROOT.push(Deck("deck.id".into())).prefix::<Key>().to_bytes();
assert_eq!(&s, b"test-root.deck.deck\\.id.key")
}
@ -735,7 +734,7 @@ mod tests {
assert_eq!(
path,
ROOT.push(Deck("deck-id".to_owned()))
ROOT.push(Deck("deck-id".into()))
.push(Key(5))
.field(CommandField)
);
@ -749,7 +748,7 @@ mod tests {
let path = parser.parse::<Box<dyn std::error::Error>>(s).unwrap();
assert_eq!(path, ROOT.push(Deck("deck-id".to_owned())).prefix::<Key>());
assert_eq!(path, ROOT.push(Deck("deck-id".into())).prefix::<Key>());
}
#[test]
@ -760,18 +759,21 @@ mod tests {
let path = parser.parse::<Box<dyn std::error::Error>>(s).unwrap();
assert_eq!(path, ROOT.push(Deck("deck.id".to_owned())).prefix::<Key>());
assert_eq!(path, ROOT.push(Deck("deck.id".into())).prefix::<Key>());
}
#[test]
fn test_parts() {
super::test::test_pathnode(Deck("hello".to_string()));
super::test::test_pathnode(Key(5));
super::test::test_field(CommandField);
let string = String::from("hello");
let brw: &str = &string;
super::test::test_pathnode::<_, Deck<'_>, _>(Deck(Cow::Borrowed(brw)));
super::test::test_pathnode::<_, Key, _>(Key(5));
super::test::test_field::<_, CommandField, _>(CommandField);
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct Deck(String);
struct Deck<'a>(Cow<'a, str>);
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct Key(u8);
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -787,14 +789,22 @@ mod tests {
impl std::error::Error for CommandFieldError {}
impl PathItem for Deck {
impl<'a> PathItem<'a> for Deck<'a> {
type Error = Utf8Error;
fn parse<'a>(bytes: &'a [u8]) -> Result<Self, Self::Error>
fn parse(bytes: Cow<'a, [u8]>) -> Result<Self, Self::Error>
where
Self: Sized + 'a,
{
Ok(Deck(from_utf8(bytes)?.to_string()))
let cow = match bytes {
Cow::Owned(bytes) => {
from_utf8(&bytes)?;
Cow::Owned(String::from_utf8(bytes).unwrap())
}
Cow::Borrowed(bytes) => Cow::Borrowed(from_utf8(bytes)?),
};
Ok(Deck(cow))
}
fn to_bytes(&self) -> Vec<u8> {
@ -802,14 +812,14 @@ mod tests {
}
}
impl PathItem for Key {
impl<'a> PathItem<'a> for Key {
type Error = ParseIntError;
fn parse<'a>(bytes: &'a [u8]) -> Result<Self, Self::Error>
fn parse(bytes: Cow<'a, [u8]>) -> Result<Self, Self::Error>
where
Self: Sized + 'a,
{
Ok(Key(String::from_utf8_lossy(bytes).parse()?))
Ok(Key(String::from_utf8_lossy(&bytes).parse()?))
}
fn to_bytes(&self) -> Vec<u8> {
@ -817,14 +827,14 @@ mod tests {
}
}
impl PathItem for CommandField {
impl<'a> PathItem<'a> for CommandField {
type Error = CommandFieldError;
fn parse<'a>(bytes: &'a [u8]) -> Result<Self, Self::Error>
fn parse(bytes: Cow<'a, [u8]>) -> Result<Self, Self::Error>
where
Self: Sized + 'a,
{
if bytes == b"command" {
if bytes.as_ref() == b"command" {
return Ok(CommandField);
}
@ -836,13 +846,13 @@ mod tests {
}
}
impl PathNode for Deck {
impl<'a> PathNode<'a> for Deck<'a> {
const NAME: &'static [u8] = b"deck";
}
impl PathNode for Key {
impl<'a> PathNode<'a> for Key {
const NAME: &'static [u8] = b"key";
}
impl PathField for CommandField {}
impl<'a> PathField<'a> for CommandField {}
}