Skip to content

Commit

Permalink
draft rust implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
rizsotto committed May 18, 2024
1 parent da0209b commit d09f9e5
Show file tree
Hide file tree
Showing 25 changed files with 3,035 additions and 0 deletions.
44 changes: 44 additions & 0 deletions .github/workflows/build_rust.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: continuous integration

on:
push:
pull_request:

env:
CARGO_TERM_COLOR: always

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: rustup component add clippy && rustup update stable && rustup default stable
- run: cd rust && cargo clippy
compile:
name: Compile
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: rustup update stable && rustup default stable
- run: cd rust && cargo check --verbose
test:
name: Test
strategy:
matrix:
os:
- ubuntu-latest
- windows-latest
- macOS-latest
toolchain:
- stable
- beta
- nightly
runs-on: ${{ matrix.os }}
needs: [compile]
steps:
- uses: actions/checkout@v3
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
- run: cd rust && cargo build --verbose
- run: cd rust && cargo test --verbose

6 changes: 6 additions & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[workspace]
members = [
"semantic",
"intercept"
]
resolver = "2"
35 changes: 35 additions & 0 deletions rust/intercept/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[package]
name = "intercept"
version = "4.0.0"
authors = ["László Nagy <rizsotto at gmail dot com>"]
description = "Rust crate to intercept executed of commands."
keywords = ["clang", "clang-tooling", "compilation-database"]
repository = "https://github.com/rizsotto/Bear"
homepage = "https://github.com/rizsotto/Bear"
license = "GPL-3"
edition = "2021"

[dependencies]
anyhow = "1.0"
lazy_static = "1.4"
serde = { version = "1.0", default-features = false, features = ["derive"] }
serde_json = { version = "1.0", default-features = false, features = ["std"] }
log = "0.4"
simple_logger = { version = "4.2", default-features = false, features = ["timestamps"]}
clap = { version = "4.4", default-features = false, features = ["std", "cargo", "help", "usage", "suggestions"] }
crossbeam = "0.8"
crossbeam-channel = "0.5"
rand = "0.8.5"
chrono = "0.4.33"

[lib]
name = "intercept"
path = "src/lib.rs"

[[bin]]
name = "intercept"
path = "src/bin/intercept.rs"

