tuxtrain 0.0.4

An easy-to-use generic trainer for Linux
Documentation
pub mod daemon;

use crate::display;
use crate::error::TrainerError;
use crate::Hack;
use daemon::Daemon;
use procfs::{process, process::Process};
use serde::Deserialize;
use std::error::Error;
use std::path::{Path, PathBuf};

#[derive(Deserialize)]
pub struct Trainer {
	pub name: String,
	pub version: String,
	pub process: String,
	#[serde(rename = "feature")]
	pub features: Vec<Hack>,
	pub daemon: Option<Daemon>,
	pub enable: bool,
}

impl Trainer {
	pub fn run(&self, full: bool, display: bool) {
		display.then(|| display::trainer(&self));

		let proc = self.probe();
		if let Ok(proc) = proc {
			for feature in &self.features {
				if feature.enable {
					display.then(|| display::feature(feature));
					let res = feature.run(&proc, full);
					display.then(|| display::feature_result(&feature, &res, full));
				}
			}
		} else if let Err(e) = proc {
			display.then(|| display::trainer_err(&e));
		}
	}

	pub fn probe(&self) -> Result<Process, TrainerError> {
		let procs = process::all_processes()?;
		for proc in procs {
			if proc.stat.comm == self.process {
				return Ok(proc);
			}
		}

		Err(TrainerError::Process(format!(
			"Could not find process by '{}'.",
			self.process
		)))?
	}

	pub fn features(&self) -> &Vec<Hack> {
		return &self.features;
	}
}

impl TryFrom<PathBuf> for Trainer {
	type Error = Box<dyn Error>;

	fn try_from(path: PathBuf) -> Result<Self, Self::Error> {
		let trainer_str = std::fs::read_to_string(path)?;
		let trainer: Trainer = toml::from_str(&trainer_str)?;
		Ok(trainer)
	}
}

pub fn from_dir(dir: &Path) -> Result<Vec<Trainer>, Box<dyn Error>> {
	let mut trainers = vec![];

	let files = dir
		.read_dir()?
		.filter_map(|r| r.ok())
		.map(|e| e.path())
		.filter(|p| p.is_file());

	for file in files {
		trainers.push(file.clone().try_into()?);
	}

	Ok(trainers)
}

pub fn from_args(
	dir: &Path,
	trainer: Option<String>,
	trainer_file: Option<PathBuf>,
) -> Result<Vec<Trainer>, Box<dyn Error>> {
	Ok(if let Some(trainer) = trainer {
		vec![dir.join(format!("{}.toml", trainer)).try_into()?]
	} else if let Some(file) = trainer_file {
		vec![file.try_into()?]
	} else {
		from_dir(dir)?
	})
}