use crate::style::Style;
use alloc::string::String;
use alloc::vec::Vec;
use unicode_width::UnicodeWidthStr;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Span {
pub content: String,
pub style: Style,
}
impl Span {
#[must_use]
pub fn raw(content: impl Into<String>) -> Self {
Self {
content: content.into(),
style: Style::default(),
}
}
#[must_use]
pub fn styled(content: impl Into<String>, style: Style) -> Self {
Self {
content: content.into(),
style,
}
}
#[must_use]
pub fn width(&self) -> usize {
self.content.as_str().width()
}
}
impl<S: Into<String>> From<S> for Span {
fn from(s: S) -> Self {
Self::raw(s)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Line {
pub spans: Vec<Span>,
}
impl Line {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn raw(content: impl Into<String>) -> Self {
Self {
spans: alloc::vec![Span::raw(content)],
}
}
#[must_use]
pub fn width(&self) -> usize {
self.spans.iter().map(Span::width).sum()
}
}
impl From<&str> for Line {
fn from(s: &str) -> Self {
Self::raw(s)
}
}
impl From<String> for Line {
fn from(s: String) -> Self {
Self::raw(s)
}
}
impl From<Span> for Line {
fn from(span: Span) -> Self {
Self {
spans: alloc::vec![span],
}
}
}
impl From<Vec<Span>> for Line {
fn from(spans: Vec<Span>) -> Self {
Self { spans }
}
}
#[macro_export]
macro_rules! spans {
($(($style:expr, $text:expr)),* $(,)?) => {
$crate::text::Line::from(alloc::vec![
$($crate::text::Span::styled($text, $style)),*
])
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::color::Color;
#[test]
fn test_span_raw() {
let s = Span::raw("hello");
assert_eq!(s.content, "hello");
assert_eq!(s.style, Style::default());
assert_eq!(s.width(), 5);
}
#[test]
fn test_span_styled() {
let style = Style::new().fg(Color::RED);
let s = Span::styled("hi", style);
assert_eq!(s.content, "hi");
assert_eq!(s.style, style);
}
#[test]
fn test_span_width_wide_chars() {
let s = Span::raw("中文"); assert_eq!(s.width(), 4);
}
#[test]
fn test_line_from_str() {
let line = Line::from("hello");
assert_eq!(line.spans.len(), 1);
assert_eq!(line.width(), 5);
}
#[test]
fn test_line_from_spans() {
let line = Line::from(vec![
Span::raw("HP: "),
Span::styled("100", Style::new().fg(Color::GREEN)),
]);
assert_eq!(line.width(), 7);
}
#[test]
fn test_line_width_wide_chars() {
let line = Line::from(vec![Span::raw("中"), Span::raw("x")]);
assert_eq!(line.width(), 3); }
#[test]
fn test_line_empty() {
let line = Line::new();
assert_eq!(line.width(), 0);
}
}