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",
]
[[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]]
name = "crossbeam-queue"
version = "0.3.12"
@ -438,6 +479,12 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
[[package]]
name = "humantime"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f"
[[package]]
name = "indexmap"
version = "2.9.0"
@ -2155,6 +2202,8 @@ checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda"
name = "zenyx"
version = "0.1.0"
dependencies = [
"crossbeam",
"humantime",
"terminator",
"thiserror 2.0.12",
"tracing",

View file

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

View file

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

View file

@ -1,39 +1,85 @@
use crossbeam::channel::{Sender, unbounded};
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)]
struct App;
struct BufferWriter {
buffer: Arc<RwLock<Vec<u8>>>,
#[derive(Debug, Clone)]
pub struct LogEntry {
timestamp: SystemTime,
level: Level,
message: String,
}
impl Write for BufferWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
if let Ok(mut guard) = self.buffer.write() {
guard.extend_from_slice(buf);
Ok(buf.len())
struct BufferLayer {
log_entries: Arc<RwLock<Vec<LogEntry>>>,
sender: Sender<LogEntry>,
}
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 {
Err(io::Error::new(
io::ErrorKind::Other,
"Failed to acquire write lock on log buffer",
))
write!(self.message, "{}={:?} ", field.name(), value).unwrap();
}
}
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
enum LogLevel {
pub enum LogLevel {
Error,
#[default]
Info,
@ -41,9 +87,9 @@ enum LogLevel {
Trace,
}
impl std::fmt::Display for LogLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f,"{self:?}")
impl fmt::Display for LogLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
@ -61,14 +107,15 @@ impl FromStr for LogLevel {
}
}
#[derive(Debug)]
#[derive(Debug, Clone,PartialEq,Eq,PartialOrd)]
pub struct LoggerConfig {
pub log_level: LogLevel,
pub log_to_file: bool,
pub log_file_path: PathBuf,
pub log_to_stdout: bool,
pub stdout_color: bool,
pub log_to_buffer: bool,
pub stdout_include_time: bool,
pub file_include_time: bool,
}
impl LoggerConfig {
@ -92,6 +139,14 @@ impl LoggerConfig {
self.log_file_path = path.as_ref().to_path_buf();
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 {
@ -102,64 +157,48 @@ impl Default for LoggerConfig {
log_file_path: "zenyx.log".into(),
log_to_stdout: 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 {
config: LoggerConfig,
log_buffer: Arc<RwLock<Vec<u8>>>,
}
enum Log {
All,
From(std::time::SystemTime),
log_entries: Arc<RwLock<Vec<LogEntry>>>,
_sender: Sender<LogEntry>,
}
impl Logger {
pub fn new(config: LoggerConfig) -> Self {
Self {
config,
log_buffer: Arc::new(RwLock::new(Vec::new())),
}
}
let (sender, receiver) = unbounded();
let log_entries = Arc::new(RwLock::new(Vec::new()));
pub fn init(&self) {
let env_filter =
tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| {
format!("{}={}", env!("CARGO_CRATE_NAME"), self.config.log_level).into()
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);
}
});
let mut layers = Vec::new();
if self.config.log_to_stdout {
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 || {
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()
@ -169,77 +208,110 @@ impl Logger {
.open(&log_file_path)
.expect("Failed to create log file")
});
BufWriter::new(file)
})
.boxed();
layers.push(fmt_layer_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 (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);
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 {
config,
log_entries,
_sender: sender,
}
}
pub fn get_logs(&self) -> Option<String> {
if !self.config.log_to_buffer {
return None;
pub fn get_logs(&self, log_query: LogQuery) -> Vec<LogEntry> {
let guard = self.log_entries.read().unwrap();
match log_query {
LogQuery::All => guard.clone(),
LogQuery::From(time) => guard
.iter()
.filter(|e| e.timestamp >= time)
.cloned()
.collect(),
}
self.log_buffer
.read()
.ok()
.map(|guard| String::from_utf8_lossy(&guard).into_owned())
}
}
fn format_entry(entry: &LogEntry, use_color: bool, include_time: bool) -> String {
let timestamp = if include_time {
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),
}
} else {
entry.level.to_string()
};
format!("{}{} {}", timestamp, level, entry.message)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let logger_config = LoggerConfig::default()
.colored_stdout(true)
.level(LogLevel::Debug)
.log_to_file(true)
.stdout_include_time(false)
.file_include_time(true)
.log_path(format!("{}.log", env!("CARGO_PKG_NAME")));
let logger = Logger::new(logger_config);
logger.init();
let event_loop = EventLoop::new()?;
info!("Application initialized");
let logger = Logger::new(logger_config);
let app = App::default();
tracing::info!("Application initialized");
tracing::error!("Another log message with value: {}", 42);
info!("This is a test log message");
error!("Another log message with value: {}", 42);
if let Some(logs) = logger.get_logs() {
println!("\n--- Logs from Buffer ---");
println!("{}", logs);
println!("------------------------");
} else {
eprintln!("Failed to read logs from buffer or buffer logging is disabled");
let logs = logger.get_logs(LogQuery::All);
println!("\n--- All Logs ---");
for entry in logs {
println!("{}", format_entry(&entry, false, true));
}
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(())
}