feat: modify flakenix to include cargo utils

This commit is contained in:
Chance 2025-04-13 16:08:18 -04:00 committed by BitSyndicate
parent 16a4aee633
commit 1f90088350
Signed by: bitsyndicate
GPG key ID: 443E4198D6BBA6DE
15 changed files with 966 additions and 838 deletions

View file

@ -1,11 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "cargo" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"

49
Cargo.lock generated
View file

@ -273,6 +273,47 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "crossbeam"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-epoch",
"crossbeam-queue",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]] [[package]]
name = "crossbeam-queue" name = "crossbeam-queue"
version = "0.3.12" version = "0.3.12"
@ -438,6 +479,12 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
[[package]]
name = "humantime"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.9.0" version = "2.9.0"
@ -2155,6 +2202,8 @@ checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda"
name = "zenyx" name = "zenyx"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"crossbeam",
"humantime",
"terminator", "terminator",
"thiserror 2.0.12", "thiserror 2.0.12",
"tracing", "tracing",

View file

@ -25,7 +25,19 @@ split-debuginfo = "off"
[profile.dev] [profile.dev]
debug = 0 debug = 0
[profile.mini]
inherits = "release"
opt-level = "z"
debug = false
strip = true
lto = true
codegen-units = 1
incremental = false
panic = "abort"
[dependencies] [dependencies]
crossbeam = "0.8.4"
humantime = "2.2.0"
terminator = "0.3.2" terminator = "0.3.2"
thiserror = "2.0.12" thiserror = "2.0.12"
tracing = "0.1.41" tracing = "0.1.41"

View file

@ -43,6 +43,12 @@
xorg.libxcb xorg.libxcb
pkg-config pkg-config
kdePackages.kdialog kdePackages.kdialog
cargo-xbuild
cargo-pgo
cargo-cross
cargo-xwin
cargo-wizard
wine64
]; ];
in { in {
packages = { packages = {
@ -61,17 +67,17 @@
nativeBuildInputs = with pkgs; [ nativeBuildInputs = with pkgs; [
(rust-bin.stable.latest.default.override { (rust-bin.stable.latest.default.override {
extensions = ["rust-src" "cargo" "rustfmt" "clippy"]; extensions = ["rust-src" "cargo" "rustfmt" "clippy"];
targets = [ "x86_64-pc-windows-msvc" "x86_64-unknown-linux-gnu" ];
}) })
pkg-config pkg-config
]; ];
buildInputs = buildInputs; buildInputs = buildInputs;
shellHook = '' shellHook = ''
export PATH="$HOME/.cargo/bin:$PATH"
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${builtins.toString (pkgs.lib.makeLibraryPath buildInputs)}" export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${builtins.toString (pkgs.lib.makeLibraryPath buildInputs)}"
echo "Rust version: $(rustc --version)" echo "Rust version: $(rustc --version)"
''; '';
}; };
formatter = alejandra.packages.${system}.default; formatter = alejandra.packages.${system}.default;
} }
); );

View file

