use std::{fmt::Display, mem, string};
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct String {
pub(crate) data: Option<string::String>,
pub(crate) alloc_location: AllocLocation,
}
#[derive(Clone, Hash, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum AllocLocation {
#[default]
Rust,
C,
}
impl From<&str> for String {
fn from(value: &str) -> Self {
Self {
data: Some(value.to_owned()),
alloc_location: AllocLocation::Rust,
}
}
}
impl From<string::String> for String {
fn from(value: string::String) -> Self {
Self {
data: Some(value),
alloc_location: AllocLocation::Rust,
}
}
}
impl Display for String {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl String {
pub fn as_str(&self) -> &str {
&self
.data
.as_ref()
.expect("The string should only be empty when it's dropped")
}
pub fn into_std_string(mut self) -> string::String {
match self.alloc_location {
AllocLocation::C => {
let c_alloc_string = mem::take(&mut self.data).expect("It should only be Some");
let rust_alloc_string = c_alloc_string.clone();
mem::forget(c_alloc_string);
rust_alloc_string
}
AllocLocation::Rust => {
let string = mem::take(&mut self.data).expect("Will always be some");
string
}
}
}
}
impl Drop for String {
fn drop(&mut self) {
match self.alloc_location {
AllocLocation::C => {
let string = mem::take(&mut self.data);
mem::forget(string)
}
AllocLocation::Rust => {
}
}
}
}
#[cfg(test)]
mod tests {
use crate::types::types_list;
use super::String;
#[test]
fn test_string_round_trip() {
let start = "HI! I'm a nice string".to_owned();
let wrapper: String = start.clone().into();
assert_eq!(&start, wrapper.as_str());
assert_eq!(start, wrapper.to_string());
assert_eq!(start, wrapper.into_std_string());
}
#[test]
fn test_string_round_trip_through_c() {
let start = "HI! I'm a nice string".to_owned();
let c_string: types_list::String = start.clone().into();
let wrapper: String = Into::<types_list::String>::into(c_string)
.try_into()
.unwrap();
assert_eq!(&start, wrapper.as_str());
assert_eq!(start, wrapper.to_string());
assert_eq!(start, wrapper.into_std_string());
}
}