1use core::fmt;
4
5use self::slice::SpansSlice;
6pub(crate) use self::spans::StyledSpan;
7pub use self::{
8 lines::Lines,
9 spans::SpanStr,
10 str::StyledStr,
11 string::{StyledString, StyledStringBuilder},
12};
13use crate::{
14 StyleDiff,
15 utils::{Stack, StackStr},
16};
17
18mod lines;
19mod slice;
20mod spans;
21mod str;
22mod string;
23
24#[derive(Debug)]
48pub struct TextDiff<'a> {
49 lhs: &'a str,
50 rhs: &'a str,
51}
52
53impl<'a> TextDiff<'a> {
54 pub const fn new(lhs: &'a str, rhs: &'a str) -> Self {
56 Self { lhs, rhs }
57 }
58}
59
60impl fmt::Display for TextDiff<'_> {
61 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
62 use pretty_assertions::Comparison;
63
64 struct DebugStr<'a> {
67 s: &'a str,
68 padding: usize,
69 }
70
71 impl<'a> DebugStr<'a> {
72 fn new(s: &'a str, padding: usize) -> Self {
73 Self { s, padding }
74 }
75 }
76
77 impl fmt::Debug for DebugStr<'_> {
78 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
79 if self.padding == 0 {
80 formatter.write_str(self.s)
81 } else {
82 for line in self.s.lines() {
83 writeln!(formatter, "{:>padding$}{line}", "", padding = self.padding)?;
84 }
85 Ok(())
86 }
87 }
88 }
89
90 let padding = if matches!(formatter.align(), Some(fmt::Alignment::Right) | None) {
91 formatter.width().map_or(0, |width| width.saturating_sub(1))
92 } else {
93 0
94 };
95
96 write!(
97 formatter,
98 "{}",
99 Comparison::new(
100 &DebugStr::new(self.lhs, padding),
101 &DebugStr::new(self.rhs, padding)
102 )
103 )
104 }
105}
106
107pub enum Diff<'a> {
111 Text(TextDiff<'a>),
113 Style(StyleDiff<'a>),
115}
116
117impl fmt::Display for Diff<'_> {
118 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
119 match self {
120 Self::Text(diff) => write!(formatter, "styled strings differ by text\n{diff}"),
121 Self::Style(diff) => write!(
122 formatter,
123 "styled strings differ by style\n{diff}\n{diff:#}"
124 ),
125 }
126 }
127}
128
129impl fmt::Debug for Diff<'_> {
131 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
132 fmt::Display::fmt(self, formatter)
133 }
134}
135
136#[cfg(feature = "std")]
137impl std::error::Error for Diff<'_> {}
138
139#[doc(hidden)]
141#[derive(Debug)]
142pub struct StackStyled<const TEXT_CAP: usize, const SPAN_CAP: usize> {
143 pub(crate) text: StackStr<TEXT_CAP>,
144 pub(crate) spans: Stack<StyledSpan, SPAN_CAP>,
145}
146
147impl<const TEXT_CAP: usize, const SPAN_CAP: usize> StackStyled<TEXT_CAP, SPAN_CAP> {
148 #[track_caller]
154 pub const fn new(raw: &str) -> Self {
155 match Self::parse(raw) {
156 Ok(styled) => styled,
157 Err(err) => err.compile_panic(raw),
158 }
159 }
160
161 pub const fn as_ref(&'static self) -> StyledStr<'static> {
162 StyledStr {
163 text: self.text.as_str(),
164 spans: SpansSlice::new(self.spans.as_slice()),
165 }
166 }
167}