1use lsp_types::Uri;
4use std::path::{Path, PathBuf};
5
6#[derive(Debug, thiserror::Error)]
7#[error("Invalid path '{}': {reason}", path.display())]
8pub struct UriError {
9 path: PathBuf,
10 reason: String,
11}
12
13pub fn path_to_uri(path: &Path) -> Result<Uri, UriError> {
17 let absolute =
18 if path.is_absolute() { path.to_path_buf() } else { std::env::current_dir().unwrap_or_default().join(path) };
19 let absolute = absolute.canonicalize().unwrap_or(absolute);
22
23 #[cfg(windows)]
24 let uri_str = {
25 let path_str = absolute.to_string_lossy().replace('\\', "/");
26 format!("file:///{}", path_str)
27 };
28
29 #[cfg(not(windows))]
30 let uri_str = format!("file://{}", absolute.display());
31
32 uri_str.parse::<Uri>().map_err(|e| UriError { path: absolute, reason: e.to_string() })
33}
34
35pub fn uri_to_path(uri: &Uri) -> String {
37 let uri_str = uri.as_str();
38 if let Some(path) = uri_str.strip_prefix("file://") {
39 if path.starts_with('/') && path.len() > 2 && path.chars().nth(2) == Some(':') {
41 path[1..].to_string()
42 } else {
43 path.to_string()
44 }
45 } else {
46 uri_str.to_string()
47 }
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53 use std::path::Path;
54
55 #[test]
56 fn test_path_to_uri_absolute() {
57 let uri = path_to_uri(Path::new("/src/main.rs")).unwrap();
58 assert_eq!(uri.as_str(), "file:///src/main.rs");
59 }
60
61 #[test]
62 fn test_uri_to_path_basic() {
63 let uri: Uri = "file:///src/main.rs".parse().unwrap();
64 assert_eq!(uri_to_path(&uri), "/src/main.rs");
65 }
66
67 #[test]
68 fn test_roundtrip() {
69 let path = Path::new("/home/user/project/src/lib.rs");
70 let uri = path_to_uri(path).unwrap();
71 let back = uri_to_path(&uri);
72 assert_eq!(back, path.to_str().unwrap());
73 }
74}