use super::error::CalcError;
pub const SCALC_STRING_SIZE: usize = 40;
pub const SCALC_STRING_MAX: usize = SCALC_STRING_SIZE - 1;
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct ScalcString(Vec<u8>);
impl ScalcString {
pub fn new() -> Self {
ScalcString(Vec::new())
}
pub fn from_c(bytes: impl AsRef<[u8]>) -> Self {
let b = bytes.as_ref();
let end = b
.iter()
.position(|&c| c == 0)
.unwrap_or(b.len())
.min(SCALC_STRING_MAX);
ScalcString(b[..end].to_vec())
}
pub fn from_strncpy(bytes: impl AsRef<[u8]>) -> Self {
let mut s = ScalcString::from_c(bytes);
s.0.truncate(SCALC_STRING_SIZE - 2);
s
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
pub fn into_bytes(self) -> Vec<u8> {
self.0
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn as_str_lossy(&self) -> std::borrow::Cow<'_, str> {
String::from_utf8_lossy(&self.0)
}
}
impl From<&str> for ScalcString {
fn from(s: &str) -> Self {
ScalcString::from_c(s.as_bytes())
}
}
impl From<String> for ScalcString {
fn from(s: String) -> Self {
ScalcString::from_c(s.as_bytes())
}
}
impl From<&[u8]> for ScalcString {
fn from(b: &[u8]) -> Self {
ScalcString::from_c(b)
}
}
impl From<Vec<u8>> for ScalcString {
fn from(b: Vec<u8>) -> Self {
ScalcString::from_c(b)
}
}
impl PartialEq<str> for ScalcString {
fn eq(&self, other: &str) -> bool {
self.0 == other.as_bytes()
}
}
impl PartialEq<&str> for ScalcString {
fn eq(&self, other: &&str) -> bool {
self.0 == other.as_bytes()
}
}
impl std::fmt::Display for ScalcString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str_lossy())
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum StackValue {
Double(f64),
Str(ScalcString),
}
impl StackValue {
pub fn str(bytes: impl AsRef<[u8]>) -> StackValue {
StackValue::Str(ScalcString::from_c(bytes))
}
pub fn str_ncpy(bytes: impl AsRef<[u8]>) -> StackValue {
StackValue::Str(ScalcString::from_strncpy(bytes))
}
pub fn is_double(&self) -> bool {
matches!(self, StackValue::Double(_))
}
pub fn is_string(&self) -> bool {
matches!(self, StackValue::Str(_))
}
pub fn to_double(&self) -> f64 {
match self {
StackValue::Double(v) => *v,
StackValue::Str(s) => super::strtod::strtod(s.as_bytes()).value,
}
}
pub fn as_bytes(&self) -> Result<&[u8], CalcError> {
match self {
StackValue::Str(s) => Ok(s.as_bytes()),
StackValue::Double(_) => Err(CalcError::TypeMismatch),
}
}
pub fn into_string_value(self) -> ScalcString {
match self {
StackValue::Str(s) => s,
StackValue::Double(v) => ScalcString::from_c(super::cvt::to_string(v)),
}
}
}