mention.rs

  1use agent_client_protocol as acp;
  2use anyhow::{Result, bail};
  3use std::path::PathBuf;
  4
  5#[derive(Clone, Debug, PartialEq, Eq)]
  6pub enum MentionUri {
  7    File(PathBuf),
  8    Symbol(PathBuf, String),
  9    Thread(acp::SessionId),
 10    Rule(String),
 11}
 12
 13impl MentionUri {
 14    pub fn parse(input: &str) -> Result<Self> {
 15        let url = url::Url::parse(input)?;
 16        let path = url.path();
 17        match url.scheme() {
 18            "file" => {
 19                if let Some(fragment) = url.fragment() {
 20                    Ok(Self::Symbol(path.into(), fragment.into()))
 21                } else {
 22                    let file_path =
 23                        PathBuf::from(format!("{}{}", url.host_str().unwrap_or(""), path));
 24
 25                    Ok(Self::File(file_path))
 26                }
 27            }
 28            "zed" => {
 29                if let Some(thread) = path.strip_prefix("/agent/thread/") {
 30                    Ok(Self::Thread(acp::SessionId(thread.into())))
 31                } else if let Some(rule) = path.strip_prefix("/agent/rule/") {
 32                    Ok(Self::Rule(rule.into()))
 33                } else {
 34                    bail!("invalid zed url: {:?}", input);
 35                }
 36            }
 37            other => bail!("unrecognized scheme {:?}", other),
 38        }
 39    }
 40
 41    pub fn name(&self) -> String {
 42        match self {
 43            MentionUri::File(path) => path.file_name().unwrap().to_string_lossy().into_owned(),
 44            MentionUri::Symbol(_path, name) => name.clone(),
 45            MentionUri::Thread(thread) => thread.to_string(),
 46            MentionUri::Rule(rule) => rule.clone(),
 47        }
 48    }
 49
 50    pub fn to_link(&self) -> String {
 51        let name = self.name();
 52        let uri = self.to_uri();
 53        format!("[{name}]({uri})")
 54    }
 55
 56    pub fn to_uri(&self) -> String {
 57        match self {
 58            MentionUri::File(path) => {
 59                format!("file://{}", path.display())
 60            }
 61            MentionUri::Symbol(path, name) => {
 62                format!("file://{}#{}", path.display(), name)
 63            }
 64            MentionUri::Thread(thread) => {
 65                format!("zed:///agent/thread/{}", thread.0)
 66            }
 67            MentionUri::Rule(rule) => {
 68                format!("zed:///agent/rule/{}", rule)
 69            }
 70        }
 71    }
 72}
 73
 74#[cfg(test)]
 75mod tests {
 76    use super::*;
 77
 78    #[test]
 79    fn test_mention_uri_parse_and_display() {
 80        // Test file URI
 81        let file_uri = "file:///path/to/file.rs";
 82        let parsed = MentionUri::parse(file_uri).unwrap();
 83        match &parsed {
 84            MentionUri::File(path) => assert_eq!(path.to_str().unwrap(), "/path/to/file.rs"),
 85            _ => panic!("Expected File variant"),
 86        }
 87        assert_eq!(parsed.to_uri(), file_uri);
 88
 89        // Test symbol URI
 90        let symbol_uri = "file:///path/to/file.rs#MySymbol";
 91        let parsed = MentionUri::parse(symbol_uri).unwrap();
 92        match &parsed {
 93            MentionUri::Symbol(path, symbol) => {
 94                assert_eq!(path.to_str().unwrap(), "/path/to/file.rs");
 95                assert_eq!(symbol, "MySymbol");
 96            }
 97            _ => panic!("Expected Symbol variant"),
 98        }
 99        assert_eq!(parsed.to_uri(), symbol_uri);
100
101        // Test thread URI
102        let thread_uri = "zed:///agent/thread/session123";
103        let parsed = MentionUri::parse(thread_uri).unwrap();
104        match &parsed {
105            MentionUri::Thread(session_id) => assert_eq!(session_id.0.as_ref(), "session123"),
106            _ => panic!("Expected Thread variant"),
107        }
108        assert_eq!(parsed.to_uri(), thread_uri);
109
110        // Test rule URI
111        let rule_uri = "zed:///agent/rule/my_rule";
112        let parsed = MentionUri::parse(rule_uri).unwrap();
113        match &parsed {
114            MentionUri::Rule(rule) => assert_eq!(rule, "my_rule"),
115            _ => panic!("Expected Rule variant"),
116        }
117        assert_eq!(parsed.to_uri(), rule_uri);
118
119        // Test invalid scheme
120        assert!(MentionUri::parse("http://example.com").is_err());
121
122        // Test invalid zed path
123        assert!(MentionUri::parse("zed:///invalid/path").is_err());
124    }
125}