track_core/
project_catalog.rs1use std::collections::BTreeMap;
2use std::path::PathBuf;
3
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
19pub struct ProjectInfo {
20 #[serde(rename = "canonicalName")]
21 pub canonical_name: String,
22 #[serde(with = "path_string")]
23 pub path: PathBuf,
24 pub aliases: Vec<String>,
25}
26
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct ProjectCatalog {
29 projects: Vec<ProjectInfo>,
30 lookup_by_name: BTreeMap<String, usize>,
31}
32
33impl ProjectCatalog {
34 pub fn new(projects: Vec<ProjectInfo>) -> Self {
35 let mut lookup_by_name = BTreeMap::new();
36
37 for (index, project) in projects.iter().enumerate() {
38 lookup_by_name
39 .entry(normalize_lookup_key(&project.canonical_name))
40 .or_insert(index);
41 }
42
43 for (index, project) in projects.iter().enumerate() {
44 for alias in &project.aliases {
45 lookup_by_name
46 .entry(normalize_lookup_key(alias))
47 .or_insert(index);
48 }
49 }
50
51 Self {
52 projects,
53 lookup_by_name,
54 }
55 }
56
57 pub fn is_empty(&self) -> bool {
58 self.projects.is_empty()
59 }
60
61 pub fn projects(&self) -> &[ProjectInfo] {
62 &self.projects
63 }
64
65 pub fn into_projects(self) -> Vec<ProjectInfo> {
66 self.projects
67 }
68
69 pub fn resolve(&self, name: &str) -> Option<&ProjectInfo> {
70 let key = normalize_lookup_key(name);
71 let index = self.lookup_by_name.get(&key)?;
72 self.projects.get(*index)
73 }
74}
75
76fn normalize_lookup_key(value: &str) -> String {
77 value.trim().to_lowercase()
78}
79
80mod path_string {
81 use std::path::PathBuf;
82
83 use serde::{Deserialize, Deserializer, Serializer};
84
85 pub fn serialize<S>(path: &PathBuf, serializer: S) -> Result<S::Ok, S::Error>
86 where
87 S: Serializer,
88 {
89 serializer.serialize_str(&path.to_string_lossy())
90 }
91
92 pub fn deserialize<'de, D>(deserializer: D) -> Result<PathBuf, D::Error>
93 where
94 D: Deserializer<'de>,
95 {
96 let path = String::deserialize(deserializer)?;
97 Ok(PathBuf::from(path))
98 }
99}