1use std::fmt;
4
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
9pub enum TagKind {
10 Def,
12 Ref,
14}
15
16impl fmt::Display for TagKind {
17 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18 match self {
19 TagKind::Def => write!(f, "def"),
20 TagKind::Ref => write!(f, "ref"),
21 }
22 }
23}
24
25impl std::str::FromStr for TagKind {
26 type Err = ();
27
28 fn from_str(s: &str) -> Result<Self, Self::Err> {
29 match s {
30 "def" => Ok(TagKind::Def),
31 "ref" => Ok(TagKind::Ref),
32 _ => Err(()),
33 }
34 }
35}
36
37#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
42pub struct Tag {
43 pub rel_fname: String,
45 pub fname: String,
47 pub line: i32,
49 pub name: String,
51 pub kind: TagKind,
53}
54
55impl Tag {
56 pub fn new(
58 rel_fname: impl Into<String>,
59 fname: impl Into<String>,
60 line: i32,
61 name: impl Into<String>,
62 kind: TagKind,
63 ) -> Self {
64 Self {
65 rel_fname: rel_fname.into(),
66 fname: fname.into(),
67 line,
68 name: name.into(),
69 kind,
70 }
71 }
72
73 pub fn def(
75 rel_fname: impl Into<String>,
76 fname: impl Into<String>,
77 line: i32,
78 name: impl Into<String>,
79 ) -> Self {
80 Self::new(rel_fname, fname, line, name, TagKind::Def)
81 }
82
83 pub fn reference(
85 rel_fname: impl Into<String>,
86 fname: impl Into<String>,
87 line: i32,
88 name: impl Into<String>,
89 ) -> Self {
90 Self::new(rel_fname, fname, line, name, TagKind::Ref)
91 }
92
93 pub fn is_def(&self) -> bool {
95 self.kind == TagKind::Def
96 }
97
98 pub fn is_ref(&self) -> bool {
100 self.kind == TagKind::Ref
101 }
102}
103
104impl fmt::Display for Tag {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 write!(
107 f,
108 "{}:{}:{}:{}",
109 self.rel_fname, self.line, self.name, self.kind
110 )
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn tag_kind_display() {
120 assert_eq!(TagKind::Def.to_string(), "def");
121 assert_eq!(TagKind::Ref.to_string(), "ref");
122 }
123
124 #[test]
125 fn tag_kind_from_str() {
126 assert_eq!("def".parse::<TagKind>(), Ok(TagKind::Def));
127 assert_eq!("ref".parse::<TagKind>(), Ok(TagKind::Ref));
128 assert!("other".parse::<TagKind>().is_err());
129 }
130
131 #[test]
132 fn tag_construction() {
133 let tag = Tag::def("src/main.rs", "/home/user/proj/src/main.rs", 10, "main");
134 assert_eq!(tag.rel_fname, "src/main.rs");
135 assert_eq!(tag.fname, "/home/user/proj/src/main.rs");
136 assert_eq!(tag.line, 10);
137 assert_eq!(tag.name, "main");
138 assert!(tag.is_def());
139 assert!(!tag.is_ref());
140 }
141
142 #[test]
143 fn tag_ordering() {
144 let t1 = Tag::def("a.rs", "/a.rs", 1, "foo");
146 let t2 = Tag::def("a.rs", "/a.rs", 2, "foo");
147 let t3 = Tag::def("b.rs", "/b.rs", 1, "foo");
148
149 assert!(t1 < t2); assert!(t2 < t3); }
152
153 #[test]
154 fn tag_fallback_line() {
155 let tag = Tag::reference("a.rs", "/a.rs", -1, "ident");
157 assert_eq!(tag.line, -1);
158 }
159}