diff --git a/Cargo.toml b/Cargo.toml index ce567a0..4c968ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "imstr" -version = "0.2.0" -edition = "2021" +version = "0.3.0" +edition = "2024" description = "Cheaply clonable and slicable immutable strings" documentation = "https://docs.rs/imstr" repository = "https://github.com/xfbs/imstr" @@ -21,9 +21,9 @@ name = "peg-list" required-features = ["peg"] [dependencies] -nom = { version = "7.1.3", optional = true } -peg-runtime = { version = "0.8.1", optional = true } -serde = { version = "1.0.159", features = ["derive"], optional = true } +nom = { version = "8", optional = true } +peg-runtime = { version = "0.8", optional = true } +serde = { version = "1.0", features = ["derive"], optional = true } [features] default = [] @@ -37,3 +37,4 @@ std = [] [dev-dependencies] criterion = "0.4.0" peg = "0.8.1" +nom-language = "0.1.0" diff --git a/examples/nom-json.rs b/examples/nom-json.rs index ddf430f..677df4c 100644 --- a/examples/nom-json.rs +++ b/examples/nom-json.rs @@ -4,12 +4,13 @@ use nom::{ bytes::complete::{escaped, tag, take_while}, character::complete::{alphanumeric1 as alphanumeric, char, one_of}, combinator::{cut, map, opt, value}, - error::{context, convert_error, ContextError, ParseError, VerboseError}, + error::{context, ContextError, ParseError}, multi::separated_list0, number::complete::double, sequence::{delimited, preceded, separated_pair, terminated}, - Err, IResult, + Err, IResult, Parser, }; +use nom_language::error::{convert_error, VerboseError}; use std::collections::HashMap; #[derive(Debug, PartialEq)] @@ -77,7 +78,7 @@ fn test_string_inner() { fn boolean>(input: ImString) -> IResult { let parse_true = value(true, tag("true")); let parse_false = value(false, tag("false")); - alt((parse_true, parse_false))(input) + alt((parse_true, parse_false)).parse(input) } #[test] @@ -90,7 +91,7 @@ fn test_boolean() { } fn null>(input: ImString) -> IResult { - value((), tag("null"))(input) + value((), tag("null")).parse(input) } #[test] @@ -108,7 +109,8 @@ fn string + ContextError>( context( "string", preceded(char('\"'), cut(terminated(string_inner, char('\"')))), - )(i) + ) + .parse(i) } fn array + ContextError>( @@ -123,7 +125,8 @@ fn array + ContextError>( preceded(sp, char(']')), )), ), - )(i) + ) + .parse(i) } fn key_value + ContextError>( @@ -133,7 +136,8 @@ fn key_value + ContextError>( preceded(sp, string), cut(preceded(sp, char(':'))), json_value, - )(i) + ) + .parse(i) } fn hash + ContextError>( @@ -151,7 +155,8 @@ fn hash + ContextError>( preceded(sp, char('}')), )), ), - )(i) + ) + .parse(i) } fn json_value + ContextError>( @@ -167,7 +172,8 @@ fn json_value + ContextError>( map(boolean, JsonValue::Boolean), map(null, |_| JsonValue::Null), )), - )(i) + ) + .parse(i) } fn root + ContextError>( @@ -181,7 +187,8 @@ fn root + ContextError>( map(null, |_| JsonValue::Null), )), opt(sp), - )(i) + ) + .parse(i) } fn main() { diff --git a/src/data.rs b/src/data.rs index 51c270e..bb1caf0 100644 --- a/src/data.rs +++ b/src/data.rs @@ -115,7 +115,7 @@ impl Data for Arc { } fn get(&self) -> &T { - &self + self } fn get_mut(&mut self) -> Option<&mut T> { @@ -123,7 +123,7 @@ impl Data for Arc { } fn ptr_eq(&self, other: &Self) -> bool { - Arc::ptr_eq(&self, &other) + Arc::ptr_eq(self, other) } } @@ -133,7 +133,7 @@ impl Data for Rc { } fn get(&self) -> &T { - &self + self } fn get_mut(&mut self) -> Option<&mut T> { @@ -141,7 +141,7 @@ impl Data for Rc { } fn ptr_eq(&self, other: &Self) -> bool { - Rc::ptr_eq(&self, &other) + Rc::ptr_eq(self, other) } } @@ -151,7 +151,7 @@ impl Data for Box { } fn get(&self) -> &T { - &self + self } fn get_mut(&mut self) -> Option<&mut T> { @@ -159,7 +159,7 @@ impl Data for Box { } fn ptr_eq(&self, other: &Self) -> bool { - core::ptr::eq(&self, &other) + core::ptr::eq(self, other) } } diff --git a/src/error.rs b/src/error.rs index 5eb938a..e704302 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,6 @@ //! Error types use alloc::fmt::{Display, Formatter, Result}; -pub use alloc::string::{FromUtf16Error, FromUtf8Error}; +pub use alloc::string::{FromUtf8Error, FromUtf16Error}; /// A possible error when slicing a [`ImString`](crate::ImString). #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -46,8 +46,8 @@ fn slice_error_traits() { // implements partial eq assert_eq!(error, new); // implements debug - alloc::format!("{error:?}"); + let _ = alloc::format!("{error:?}"); // implements display - alloc::format!("{new}"); + let _ = alloc::format!("{new}"); } } diff --git a/src/nom.rs b/src/nom.rs index 34fd62c..dc6a4cf 100644 --- a/src/nom.rs +++ b/src/nom.rs @@ -1,12 +1,12 @@ use crate::data::Data; use crate::string::{CharIndices, Chars, ImString}; -use core::ops::{Range, RangeFrom, RangeFull, RangeTo}; +use alloc::string::String; use core::str::FromStr; use nom::{ - error::{ErrorKind, ParseError}, - AsBytes, Compare, CompareResult, Err, ExtendInto, FindSubstring, IResult, InputIter, - InputLength, InputTake, InputTakeAtPosition, Needed, Offset, ParseTo, Slice, + AsBytes, Compare, CompareResult, ExtendInto, FindSubstring, Input, Needed, Offset, ParseTo, }; +#[cfg(test)] +use nom::{Err, error::ErrorKind}; /// Test that the specified function behaves the same regardless of whether the type is `&str` or /// `ImString`. @@ -33,89 +33,60 @@ macro_rules! test_equivalence { }}; } -impl> Slice> for ImString { - fn slice(&self, range: Range) -> Self { - self.slice(range) - } -} - -#[test] -fn test_slice_range() { - test_equivalence!("this is some string", |string: Slice>| { - assert_eq!(string.slice(0..0), ""); - assert_eq!(string.slice(0..4), "this"); - assert_eq!(string.slice(5..7), "is"); - assert_eq!(string.slice(8..12), "some"); - assert_eq!(string.slice(13..19), "string"); - }); -} +impl> Input for ImString { + type Item = char; + type Iter = Chars; + type IterIndices = CharIndices; -impl> Slice> for ImString { - fn slice(&self, range: RangeFrom) -> Self { - self.slice(range) + fn input_len(&self) -> usize { + self.len() } -} -#[test] -fn test_slice_range_from() { - test_equivalence!("this is some string", |string: Slice>| { - assert_eq!(string.slice(0..), "this is some string"); - assert_eq!(string.slice(8..), "some string"); - assert_eq!(string.slice(13..), "string"); - assert_eq!(string.slice(19..), ""); - }); -} - -impl> Slice> for ImString { - fn slice(&self, range: RangeTo) -> Self { - self.slice(range) + fn take(&self, index: usize) -> Self { + self.slice(..index) } -} -#[test] -fn test_slice_range_to() { - test_equivalence!("this is some string", |string: Slice>| { - assert_eq!(string.slice(..0), ""); - assert_eq!(string.slice(..4), "this"); - assert_eq!(string.slice(..7), "this is"); - assert_eq!(string.slice(..12), "this is some"); - }); -} - -impl> Slice for ImString { - fn slice(&self, range: RangeFull) -> Self { - self.slice(range) + fn take_from(&self, index: usize) -> Self { + self.slice(index..) } -} -#[test] -fn test_slice_range_full() { - test_equivalence!("this is some string", |string: Slice| { - assert_eq!(string.slice(..), "this is some string"); - }); + fn take_split(&self, index: usize) -> (Self, Self) { + (self.slice(index..), self.slice(..index)) + } - test_equivalence!("", |string: Slice| { - assert_eq!(string.slice(..), ""); - }); + fn position

