use std::fmt;
use ecow::EcoString;
use serde::*;
use crate::SUBSCRIPT_DIGITS;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(default)]
pub struct Subscript<N = NumericSubscript> {
#[serde(skip_serializing_if = "Option::is_none")]
pub num: Option<N>,
#[serde(skip_serializing_if = "Option::is_none")]
pub side: Option<SidedSubscript>,
}
impl<N> Default for Subscript<N> {
fn default() -> Self {
Self {
num: None,
side: None,
}
}
}
impl From<i32> for Subscript {
fn from(i: i32) -> Self {
Subscript {
num: Some(i.into()),
side: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", content = "value", rename_all = "snake_case")]
pub enum NumericSubscript {
NegOnly,
TooLarge(EcoString),
#[serde(untagged)]
N(i32),
}
impl From<i32> for NumericSubscript {
fn from(i: i32) -> Self {
NumericSubscript::N(i)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct SidedSubscript {
pub side: SubSide,
#[serde(skip_serializing_if = "Option::is_none")]
pub n: Option<usize>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[allow(missing_docs)]
pub enum SubSide {
Left,
Right,
}
impl Subscript {
pub fn numeric(i: i32) -> Self {
Self {
num: Some(NumericSubscript::N(i)),
side: None,
}
}
pub fn is_useable(&self) -> bool {
matches!(self.num, Some(NumericSubscript::N(_))) || self.side.is_some()
}
pub fn n(&self) -> Option<i32> {
self.num.as_ref().and_then(|n| match n {
NumericSubscript::N(n) => Some(*n),
_ => None,
})
}
}
impl<N> Subscript<N> {
pub fn map_num<M>(self, f: impl FnOnce(N) -> M) -> Subscript<M> {
Subscript {
num: self.num.map(f),
side: self.side,
}
}
}
impl fmt::Display for SubSide {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SubSide::Left => write!(f, "⌞"),
SubSide::Right => write!(f, "⌟"),
}
}
}
impl fmt::Display for NumericSubscript {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NumericSubscript::NegOnly => write!(f, "₋"),
NumericSubscript::N(n) => {
if *n < 0 {
write!(f, "₋")?;
}
for c in n.abs().to_string().chars() {
write!(f, "{}", SUBSCRIPT_DIGITS[(c as u32 as u8 - b'0') as usize])?;
}
Ok(())
}
NumericSubscript::TooLarge(s) => s.fmt(f),
}
}
}
impl fmt::Display for SidedSubscript {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.side.fmt(f)?;
if let Some(n) = self.n {
for c in n.to_string().chars() {
write!(f, "{}", SUBSCRIPT_DIGITS[(c as u32 as u8 - b'0') as usize])?;
}
}
Ok(())
}
}
impl<N: fmt::Display> fmt::Display for Subscript<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.num.is_none() && self.side.is_none() {
return write!(f, ",");
};
if let Some(num) = &self.num {
num.fmt(f)?;
}
if let Some(side) = self.side {
side.fmt(f)?;
}
Ok(())
}
}