rust: modes module reorg

This commit is contained in:
Laszlo Nagy
2024-12-15 22:40:50 +11:00
parent ab180ba79f
commit fb299a965c
5 changed files with 147 additions and 147 deletions

View File

@@ -1,9 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use bear::modes::combined::Combined;
use bear::modes::intercept::Intercept;
use bear::modes::semantic::Semantic;
use bear::modes::Mode;
use bear::modes::{Combined, Intercept, Mode, Semantic};
use bear::{args, config};
use std::env;
use std::process::ExitCode;

View File

@@ -1,44 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use crate::modes::intercept::Interceptor;
use crate::modes::semantic::SemanticFromEnvelopes;
use crate::modes::Mode;
use crate::{args, config};
use std::process::ExitCode;
/// The all model is combining the intercept and semantic modes.
pub struct Combined {
command: args::BuildCommand,
interceptor: Interceptor,
}
impl Combined {
/// Create a new all mode instance.
pub fn from(
command: args::BuildCommand,
output: args::BuildSemantic,
config: config::Main,
) -> anyhow::Result<Self> {
let semantic = SemanticFromEnvelopes::from(output, &config)?;
let interceptor: Interceptor = Interceptor::new(config, move |envelopes| {
semantic.analyze_and_write(envelopes)
})?;
Ok(Self {
command,
interceptor,
})
}
}
impl Mode for Combined {
/// Run the all mode by setting up the collector service and the intercept environment.
/// The build command is executed in the intercept environment. The collected events are
/// then processed by the semantic recognition and transformation. The result is written
/// to the output file.
///
/// The exit code is based on the result of the build command.
fn run(self) -> anyhow::Result<ExitCode> {
self.interceptor.run_build_command(self.command)
}
}

View File

