source_location.rs

 1use std::{fmt, fmt::Display, path::Path, str::FromStr, sync::Arc};
 2
 3use ::util::{paths::PathStyle, rel_path::RelPath};
 4use anyhow::{Result, anyhow};
 5use language::Point;
 6use serde::{Deserialize, Deserializer, Serialize, Serializer};
 7
 8#[derive(Debug, Clone, Hash, Eq, PartialEq)]
 9pub struct SourceLocation {
10    pub path: Arc<RelPath>,
11    pub point: Point,
12}
13
14impl Serialize for SourceLocation {
15    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
16    where
17        S: Serializer,
18    {
19        serializer.serialize_str(&self.to_string())
20    }
21}
22
23impl<'de> Deserialize<'de> for SourceLocation {
24    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
25    where
26        D: Deserializer<'de>,
27    {
28        let s = String::deserialize(deserializer)?;
29        s.parse().map_err(serde::de::Error::custom)
30    }
31}
32
33impl Display for SourceLocation {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        write!(
36            f,
37            "{}:{}:{}",
38            self.path.display(PathStyle::Posix),
39            self.point.row + 1,
40            self.point.column + 1
41        )
42    }
43}
44
45impl FromStr for SourceLocation {
46    type Err = anyhow::Error;
47
48    fn from_str(s: &str) -> Result<Self> {
49        let parts: Vec<&str> = s.split(':').collect();
50        if parts.len() != 3 {
51            return Err(anyhow!(
52                "Invalid source location. Expected 'file.rs:line:column', got '{}'",
53                s
54            ));
55        }
56
57        let path = RelPath::new(Path::new(&parts[0]), PathStyle::local())?.into_arc();
58        let line: u32 = parts[1]
59            .parse()
60            .map_err(|_| anyhow!("Invalid line number: '{}'", parts[1]))?;
61        let column: u32 = parts[2]
62            .parse()
63            .map_err(|_| anyhow!("Invalid column number: '{}'", parts[2]))?;
64
65        // Convert from 1-based to 0-based indexing
66        let point = Point::new(line.saturating_sub(1), column.saturating_sub(1));
67
68        Ok(SourceLocation { path, point })
69    }
70}