rust: intercept persistency got separate module

This commit is contained in:
Laszlo Nagy
2024-12-26 10:40:49 +11:00
parent ecc1caebc9
commit b4c6a6f703
4 changed files with 154 additions and 144 deletions

View File

@@ -11,15 +11,14 @@
use crate::{args, config};
use serde::{Deserialize, Serialize};
use serde_json::de::IoRead;
use serde_json::StreamDeserializer;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::process::{Command, ExitCode};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::Arc;
use std::{env, fmt, io, thread};
use std::{env, fmt, thread};
pub mod persistence;
pub mod tcp;
/// Declare the environment variables used by the intercept mode.
@@ -333,143 +332,3 @@ impl InterceptEnvironment {
path.to_str().unwrap_or("").to_string()
}
}
/// Generate the build events from the file.
///
/// Returns an iterator over the build events.
/// Any error will interrupt the reading process and the remaining events will be lost.
pub fn read(reader: impl io::Read) -> impl Iterator<Item = Envelope> {
let stream = StreamDeserializer::new(IoRead::new(reader));
stream.filter_map(|result| match result {
Ok(value) => Some(value),
Err(error) => {
log::error!("Failed to read event: {:?}", error);
None
}
})
}
/// Write the build events to the file.
///
/// Can fail if the events cannot be serialized or written to the file.
/// Any error will interrupt the writing process and the file will be incomplete.
pub fn write(
mut writer: impl io::Write,
envelopes: impl IntoIterator<Item = Envelope>,
) -> Result<(), anyhow::Error> {
for envelope in envelopes {
serde_json::to_writer(&mut writer, &envelope)?;
writer.write_all(b"\n")?;
}
Ok(())
}
#[cfg(test)]
mod test {
use super::*;
use crate::vec_of_strings;
use serde_json::json;
use std::collections::HashMap;
use std::path::PathBuf;
#[test]
fn read_write() {
let events = expected_values();
let mut buffer = Vec::new();
write(&mut buffer, events.iter().cloned()).unwrap();
let mut cursor = io::Cursor::new(buffer);
let read_events: Vec<_> = read(&mut cursor).collect();
assert_eq!(events, read_events);
}
#[test]
fn read_write_empty() {
let events = Vec::<Envelope>::new();
let mut buffer = Vec::new();
write(&mut buffer, events.iter().cloned()).unwrap();
let mut cursor = io::Cursor::new(buffer);
let read_events: Vec<_> = read(&mut cursor).collect();
assert_eq!(events, read_events);
}
#[test]
fn read_stops_on_errors() {
let line1 = json!({
"rid": 42,
"timestamp": 0,
"event": {
"pid": 11782,
"execution": {
"executable": "/usr/bin/clang",
"arguments": ["clang", "-c", "main.c"],
"working_dir": "/home/user",
"environment": {
"PATH": "/usr/bin",
"HOME": "/home/user"
}
}
}
});
let line2 = json!({"rid": 42 });
let line3 = json!({
"rid": 42,
"timestamp": 273,
"event": {
"pid": 11934,
"execution": {
"executable": "/usr/bin/clang",
"arguments": ["clang", "-c", "output.c"],
"working_dir": "/home/user",
"environment": {}
}
}
});
let content = format!("{}\n{}\n{}\n", line1, line2, line3);
let mut cursor = io::Cursor::new(content);
let read_events: Vec<_> = read(&mut cursor).collect();
// Only the fist event is read, all other lines are ignored.
assert_eq!(expected_values()[0..1], read_events);
}
const REPORTER_ID: ReporterId = ReporterId(42);
fn expected_values() -> Vec<Envelope> {
vec![
Envelope {
rid: REPORTER_ID,
timestamp: 0,
event: Event {
pid: ProcessId(11782),
execution: Execution {
executable: PathBuf::from("/usr/bin/clang"),
arguments: vec_of_strings!["clang", "-c", "main.c"],
working_dir: PathBuf::from("/home/user"),
environment: HashMap::from([
("PATH".to_string(), "/usr/bin".to_string()),
("HOME".to_string(), "/home/user".to_string()),
]),
},
},
},
Envelope {
rid: REPORTER_ID,
timestamp: 273,
event: Event {
pid: ProcessId(11934),
execution: Execution {
executable: PathBuf::from("/usr/bin/clang"),
arguments: vec_of_strings!["clang", "-c", "output.c"],
working_dir: PathBuf::from("/home/user"),
environment: HashMap::from([]),
},
},
},
]
}
}

View File