@@ -1,13 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use super::Mode;
use crate::ipc::tcp::CollectorOnTcp;
use crate::ipc::{Collector, Envelope};
use crate::output::event::write;
use crate::{args, config};
use anyhow::Context;
use std::fs::OpenOptions;
use std::io;
use std::path::{Path, PathBuf};
use std::process::{Command, ExitCode};
use std::sync::mpsc::channel;
@@ -19,56 +15,17 @@ use std::{env, thread};
pub const KEY_DESTINATION: &str = "INTERCEPT_REPORTER_ADDRESS";
pub const KEY_PRELOAD_PATH: &str = "LD_PRELOAD";
/// The intercept mode we are only capturing the build commands
/// and write it into the output file.
pub struct Intercept {
command: args::BuildCommand,
interceptor: Interceptor,
}
impl Intercept {
/// Create a new intercept mode instance.
pub fn from(
command: args::BuildCommand,
output: args::BuildEvents,
config: config::Main,
) -> anyhow::Result<Self> {
let file_name = output.file_name.as_str();
let output_file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(file_name)
.map(io::BufWriter::new)
.with_context(|| format!("Failed to open file: {:?}", file_name))?;
let interceptor: Interceptor =
Interceptor::new(config, move |envelopes| write(output_file, envelopes))?;
Ok(Self {
command,
interceptor,
})
}
}
impl Mode for Intercept {
/// Run the intercept mode by setting up the collector service and
/// the intercept environment. The build command is executed in the
/// intercept environment.
///
/// The exit code is based on the result of the build command.
fn run(self) -> anyhow::Result<ExitCode> {
self.interceptor.run_build_command(self.command)
}
}
pub(super) struct Interceptor {
/// The build interceptor is responsible for capturing the build commands and
/// dispatching them to the consumer. The consumer is a function that processes
/// the intercepted command executions.
pub(super) struct BuildInterceptor {
#[allow(dead_code)]
service: CollectorService,
environment: InterceptEnvironment,
}
impl Interceptor {
impl BuildInterceptor {
/// Create a new process execution interceptor.
pub(super) fn new<F>(config: config::Main, consumer: F) -> anyhow::Result<Self>
where
F: FnOnce(Receiver<Envelope>) -> anyhow::Result<()>,
@@ -86,6 +43,7 @@ impl Interceptor {
})
}
/// Run the build command in the intercept environment.
pub(super) fn run_build_command(self, command: args::BuildCommand) -> anyhow::Result<ExitCode> {
self.environment
.execute_build_command(command)
@@ -93,6 +51,13 @@ impl Interceptor {
}
}
impl Drop for BuildInterceptor {
fn drop(&mut self) {
// The `CollectorService` already implements `Drop`, so we don't need to do anything here.
// The `CollectorService`'s `Drop` implementation will handle the shutdown of the service.
}
}
/// The service is responsible for collecting the events from the supervised processes.
///
/// The service is implemented as TCP server that listens on a random port on the loopback

View File

@@ -1,12 +1,140 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pub mod combined;
pub mod intercept;
pub mod semantic;
use crate::modes::intercept::BuildInterceptor;
use crate::modes::semantic::SemanticAnalysisPipeline;
use crate::output::event::{read, write};
use crate::{args, config};
use anyhow::Context;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::BufReader;
use std::process::ExitCode;
/// The mode trait is used to run the application in different modes.
pub trait Mode {
fn run(self) -> anyhow::Result<ExitCode>;
}
/// The intercept mode we are only capturing the build commands
/// and write it into the output file.
pub struct Intercept {
command: args::BuildCommand,
interceptor: BuildInterceptor,
}
impl Intercept {
/// Create a new intercept mode instance.
pub fn from(
command: args::BuildCommand,
output: args::BuildEvents,
config: config::Main,
) -> anyhow::Result<Self> {
let file_name = output.file_name.as_str();
let output_file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(file_name)
.map(io::BufWriter::new)
.with_context(|| format!("Failed to open file: {:?}", file_name))?;
let interceptor =
BuildInterceptor::new(config, move |envelopes| write(output_file, envelopes))?;
Ok(Self {
command,
interceptor,
})
}
}
impl Mode for Intercept {
/// Run the intercept mode by setting up the collector service and
/// the intercept environment. The build command is executed in the
/// intercept environment.
///
/// The exit code is based on the result of the build command.
fn run(self) -> anyhow::Result<ExitCode> {
self.interceptor.run_build_command(self.command)
}
}
/// The semantic mode we are deduct the semantic meaning of the
/// executed commands from the build process.
pub struct Semantic {
event_file: BufReader<File>,
semantic: SemanticAnalysisPipeline,
}
impl Semantic {
pub fn from(
input: args::BuildEvents,
output: args::BuildSemantic,
config: config::Main,
) -> anyhow::Result<Self> {
let file_name = input.file_name.as_str();
let event_file = OpenOptions::new()
.read(true)
.open(file_name)
.map(BufReader::new)
.with_context(|| format!("Failed to open file: {:?}", file_name))?;
let semantic = SemanticAnalysisPipeline::from(output, &config)?;
Ok(Self {
event_file,
semantic,
})
}
}
impl Mode for Semantic {
/// Run the semantic mode by reading the event file and analyzing the events.
///
/// The exit code is based on the result of the output writer.
fn run(self) -> anyhow::Result<ExitCode> {
self.semantic
.analyze_and_write(read(self.event_file))
.map(|_| ExitCode::SUCCESS)
}
}
/// The all model is combining the intercept and semantic modes.
pub struct Combined {
command: args::BuildCommand,
interceptor: BuildInterceptor,
}
impl Combined {
/// Create a new all mode instance.
pub fn from(
command: args::BuildCommand,
output: args::BuildSemantic,
config: config::Main,
) -> anyhow::Result<Self> {
let semantic = SemanticAnalysisPipeline::from(output, &config)?;
let interceptor = BuildInterceptor::new(config, move |envelopes| {
semantic.analyze_and_write(envelopes)
})?;
Ok(Self {
command,
interceptor,
})
}
}
impl Mode for Combined {
/// Run the all mode by setting up the collector service and the intercept environment.
/// The build command is executed in the intercept environment. The collected events are
/// then processed by the semantic recognition and transformation. The result is written
/// to the output file.
///
/// The exit code is based on the result of the build command.
fn run(self) -> anyhow::Result<ExitCode> {
self.interceptor.run_build_command(self.command)
}
}

View File

@@ -1,65 +1,19 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use crate::ipc::Envelope;
use crate::modes::Mode;
use crate::output::event::read;
use crate::output::{OutputWriter, OutputWriterImpl};
use crate::semantic::transformation::Transformation;
use crate::semantic::Transform;
use crate::{args, config, semantic};
use anyhow::Context;
use std::fs::{File, OpenOptions};
use std::io::BufReader;
use std::process::ExitCode;
/// The semantic mode we are deduct the semantic meaning of the
/// executed commands from the build process.
pub struct Semantic {
event_file: BufReader<File>,
semantic: SemanticFromEnvelopes,
}
impl Semantic {
pub fn from(
input: args::BuildEvents,
output: args::BuildSemantic,
config: config::Main,
) -> anyhow::Result<Self> {
let file_name = input.file_name.as_str();
let event_file = OpenOptions::new()
.read(true)
.open(file_name)
.map(BufReader::new)
.with_context(|| format!("Failed to open file: {:?}", file_name))?;
let semantic = SemanticFromEnvelopes::from(output, &config)?;
Ok(Self {
event_file,
semantic,
})
}
}
impl Mode for Semantic {
/// Run the semantic mode by reading the event file and analyzing the events.
///
/// The exit code is based on the result of the output writer.
fn run(self) -> anyhow::Result<ExitCode> {
self.semantic
.analyze_and_write(read(self.event_file))
.map(|_| ExitCode::SUCCESS)
}
}
/// The semantic analysis that is independent of the event source.
pub struct SemanticFromEnvelopes {
pub(super) struct SemanticAnalysisPipeline {
interpreter: Box<dyn semantic::Interpreter>,
transform: Transformation,
output_writer: OutputWriterImpl,
}
impl SemanticFromEnvelopes {
impl SemanticAnalysisPipeline {
/// Create a new semantic mode instance.
pub(super) fn from(output: args::BuildSemantic, config: &config::Main) -> anyhow::Result<Self> {
let interpreter = Self::interpreter(config)?;