1use perspt_sdk::ResidualClass;
9
10use super::types::StructuredDiagnostic;
11
12pub const PARSER_ID: &str = "ty-check-text-v1";
14
15fn classify_rule(rule: &str) -> ResidualClass {
16 match rule {
17 "unresolved-import" | "unresolved-module" => ResidualClass::ImportGraph,
18 "unresolved-reference" | "unresolved-attribute" | "possibly-unresolved-reference" => {
19 ResidualClass::SymbolMismatch
20 }
21 "invalid-assignment"
22 | "invalid-argument-type"
23 | "invalid-return-type"
24 | "unsupported-operator"
25 | "invalid-type-form" => ResidualClass::Type,
26 _ => ResidualClass::Type,
27 }
28}
29
30pub fn parse_check_text(raw: &str) -> Vec<StructuredDiagnostic> {
32 raw.lines().filter_map(parse_line).collect()
33}
34
35fn parse_line(line: &str) -> Option<StructuredDiagnostic> {
36 let (location, rest) = line.split_once(": error[")?;
38 let (rule, message) = rest.split_once("] ")?;
39 let mut parts = location.rsplitn(3, ':');
40 let column: u32 = parts.next()?.parse().ok()?;
41 let line_no: u32 = parts.next()?.parse().ok()?;
42 let path = parts.next()?.to_string();
43 Some(StructuredDiagnostic {
44 class: classify_rule(rule),
45 code: Some(rule.to_string()),
46 message: message.to_string(),
47 path: Some(path),
48 line: Some(line_no),
49 column: Some(column),
50 primary: true,
51 suggestion: None,
52 })
53}
54
55pub fn from_lsp_diagnostics(path: &str, items: &[serde_json::Value]) -> Vec<StructuredDiagnostic> {
59 items
60 .iter()
61 .filter(|item| item.get("severity").and_then(|s| s.as_u64()).unwrap_or(1) == 1)
62 .map(|item| {
63 let rule = item
64 .get("code")
65 .and_then(|code| code.as_str())
66 .map(str::to_string);
67 StructuredDiagnostic {
68 class: rule
69 .as_deref()
70 .map(classify_rule)
71 .unwrap_or(ResidualClass::Type),
72 code: rule,
73 message: item
74 .get("message")
75 .and_then(|m| m.as_str())
76 .unwrap_or_default()
77 .to_string(),
78 path: Some(path.to_string()),
79 line: item
80 .pointer("/range/start/line")
81 .and_then(|l| l.as_u64())
82 .and_then(|l| u32::try_from(l + 1).ok()),
83 column: item
84 .pointer("/range/start/character")
85 .and_then(|c| c.as_u64())
86 .and_then(|c| u32::try_from(c + 1).ok()),
87 primary: true,
88 suggestion: None,
89 }
90 })
91 .collect()
92}