Skip to content

Commit c7e965e

Browse files
committed
rust: implement clang output command field support for writing
1 parent 9f317ff commit c7e965e

File tree

5 files changed

+120
-10
lines changed

5 files changed

+120
-10
lines changed

rust/bear/src/config.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -519,9 +519,9 @@ pub enum OutputFields {
519519
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
520520
pub struct Format {
521521
#[serde(default = "default_enabled")]
522-
command_as_array: bool,
522+
pub command_as_array: bool,
523523
#[serde(default = "default_disabled")]
524-
drop_output_field: bool,
524+
pub drop_output_field: bool,
525525
}
526526

527527
impl Default for Format {

rust/bear/src/output/clang/mod.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,38 @@ pub struct Entry {
3939
pub output: Option<std::path::PathBuf>,
4040
}
4141

42-
pub fn write(
42+
/// Serialize entries from an iterator into a JSON array.
43+
///
44+
/// It uses the `arguments` field of the `Entry` struct to serialize the array of strings.
45+
pub fn write_with_arguments(
4346
writer: impl std::io::Write,
4447
entries: impl Iterator<Item = Entry>,
4548
) -> Result<(), Error> {
46-
let mut ser = serde_json::Serializer::new(writer);
49+
let mut ser = serde_json::Serializer::pretty(writer);
4750
let mut seq = ser.serialize_seq(None)?;
4851
for entry in entries {
4952
seq.serialize_element(&entry)?;
5053
}
5154
seq.end()
5255
}
5356

57+
/// Serialize entries from an iterator into a JSON array.
58+
///
59+
/// It uses the `arguments` field of the `Entry` struct to serialize the array of strings.
60+
pub fn write_with_command(
61+
writer: impl std::io::Write,
62+
entries: impl Iterator<Item = Entry>,
63+
) -> Result<(), Error> {
64+
let mut ser = serde_json::Serializer::pretty(writer);
65+
let mut seq = ser.serialize_seq(None)?;
66+
for entry in entries {
67+
let entry = type_ser::EntryWithCommand::from(entry);
68+
seq.serialize_element(&entry)?;
69+
}
70+
seq.end()
71+
}
72+
73+
/// Deserialize entries from a JSON array into an iterator.
5474
pub fn read(reader: impl std::io::Read) -> impl Iterator<Item = Result<Entry, Error>> {
5575
iterator::iter_json_array(reader)
5676
}

rust/bear/src/output/clang/tests.rs

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ mod success {
8888
mod basic {
8989
use super::*;
9090
use crate::vec_of_strings;
91-
use serde_json::Value;
9291
use std::io::{Cursor, Seek, SeekFrom};
9392

9493
fn expected_values() -> Vec<Entry> {
@@ -166,17 +165,35 @@ mod success {
166165

167166
// Create fake "file"
168167
let mut buffer = Cursor::new(Vec::new());
169-
let result = write(&mut buffer, input.into_iter());
168+
let result = write_with_arguments(&mut buffer, input.into_iter());
170169
assert!(result.is_ok());
171170

172171
// Use the fake "file" as input
173172
buffer.seek(SeekFrom::Start(0)).unwrap();
174-
let content: Value = serde_json::from_reader(&mut buffer)?;
173+
let content: serde_json::Value = serde_json::from_reader(&mut buffer)?;
175174

176175
assert_eq!(expected_with_array_syntax(), content);
177176

178177
Ok(())
179178
}
179+
180+
#[test]
181+
fn save_with_string_command_syntax() -> Result<(), Error> {
182+
let input = expected_values();
183+
184+
// Create fake "file"
185+
let mut buffer = Cursor::new(Vec::new());
186+
let result = write_with_command(&mut buffer, input.into_iter());
187+
assert!(result.is_ok());
188+
189+
// Use the fake "file" as input
190+
buffer.seek(SeekFrom::Start(0)).unwrap();
191+
let content: serde_json::Value = serde_json::from_reader(&mut buffer)?;
192+
193+
assert_eq!(expected_with_string_syntax(), content);
194+
195+
Ok(())
196+
}
180197
}
181198

182199
mod quoted {
@@ -233,6 +250,21 @@ mod success {
233250
])
234251
}
235252

253+
fn expected_with_string_syntax() -> serde_json::Value {
254+
json!([
255+
{
256+
"directory": "/home/user",
257+
"file": "./file_a.c",
258+
"command": r#"cc -c -D 'name=\"me\"' ./file_a.c -o ./file_a.o"#
259+
},
260+
{
261+
"directory": "/home/user",
262+
"file": "./file_b.c",
263+
"command": r#"cc -c -D 'name="me"' ./file_b.c -o ./file_b.o"#
264+
}
265+
])
266+
}
267+
236268
#[test]
237269
fn load_content_with_array_command_syntax() {
238270
let content = expected_with_array_syntax().to_string();
@@ -249,7 +281,7 @@ mod success {
249281

250282
// Create fake "file"
251283
let mut buffer = Cursor::new(Vec::new());
252-
let result = write(&mut buffer, input.into_iter());
284+
let result = write_with_arguments(&mut buffer, input.into_iter());
253285
assert!(result.is_ok());
254286

255287
// Use the fake "file" as input
@@ -260,5 +292,23 @@ mod success {
260292

261293
Ok(())
262294
}
295+
296+
#[test]
297+
fn save_with_string_command_syntax() -> Result<(), Error> {
298+
let input = expected_values();
299+
300+
// Create fake "file"
301+
let mut buffer = Cursor::new(Vec::new());
302+
let result = write_with_command(&mut buffer, input.into_iter());
303+
assert!(result.is_ok());
304+
305+
// Use the fake "file" as input
306+
buffer.seek(SeekFrom::Start(0)).unwrap();
307+
let content: Value = serde_json::from_reader(&mut buffer)?;
308+
309+
assert_eq!(expected_with_string_syntax(), content);
310+
311+
Ok(())
312+
}
263313
}
264314
}

rust/bear/src/output/clang/type_ser.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,39 @@ impl Serialize for Entry {
2222
state.end()
2323
}
2424
}
25+
26+
#[derive(Clone, Debug, PartialEq, Eq)]
27+
pub struct EntryWithCommand {
28+
pub file: std::path::PathBuf,
29+
pub command: String,
30+
pub directory: std::path::PathBuf,
31+
pub output: Option<std::path::PathBuf>,
32+
}
33+
34+
impl From<Entry> for EntryWithCommand {
35+
fn from(entry: Entry) -> Self {
36+
Self {
37+
file: entry.file,
38+
command: shell_words::join(&entry.arguments),
39+
directory: entry.directory,
40+
output: entry.output,
41+
}
42+
}
43+
}
44+
45+
impl Serialize for EntryWithCommand {
46+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
47+
where
48+
S: Serializer,
49+
{
50+
let size = if self.output.is_some() { 4 } else { 3 };
51+
let mut state = serializer.serialize_struct("Entry", size)?;
52+
state.serialize_field("directory", &self.directory)?;
53+
state.serialize_field("file", &self.file)?;
54+
state.serialize_field("command", &self.command)?;
55+
if self.output.is_some() {
56+
state.serialize_field("output", &self.output)?;
57+
}
58+
state.end()
59+
}
60+
}

rust/bear/src/output/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,15 +94,19 @@ impl OutputWriter {
9494
&self,
9595
entries: impl Iterator<Item = Entry>,
9696
) -> anyhow::Result<PathBuf> {
97-
// FIXME: Implement entry formatting.
97+
// FIXME: Implement entry formatting. (dropping the output field before this function)
9898

9999
// Generate a temporary file name.
100100
let file_name = self.output.with_extension("tmp");
101101
// Open the file for writing.
102102
let file = File::create(&file_name)
103103
.with_context(|| format!("Failed to create file: {:?}", file_name.as_path()))?;
104104
// Write the entries to the file.
105-
clang::write(BufWriter::new(file), entries)?;
105+
if self.format.command_as_array {
106+
clang::write_with_arguments(BufWriter::new(file), entries)?
107+
} else {
108+
clang::write_with_command(BufWriter::new(file), entries)?
109+
}
106110
// Return the temporary file name.
107111
Ok(file_name)
108112
}

0 commit comments

Comments
 (0)