1use crate::duplication::CodeChunk;
2use crate::test_refs::CodeDefinition;
3use crate::units::CodeUnitKind;
4use crate::violation::Violation;
5use serde::{Deserialize, Serialize};
6use std::path::{Path, PathBuf};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct CachedViolation {
10 pub file: String,
11 pub line: usize,
12 pub unit_name: String,
13 pub metric: String,
14 pub value: usize,
15 pub threshold: usize,
16 pub message: String,
17 pub suggestion: String,
18}
19
20impl CachedViolation {
21 pub fn into_violation(self) -> Violation {
22 Violation {
23 file: PathBuf::from(self.file),
24 line: self.line,
25 unit_name: self.unit_name,
26 metric: self.metric,
27 value: self.value,
28 threshold: self.threshold,
29 message: self.message,
30 suggestion: self.suggestion,
31 }
32 }
33}
34
35impl From<&Violation> for CachedViolation {
36 fn from(v: &Violation) -> Self {
37 Self {
38 file: v.file.to_string_lossy().to_string(),
39 line: v.line,
40 unit_name: v.unit_name.clone(),
41 metric: v.metric.clone(),
42 value: v.value,
43 threshold: v.threshold,
44 message: v.message.clone(),
45 suggestion: v.suggestion.clone(),
46 }
47 }
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct CachedCodeChunk {
52 pub file: String,
53 pub name: String,
54 pub start_line: usize,
55 pub end_line: usize,
56 pub normalized: String,
57}
58
59impl CachedCodeChunk {
60 pub fn into_chunk(self) -> CodeChunk {
61 CodeChunk {
62 file: PathBuf::from(self.file),
63 name: self.name,
64 start_line: self.start_line,
65 end_line: self.end_line,
66 normalized: self.normalized,
67 }
68 }
69}
70
71impl From<&CodeChunk> for CachedCodeChunk {
72 fn from(c: &CodeChunk) -> Self {
73 Self {
74 file: c.file.to_string_lossy().to_string(),
75 name: c.name.clone(),
76 start_line: c.start_line,
77 end_line: c.end_line,
78 normalized: c.normalized.clone(),
79 }
80 }
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct CachedCodeDefinition {
85 pub name: String,
86 pub kind: String,
87 pub file: String,
88 pub line: usize,
89 pub containing_class: Option<String>,
90}
91
92const fn kind_to_str(k: CodeUnitKind) -> &'static str {
93 match k {
94 CodeUnitKind::Function => "function",
95 CodeUnitKind::Method => "method",
96 CodeUnitKind::Class => "class",
97 CodeUnitKind::Module => "module",
98 CodeUnitKind::Struct => "struct",
99 CodeUnitKind::Enum => "enum",
100 CodeUnitKind::TraitImplMethod => "trait_impl_method",
101 }
102}
103
104fn kind_from_str(s: &str) -> CodeUnitKind {
105 match s {
106 "method" => CodeUnitKind::Method,
107 "class" => CodeUnitKind::Class,
108 "module" => CodeUnitKind::Module,
109 "struct" => CodeUnitKind::Struct,
110 "enum" => CodeUnitKind::Enum,
111 "trait_impl_method" => CodeUnitKind::TraitImplMethod,
112 _ => CodeUnitKind::Function,
113 }
114}
115
116impl From<&CodeDefinition> for CachedCodeDefinition {
117 fn from(d: &CodeDefinition) -> Self {
118 Self {
119 name: d.name.clone(),
120 kind: kind_to_str(d.kind).to_string(),
121 file: d.file.to_string_lossy().to_string(),
122 line: d.line,
123 containing_class: d.containing_class.clone(),
124 }
125 }
126}
127
128impl CachedCodeDefinition {
129 pub fn into_definition(self) -> CodeDefinition {
130 CodeDefinition {
131 name: self.name,
132 kind: kind_from_str(&self.kind),
133 file: PathBuf::from(self.file),
134 line: self.line,
135 containing_class: self.containing_class,
136 }
137 }
138}
139
140pub fn cache_dir() -> PathBuf {
141 if let Some(home) = std::env::var_os("HOME") {
143 return Path::new(&home).join(".cache").join("kiss");
144 }
145 std::env::temp_dir().join("kiss-cache")
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 #[test]
153 fn test_cached_violation_roundtrip() {
154 let v = Violation::builder("foo.py")
155 .line(12)
156 .unit_name("f")
157 .metric("m")
158 .value(2)
159 .threshold(1)
160 .message("msg")
161 .suggestion("sugg")
162 .build();
163 let cached = CachedViolation::from(&v);
164 let v2 = cached.into_violation();
165 assert_eq!(v2.file, PathBuf::from("foo.py"));
166 assert_eq!(v2.line, 12);
167 assert_eq!(v2.unit_name, "f");
168 }
169
170 #[test]
171 fn test_cached_chunk_roundtrip() {
172 let c = CodeChunk {
173 file: PathBuf::from("a.py"),
174 name: "x".to_string(),
175 start_line: 1,
176 end_line: 2,
177 normalized: "norm".to_string(),
178 };
179 let cached = CachedCodeChunk::from(&c);
180 let c2 = cached.into_chunk();
181 assert_eq!(c2.file, PathBuf::from("a.py"));
182 assert_eq!(c2.name, "x");
183 }
184
185 #[test]
186 fn test_cached_definition_roundtrip() {
187 let d = CodeDefinition {
188 name: "C".to_string(),
189 kind: CodeUnitKind::Class,
190 file: PathBuf::from("x.py"),
191 line: 3,
192 containing_class: None,
193 };
194 let cached = CachedCodeDefinition::from(&d);
195 let d2 = cached.into_definition();
196 assert_eq!(d2.name, "C");
197 assert_eq!(d2.kind, CodeUnitKind::Class);
198 assert_eq!(d2.file, PathBuf::from("x.py"));
199 }
200
201 #[test]
202 fn test_cache_dir_smoke() {
203 let _ = cache_dir();
205
206 let _ = kind_to_str(CodeUnitKind::Function);
208 let _ = kind_from_str("class");
209 }
210}