Cow tools
This commit is contained in:
parent
3dfc69dd0c
commit
df9dd2ddc5
156
src/lib.rs
156
src/lib.rs
|
@ -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 {}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue