Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
jdx committed Dec 6, 2024
1 parent 87c7c16 commit 3c219fc
Show file tree
Hide file tree
Showing 11 changed files with 329 additions and 48 deletions.
50 changes: 23 additions & 27 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ dirs = "5.0.1"
once_cell = "1.20.2"
xx = {version = "2", features = ["fslock"]}
log = "0.4.22"
env_logger = "0.11.5"
psutil = "3.3.0"
eyre = "0.6.12"
interprocess = { version = "2.2.2", features = ["tokio"] }
tokio = { version = "1.42.0", features = ["full"] }
sysinfo = "0.33.0"
serde = { version = "1.0.215", features = ["derive"] }
toml = "0.8.19"
console = "0.15.8"
22 changes: 22 additions & 0 deletions src/cli/daemon/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use crate::Result;

mod run;

#[derive(Debug, clap::Args)]
pub struct Daemon {
#[clap(subcommand)]
command: Commands,
}

#[derive(Debug, clap::Subcommand)]
enum Commands {
Run(run::Run),
}

impl Daemon {
pub async fn run(self) -> Result<()> {
match self.command {
Commands::Run(run) => run.run().await,
}
}
}
10 changes: 4 additions & 6 deletions src/cli/daemon.rs → src/cli/daemon/run.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
use std::time::Duration;
use log::{info, warn};
use sysinfo::Pid;
use crate::{env, procs};
use crate::Result;
use tokio::time;
use crate::pid_file::PidFile;

