diff --git a/Cargo.lock b/Cargo.lock index 9610864..67a6940 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,7 +100,7 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "bytes-str" -version = "0.2.3" +version = "0.2.4" dependencies = [ "bytes", "rkyv", diff --git a/crates/bytes-str/Cargo.toml b/crates/bytes-str/Cargo.toml index 293986f..388ff42 100644 --- a/crates/bytes-str/Cargo.toml +++ b/crates/bytes-str/Cargo.toml @@ -6,7 +6,7 @@ include = ["Cargo.toml", "src/**/*.rs"] license = { workspace = true } name = "bytes-str" repository = { workspace = true } -version = "0.2.3" +version = "0.2.4" [features] rkyv = ["dep:rkyv", "rkyv/bytes-1"] diff --git a/crates/bytes-str/src/byte_str.rs b/crates/bytes-str/src/byte_str.rs index ce9b697..6a6a6e2 100644 --- a/crates/bytes-str/src/byte_str.rs +++ b/crates/bytes-str/src/byte_str.rs @@ -4,7 +4,7 @@ use std::{ ffi::OsStr, fmt::{self, Debug, Display}, hash::{Hash, Hasher}, - ops::{Deref, Index}, + ops::{Deref, Index, RangeBounds}, path::Path, slice::SliceIndex, str::Utf8Error, @@ -114,6 +114,20 @@ impl BytesStr { }) } + /// Creates a new BytesStr from an owner. + /// + /// See [Bytes::from_owner] for more information. + pub fn from_owned_utf8(owner: T) -> Result + where + T: AsRef<[u8]> + Send + 'static, + { + std::str::from_utf8(owner.as_ref())?; + + Ok(Self { + bytes: Bytes::from_owner(owner), + }) + } + /// Creates a new BytesStr from a [Bytes] without checking if the bytes /// are valid UTF-8. /// @@ -236,6 +250,75 @@ impl BytesStr { self.bytes } + /// Returns the length of the [BytesStr]. + /// + /// # Examples + /// + /// ``` + /// use bytes_str::BytesStr; + /// + /// let s = BytesStr::from_static("hello"); + /// + /// assert_eq!(s.len(), 5); + /// ``` + pub const fn len(&self) -> usize { + self.bytes.len() + } + + /// Returns true if the [BytesStr] is empty. + /// + /// # Examples + /// + /// ``` + /// use bytes_str::BytesStr; + /// + /// let s = BytesStr::new(); + /// + /// assert!(s.is_empty()); + /// ``` + pub const fn is_empty(&self) -> bool { + self.bytes.is_empty() + } + + /// Returns a slice of the [BytesStr]. + /// + /// # Panics + /// + /// Panics if the bounds are not character boundaries. + /// + /// # Examples + /// + /// ``` + /// use bytes_str::BytesStr; + /// + /// let s = BytesStr::from_static("hello"); + /// let slice = s.slice(1..3); + /// + /// assert_eq!(slice.as_str(), "el"); + /// ``` + pub fn slice(&self, range: impl RangeBounds) -> Self { + let s = Self { + bytes: self.bytes.slice(range), + }; + + if !s.is_char_boundary(0) { + panic!("range start is not a character boundary"); + } + + if !s.is_char_boundary(s.len()) { + panic!("range end is not a character boundary"); + } + + s + } + + /// See [Bytes::slice_ref] + pub fn slice_ref(&self, subset: &str) -> Self { + Self { + bytes: self.bytes.slice_ref(subset.as_bytes()), + } + } + /// Advances the [BytesStr] by `n` bytes. /// /// # Panics