Skip to main content

es_fluent_shared/
source.rs

1//! Shared source-location metadata types.
2
3use std::fmt;
4
5/// Source file path recorded in generated inventory metadata.
6#[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    /// Creates a source file path when the recorded path is non-empty.
14    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    /// Returns the source file path as a string slice.
24    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/// Source line recorded in generated inventory metadata.
49#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
50pub struct SourceLine(u32);
51
52impl SourceLine {
53    /// Creates source line metadata.
54    pub fn new(line: u32) -> Self {
55        Self(line)
56    }
57
58    /// Returns the recorded line number.
59    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/// Source file and line metadata for a generated Fluent entry.
91#[derive(Clone, Debug, Eq, Hash, PartialEq)]
92pub struct SourceLocation {
93    file: SourceFile,
94    line: SourceLine,
95}
96
97impl SourceLocation {
98    /// Creates a source location when the recorded file path is non-empty.
99    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    /// Returns the source file.
107    pub fn file(&self) -> &SourceFile {
108        &self.file
109    }
110
111    /// Returns the source line.
112    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}