1extern crate alloc;
2
3use {
4 crate::{nonzero, utils::PathLike},
5 alloc::borrow::Cow,
6 core::{
7 char::REPLACEMENT_CHARACTER,
8 fmt::{Display, Formatter, Write},
9 num::NonZero,
10 },
11};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub struct Location {
16 pub line: NonZero<u32>,
18 pub col: u32,
20}
21
22#[cfg(feature = "proc-macro2")]
24#[derive(Debug, Clone, Copy)]
25pub enum LineColumnToLocationError {
26 LineZero,
28 LineNumberTooBig,
30 ColumnNumberTooBig,
32}
33
34#[cfg(feature = "proc-macro2")]
35impl Display for LineColumnToLocationError {
36 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
37 f.write_str(match self {
38 Self::LineZero => "`LineColumn` with line #0 found",
39 Self::LineNumberTooBig => "`LineColumn`s line number overflowed a u32",
40 Self::ColumnNumberTooBig => "`LineColumn`s column number overflowed a u32",
41 })
42 }
43}
44
45#[cfg(feature = "proc-macro2")]
46impl TryFrom<proc_macro2::LineColumn> for Location {
47 type Error = LineColumnToLocationError;
48
49 fn try_from(value: proc_macro2::LineColumn) -> Result<Self, Self::Error> {
50 let line = u32::try_from(value.line).map_err(|_| LineColumnToLocationError::LineNumberTooBig)?;
51 let line = NonZero::new(line).ok_or(LineColumnToLocationError::LineZero)?;
52 let col = u32::try_from(value.column).map_err(|_| LineColumnToLocationError::ColumnNumberTooBig)?;
53
54 Ok(Self { line, col })
55 }
56}
57
58#[cfg(feature = "proc-macro2")]
59impl From<Location> for proc_macro2::LineColumn {
60 fn from(value: Location) -> Self {
61 Self { line: value.line.get() as usize, column: value.col as usize }
62 }
63}
64
65impl Default for Location {
66 fn default() -> Self {
67 Self {
68 line: nonzero!(1),
69 col: 0,
70 }
71 }
72}
73
74impl Display for Location {
75 fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
76 write!(f, "{}:{}", self.line, self.col)
77 }
78}
79
80impl Location {
81 #[must_use]
87 pub const fn offset(self, rhs: Self) -> Self {
88 if rhs.line.get() == 1 {
89 Self {
90 line: self.line,
91 col: self.col + rhs.col,
92 }
93 } else {
94 Self {
95 line: NonZero::new(self.line.get() + rhs.line.get() - 1).expect("no overflow"),
96 col: rhs.col,
97 }
98 }
99 }
100
101 pub fn with_path<'path>(self, path: impl PathLike<'path>) -> FullLocation<'path> {
103 FullLocation {
104 path: path.into_path_bytes(),
105 loc: self,
106 }
107 }
108
109 pub fn find(ptr: *const u8, src: &str) -> Option<Self> {
112 let progress =
113 usize::checked_sub(ptr as _, src.as_ptr() as _).filter(|x| *x <= src.len())?;
114
115 Some(
116 src.bytes()
117 .take(progress)
118 .fold(Self::default(), |loc, b| match b {
119 b'\n' => Self {
120 line: loc.line.saturating_add(1),
121 col: 0,
122 },
123 _ => Self {
124 col: loc.col.saturating_add(1),
125 ..loc
126 },
127 }),
128 )
129 }
130
131 pub fn find_saturating(ptr: *const u8, src: &str) -> Self {
137 let progress = usize::saturating_sub(ptr as _, src.as_ptr() as _);
138
139 let res = src
140 .bytes()
141 .take(progress)
142 .fold(Self::default(), |loc, b| match b {
143 b'\n' => Self {
144 line: loc.line.saturating_add(1),
145 col: 0,
146 },
147 _ => Self {
148 col: loc.col.saturating_add(1),
149 ..loc
150 },
151 });
152 res
153 }
154
155 pub fn find_in_multiple<K>(
180 ptr: *const u8,
181 files: impl IntoIterator<Item = (K, impl AsRef<str>)>,
182 ) -> Option<(K, Self)> {
183 files
184 .into_iter()
185 .find_map(|(k, src)| Some((k, Self::find(ptr, src.as_ref())?)))
186 }
187}
188
189#[derive(Debug, Clone, PartialEq, Eq, Hash)]
191pub struct FullLocation<'path> {
192 pub path: Cow<'path, [u8]>,
194 pub loc: Location,
196}
197
198impl Display for FullLocation<'_> {
199 fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
200 for chunk in self.path.utf8_chunks() {
201 f.write_str(chunk.valid())?;
202 if !chunk.invalid().is_empty() {
203 f.write_char(REPLACEMENT_CHARACTER)?;
204 }
205 }
206 write!(f, ":{}", self.loc)
207 }
208}
209
210impl FullLocation<'_> {
211 pub fn own(self) -> FullLocation<'static> {
213 FullLocation {
214 path: self.path.into_owned().into(),
215 loc: self.loc,
216 }
217 }
218}