use std::fmt;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub enum TagKind {
Def,
Ref,
}
impl fmt::Display for TagKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TagKind::Def => write!(f, "def"),
TagKind::Ref => write!(f, "ref"),
}
}
}
impl std::str::FromStr for TagKind {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"def" => Ok(TagKind::Def),
"ref" => Ok(TagKind::Ref),
_ => Err(()),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Tag {
pub rel_fname: String,
pub fname: String,
pub line: i32,
pub name: String,
pub kind: TagKind,
}
impl Tag {
pub fn new(
rel_fname: impl Into<String>,
fname: impl Into<String>,
line: i32,
name: impl Into<String>,
kind: TagKind,
) -> Self {
Self {
rel_fname: rel_fname.into(),
fname: fname.into(),
line,
name: name.into(),
kind,
}
}
pub fn def(
rel_fname: impl Into<String>,
fname: impl Into<String>,
line: i32,
name: impl Into<String>,
) -> Self {
Self::new(rel_fname, fname, line, name, TagKind::Def)
}
pub fn reference(
rel_fname: impl Into<String>,
fname: impl Into<String>,
line: i32,
name: impl Into<String>,
) -> Self {
Self::new(rel_fname, fname, line, name, TagKind::Ref)
}
pub fn is_def(&self) -> bool {
self.kind == TagKind::Def
}
pub fn is_ref(&self) -> bool {
self.kind == TagKind::Ref
}
}
impl fmt::Display for Tag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}:{}:{}:{}",
self.rel_fname, self.line, self.name, self.kind
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tag_kind_display() {
assert_eq!(TagKind::Def.to_string(), "def");
assert_eq!(TagKind::Ref.to_string(), "ref");
}
#[test]
fn tag_kind_from_str() {
assert_eq!("def".parse::<TagKind>(), Ok(TagKind::Def));
assert_eq!("ref".parse::<TagKind>(), Ok(TagKind::Ref));
assert!("other".parse::<TagKind>().is_err());
}
#[test]
fn tag_construction() {
let tag = Tag::def("src/main.rs", "/home/user/proj/src/main.rs", 10, "main");
assert_eq!(tag.rel_fname, "src/main.rs");
assert_eq!(tag.fname, "/home/user/proj/src/main.rs");
assert_eq!(tag.line, 10);
assert_eq!(tag.name, "main");
assert!(tag.is_def());
assert!(!tag.is_ref());
}
#[test]
fn tag_ordering() {
let t1 = Tag::def("a.rs", "/a.rs", 1, "foo");
let t2 = Tag::def("a.rs", "/a.rs", 2, "foo");
let t3 = Tag::def("b.rs", "/b.rs", 1, "foo");
assert!(t1 < t2); assert!(t2 < t3); }
#[test]
fn tag_fallback_line() {
let tag = Tag::reference("a.rs", "/a.rs", -1, "ident");
assert_eq!(tag.line, -1);
}
}