use core::fmt;
use core::iter;
use core::mem::take;
use core::num::NonZeroUsize;
use core::str;
use musli::{Decode, Encode};
use serde::{Deserialize, Serialize};
use crate as rune;
use crate::alloc::clone::TryClone;
use crate::alloc::fmt::TryWrite;
use crate::alloc::String;
use crate::runtime::{Formatter, Inline, ProtocolCaller, Repr, Value, VmErrorKind, VmResult};
use crate::{Any, TypeHash};
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct TypeFromStrError;
impl fmt::Display for TypeFromStrError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Bad type string")
}
}
#[derive(Debug, Clone, Copy)]
pub struct AlignmentFromStrError;
impl fmt::Display for AlignmentFromStrError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Bad alignment string")
}
}
#[derive(Any, Debug, Clone, TryClone)]
#[rune(item = ::std::fmt)]
pub struct Format {
pub(crate) value: Value,
#[try_clone(copy)]
pub(crate) spec: FormatSpec,
}
#[derive(Debug, Clone, Copy, TryClone, Serialize, Deserialize, Decode, Encode)]
#[try_clone(copy)]
#[non_exhaustive]
pub struct FormatSpec {
pub(crate) flags: Flags,
pub(crate) fill: char,
pub(crate) align: Alignment,
pub(crate) width: Option<NonZeroUsize>,
pub(crate) precision: Option<NonZeroUsize>,
pub(crate) format_type: Type,
}
impl FormatSpec {
pub fn new(
flags: Flags,
fill: char,
align: Alignment,
width: Option<NonZeroUsize>,
precision: Option<NonZeroUsize>,
format_type: Type,
) -> Self {
Self {
flags,
fill,
align,
width,
precision,
format_type,
}
}
fn float_traits(&self, n: f64) -> (f64, Alignment, char, Option<char>) {
if self.flags.test(Flag::SignAwareZeroPad) {
if n.is_sign_negative() {
(-n, Alignment::Right, '0', Some('-'))
} else {
(n, Alignment::Right, '0', None)
}
} else if self.flags.test(Flag::SignPlus) && n.is_sign_positive() {
(n, self.align, self.fill, Some('+'))
} else {
(n, self.align, self.fill, None)
}
}
fn int_traits(&self, n: i64) -> (i64, Alignment, char, Option<char>) {
if self.flags.test(Flag::SignAwareZeroPad) {
if n < 0 {
(-n, Alignment::Right, '0', Some('-'))
} else {
(n, Alignment::Right, '0', None)
}
} else if self.flags.test(Flag::SignPlus) && n >= 0 {
(n, self.align, self.fill, Some('+'))
} else {
(n, self.align, self.fill, None)
}
}
fn format_number(&self, buf: &mut String, n: i64) -> VmResult<()> {
let mut buffer = itoa::Buffer::new();
vm_try!(buf.try_push_str(buffer.format(n)));
VmResult::Ok(())
}
fn format_float(&self, buf: &mut String, n: f64) -> VmResult<()> {
if let Some(precision) = self.precision {
vm_try!(vm_write!(buf, "{:.*}", precision.get(), n));
} else {
let mut buffer = ryu::Buffer::new();
vm_try!(buf.try_push_str(buffer.format(n)));
}
VmResult::Ok(())
}
fn format_fill(
&self,
f: &mut Formatter,
align: Alignment,
fill: char,
sign: Option<char>,
) -> VmResult<()> {
let (f, buf) = f.parts_mut();
if let Some(sign) = sign {
vm_try!(f.try_write_char(sign));
}
let mut w = self.width.map(|n| n.get()).unwrap_or_default();
if w == 0 {
vm_try!(f.try_write_str(buf));
return VmResult::Ok(());
}
w = w
.saturating_sub(buf.chars().count())
.saturating_sub(sign.map(|_| 1).unwrap_or_default());
if w == 0 {
vm_try!(f.try_write_str(buf));
return VmResult::Ok(());
}
let mut filler = iter::repeat(fill).take(w);
match align {
Alignment::Left => {
vm_try!(f.try_write_str(buf));
for c in filler {
vm_try!(f.try_write_char(c));
}
}
Alignment::Center => {
for c in (&mut filler).take(w / 2) {
vm_try!(f.try_write_char(c));
}
vm_try!(f.try_write_str(buf));
for c in filler {
vm_try!(f.try_write_char(c));
}
}
Alignment::Right => {
for c in filler {
vm_try!(f.try_write_char(c));
}
vm_try!(f.try_write_str(buf));
}
}
VmResult::Ok(())
}
fn format_display(
&self,
value: &Value,
f: &mut Formatter,
caller: &mut dyn ProtocolCaller,
) -> VmResult<()> {
'fallback: {
match value.as_ref() {
Repr::Inline(value) => match value {
Inline::Char(c) => {
vm_try!(f.buf_mut().try_push(*c));
vm_try!(self.format_fill(f, self.align, self.fill, None));
}
Inline::Signed(n) => {
let (n, align, fill, sign) = self.int_traits(*n);
vm_try!(self.format_number(f.buf_mut(), n));
vm_try!(self.format_fill(f, align, fill, sign));
}
Inline::Float(n) => {
let (n, align, fill, sign) = self.float_traits(*n);
vm_try!(self.format_float(f.buf_mut(), n));
vm_try!(self.format_fill(f, align, fill, sign));
}
_ => {
break 'fallback;
}
},
Repr::Dynamic(..) => {
break 'fallback;
}
Repr::Any(value) => match value.type_hash() {
String::HASH => {
let s = vm_try!(value.borrow_ref::<String>());
vm_try!(f.buf_mut().try_push_str(&s));
vm_try!(self.format_fill(f, self.align, self.fill, None));
}
_ => {
break 'fallback;
}
},
}
return VmResult::Ok(());
}
value.display_fmt_with(f, caller)
}
fn format_debug(
&self,
value: &Value,
f: &mut Formatter,
caller: &mut dyn ProtocolCaller,
) -> VmResult<()> {
'fallback: {
match value.as_ref() {
Repr::Inline(value) => match value {
Inline::Signed(n) => {
let (n, align, fill, sign) = self.int_traits(*n);
vm_try!(self.format_number(f.buf_mut(), n));
vm_try!(self.format_fill(f, align, fill, sign));
}
Inline::Float(n) => {
let (n, align, fill, sign) = self.float_traits(*n);
vm_try!(self.format_float(f.buf_mut(), n));
vm_try!(self.format_fill(f, align, fill, sign));
}
_ => {
break 'fallback;
}
},
Repr::Dynamic(..) => {
break 'fallback;
}
Repr::Any(value) => match value.type_hash() {
String::HASH => {
let s = vm_try!(value.borrow_ref::<String>());
vm_try!(vm_write!(f, "{s:?}"));
}
_ => {
break 'fallback;
}
},
}
return VmResult::Ok(());
};
value.debug_fmt_with(f, caller)
}
fn format_upper_hex(&self, value: &Value, f: &mut Formatter) -> VmResult<()> {
match value.as_inline() {
Some(Inline::Signed(n)) => {
let (n, align, fill, sign) = self.int_traits(*n);
vm_try!(vm_write!(f.buf_mut(), "{:X}", n));
vm_try!(self.format_fill(f, align, fill, sign));
}
_ => {
return VmResult::err(VmErrorKind::IllegalFormat);
}
}
VmResult::Ok(())
}
fn format_lower_hex(&self, value: &Value, f: &mut Formatter) -> VmResult<()> {
match value.as_inline() {
Some(Inline::Signed(n)) => {
let (n, align, fill, sign) = self.int_traits(*n);
vm_try!(vm_write!(f.buf_mut(), "{:x}", n));
vm_try!(self.format_fill(f, align, fill, sign));
}
_ => {
return VmResult::err(VmErrorKind::IllegalFormat);
}
}
VmResult::Ok(())
}
fn format_binary(&self, value: &Value, f: &mut Formatter) -> VmResult<()> {
match value.as_inline() {
Some(Inline::Signed(n)) => {
let (n, align, fill, sign) = self.int_traits(*n);
vm_try!(vm_write!(f.buf_mut(), "{:b}", n));
vm_try!(self.format_fill(f, align, fill, sign));
}
_ => {
return VmResult::err(VmErrorKind::IllegalFormat);
}
}
VmResult::Ok(())
}
fn format_pointer(&self, value: &Value, f: &mut Formatter) -> VmResult<()> {
match value.as_inline() {
Some(Inline::Signed(n)) => {
let (n, align, fill, sign) = self.int_traits(*n);
vm_try!(vm_write!(f.buf_mut(), "{:p}", n as *const ()));
vm_try!(self.format_fill(f, align, fill, sign));
}
_ => {
return VmResult::err(VmErrorKind::IllegalFormat);
}
}
VmResult::Ok(())
}
pub(crate) fn format(
&self,
value: &Value,
f: &mut Formatter,
caller: &mut dyn ProtocolCaller,
) -> VmResult<()> {
f.buf_mut().clear();
match self.format_type {
Type::Display => vm_try!(self.format_display(value, f, caller)),
Type::Debug => vm_try!(self.format_debug(value, f, caller)),
Type::UpperHex => vm_try!(self.format_upper_hex(value, f)),
Type::LowerHex => vm_try!(self.format_lower_hex(value, f)),
Type::Binary => vm_try!(self.format_binary(value, f)),
Type::Pointer => vm_try!(self.format_pointer(value, f)),
}
VmResult::Ok(())
}
}
impl fmt::Display for FormatSpec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"format(fill = {fill:?}, align = {align}, flags = {flags:?}, width = {width}, precision = {precision}, format_type = {format_type})",
fill = self.fill,
align = self.align,
flags = self.flags,
width = OptionDebug(self.width.as_ref()),
precision = OptionDebug(self.precision.as_ref()),
format_type = self.format_type
)
}
}
struct OptionDebug<'a, T>(Option<&'a T>);
impl<T> fmt::Display for OptionDebug<'_, T>
where
T: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
Some(value) => write!(f, "{}", value),
None => write!(f, "?"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Decode, Encode)]
#[non_exhaustive]
pub enum Type {
Display,
Debug,
UpperHex,
LowerHex,
Binary,
Pointer,
}
impl str::FromStr for Type {
type Err = TypeFromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"display" => Ok(Self::Display),
"debug" => Ok(Self::Debug),
"upper_hex" => Ok(Self::UpperHex),
"lower_hex" => Ok(Self::LowerHex),
"binary" => Ok(Self::Binary),
"pointer" => Ok(Self::Pointer),
_ => Err(TypeFromStrError),
}
}
}
impl Default for Type {
fn default() -> Self {
Self::Display
}
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Display => {
write!(f, "display")?;
}
Self::Debug => {
write!(f, "debug")?;
}
Self::UpperHex => {
write!(f, "upper_hex")?;
}
Self::LowerHex => {
write!(f, "lower_hex")?;
}
Self::Binary => {
write!(f, "binary")?;
}
Self::Pointer => {
write!(f, "pointer")?;
}
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, TryClone, PartialEq, Eq, Serialize, Deserialize, Decode, Encode)]
#[try_clone(copy)]
#[non_exhaustive]
pub enum Alignment {
Left,
Center,
Right,
}
impl Default for Alignment {
fn default() -> Self {
Self::Left
}
}
impl str::FromStr for Alignment {
type Err = AlignmentFromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"left" => Ok(Self::Left),
"center" => Ok(Self::Center),
"right" => Ok(Self::Right),
_ => Err(AlignmentFromStrError),
}
}
}
impl fmt::Display for Alignment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Left => {
write!(f, "left")?;
}
Self::Center => {
write!(f, "center")?;
}
Self::Right => {
write!(f, "right")?;
}
}
Ok(())
}
}
#[derive(Clone, Copy)]
#[repr(u32)]
#[non_exhaustive]
pub enum Flag {
SignPlus,
SignMinus,
Alternate,
SignAwareZeroPad,
}
#[derive(Clone, Copy, TryClone, Default, PartialEq, Eq, Serialize, Deserialize, Decode, Encode)]
#[repr(transparent)]
#[musli(transparent)]
#[try_clone(copy)]
pub struct Flags(u32);
impl Flags {
pub fn is_empty(self) -> bool {
self.0 == 0
}
pub fn into_u32(self) -> u32 {
self.0
}
#[inline]
pub fn set(&mut self, flag: Flag) {
self.0 |= &(1 << flag as u32);
}
#[inline]
pub fn test(&self, flag: Flag) -> bool {
(self.0 & (1 << flag as u32)) != 0
}
}
impl From<u32> for Flags {
fn from(flags: u32) -> Self {
Self(flags)
}
}
impl fmt::Debug for Flags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
macro_rules! fmt_flag {
($flag:ident, $o:ident, $spec:literal) => {
if self.test(Flag::$flag) {
if !take(&mut $o) {
write!(f, ", ")?;
}
write!(f, $spec)?;
}
};
}
let mut o = true;
write!(f, "Flags{{")?;
fmt_flag!(SignPlus, o, "+");
fmt_flag!(SignMinus, o, "-");
fmt_flag!(Alternate, o, "#");
fmt_flag!(SignAwareZeroPad, o, "0");
write!(f, "}}")?;
Ok(())
}
}