@@ -0,0 +1,147 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use super::Envelope;
use serde_json::de::IoRead;
use serde_json::StreamDeserializer;
use std::io;
/// Generate the build events from the file.
///
/// Returns an iterator over the build events.
/// Any error will interrupt the reading process and the remaining events will be lost.
pub fn read(reader: impl io::Read) -> impl Iterator<Item = Envelope> {
let stream = StreamDeserializer::new(IoRead::new(reader));
stream.filter_map(|result| match result {
Ok(value) => Some(value),
Err(error) => {
log::error!("Failed to read event: {:?}", error);
None
}
})
}
/// Write the build events to the file.
///
/// Can fail if the events cannot be serialized or written to the file.
/// Any error will interrupt the writing process and the file will be incomplete.
pub fn write(
mut writer: impl io::Write,
envelopes: impl IntoIterator<Item = Envelope>,
) -> Result<(), anyhow::Error> {
for envelope in envelopes {
serde_json::to_writer(&mut writer, &envelope)?;
writer.write_all(b"\n")?;
}
Ok(())
}
#[cfg(test)]
mod test {
use super::*;
use crate::intercept::{Event, Execution, ProcessId, ReporterId};
use crate::vec_of_strings;
use serde_json::json;
use std::collections::HashMap;
use std::path::PathBuf;
#[test]
fn read_write() {
let events = expected_values();
let mut buffer = Vec::new();
write(&mut buffer, events.iter().cloned()).unwrap();
let mut cursor = io::Cursor::new(buffer);
let read_events: Vec<_> = read(&mut cursor).collect();
assert_eq!(events, read_events);
}
#[test]
fn read_write_empty() {
let events = Vec::<Envelope>::new();
let mut buffer = Vec::new();
write(&mut buffer, events.iter().cloned()).unwrap();
let mut cursor = io::Cursor::new(buffer);
let read_events: Vec<_> = read(&mut cursor).collect();
assert_eq!(events, read_events);
}
#[test]
fn read_stops_on_errors() {
let line1 = json!({
"rid": 42,
"timestamp": 0,
"event": {
"pid": 11782,
"execution": {
"executable": "/usr/bin/clang",
"arguments": ["clang", "-c", "main.c"],
"working_dir": "/home/user",
"environment": {
"PATH": "/usr/bin",
"HOME": "/home/user"
}
}
}
});
let line2 = json!({"rid": 42 });
let line3 = json!({
"rid": 42,
"timestamp": 273,
"event": {
"pid": 11934,
"execution": {
"executable": "/usr/bin/clang",
"arguments": ["clang", "-c", "output.c"],
"working_dir": "/home/user",
"environment": {}
}
}
});
let content = format!("{}\n{}\n{}\n", line1, line2, line3);
let mut cursor = io::Cursor::new(content);
let read_events: Vec<_> = read(&mut cursor).collect();
// Only the fist event is read, all other lines are ignored.
assert_eq!(expected_values()[0..1], read_events);
}
const REPORTER_ID: ReporterId = ReporterId(42);
fn expected_values() -> Vec<Envelope> {
vec![
Envelope {
rid: REPORTER_ID,
timestamp: 0,
event: Event {
pid: ProcessId(11782),
execution: Execution {
executable: PathBuf::from("/usr/bin/clang"),
arguments: vec_of_strings!["clang", "-c", "main.c"],
working_dir: PathBuf::from("/home/user"),
environment: HashMap::from([
("PATH".to_string(), "/usr/bin".to_string()),
("HOME".to_string(), "/home/user".to_string()),
]),
},
},
},
Envelope {
rid: REPORTER_ID,
timestamp: 273,
event: Event {
pid: ProcessId(11934),
execution: Execution {
executable: PathBuf::from("/usr/bin/clang"),
arguments: vec_of_strings!["clang", "-c", "output.c"],
working_dir: PathBuf::from("/home/user"),
environment: HashMap::from([]),
},
},
},
]
}
}

View File

@@ -3,7 +3,7 @@
pub mod intercept;
pub mod semantic;
use crate::intercept::{read, write};
use crate::intercept::persistence::{read, write};
use crate::modes::intercept::BuildInterceptor;
use crate::modes::semantic::SemanticAnalysisPipeline;
use crate::{args, config};

View File

@@ -152,6 +152,10 @@ impl OutputWriter for ClangOutputWriter {
}
impl ClangOutputWriter {
/// Write the entries to the compilation database.
///
/// The entries are written to a temporary file and then renamed to the final output.
/// This guaranties that the output file is always in a consistent state.
fn write_into_compilation_db(
&self,
entries: impl Iterator<Item = crate::output::clang::Entry>,