#[derive(Debug, clap::Args)]
#[clap(hide = false)]
pub struct Daemon {
pub struct Run {
#[clap(short, long)]
force: bool,
}

impl Daemon {
impl Run {
pub async fn run(&self) -> Result<()> {
let mut pid_file = PidFile::read(&*env::PITCHFORK_PID_FILE)?;
if let Some(existing_pid) = pid_file.pids.get("pitchfork") {
if let Some(existing_pid) = pid_file.get("pitchfork") {
if self.kill_or_stop(*existing_pid)? == false {
return Ok(());
}
}
let pid = std::process::id();
pid_file.pids.insert("pitchfork".to_string(), pid);
pid_file.set("pitchfork".to_string(), pid);
pid_file.write(&*env::PITCHFORK_PID_FILE)?;

let mut interval = time::interval(Duration::from_millis(1000));
Expand Down
10 changes: 5 additions & 5 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
use clap::Parser;
use crate::Result;

mod daemon;
mod start;
mod daemon;

#[derive(Debug, clap::Parser)]
struct Cli {
#[clap(subcommand)]
command: Command,
command: Commands,
}

#[derive(Debug, clap::Subcommand)]
enum Command {
enum Commands {
Daemon(daemon::Daemon),
Start(start::Start),
}
Expand All @@ -20,7 +20,7 @@ enum Command {
pub async fn run() -> Result<()> {
let args = Cli::parse();
match args.command {
Command::Daemon(daemon) => daemon.run().await,
Command::Start(start) => start.run().await,
Commands::Daemon(daemon) => daemon.run().await,
Commands::Start(start) => start.run().await,
}
}
35 changes: 30 additions & 5 deletions src/env.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,37 @@
pub use std::env;
use once_cell::sync::Lazy;
pub use std::env;
use std::path::PathBuf;
use log::trace;

pub static HOME_DIR: Lazy<PathBuf> = Lazy::new(|| dirs::home_dir().unwrap_or(PathBuf::new()));
pub static PITCHFORK_STATE_DIR: Lazy<PathBuf> = Lazy::new(|| {
dirs::state_dir()
.unwrap_or(HOME_DIR.join(".local").join("state"))
.join("pitchfork")
var_path("PITCHFORK_STATE_DIR").unwrap_or(
dirs::state_dir()
.unwrap_or(HOME_DIR.join(".local").join("state"))
.join("pitchfork"),
)
});
pub static PITCHFORK_PID_FILE: Lazy<PathBuf> = Lazy::new(|| PITCHFORK_STATE_DIR.join("pids.toml"));
pub static PITCHFORK_LOG: Lazy<log::LevelFilter> = Lazy::new(|| {
env::var("PITCHFORK_LOG")
.ok()
.and_then(|level| level.parse().ok())
.unwrap_or(log::LevelFilter::Info)
});
pub static PITCHFORK_LOG_FILE_LEVEL: Lazy<log::LevelFilter> = Lazy::new(|| {
env::var("PITCHFORK_LOG_FILE_LEVEL")
.ok()
.and_then(|level| level.parse().ok())
.unwrap_or(*PITCHFORK_LOG)
});
pub static PITCHFORK_LOG_FILE: Lazy<PathBuf> = Lazy::new(|| {
var_path("PITCHFORK_LOG_FILE").unwrap_or(
PITCHFORK_STATE_DIR
.join("logs")
.join("pitchfork")
.join("pitchfork.log"),
)
});

fn var_path(name: &str) -> Option<PathBuf> {
env::var(name).map(|path| PathBuf::from(path)).ok()
}
140 changes: 140 additions & 0 deletions src/logger.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
use eyre::Result;
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::path::Path;
use std::sync::Mutex;
use std::thread;

use crate::{env, ui};
use log::{warn, Level, LevelFilter, Metadata, Record};
use once_cell::sync::Lazy;

#[derive(Debug)]
struct Logger {
level: LevelFilter,
term_level: LevelFilter,
file_level: LevelFilter,
log_file: Option<Mutex<File>>,
}

impl log::Log for Logger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= self.level
}

fn log(&self, record: &Record) {
if record.level() <= self.file_level {
if let Some(log_file) = &self.log_file {
let mut log_file = log_file.lock().unwrap();
let out = self.render(record, self.file_level);
let _ = writeln!(log_file, "{}", console::strip_ansi_codes(&out));
}
}
if record.level() <= self.term_level {
let out = self.render(record, self.term_level);
eprintln!("{}", out);
}
}

fn flush(&self) {}
}

static LOGGER: Lazy<Logger> = Lazy::new(Logger::init);

impl Logger {
fn init() -> Self {
let term_level = *env::PITCHFORK_LOG;
let file_level = *env::PITCHFORK_LOG_FILE_LEVEL;

let mut logger = Logger {
level: std::cmp::max(term_level, file_level),
file_level,
term_level,
log_file: None,
};

let log_file = &*env::PITCHFORK_LOG_FILE;
if let Ok(log_file) = init_log_file(log_file) {
logger.log_file = Some(Mutex::new(log_file));
} else {
warn!("could not open log file: {log_file:?}");
}

logger
}

fn render(&self, record: &Record, level: LevelFilter) -> String {
match level {
LevelFilter::Off => "".to_string(),
LevelFilter::Trace => {
let meta = ui::style::edim(format!(
"{thread_id:>2} [{file}:{line}]",
thread_id = thread_id(),
file = record.file().unwrap_or("<unknown>"),
line = record.line().unwrap_or(0),
));
format!(
"{level} {meta} {args}",
level = self.styled_level(record.level()),
args = record.args()
)
}
LevelFilter::Debug => format!(
"{level} {args}",
level = self.styled_level(record.level()),
args = record.args()
),
_ => {
let mise = match record.level() {
Level::Error => ui::style::ered("mise"),
Level::Warn => ui::style::eyellow("mise"),
_ => ui::style::edim("mise"),
};
match record.level() {
Level::Info => format!("{mise} {args}", args = record.args()),
_ => format!(
"{mise} {level} {args}",
level = self.styled_level(record.level()),
args = record.args()
),
}
}
}
}

fn styled_level(&self, level: Level) -> String {
let level = match level {
Level::Error => ui::style::ered("ERROR").to_string(),
Level::Warn => ui::style::eyellow("WARN").to_string(),
Level::Info => ui::style::ecyan("INFO").to_string(),
Level::Debug => ui::style::emagenta("DEBUG").to_string(),
Level::Trace => ui::style::edim("TRACE").to_string(),
};
console::pad_str(&level, 5, console::Alignment::Left, None).to_string()
}
}

pub fn thread_id() -> String {
let id = format!("{:?}", thread::current().id());
let id = id.replace("ThreadId(", "");
id.replace(")", "")
}

pub fn init() {
static INIT: std::sync::Once = std::sync::Once::new();
INIT.call_once(|| {
if let Err(err) = log::set_logger(&*LOGGER).map(|()| log::set_max_level(LOGGER.level)) {
eprintln!("mise: could not initialize logger: {err}");
}
});
}

fn init_log_file(log_file: &Path) -> Result<File> {
if let Some(log_dir) = log_file.parent() {
xx::file::mkdirp(log_dir)?;
}
Ok(OpenOptions::new()
.create(true)
.append(true)
.open(log_file)?)
}
5 changes: 3 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ mod cli;
mod env;
mod pid_file;
mod procs;
mod logger;
mod ui;

pub use eyre::Result;

fn main() -> Result<()> {
let env = env_logger::Env::new().filter("PITCHFORK_LOG").write_style("PITCHFORK_LOG_STYLE");
env_logger::init_from_env(env);
logger::init();
cli::run()
}
Loading

0 comments on commit 3c219fc

Please sign in to comment.