es_fluent_shared/
source.rs1use std::fmt;
4
5#[derive(
7 Clone, Debug, derive_more::AsRef, derive_more::Display, Eq, Hash, Ord, PartialEq, PartialOrd,
8)]
9#[as_ref(str)]
10pub struct SourceFile(String);
11
12impl SourceFile {
13 pub fn new(path: impl Into<String>) -> Option<Self> {
15 let path = path.into();
16 if path.is_empty() {
17 None
18 } else {
19 Some(Self(path))
20 }
21 }
22
23 pub fn as_str(&self) -> &str {
25 &self.0
26 }
27}
28
29impl serde::Serialize for SourceFile {
30 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
31 where
32 S: serde::Serializer,
33 {
34 serializer.serialize_str(self.as_str())
35 }
36}
37
38impl<'de> serde::Deserialize<'de> for SourceFile {
39 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
40 where
41 D: serde::Deserializer<'de>,
42 {
43 let path = <String as serde::Deserialize>::deserialize(deserializer)?;
44 Self::new(path).ok_or_else(|| serde::de::Error::custom("source file path is empty"))
45 }
46}
47
48#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
50pub struct SourceLine(u32);
51
52impl SourceLine {
53 pub fn new(line: u32) -> Self {
55 Self(line)
56 }
57
58 pub fn get(self) -> u32 {
60 self.0
61 }
62}
63
64impl fmt::Display for SourceLine {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 write!(f, "{}", self.get())
67 }
68}
69
70impl serde::Serialize for SourceLine {
71 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
72 where
73 S: serde::Serializer,
74 {
75 serializer.serialize_u32(self.get())
76 }
77}
78
79impl<'de> serde::Deserialize<'de> for SourceLine {
80 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
81 where
82 D: serde::Deserializer<'de>,
83 {
84 Ok(Self::new(<u32 as serde::Deserialize>::deserialize(
85 deserializer,
86 )?))
87 }
88}
89
90#[derive(Clone, Debug, Eq, Hash, PartialEq)]
92pub struct SourceLocation {
93 file: SourceFile,
94 line: SourceLine,
95}
96
97impl SourceLocation {
98 pub fn new(file: impl Into<String>, line: u32) -> Option<Self> {
100 Some(Self {
101 file: SourceFile::new(file)?,
102 line: SourceLine::new(line),
103 })
104 }
105
106 pub fn file(&self) -> &SourceFile {
108 &self.file
109 }
110
111 pub fn line(&self) -> SourceLine {
113 self.line
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn source_file_rejects_empty_paths() {
123 assert!(SourceFile::new("").is_none());
124 assert_eq!(
125 SourceFile::new("src/lib.rs").unwrap().as_str(),
126 "src/lib.rs"
127 );
128 }
129
130 #[test]
131 fn source_line_and_location_preserve_values() {
132 let line = SourceLine::new(42);
133 assert_eq!(line.get(), 42);
134 assert_eq!(line.to_string(), "42");
135
136 let location = SourceLocation::new("src/lib.rs", 42).unwrap();
137 assert_eq!(location.file().as_str(), "src/lib.rs");
138 assert_eq!(location.line().get(), 42);
139 }
140}