[[bin]]
name = "wrapper"
path = "src/bin/wrapper.rs"
118 changes: 118 additions & 0 deletions rust/intercept/src/bin/intercept.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/* Copyright (C) 2012-2024 by László Nagy
This file is part of Bear.
Bear is a tool to generate compilation database for clang tooling.
Bear is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bear is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

extern crate core;

use std::io::Write;

use anyhow::Result;
use clap::{arg, ArgAction, command};
use crossbeam_channel::bounded;

use intercept::ipc::{Envelope, Event, ReporterId};
use intercept::collector::{EventCollector, EventCollectorOnTcp};

#[derive(Debug, PartialEq)]
struct Arguments {
command: Vec<String>,
output: String,
config: Option<String>,
verbose: u8,
}

impl Arguments {
fn parse() -> Result<Self> {
let matches = command!()
.args(&[
arg!(<COMMAND> "Build command")
.action(ArgAction::Append)
.value_terminator("--")
.num_args(1..)
.last(true)
.required(true),
arg!(-o --output <FILE> "Path of the result file")
.default_value("events.json")
.hide_default_value(false),
arg!(-c --config <FILE> "Path of the config file"),
arg!(-v --verbose ... "Sets the level of verbosity")
.action(ArgAction::Count),
])
.get_matches();

let result = Arguments {
command: matches.get_many("COMMAND")
.expect("command is required")
.map(String::to_string)
.collect(),
output: matches.get_one::<String>("output")
.expect("output is defaulted")
.clone(),
config: matches.get_one::<String>("config")
.map(String::to_string),
verbose: matches.get_count("verbose"),
};

Ok(result)
}
}

fn run() -> Result<i32> {
let arguments = Arguments::parse()?;

// let collector = EventCollectorOnTcp::new()?;
// let destination = collector.address()?;
//
// std::env::set_var("INTERCEPT_REPORT_DESTINATION", &destination.0);
// std::env::set_var("INTERCEPT_VERBOSE", arguments.verbose.to_string());
// let mut build = std::process::Command::new(arguments.command[0].clone())
// .args(&arguments.command[1..])
// .envs(std::env::vars())
// .spawn()?;
//
// let (sender, mut receiver) = bounded::<Envelope>(10);
// let collector_loop = std::thread::spawn(move || {
// collector.collect(sender)
// });
// let writer_loop = std::thread::spawn(move || {
// let mut writer = std::fs::File::create(arguments.output)?;
// loop {
// let envelope = receiver.recv()?;
// let _ = envelope.write_into(&mut writer)?;
// writer.flush()?;
// }
// });
//
// let build_status = build.wait()?;
// collector.stop()?;
//
// collector_loop.join().unwrap()?;
// writer_loop.join().unwrap()?;
//
// Ok(build_status.code().unwrap())
Ok(0)
}

fn main() {
let exit_code = run().unwrap_or_else(|error| {
eprintln!("Error: {}", error);
1
});

std::process::exit(exit_code);
}
28 changes: 28 additions & 0 deletions rust/intercept/src/bin/wrapper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* Copyright (C) 2012-2024 by László Nagy
This file is part of Bear.
Bear is a tool to generate compilation database for clang tooling.
Bear is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bear is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

extern crate core;

use anyhow::Result;

use intercept::ipc::{Envelope, Event, ReporterId};

fn main() -> Result<()> {
Ok(())
}
101 changes: 101 additions & 0 deletions rust/intercept/src/collector.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/* Copyright (C) 2012-2024 by László Nagy
This file is part of Bear.
Bear is a tool to generate compilation database for clang tooling.
Bear is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bear is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

use std::net::{TcpListener, TcpStream};

use crossbeam::channel::{Receiver, Sender};
use crossbeam_channel::bounded;

use super::ipc::{Envelope, SessionLocator};

pub trait EventCollector {
fn address(&self) -> Result<SessionLocator, anyhow::Error>;
fn collect(&self, destination: Sender<Envelope>) -> Result<(), anyhow::Error>;
fn stop(&self) -> Result<(), anyhow::Error>;
}

pub struct EventCollectorOnTcp {
control_input: Sender<bool>,
control_output: Receiver<bool>,
listener: TcpListener,
}

impl EventCollectorOnTcp {
pub fn new() -> Result<Self, anyhow::Error> {
let (control_input, control_output) = bounded(0);
let listener = TcpListener::bind("127.0.0.1:0")?;

let result = EventCollectorOnTcp { control_input, control_output, listener };

Ok(result)
}

fn send(
&self,
mut socket: TcpStream,
destination: Sender<Envelope>,
) -> Result<(), anyhow::Error> {
let envelope = Envelope::read_from(&mut socket)?;
destination.send(envelope)?;

Ok(())
}
}

impl EventCollector for EventCollectorOnTcp {
fn address(&self) -> Result<SessionLocator, anyhow::Error> {
let local_addr = self.listener.local_addr()?;
let locator = SessionLocator(local_addr.to_string());
Ok(locator)
}

fn collect(&self, destination: Sender<Envelope>) -> Result<(), anyhow::Error> {
loop {
if let Ok(shutdown) = self.control_output.try_recv() {
if shutdown {
break;
}
}

match self.listener.accept() {
Ok((stream, _)) => {
println!("Got a connection");
// ... (process the connection in a separate thread or task)
self.send(stream, destination.clone())?;
}
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
// No new connection available, continue checking for shutdown
continue;
}
Err(e) => {
println!("Error: {}", e);
break;
}
}
}

println!("Server shutting down");
Ok(())
}

fn stop(&self) -> Result<(), anyhow::Error> {
self.control_input.send(true)?;
Ok(())
}
}
Loading

0 comments on commit d09f9e5

Please sign in to comment.