pub mod blink;
pub mod bold;
pub mod border_bg;
pub mod border_fg;
pub mod fill_bg;
pub mod fill_invert;
pub mod halign;
pub mod header;
pub mod italic;
pub mod max_width;
pub mod min_width;
pub mod palette_16;
pub mod separator;
pub mod strikethrough;
pub mod text_bg;
pub mod text_fg;
pub mod text_invert;
pub mod underline;
use alloc::borrow::Cow;
use alloc::boxed::Box;
use alloc::collections::btree_map::Iter;
use alloc::collections::BTreeMap;
use alloc::string::String;
pub use blink::Blink;
pub use bold::Bold;
pub use border_bg::BorderBg;
pub use border_fg::BorderFg;
use core::any;
use core::any::Any;
pub use fill_bg::FillBg;
pub use fill_invert::FillInvert;
pub use halign::HAlign;
pub use header::Header;
pub use italic::Italic;
pub use max_width::MaxWidth;
pub use min_width::MinWidth;
pub use palette_16::Palette16;
pub use separator::Separator;
pub use strikethrough::Strikethrough;
pub use text_bg::TextBg;
pub use text_fg::TextFg;
pub use text_invert::TextInvert;
pub use underline::Underline;
mod private {
use alloc::boxed::Box;
use super::Style;
use core::any::Any;
pub trait Replica {
fn replicate(&self) -> Box<dyn Style>;
}
impl<C: Clone + Style> Replica for C {
fn replicate(&self) -> Box<dyn Style> {
Box::new(self.clone())
}
}
pub trait Upcast {
fn as_any_ref(&self) -> &dyn Any;
}
impl<U: Style> Upcast for U {
fn as_any_ref(&self) -> &dyn Any {
self
}
}
}
pub trait Style: Any + private::Replica + private::Upcast {
fn assignability(&self) -> Assignability;
fn id() -> Cow<'static, str>
where
Self: Sized,
{
Cow::Borrowed(any::type_name::<Self>())
}
fn resolve(styles: &Styles) -> Option<&Self>
where
Self: Sized,
{
let style_for_id = styles.get(&Self::id());
match style_for_id {
None => None,
Some(style) => style.as_any_ref().downcast_ref(),
}
}
fn resolve_or_default(styles: &Styles) -> Cow<Self>
where
Self: Default + Sized + Clone,
{
let style = Self::resolve(styles);
match style {
None => Cow::Owned(Self::default()),
Some(style) => Cow::Borrowed(style),
}
}
}
#[derive(Default)]
pub struct Styles(BTreeMap<String, Box<dyn Style>>);
impl Styles {
#[must_use]
pub fn with(mut self, style: impl Style) -> Self {
self.insert(style);
self
}
pub fn insert<S: Style>(&mut self, style: S) -> Option<Box<dyn Style>> {
self.0.insert(S::id().into(), Box::new(style))
}
#[must_use]
pub fn with_all(mut self, styles: &Styles) -> Self {
self.insert_all(styles);
self
}
pub fn insert_all(&mut self, styles: &Styles) {
for (key, style) in &styles.0 {
self.0.insert(key.into(), style.replicate());
}
}
pub fn get(&self, key: &str) -> Option<&dyn Style> {
self.0.get(key).map(|style| &**style)
}
pub fn take(&mut self, key: &str) -> Option<Box<dyn Style>> {
self.0.remove(key)
}
pub fn assert_assignability<S>(&self, mut check: impl FnMut(Assignability) -> bool) {
for entry in self {
assert!(
check(entry.1.assignability()),
"cannot assign style {} to a {}",
entry.0,
any::type_name::<S>()
);
}
}
}
impl Clone for Styles {
fn clone(&self) -> Self {
let mut clone = BTreeMap::new();
for (key, style) in &self.0 {
clone.insert(key.clone(), style.replicate());
}
Self(clone)
}
}
impl<'a> IntoIterator for &'a Styles {
type Item = (&'a String, &'a Box<dyn Style>);
type IntoIter = Iter<'a, String, Box<dyn Style>>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
pub trait Styled {
fn styles(&self) -> &Styles;
}
pub enum Assignability {
TableOnly,
ColTable,
RowTable,
RowColTable,
CellRowColTable,
}
impl Assignability {
pub fn at_col(&self) -> bool {
matches!(
self,
Assignability::ColTable | Assignability::RowColTable | Assignability::CellRowColTable
)
}
pub fn at_row(&self) -> bool {
matches!(
self,
Assignability::RowTable | Assignability::RowColTable | Assignability::CellRowColTable
)
}
pub fn at_cell(&self) -> bool {
matches!(self, Assignability::CellRowColTable)
}
}
#[cfg(test)]
mod tests;