(&self, predicate: P) -> Option + where + P: Fn(Self::Item) -> bool, + { + self.as_str().find(predicate) + } - test_equivalence!("string", |string: Slice| { - assert_eq!(string.slice(..), "string"); - }); -} + fn iter_elements(&self) -> Self::Iter { + self.chars() + } -impl> InputTake for ImString { - fn take(&self, count: usize) -> Self { - self.slice(..count) + fn iter_indices(&self) -> Self::IterIndices { + self.char_indices() } - fn take_split(&self, count: usize) -> (Self, Self) { - (self.slice(count..), self.slice(..count)) + fn slice_index(&self, count: usize) -> Result { + let mut cnt = 0; + for (index, _) in self.char_indices() { + if cnt == count { + return Ok(index); + } + cnt += 1; + } + if cnt == count { + return Ok(self.len()); + } + Err(Needed::Unknown) } } #[test] fn test_input_take() { - test_equivalence!("this is some string", |string: InputTake| { + test_equivalence!("this is some string", |string: Input| { assert_eq!(string.take(0), ""); assert_eq!(string.take(4), "this"); assert_eq!(string.take(19), "this is some string"); @@ -137,70 +108,32 @@ fn test_input_take() { }); } -impl> InputLength for ImString { - fn input_len(&self) -> usize { - self.len() - } -} - #[test] fn test_input_length() { - test_equivalence!("this is some string", |string: InputLength| { + test_equivalence!("this is some string", |string: Input| { assert_eq!(string.input_len(), 19); }); - test_equivalence!("", |string: InputLength| { + test_equivalence!("", |string: Input| { assert_eq!(string.input_len(), 0); }); - test_equivalence!("string", |string: InputLength| { + test_equivalence!("string", |string: Input| { assert_eq!(string.input_len(), 6); }); } -impl> InputIter for ImString { - type Item = char; - type Iter = CharIndices; - type IterElem = Chars; - - fn iter_indices(&self) -> Self::Iter { - self.char_indices() - } - - fn iter_elements(&self) -> Self::IterElem { - self.chars() - } - - fn position bool>(&self, predicate: P) -> Option { - self.as_str().find(predicate) - } - - fn slice_index(&self, count: usize) -> Result { - let mut cnt = 0; - for (index, _) in self.char_indices() { - if cnt == count { - return Ok(index); - } - cnt += 1; - } - if cnt == count { - return Ok(self.len()); - } - Err(Needed::Unknown) - } -} - #[test] fn test_input_iter() { - test_equivalence!("", |string: InputIter| { - assert_eq!(string.iter_indices().next(), None); - assert_eq!(string.iter_elements().next(), None); + test_equivalence!("", |string: Input| { + assert!(string.iter_indices().next().is_none()); + assert!(string.iter_elements().next().is_none()); assert_eq!(string.position(|_| true), None); assert_eq!(string.slice_index(0), Ok(0)); assert_eq!(string.slice_index(1), Err(Needed::Unknown)); }); - test_equivalence!("über", |string: InputIter| { + test_equivalence!("über", |string: Input| { let indices: Vec<_> = string.iter_indices().collect(); assert_eq!(indices, &[(0, 'ü'), (2, 'b'), (3, 'e'), (4, 'r')]); let chars: Vec<_> = string.iter_elements().collect(); @@ -221,67 +154,9 @@ fn test_input_iter() { }); } -impl> InputTakeAtPosition for ImString { - type Item = char; - - fn split_at_position>(&self, predicate: P) -> IResult - where - P: Fn(Self::Item) -> bool, - { - match self.as_str().find(predicate) { - Some(i) => Ok((self.slice(i..), self.slice(..i))), - None => Err(Err::Incomplete(Needed::new(1))), - } - } - - fn split_at_position1>( - &self, - predicate: P, - e: ErrorKind, - ) -> IResult - where - P: Fn(Self::Item) -> bool, - { - match self.as_str().find(predicate) { - Some(0) => Err(Err::Error(E::from_error_kind(self.clone(), e))), - Some(i) => Ok((self.slice(i..), self.slice(..i))), - None => Err(Err::Incomplete(Needed::new(1))), - } - } - - fn split_at_position_complete>( - &self, - predicate: P, - ) -> IResult - where - P: Fn(Self::Item) -> bool, - { - match self.as_str().find(predicate) { - Some(i) => Ok((self.slice(i..), self.slice(..i))), - None => Ok((self.slice(self.len()..), self.clone())), - } - } - - fn split_at_position1_complete>( - &self, - predicate: P, - e: ErrorKind, - ) -> IResult - where - P: Fn(Self::Item) -> bool, - { - match self.as_str().find(predicate) { - Some(0) => Err(Err::Error(E::from_error_kind(self.clone(), e))), - Some(i) => Ok((self.slice(i..), self.slice(..i))), - None if self.is_empty() => Err(Err::Error(E::from_error_kind(self.clone(), e))), - None => Ok((self.slice(self.len()..), self.clone())), - } - } -} - #[test] fn test_input_take_at_position() { - test_equivalence!("", |string: InputTakeAtPosition| { + test_equivalence!("", |string: Input| { assert_eq!( string.split_at_position::<_, ()>(|_| true).err().unwrap(), Err::Incomplete(Needed::new(1)) @@ -308,7 +183,7 @@ fn test_input_take_at_position() { assert_eq!(result, Err::Error(())); }); - test_equivalence!("some input", |string: InputTakeAtPosition| { + test_equivalence!("some input", |string: Input| { assert_eq!( string .split_at_position::<_, ()>(|c| c == 'x') @@ -391,13 +266,13 @@ fn test_offset() { assert_eq!(string.offset(&string), 0); }); - test_equivalence!("hello", |string: Offset, Slice>| { + test_equivalence!("hello", |string: Offset, Input| { assert_eq!(string.offset(&string), 0); - assert_eq!(string.offset(&string.slice(1..5)), 1); - assert_eq!(string.offset(&string.slice(2..5)), 2); - assert_eq!(string.offset(&string.slice(3..5)), 3); - assert_eq!(string.offset(&string.slice(4..5)), 4); - assert_eq!(string.offset(&string.slice(5..5)), 5); + assert_eq!(string.offset(&string.take_from(1)), 1); + assert_eq!(string.offset(&string.take_from(2)), 2); + assert_eq!(string.offset(&string.take_from(3)), 3); + assert_eq!(string.offset(&string.take_from(4)), 4); + assert_eq!(string.offset(&string.take_from(5)), 5); }); } @@ -483,7 +358,7 @@ fn test_compare_bytes() { } impl<'a, S: Data> FindSubstring<&'a str> for ImString { - fn find_substring(&self, sub_string: &'a str) -> std::option::Option { + fn find_substring(&self, sub_string: &'a str) -> core::option::Option { self.as_str().find_substring(sub_string) } } diff --git a/src/string.rs b/src/string.rs index e12ea55..375c724 100644 --- a/src/string.rs +++ b/src/string.rs @@ -21,10 +21,10 @@ use core::{ }, str::FromStr, }; -#[cfg(feature = "std")] -use std::{ffi::OsStr, net::ToSocketAddrs, path::Path}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +#[cfg(feature = "std")] +use std::{ffi::OsStr, net::ToSocketAddrs, path::Path}; /// Threadsafe shared storage for string. pub type Threadsafe = Arc; @@ -136,7 +136,7 @@ impl> ImString { /// ``` pub fn from_std_string(string: String) -> Self { ImString { - offset: 0..string.as_bytes().len(), + offset: 0..string.len(), string: S::new(string), } } @@ -183,7 +183,7 @@ impl> ImString { } let string = self.string.get_mut().unwrap(); - return &mut string[self.offset.clone()]; + &mut string[self.offset.clone()] } unsafe fn try_modify_unchecked(&mut self, f: F) -> bool { @@ -235,11 +235,11 @@ impl> ImString { /// assert_eq!(string, "hello"); /// ``` pub fn into_std_string(mut self) -> String { - if self.offset.start == 0 { - if let Some(string) = self.string.get_mut() { - string.truncate(self.offset.end); - return core::mem::take(string); - } + if self.offset.start == 0 + && let Some(string) = self.string.get_mut() + { + string.truncate(self.offset.end); + return core::mem::take(string); } self.as_str().to_string() @@ -390,7 +390,7 @@ impl> ImString { /// assert_eq!(sparkle_heart, "💖"); /// ``` pub unsafe fn from_utf8_unchecked(vec: Vec) -> Self { - ImString::from_std_string(String::from_utf8_unchecked(vec)) + unsafe { ImString::from_std_string(String::from_utf8_unchecked(vec)) } } /// Converts an [`ImString`] into a byte vector. @@ -414,8 +414,8 @@ impl> ImString { unsafe fn unchecked_append String>(&mut self, f: F) { match self.string.get_mut() { - Some(mut string_ref) if self.offset.start == 0 => { - let mut string: String = core::mem::take(&mut string_ref); + Some(string_ref) if self.offset.start == 0 => { + let mut string: String = core::mem::take(string_ref); string.truncate(self.offset.end); *string_ref = f(string); } @@ -425,7 +425,7 @@ impl> ImString { } } - self.offset.end = self.string.get().as_bytes().len(); + self.offset.end = self.string.get().len(); } /// Inserts a character into this string at the specified index. @@ -534,7 +534,7 @@ impl> ImString { /// assert_eq!(string.pop(), None); /// ``` pub fn pop(&mut self) -> Option { - let last_char = self.as_str().chars().rev().next()?; + let last_char = self.as_str().chars().next_back()?; self.offset.end -= last_char.len_utf8(); Some(last_char) } @@ -1135,7 +1135,7 @@ impl> Eq for ImString {} impl> PartialOrd> for ImString { fn partial_cmp(&self, other: &ImString) -> Option { - self.as_str().partial_cmp(other.as_str()) + Some(self.cmp(other)) } } @@ -1199,8 +1199,8 @@ impl> Index> for ImString { impl> Index for ImString { type Output = str; - fn index(&self, index: RangeFull) -> &str { - &self.as_str()[index] + fn index(&self, _index: RangeFull) -> &str { + self.as_str() } } @@ -1601,7 +1601,7 @@ mod tests { // make sure both versions are identical assert_eq!(string_uppercase, &*string_slice); - drop(string_slice); + let _ = string_slice; assert_eq!(string, string_uppercase); }