use std::marker::PhantomData;
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct MojoStr<'a> {
ptr: *const u8,
len: usize,
_marker: PhantomData<&'a str>,
}
impl<'a> MojoStr<'a> {
pub fn new(s: &'a str) -> Self {
Self {
ptr: s.as_ptr(),
len: s.len(),
_marker: PhantomData,
}
}
#[inline]
pub fn as_raw(&self) -> isize {
self.ptr as isize
}
pub fn ptr(&self) -> *const u8 {
self.ptr
}
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn len_isize(&self) -> isize {
self.len as isize
}
pub unsafe fn as_str(&self) -> &'a str {
unsafe {
let bytes = std::slice::from_raw_parts(self.ptr, self.len);
std::str::from_utf8_unchecked(bytes)
}
}
}
impl<'a> From<&'a str> for MojoStr<'a> {
fn from(s: &'a str) -> Self {
Self::new(s)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_from_str() {
let s = MojoStr::new("hello");
assert_eq!(s.len(), 5);
assert!(!s.is_empty());
assert_eq!(s.len_isize(), 5);
assert_ne!(s.as_raw(), 0);
}
#[test]
fn empty_str() {
let s = MojoStr::new("");
assert_eq!(s.len(), 0);
assert!(s.is_empty());
}
#[test]
fn from_trait() {
let s: MojoStr<'_> = "world".into();
assert_eq!(s.len(), 5);
}
#[test]
fn as_raw_points_to_data() {
let text = "test";
let s = MojoStr::new(text);
assert_eq!(s.as_raw(), text.as_ptr() as isize);
assert_eq!(s.ptr(), text.as_ptr());
}
#[test]
fn roundtrip_as_str() {
let text = "hello world";
let s = MojoStr::new(text);
let recovered = unsafe { s.as_str() };
assert_eq!(recovered, text);
}
}