Skip to content

Commit

Permalink
feat(text): support constructing Line and Text from usize (rata…
Browse files Browse the repository at this point in the history
…tui#1167)

Now you can create `Line` and `Text` from numbers like so:

```rust
let line = Line::from(42);
let text = Text::from(666);
```

(I was doing little testing for my TUI app and saw that this isn't
supported - then I was like WHA and decided to make it happen ™️)
  • Loading branch information
orhun authored and joshka committed Oct 14, 2024
1 parent ccaf881 commit 8eabcb5
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 3 deletions.
6 changes: 3 additions & 3 deletions src/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ mod grapheme;
pub use grapheme::StyledGrapheme;

mod line;
pub use line::Line;
pub use line::{Line, ToLine};

mod masked;
pub use masked::Masked;

mod span;
pub use span::Span;
pub use span::{Span, ToSpan};

#[allow(clippy::module_inception)]
mod text;
pub use text::Text;
pub use text::{Text, ToText};
29 changes: 29 additions & 0 deletions src/text/line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,29 @@ fn spans_after_width<'a>(
})
}

/// A trait for converting a value to a [`Line`].
///
/// This trait is automatically implemented for any type that implements the [`Display`] trait. As
/// such, `ToLine` shouln't be implemented directly: [`Display`] should be implemented instead, and
/// you get the `ToLine` implementation for free.
///
/// [`Display`]: std::fmt::Display
pub trait ToLine {
/// Converts the value to a [`Line`].
fn to_line(&self) -> Line<'_>;
}

/// # Panics
///
/// In this implementation, the `to_line` method panics if the `Display` implementation returns an
/// error. This indicates an incorrect `Display` implementation since `fmt::Write for String` never
/// returns an error itself.
impl<T: fmt::Display> ToLine for T {
fn to_line(&self) -> Line<'_> {
Line::from(self.to_string())
}
}

impl fmt::Display for Line<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for span in &self.spans {
Expand Down Expand Up @@ -824,6 +847,12 @@ mod tests {
assert_eq!(line.spans, vec![Span::from("Hello"), Span::from("world!")]);
}

#[test]
fn to_line() {
let line = 42.to_line();
assert_eq!(vec![Span::from("42")], line.spans);
}

#[test]
fn from_vec() {
let spans = vec![
Expand Down
29 changes: 29 additions & 0 deletions src/text/span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,29 @@ impl WidgetRef for Span<'_> {
}
}

/// A trait for converting a value to a [`Span`].
///
/// This trait is automatically implemented for any type that implements the [`Display`] trait. As
/// such, `ToSpan` shouln't be implemented directly: [`Display`] should be implemented instead, and
/// you get the `ToSpan` implementation for free.
///
/// [`Display`]: std::fmt::Display
pub trait ToSpan {
/// Converts the value to a [`Span`].
fn to_span(&self) -> Span<'_>;
}

/// # Panics
///
/// In this implementation, the `to_span` method panics if the `Display` implementation returns an
/// error. This indicates an incorrect `Display` implementation since `fmt::Write for String` never
/// returns an error itself.
impl<T: fmt::Display> ToSpan for T {
fn to_span(&self) -> Span<'_> {
Span::raw(self.to_string())
}
}

impl fmt::Display for Span<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.content, f)
Expand Down Expand Up @@ -507,6 +530,12 @@ mod tests {
assert_eq!(span.style, Style::default());
}

#[test]
fn to_span() {
assert_eq!(42.to_span(), Span::raw("42"));
assert_eq!("test".to_span(), Span::raw("test"));
}

#[test]
fn reset_style() {
let span = Span::styled("test content", Style::new().green()).reset_style();
Expand Down
41 changes: 41 additions & 0 deletions src/text/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,29 @@ where
}
}

/// A trait for converting a value to a [`Text`].
///
/// This trait is automatically implemented for any type that implements the [`Display`] trait. As
/// such, `ToText` shouldn't be implemented directly: [`Display`] should be implemented instead, and
/// you get the `ToText` implementation for free.
///
/// [`Display`]: std::fmt::Display
pub trait ToText<'a> {
/// Converts the value to a [`Text`].
fn to_text(&self) -> Text<'a>;
}

/// # Panics
///
/// In this implementation, the `to_text` method panics if the `Display` implementation returns an
/// error. This indicates an incorrect `Display` implementation since `fmt::Write for String` never
/// returns an error itself.
impl<'a, T: fmt::Display> ToText<'a> for T {
fn to_text(&self) -> Text<'a> {
Text::raw(self.to_string())
}
}

impl fmt::Display for Text<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (position, line) in self.iter().with_position() {
Expand Down Expand Up @@ -747,6 +770,24 @@ mod tests {
assert_eq!(text.lines, vec![Line::from("The first line")]);
}

#[rstest]
#[case(42, Text::from("42"))]
#[case("just\ntesting", Text::from("just\ntesting"))]
#[case(true, Text::from("true"))]
#[case(6.66, Text::from("6.66"))]
#[case('a', Text::from("a"))]
#[case(String::from("hello"), Text::from("hello"))]
#[case(-1, Text::from("-1"))]
#[case("line1\nline2", Text::from("line1\nline2"))]
#[case(
"first line\nsecond line\nthird line",
Text::from("first line\nsecond line\nthird line")
)]
#[case("trailing newline\n", Text::from("trailing newline\n"))]
fn to_text(#[case] value: impl fmt::Display, #[case] expected: Text) {
assert_eq!(value.to_text(), expected);
}

#[test]
fn from_vec_line() {
let text = Text::from(vec![
Expand Down

0 comments on commit 8eabcb5

Please sign in to comment.