Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 4604f46

Browse files
committed
Expose FileLines JSON representation
1 parent 7c1ad96 commit 4604f46

File tree

1 file changed

+70
-12
lines changed

1 file changed

+70
-12
lines changed

src/config/file_lines.rs

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use std::path::PathBuf;
1515
use std::rc::Rc;
1616
use std::{cmp, fmt, iter, str};
1717

18+
use serde::ser::{self, Serialize, Serializer};
1819
use serde::de::{Deserialize, Deserializer};
1920
use serde_json as json;
2021

@@ -53,6 +54,35 @@ impl fmt::Display for FileName {
5354
}
5455
}
5556

57+
impl<'de> Deserialize<'de> for FileName {
58+
fn deserialize<D>(deserializer: D) -> Result<FileName, D::Error>
59+
where
60+
D: Deserializer<'de>,
61+
{
62+
let s = String::deserialize(deserializer)?;
63+
if s == "stdin" {
64+
Ok(FileName::Stdin)
65+
} else {
66+
Ok(FileName::Real(s.into()))
67+
}
68+
}
69+
}
70+
71+
impl Serialize for FileName {
72+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
73+
where
74+
S: Serializer,
75+
{
76+
let s = match self {
77+
FileName::Stdin => Ok("stdin"),
78+
FileName::Real(path) => path.to_str().ok_or_else(||
79+
ser::Error::custom("path can't be serialized as UTF-8 string"))
80+
};
81+
82+
s.and_then(|s| serializer.serialize_str(s))
83+
}
84+
}
85+
5686
impl LineRange {
5787
pub fn file_name(&self) -> FileName {
5888
self.file.name.clone().into()
@@ -175,6 +205,20 @@ impl FileLines {
175205
Files(self.0.as_ref().map(|m| m.keys()))
176206
}
177207

208+
/// Returns JSON representation as accepted by the `--file-lines JSON` arg.
209+
pub fn to_json_spans(&self) -> Vec<JsonSpan> {
210+
match &self.0 {
211+
None => vec![],
212+
Some(file_ranges) => file_ranges
213+
.iter()
214+
.flat_map(|(file, ranges)| ranges.iter().map(move |r| (file, r)))
215+
.map(|(file, range)| JsonSpan {
216+
file: file.to_owned(),
217+
range: (range.lo, range.hi),
218+
}).collect(),
219+
}
220+
}
221+
178222
/// Returns true if `self` includes all lines in all files. Otherwise runs `f` on all ranges in
179223
/// the designated file (if any) and returns true if `f` ever does.
180224
fn file_range_matches<F>(&self, file_name: &FileName, f: F) -> bool
@@ -249,22 +293,12 @@ impl str::FromStr for FileLines {
249293
}
250294

251295
// For JSON decoding.
252-
#[derive(Clone, Debug, Deserialize)]
253-
struct JsonSpan {
254-
#[serde(deserialize_with = "deserialize_filename")]
296+
#[derive(Clone, Debug, Deserialize, Serialize)]
297+
pub struct JsonSpan {
255298
file: FileName,
256299
range: (usize, usize),
257300
}
258301

259-
fn deserialize_filename<'de, D: Deserializer<'de>>(d: D) -> Result<FileName, D::Error> {
260-
let s = String::deserialize(d)?;
261-
if s == "stdin" {
262-
Ok(FileName::Stdin)
263-
} else {
264-
Ok(FileName::Real(s.into()))
265-
}
266-
}
267-
268302
impl JsonSpan {
269303
fn into_tuple(self) -> Result<(FileName, Range), String> {
270304
let (lo, hi) = self.range;
@@ -350,4 +384,28 @@ mod test {
350384
Range::new(3, 7).merge(Range::new(4, 5))
351385
);
352386
}
387+
388+
use std::{collections::HashMap, path::PathBuf};
389+
use super::{FileName, FileLines};
390+
use super::json::{self, json, json_internal};
391+
392+
#[test]
393+
fn file_lines_to_json() {
394+
let ranges: HashMap<FileName, Vec<Range>> = [
395+
(FileName::Real(PathBuf::from("src/main.rs")), vec![
396+
Range::new(1, 3),
397+
Range::new(5, 7)
398+
]),
399+
(FileName::Real(PathBuf::from("src/lib.rs")), vec![
400+
Range::new(1, 7)
401+
])].iter().cloned().collect();
402+
403+
let file_lines = FileLines::from_ranges(ranges);
404+
let json = json::to_value(&file_lines.to_json_spans()).unwrap();
405+
assert_eq!(json, json! {[
406+
{"file": "src/main.rs", "range": [1, 3]},
407+
{"file": "src/main.rs", "range": [5, 7]},
408+
{"file": "src/lib.rs", "range": [1, 7]},
409+
]});
410+
}
353411
}

0 commit comments

Comments
 (0)