qlty_coverage/parser/
qlty.rs

1use crate::Parser;
2use anyhow::{Context, Result};
3use qlty_types::tests::v1::FileCoverage;
4use serde_json::Value;
5
6#[derive(Debug, Clone, PartialEq, Eq, Default)]
7pub struct Qlty {}
8
9impl Qlty {
10    pub fn new() -> Self {
11        Self {}
12    }
13}
14
15impl Parser for Qlty {
16    fn parse_text(&self, text: &str) -> Result<Vec<FileCoverage>> {
17        text.lines()
18            .filter(|line| !line.is_empty())
19            .map(|line| {
20                let json_line: Value =
21                    serde_json::from_str(line).with_context(|| "Failed to parse JSONL line")?;
22
23                let path = json_line
24                    .get("path")
25                    .and_then(Value::as_str)
26                    .ok_or_else(|| anyhow::anyhow!("Missing or invalid `path` field"))?;
27
28                let hits: Vec<i64> = json_line
29                    .get("hits")
30                    .and_then(Value::as_array)
31                    .ok_or_else(|| anyhow::anyhow!("Missing or invalid `hits` field"))?
32                    .iter()
33                    .map(|v| {
34                        v.as_str()
35                            .ok_or_else(|| anyhow::anyhow!("Invalid hit value, expected a string"))?
36                            .parse::<i64>()
37                            .map_err(|e| anyhow::anyhow!("Failed to parse hit value: {}", e))
38                    })
39                    .collect::<Result<Vec<_>, _>>()?;
40
41                Ok(FileCoverage {
42                    path: path.to_string(),
43                    hits,
44                    ..Default::default()
45                })
46            })
47            .collect()
48    }
49}
50
51#[cfg(test)]
52mod test {
53    use super::*;
54
55    #[test]
56    fn qlty_single_file() {
57        let input = r#"{"path":"lib/fish.rb","hits":["-1","1","1","0","-1","-1","1","0","-1","-1","1","0","-1","-1"]}"#;
58        let results = Qlty::new().parse_text(input).unwrap();
59        insta::assert_yaml_snapshot!(results, @r#"
60        - path: lib/fish.rb
61          hits:
62            - "-1"
63            - "1"
64            - "1"
65            - "0"
66            - "-1"
67            - "-1"
68            - "1"
69            - "0"
70            - "-1"
71            - "-1"
72            - "1"
73            - "0"
74            - "-1"
75            - "-1"
76        "#);
77    }
78
79    #[test]
80    fn qlty_multiple_files() {
81        let input = r#"
82{"path":"lib/dog.rb","hits":["-1","1","1","2","-1","-1","-1","1","0","-1","-1","-1","-1","1","1","-1","-1","1","0","-1","-1","1","0","-1","-1","1","1","-1","-1","1","-1","0","-1","-1","-1","-1","-1","-1","-1","1","0","0","0","0","0","0","0","0","0","0","0","-1","0","-1","-1","-1"]}
83{"path":"lib/fish.rb","hits":["-1","1","1","0","-1","-1","1","0","-1","-1","1","0","-1","-1"]}
84"#;
85        let results = Qlty::new().parse_text(input).unwrap();
86        insta::assert_yaml_snapshot!(results, @r#"
87        - path: lib/dog.rb
88          hits:
89            - "-1"
90            - "1"
91            - "1"
92            - "2"
93            - "-1"
94            - "-1"
95            - "-1"
96            - "1"
97            - "0"
98            - "-1"
99            - "-1"
100            - "-1"
101            - "-1"
102            - "1"
103            - "1"
104            - "-1"
105            - "-1"
106            - "1"
107            - "0"
108            - "-1"
109            - "-1"
110            - "1"
111            - "0"
112            - "-1"
113            - "-1"
114            - "1"
115            - "1"
116            - "-1"
117            - "-1"
118            - "1"
119            - "-1"
120            - "0"
121            - "-1"
122            - "-1"
123            - "-1"
124            - "-1"
125            - "-1"
126            - "-1"
127            - "-1"
128            - "1"
129            - "0"
130            - "0"
131            - "0"
132            - "0"
133            - "0"
134            - "0"
135            - "0"
136            - "0"
137            - "0"
138            - "0"
139            - "0"
140            - "-1"
141            - "0"
142            - "-1"
143            - "-1"
144            - "-1"
145        - path: lib/fish.rb
146          hits:
147            - "-1"
148            - "1"
149            - "1"
150            - "0"
151            - "-1"
152            - "-1"
153            - "1"
154            - "0"
155            - "-1"
156            - "-1"
157            - "1"
158            - "0"
159            - "-1"
160            - "-1"
161        "#);
162    }
163}