separate app struct
This commit is contained in:
parent
21418ce45d
commit
c69798ffd6
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
/target
|
/target
|
||||||
|
.vscode/
|
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -110,6 +110,12 @@ dependencies = [
|
|||||||
"syn 2.0.15",
|
"syn 2.0.15",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
@ -150,6 +156,15 @@ dependencies = [
|
|||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.61"
|
version = "0.3.61"
|
||||||
@ -164,6 +179,7 @@ name = "laika"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"itertools",
|
||||||
"serde",
|
"serde",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
"toml",
|
"toml",
|
||||||
|
@ -7,6 +7,7 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.24"
|
chrono = "0.4.24"
|
||||||
|
itertools = "0.10.5"
|
||||||
serde = { version = "1.0.160", features = ["derive"] }
|
serde = { version = "1.0.160", features = ["derive"] }
|
||||||
sysinfo = { version = "0.28.4", default-features = false }
|
sysinfo = { version = "0.28.4", default-features = false }
|
||||||
toml = "0.7.3"
|
toml = "0.7.3"
|
||||||
|
@ -2,6 +2,9 @@ command = "calc.exe"
|
|||||||
target = "CalculatorApp.exe"
|
target = "CalculatorApp.exe"
|
||||||
workdir = "."
|
workdir = "."
|
||||||
delay = 2
|
delay = 2
|
||||||
start = { hour = 11, minute = 50 }
|
#start = { hour = 11, minute = 50 }
|
||||||
end = { hour = 12, minute = 55 }
|
start = "11:50"
|
||||||
|
#end = { hour = 12, minute = 55 }
|
||||||
|
end = "12:55"
|
||||||
|
|
||||||
|
|
||||||
|
113
src/main.rs
113
src/main.rs
@ -2,16 +2,37 @@
|
|||||||
|
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use chrono::{DateTime, Local, TimeZone};
|
use chrono::{DateTime, Local, TimeZone};
|
||||||
|
use itertools::Itertools;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::str::FromStr;
|
||||||
use std::{thread, time::Duration};
|
use std::{thread, time::Duration};
|
||||||
use sysinfo::{System, SystemExt};
|
use sysinfo::{System, SystemExt};
|
||||||
|
|
||||||
use std::{env, fs, path::PathBuf, process::Command};
|
use std::{env, fs, path::PathBuf, process::Command};
|
||||||
|
|
||||||
|
trait ErrorToString {
|
||||||
|
type Output;
|
||||||
|
fn str_err(self) -> std::result::Result<Self::Output, String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E> ErrorToString for std::result::Result<T, E>
|
||||||
|
where
|
||||||
|
E: std::error::Error,
|
||||||
|
{
|
||||||
|
type Output = T;
|
||||||
|
fn str_err(self) -> std::result::Result<Self::Output, String> {
|
||||||
|
self.map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Time with hours (24) and minutes (60)
|
||||||
#[derive(Deserialize, Default, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Debug)]
|
#[derive(Deserialize, Default, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Debug)]
|
||||||
|
#[serde(try_from = "String")]
|
||||||
struct TimeHM {
|
struct TimeHM {
|
||||||
|
/// hour num (0..23)
|
||||||
hour: u8,
|
hour: u8,
|
||||||
|
/// minute num (0..59)
|
||||||
minute: u8,
|
minute: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,17 +45,60 @@ impl<Tz: TimeZone> From<DateTime<Tz>> for TimeHM {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
impl FromStr for TimeHM {
|
||||||
|
type Err = String;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
if let Some((str_hour, str_minute)) = s.split(':').collect_tuple() {
|
||||||
|
let hour: u8 = str_hour.parse().str_err()?;
|
||||||
|
let minute: u8 = str_minute.parse().str_err()?;
|
||||||
|
Ok(TimeHM { hour, minute })
|
||||||
|
} else {
|
||||||
|
Err("invalid time, must be hh:mm".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<String> for TimeHM {
|
||||||
|
type Error = String;
|
||||||
|
|
||||||
|
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||||
|
value.parse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Application config
|
||||||
|
#[derive(Deserialize, Default, Debug)]
|
||||||
struct Config {
|
struct Config {
|
||||||
|
/// is application active
|
||||||
|
#[serde(default = "default_true")]
|
||||||
|
active: bool,
|
||||||
|
/// need to exit
|
||||||
|
#[serde(default)]
|
||||||
|
exit_now: bool,
|
||||||
|
/// target command
|
||||||
command: String,
|
command: String,
|
||||||
|
/// target process name
|
||||||
|
#[serde(default)]
|
||||||
target: String,
|
target: String,
|
||||||
|
/// workdir for launch target command
|
||||||
|
#[serde(default)]
|
||||||
workdir: String,
|
workdir: String,
|
||||||
|
/// args for launch target command
|
||||||
|
#[serde(default)]
|
||||||
args: Option<Vec<String>>,
|
args: Option<Vec<String>>,
|
||||||
|
/// start time
|
||||||
start: TimeHM,
|
start: TimeHM,
|
||||||
|
/// end time
|
||||||
end: TimeHM,
|
end: TimeHM,
|
||||||
|
/// check delay (seconds)
|
||||||
delay: u32,
|
delay: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_true() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
fn read<P: AsRef<Path>>(path: P) -> Result<Self, String> {
|
fn read<P: AsRef<Path>>(path: P) -> Result<Self, String> {
|
||||||
let data = fs::read_to_string(path).map_err(|e| format!("can't read config: {:?}", e))?;
|
let data = fs::read_to_string(path).map_err(|e| format!("can't read config: {:?}", e))?;
|
||||||
@ -75,39 +139,58 @@ impl Config {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn is_valid(&self) -> bool {
|
||||||
|
(!self.command.is_empty()) && self.delay > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Laika {
|
||||||
|
config: Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Config> for Laika {
|
||||||
|
fn from(config: Config) -> Self {
|
||||||
|
Laika { config }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Laika {
|
||||||
fn is_in_time(&self, value: TimeHM) -> bool {
|
fn is_in_time(&self, value: TimeHM) -> bool {
|
||||||
self.start <= value && value <= self.end
|
self.config.start <= value && value <= self.config.end
|
||||||
}
|
}
|
||||||
fn is_active(&self) -> bool {
|
fn is_active(&self) -> bool {
|
||||||
let now: DateTime<Local> = Local::now();
|
let now: DateTime<Local> = Local::now();
|
||||||
self.is_in_time(now.into())
|
self.config.active && self.config.is_valid() && self.is_in_time(now.into())
|
||||||
}
|
}
|
||||||
fn sleep(&self) {
|
fn sleep(&self) {
|
||||||
thread::sleep(Duration::from_secs(self.delay.into()));
|
thread::sleep(Duration::from_secs(self.config.delay.into()));
|
||||||
}
|
}
|
||||||
fn is_target_alive(&self) -> bool {
|
fn is_target_alive(&self) -> bool {
|
||||||
let name = self.target_name();
|
let name = self.config.target_name();
|
||||||
|
if name.is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
let sp = System::new_all();
|
let sp = System::new_all();
|
||||||
let procs = sp.processes_by_exact_name(&name);
|
let procs = sp.processes_by_exact_name(&name);
|
||||||
procs.count() > 0
|
procs.count() > 0
|
||||||
}
|
}
|
||||||
fn relaunch_target(&self) {
|
fn relaunch_target(&self) {
|
||||||
let mut cmd = Command::new(&self.command);
|
let mut cmd = Command::new(&self.config.command);
|
||||||
cmd.current_dir(&self.workdir);
|
cmd.current_dir(&self.config.workdir);
|
||||||
if let Some(args) = &self.args {
|
if let Some(args) = &self.config.args {
|
||||||
cmd.args(args);
|
cmd.args(args);
|
||||||
}
|
}
|
||||||
let _res = cmd.spawn();
|
let _res = cmd.spawn();
|
||||||
}
|
}
|
||||||
fn is_valid(&self) -> bool {
|
|
||||||
(!self.command.is_empty()) && self.delay > 0
|
|
||||||
}
|
|
||||||
fn main_loop(self) {
|
fn main_loop(self) {
|
||||||
if !self.is_valid() {
|
if !self.config.is_valid() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
if self.is_active() && !self.is_target_alive() {
|
if self.config.exit_now {
|
||||||
|
return;
|
||||||
|
} else if self.is_active() && !self.is_target_alive() {
|
||||||
self.relaunch_target();
|
self.relaunch_target();
|
||||||
}
|
}
|
||||||
self.sleep();
|
self.sleep();
|
||||||
@ -116,5 +199,7 @@ impl Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
Config::get().main_loop()
|
let config = Config::get();
|
||||||
|
let laika: Laika = config.into();
|
||||||
|
laika.main_loop();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user