Skip to content

Commit

Permalink
Implement std::string::String-like wrapper around FuriString
Browse files Browse the repository at this point in the history
  • Loading branch information
str4d committed Mar 4, 2023
1 parent 6c2ca6b commit 8309293
Show file tree
Hide file tree
Showing 2 changed files with 186 additions and 0 deletions.
1 change: 1 addition & 0 deletions crates/flipperzero/src/furi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pub mod io;
pub mod message_queue;
pub mod string;
pub mod sync;
pub mod thread;

Expand Down
185 changes: 185 additions & 0 deletions crates/flipperzero/src/furi/string.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
//! Furi String primitive.
//!
//! This is similar to Rust's `CString`, but used inside the Flipper Zero SDK.
use core::{
cmp::Ordering,
convert::Infallible,
ffi::{c_char, CStr},
fmt,
};

#[cfg(feature = "alloc")]
use alloc::ffi::CString;

use flipperzero_sys as sys;

/// A Furi string.
//
// This is similar to Rust's [`CString`], but used inside the Flipper Zero SDK.
#[derive(Eq)]
pub struct String(*mut sys::FuriString);

impl Drop for String {
fn drop(&mut self) {
unsafe { sys::furi_string_free(self.0) };
}
}

// Implementations matching `std::string::String`.
impl String {
/// Creates a new empty `String`.
pub fn new() -> Self {
String(unsafe { sys::furi_string_alloc() })
}

/// Creates a new empty `String` with at least the specified capacity.
pub fn with_capacity(capacity: usize) -> Self {
let mut s = Self::new();
s.reserve(capacity);
s
}

/// Extracts a `CStr` slice containing the entire `String`.
#[must_use]
pub fn as_cstr(&self) -> &CStr {
unsafe { CStr::from_ptr(sys::furi_string_get_cstr(self.0)) }
}

/// Appends a given `CStr` slice onto the end of this `String`.
pub fn push_cstr(&mut self, string: &CStr) {
unsafe { sys::furi_string_cat_str(self.0, string.as_ptr()) }
}

/// Reserves capacity for at least `additional` bytes more than the current length.
pub fn reserve(&mut self, additional: usize) {
unsafe { sys::furi_string_reserve(self.0, self.len() + additional) };
}

/// Appends the given [`char`] to the end of this `String`.
pub fn push(&mut self, ch: char) {
match ch.len_utf8() {
1 => unsafe { sys::furi_string_push_back(self.0, ch as c_char) },
_ => unsafe { sys::furi_string_utf8_push(self.0, ch as u32) },
}
}

/// Shortens this `String` to the specified length.
///
/// If `new_len` is greater than the string's current length, this has no effect.
pub fn truncate(&mut self, new_len: usize) {
if new_len < self.len() {
unsafe { sys::furi_string_right(self.0, new_len) };
}
}

/// Truncates this `String`, removing all contents.
///
/// While this means the `String` will have a length of zero, it does not
/// touch its capacity.
pub fn clear(&mut self) {
unsafe { sys::furi_string_reset(self.0) };
}
}

// Implementations matching `&str`.
impl String {
/// Returns the length of `self`.
///
/// This length is in bytes, not [`char`]s or graphemes. In other words, it might not
/// be what a human considers the length of the string.
pub fn len(&self) -> usize {
unsafe { sys::furi_string_size(self.0) }
}

/// Returns `true` if `self` has a length of zero bytes.
pub fn is_empty(&self) -> bool {
unsafe { sys::furi_string_empty(self.0) }
}
}

impl Default for String {
fn default() -> Self {
Self::new()
}
}

impl PartialEq for String {
fn eq(&self, other: &Self) -> bool {
unsafe { sys::furi_string_equal(self.0, other.0) }
}
}

#[cfg(feature = "alloc")]
impl PartialEq<CString> for String {
fn eq(&self, other: &CString) -> bool {
unsafe { sys::furi_string_equal_str(self.0, other.as_ptr()) }
}
}

#[cfg(feature = "alloc")]
impl PartialEq<String> for CString {
fn eq(&self, other: &String) -> bool {
unsafe { sys::furi_string_equal_str(other.0, self.as_ptr()) }
}
}

impl PartialEq<&CStr> for String {
fn eq(&self, other: &&CStr) -> bool {
unsafe { sys::furi_string_equal_str(self.0, other.as_ptr()) }
}
}

impl PartialEq<String> for &CStr {
fn eq(&self, other: &String) -> bool {
unsafe { sys::furi_string_equal_str(other.0, self.as_ptr()) }
}
}

impl Ord for String {
fn cmp(&self, other: &Self) -> Ordering {
match unsafe { sys::furi_string_cmp(self.0, other.0) } {
..=-1 => Ordering::Less,
0 => Ordering::Equal,
1.. => Ordering::Greater,
}
}
}

impl PartialOrd for String {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

impl fmt::Write for String {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.reserve(s.len());
for ch in s.chars() {
self.push(ch);
}
Ok(())
}

fn write_char(&mut self, c: char) -> fmt::Result {
self.push(c);
Ok(())
}
}

impl ufmt::uWrite for String {
type Error = Infallible;

fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
self.reserve(s.len());
for ch in s.chars() {
self.push(ch);
}
Ok(())
}

fn write_char(&mut self, c: char) -> Result<(), Self::Error> {
self.push(c);
Ok(())
}
}

0 comments on commit 8309293

Please sign in to comment.