@ -1,39 +1,85 @@
use crossbeam::channel::{Sender, unbounded};
use std::{ use std::{
fs::OpenOptions, io::{self, BufWriter, Stdout, Write}, path::{Path, PathBuf}, str::FromStr, sync::{mpsc, Arc, RwLock} fmt,
fs::OpenOptions,
io::{BufWriter, Write},
path::{Path, PathBuf},
str::FromStr,
sync::{Arc, RwLock},
time::SystemTime,
};
use tracing::{Event, Level, Subscriber, info};
use tracing_subscriber::{
EnvFilter,
layer::{Context, Layer, SubscriberExt},
registry::LookupSpan,
util::SubscriberInitExt,
}; };
use tracing::{error, info};
use tracing_subscriber::fmt::format::FmtSpan;
use tracing_subscriber::{Layer, Registry, layer::SubscriberExt, util::SubscriberInitExt};
use winit::event_loop::EventLoop;
#[derive(Debug, Default)] #[derive(Debug, Default)]
struct App; struct App;
struct BufferWriter { #[derive(Debug, Clone)]
buffer: Arc<RwLock<Vec<u8>>>, pub struct LogEntry {
timestamp: SystemTime,
level: Level,
message: String,
} }
impl Write for BufferWriter { struct BufferLayer {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { log_entries: Arc<RwLock<Vec<LogEntry>>>,
if let Ok(mut guard) = self.buffer.write() { sender: Sender<LogEntry>,
guard.extend_from_slice(buf); }
Ok(buf.len())
impl<S> Layer<S> for BufferLayer
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
let metadata = event.metadata();
let level = *metadata.level();
let timestamp = SystemTime::now();
let mut message = String::new();
let mut visitor = LogVisitor::new(&mut message);
event.record(&mut visitor);
let log_entry = LogEntry {
timestamp,
level,
message,
};
if let Ok(mut guard) = self.log_entries.write() {
guard.push(log_entry.clone());
}
let _ = self.sender.send(log_entry);
}
}
struct LogVisitor<'msg> {
message: &'msg mut String,
}
impl<'msg> LogVisitor<'msg> {
fn new(message: &'msg mut String) -> Self {
Self { message }
}
}
impl<'msg> tracing::field::Visit for LogVisitor<'msg> {
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn fmt::Debug) {
use std::fmt::Write as _;
if field.name() == "message" {
write!(self.message, "{:?}", value).unwrap();
} else { } else {
Err(io::Error::new( write!(self.message, "{}={:?} ", field.name(), value).unwrap();
io::ErrorKind::Other,
"Failed to acquire write lock on log buffer",
))
} }
} }
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
enum LogLevel { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum LogLevel {
Error, Error,
#[default] #[default]
Info, Info,
@ -41,9 +87,9 @@ enum LogLevel {
Trace, Trace,
} }
impl std::fmt::Display for LogLevel { impl fmt::Display for LogLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f,"{self:?}") write!(f, "{:?}", self)
} }
} }
@ -54,44 +100,53 @@ impl FromStr for LogLevel {
match s { match s {
"trace" => Ok(Self::Trace), "trace" => Ok(Self::Trace),
"debug" => Ok(Self::Debug), "debug" => Ok(Self::Debug),
"info" => Ok(Self::Info), "info" => Ok(Self::Info),
"error" => Ok(Self::Error), "error" => Ok(Self::Error),
_ => Err(()), _ => Err(()),
} }
} }
} }
#[derive(Debug)] #[derive(Debug, Clone,PartialEq,Eq,PartialOrd)]
pub struct LoggerConfig { pub struct LoggerConfig {
pub log_level: LogLevel, pub log_level: LogLevel,
pub log_to_file: bool, pub log_to_file: bool,
pub log_file_path: PathBuf, pub log_file_path: PathBuf,
pub log_to_stdout: bool, pub log_to_stdout: bool,
pub stdout_color: bool, pub stdout_color: bool,
pub log_to_buffer: bool, pub stdout_include_time: bool,
pub file_include_time: bool,
} }
impl LoggerConfig { impl LoggerConfig {
pub fn level(mut self,level: LogLevel) -> Self { pub fn level(mut self, level: LogLevel) -> Self {
self.log_level = level; self.log_level = level;
self self
} }
pub fn log_to_file(mut self,log_to_file: bool ) -> Self { pub fn log_to_file(mut self, log_to_file: bool) -> Self {
self.log_to_file = log_to_file; self.log_to_file = log_to_file;
self self
} }
pub fn colored_stdout(mut self,show_color: bool ) -> Self { pub fn colored_stdout(mut self, show_color: bool) -> Self {
self.stdout_color = show_color; self.stdout_color = show_color;
self self
} }
pub fn log_to_stdout(mut self,stdout: bool ) -> Self { pub fn log_to_stdout(mut self, stdout: bool) -> Self {
self.log_to_stdout = stdout; self.log_to_stdout = stdout;
self self
} }
pub fn log_path<P: AsRef<Path>>(mut self,path: P ) -> Self { pub fn log_path<P: AsRef<Path>>(mut self, path: P) -> Self {
self.log_file_path = path.as_ref().to_path_buf(); self.log_file_path = path.as_ref().to_path_buf();
self self
} }
pub fn stdout_include_time(mut self, include: bool) -> Self {
self.stdout_include_time = include;
self
}
pub fn file_include_time(mut self, include: bool) -> Self {
self.file_include_time = include;
self
}
} }
impl Default for LoggerConfig { impl Default for LoggerConfig {
@ -102,118 +157,128 @@ impl Default for LoggerConfig {
log_file_path: "zenyx.log".into(), log_file_path: "zenyx.log".into(),
log_to_stdout: true, log_to_stdout: true,
stdout_color: true, stdout_color: true,
log_to_buffer: true, stdout_include_time: false,
file_include_time: false,
} }
} }
} }
#[derive(Debug, Clone,Copy, PartialEq,Eq,PartialOrd)]
pub enum LogQuery {
All,
From(SystemTime),
}
#[derive(Debug, Clone)]
pub struct Logger { pub struct Logger {
config: LoggerConfig, config: LoggerConfig,
log_buffer: Arc<RwLock<Vec<u8>>>, log_entries: Arc<RwLock<Vec<LogEntry>>>,
} _sender: Sender<LogEntry>,
enum Log {
All,
From(std::time::SystemTime),
} }
impl Logger { impl Logger {
pub fn new(config: LoggerConfig) -> Self { pub fn new(config: LoggerConfig) -> Self {
let (sender, receiver) = unbounded();
let log_entries = Arc::new(RwLock::new(Vec::new()));
if config.log_to_stdout {
let stdout_receiver = receiver.clone();
let stdout_color = config.stdout_color;
let stdout_include_time = config.stdout_include_time;
std::thread::spawn(move || {
for entry in stdout_receiver {
let line = format_entry(&entry, stdout_color, stdout_include_time);
println!("{}", line);
}
});
}
if config.log_to_file {
let file_receiver = receiver.clone();
let log_file_path = config.log_file_path.clone();
let file_include_time = config.file_include_time;
std::thread::spawn(move || {
let file = OpenOptions::new()
.append(true)
.create(true)
.open(&log_file_path)
.unwrap_or_else(|_| {
OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&log_file_path)
.expect("Failed to create log file")
});
let mut writer = BufWriter::new(file);
for entry in file_receiver {
let line = format_entry(&entry, false, file_include_time);
writer
.write_all(format!("{}\n", line).as_bytes())
.expect("Failed to write to log file");
writer.flush().expect("Failed to flush log file");
}
});
}
let buffer_layer = BufferLayer {
log_entries: log_entries.clone(),
sender: sender.clone(),
};
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
format!("{}={}", env!("CARGO_CRATE_NAME"), config.log_level).into()
});
let subscriber = tracing_subscriber::registry()
.with(env_filter)
.with(buffer_layer);
subscriber.init();
Self { Self {
config, config,
log_buffer: Arc::new(RwLock::new(Vec::new())), log_entries,
_sender: sender,
} }
} }
pub fn init(&self) { pub fn get_logs(&self, log_query: LogQuery) -> Vec<LogEntry> {
let env_filter = let guard = self.log_entries.read().unwrap();
tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| { match log_query {
format!("{}={}", env!("CARGO_CRATE_NAME"), self.config.log_level).into() LogQuery::All => guard.clone(),
}); LogQuery::From(time) => guard
.iter()
let mut layers = Vec::new(); .filter(|e| e.timestamp >= time)
.cloned()
if self.config.log_to_stdout { .collect(),
let fmt_layer_stdout = tracing_subscriber::fmt::layer()
.with_level(true)
.compact()
.with_ansi(self.config.stdout_color)
.log_internal_errors(false)
.without_time()
.with_thread_names(true)
.with_span_events(FmtSpan::CLOSE)
.with_writer(std::io::stdout)
.boxed();
layers.push(fmt_layer_stdout);
}
if self.config.log_to_file {
let log_file_path = self.config.log_file_path.clone();
let fmt_layer_file = tracing_subscriber::fmt::layer()
.with_level(true)
.compact()
.with_ansi(false)
.log_internal_errors(false)
.without_time()
.with_thread_names(true)
.with_span_events(FmtSpan::CLOSE)
.with_writer(move || {
let file = OpenOptions::new()
.append(true)
.open(&log_file_path)
.unwrap_or_else(|_| {
OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&log_file_path)
.expect("Failed to create log file")
});
BufWriter::new(file)
})
.boxed();
layers.push(fmt_layer_file);
}
// let (tx,rx) = mpsc::channel();
// std::thread::spawn(move || {
// for log in rx.iter() {
// println!("{log:#?}")
// }
// });
if self.config.log_to_buffer {
let log_buffer = Arc::clone(&self.log_buffer);
let fmt_layer_buffer = tracing_subscriber::fmt::layer()
.with_level(true)
.compact()
.with_ansi(false)
.log_internal_errors(false)
.without_time()
.with_thread_names(true)
.with_span_events(FmtSpan::CLOSE)
.with_writer(move || BufferWriter {
buffer: log_buffer.clone(),
})
.boxed();
layers.push(fmt_layer_buffer);
}
if !layers.is_empty() {
let subscriber = Registry::default().with(env_filter).with(layers);
subscriber.init();
} }
} }
}
pub fn get_logs(&self) -> Option<String> { fn format_entry(entry: &LogEntry, use_color: bool, include_time: bool) -> String {
if !self.config.log_to_buffer { let timestamp = if include_time {
return None; format!(
"[{}] ",
humantime::format_rfc3339_seconds(entry.timestamp)
.to_string()
.trim_end_matches('Z')
.replace('T', " ")
)
} else {
String::new()
};
let level = if use_color {
match entry.level {
Level::ERROR => format!("\x1b[31m{}\x1b[0m", entry.level),
Level::WARN => format!("\x1b[33m{}\x1b[0m", entry.level),
Level::INFO => format!("\x1b[32m{}\x1b[0m", entry.level),
Level::DEBUG => format!("\x1b[36m{}\x1b[0m", entry.level),
Level::TRACE => format!("\x1b[34m{}\x1b[0m", entry.level),
} }
self.log_buffer } else {
.read() entry.level.to_string()
.ok() };
.map(|guard| String::from_utf8_lossy(&guard).into_owned())
} format!("{}{} {}", timestamp, level, entry.message)
} }
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
@ -221,25 +286,32 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.colored_stdout(true) .colored_stdout(true)
.level(LogLevel::Debug) .level(LogLevel::Debug)
.log_to_file(true) .log_to_file(true)
.log_path(format!("{}.log",env!("CARGO_PKG_NAME"))); .stdout_include_time(false)
let logger = Logger::new(logger_config); .file_include_time(true)
logger.init(); .log_path(format!("{}.log", env!("CARGO_PKG_NAME")));
let event_loop = EventLoop::new()?; let logger = Logger::new(logger_config);
info!("Application initialized");
let app = App::default(); let app = App::default();
tracing::info!("Application initialized");
tracing::error!("Another log message with value: {}", 42);
info!("This is a test log message"); let logs = logger.get_logs(LogQuery::All);
error!("Another log message with value: {}", 42); println!("\n--- All Logs ---");
for entry in logs {
if let Some(logs) = logger.get_logs() { println!("{}", format_entry(&entry, false, true));
println!("\n--- Logs from Buffer ---");
println!("{}", logs);
println!("------------------------");
} else {
eprintln!("Failed to read logs from buffer or buffer logging is disabled");
} }
let now = SystemTime::now();
tracing::warn!("This is a warning after the initial logs");
let new_logs = logger.get_logs(LogQuery::From(now));
println!("\n--- Logs Since Now ---");
for entry in new_logs {
println!("{}", format_entry(&entry, false, true));
}
info!("Application finished");
// logger.get_logs();
Ok(()) Ok(())
} }