feat: client-server mode

This commit is contained in:
electria 2026-08-16 12:15:46 -07:00
commit a689366324
Signed by: electria
SSH key fingerprint: SHA256:8LlB3ucPbBHqozqkhsNbaV5oG3SlzzqUj8FZDL6IPQs
7 changed files with 119 additions and 28 deletions

25
Cargo.lock generated
View file

@ -292,6 +292,9 @@ name = "bitflags"
version = "2.13.1" version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
dependencies = [
"serde_core",
]
[[package]] [[package]]
name = "block" name = "block"
@ -1380,6 +1383,8 @@ name = "itop"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"iced", "iced",
"ron",
"serde",
"smol", "smol",
"sysinfo", "sysinfo",
] ]
@ -2482,6 +2487,20 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
[[package]]
name = "ron"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81116b9531d61eabc41aeb228e4b6b2435bcca3233b98cf3b3077d4e6e9debb3"
dependencies = [
"bitflags 2.13.1",
"once_cell",
"serde",
"serde_derive",
"typeid",
"unicode-ident",
]
[[package]] [[package]]
name = "roxmltree" name = "roxmltree"
version = "0.20.0" version = "0.20.0"
@ -3091,6 +3110,12 @@ dependencies = [
"core_maths", "core_maths",
] ]
[[package]]
name = "typeid"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
[[package]] [[package]]
name = "uds_windows" name = "uds_windows"
version = "1.2.1" version = "1.2.1"

View file

@ -6,6 +6,8 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
ron = "0.12.2"
serde = "1.0.229"
smol = "2.0.2" smol = "2.0.2"
sysinfo = "0.39.6" sysinfo = "0.39.6"

View file

@ -1,28 +1,63 @@
use std::env;
use iced::{ use iced::{
Task,
futures::{self, SinkExt}, futures::{self, SinkExt},
stream, stream,
}; };
use smol::Timer; use ron::de;
use smol::{Timer, unblock};
use crate::sysinfo_snapshot::SysinfoSnapshot; use crate::{app, sysinfo_snapshot::SysinfoSnapshot};
pub fn create() -> impl futures::Stream<Item = SysinfoSnapshot> { pub fn create() -> Task<app::Message> {
stream::channel( if env::args()
1, .nth(1)
async |mut sender: futures::channel::mpsc::Sender<SysinfoSnapshot>| { .is_some_and(|command| command == "client")
let mut sys = sysinfo::System::new(); {
let refreshes = sysinfo::RefreshKind::nothing() Task::run(
.with_memory(sysinfo::MemoryRefreshKind::everything()) stream::channel(
.with_cpu(sysinfo::CpuRefreshKind::nothing().with_cpu_usage()); 1,
async |mut sender: futures::channel::mpsc::Sender<SysinfoSnapshot>| {
let mut buf = String::new();
loop {
buf = unblock(move || {
std::io::stdin().read_line(&mut buf).unwrap();
buf
})
.await;
sys.refresh_specifics(refreshes); match de::from_str(&buf) {
Timer::after(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL).await; Ok(info) => sender.send(info).await.unwrap(),
Err(e) => eprintln!("{e}"),
}
buf.clear();
}
},
),
app::Message::Info,
)
} else {
Task::run(
stream::channel(
1,
async |mut sender: futures::channel::mpsc::Sender<SysinfoSnapshot>| {
let mut sys = sysinfo::System::new();
let refreshes = sysinfo::RefreshKind::nothing()
.with_memory(sysinfo::MemoryRefreshKind::everything())
.with_cpu(sysinfo::CpuRefreshKind::nothing().with_cpu_usage());
loop { sys.refresh_specifics(refreshes);
sys.refresh_specifics(refreshes); Timer::after(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL).await;
sender.send(SysinfoSnapshot::from(&sys)).await.unwrap();
Timer::after(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL).await; loop {
} sys.refresh_specifics(refreshes);
}, sender.send(SysinfoSnapshot::from(&sys)).await.unwrap();
) Timer::after(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL).await;
}
},
),
app::Message::Info,
)
}
} }

View file

@ -1,6 +1,6 @@
use std::time::Duration; use std::time::Duration;
use iced::{Color, Element, Length, Renderer, Task, Theme, application, theme, widget}; use iced::{Color, Element, Error, Length, Renderer, Task, Theme, application, theme, widget};
use crate::{app::graph::Graph, sysinfo_snapshot::SysinfoSnapshot}; use crate::{app::graph::Graph, sysinfo_snapshot::SysinfoSnapshot};
@ -23,10 +23,7 @@ struct State {
impl State { impl State {
fn new() -> (Self, Task<Message>) { fn new() -> (Self, Task<Message>) {
( (Self::default(), info_stream::create())
Self::default(),
Task::run(info_stream::create(), Message::Info),
)
} }
fn update(&mut self, message: Message) -> Task<Message> { fn update(&mut self, message: Message) -> Task<Message> {
match message { match message {
@ -95,7 +92,7 @@ impl State {
} }
} }
pub fn run() -> Result<(), impl std::error::Error> { pub fn run() -> Result<(), Error> {
application(State::new, State::update, State::view) application(State::new, State::update, State::view)
.title(State::title) .title(State::title)
.theme(theme::Theme::custom( .theme(theme::Theme::custom(

View file

@ -1,6 +1,15 @@
use std::env;
mod app; mod app;
mod server;
mod sysinfo_snapshot; mod sysinfo_snapshot;
fn main() -> Result<(), impl std::error::Error> { fn main() {
app::run() match env::args().nth(1).as_deref() {
Some("server") => server::run().unwrap(),
None | Some("client") => app::run().unwrap(),
Some(s) => panic!("invalid argument: {s}"),
}
} }

23
src/server/mod.rs Normal file
View file

@ -0,0 +1,23 @@
use std::{io::Write, thread};
use ron::{Error, ser};
use crate::sysinfo_snapshot::SysinfoSnapshot;
pub fn run() -> Result<(), Error> {
let mut sys = sysinfo::System::new();
let refreshes = sysinfo::RefreshKind::nothing()
.with_memory(sysinfo::MemoryRefreshKind::everything())
.with_cpu(sysinfo::CpuRefreshKind::nothing().with_cpu_usage());
sys.refresh_specifics(refreshes);
thread::sleep(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL);
let mut stdout = std::io::stdout();
loop {
sys.refresh_specifics(refreshes);
stdout.write_all((ser::to_string(&SysinfoSnapshot::from(&sys))? + "\n").as_bytes())?;
thread::sleep(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL);
}
}

View file

@ -1,6 +1,6 @@
use std::time::Duration; use std::time::Duration;
#[derive(Clone, Debug)] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct SysinfoSnapshot { pub struct SysinfoSnapshot {
uptime: u64, uptime: u64,