rwho: render --json response as dict

This commit is contained in:
2026-07-21 18:06:00 +09:00
parent 6ee0723d07
commit f1f8b8d0d1
2 changed files with 47 additions and 26 deletions
+37 -16
View File
@@ -1,3 +1,5 @@
use std::collections::BTreeMap;
use anyhow::Context; use anyhow::Context;
use clap::{CommandFactory, Parser}; use clap::{CommandFactory, Parser};
use clap_complete::{Shell, generate}; use clap_complete::{Shell, generate};
@@ -63,7 +65,7 @@ async fn main() -> anyhow::Result<()> {
.await .await
.expect("Failed to connect to rwhod server"); .expect("Failed to connect to rwhod server");
let mut reply = conn let reply = conn
.rwho(max_idle_seconds) .rwho(max_idle_seconds)
.await .await
.context("Failed to send rwho request")? .context("Failed to send rwho request")?
@@ -79,23 +81,42 @@ async fn main() -> anyhow::Result<()> {
} }
})?; })?;
reply.sort_by(|(host, user), (host2, user2)| {
user.user_id
.cmp(&user2.user_id)
.then_with(|| host.cmp(host2))
.then_with(|| user.tty.cmp(&user2.tty))
});
if args.json { if args.json {
println!("{}", serde_json::to_string_pretty(&reply).unwrap()); let sorted: BTreeMap<String, Vec<WhodUserEntry>> = reply
} else if args.old { .into_iter()
old_format_user_entries(&reply) .map(|(hostname, mut users)| {
.iter() users.sort_by(|a, b| a.user_id.cmp(&b.user_id).then_with(|| a.tty.cmp(&b.tty)));
.for_each(|line| println!("{}", line)); (hostname, users)
})
.collect();
println!("{}", serde_json::to_string_pretty(&sorted).unwrap());
} else { } else {
old_format_user_entries(&reply) let mut entries: Vec<(String, WhodUserEntry)> = reply
.iter() .into_iter()
.for_each(|line| println!("{}", line)); .flat_map(|(hostname, users)| {
users
.into_iter()
.map(move |user| (hostname.clone(), user))
})
.collect();
entries.sort_by(|(host, user), (host2, user2)| {
user.user_id
.cmp(&user2.user_id)
.then_with(|| host.cmp(host2))
.then_with(|| user.tty.cmp(&user2.tty))
});
if args.old {
old_format_user_entries(&entries)
.iter()
.for_each(|line| println!("{}", line));
} else {
// TODO: add a newer and nicer format here
old_format_user_entries(&entries)
.iter()
.for_each(|line| println!("{}", line));
}
} }
Ok(()) Ok(())
+10 -10
View File
@@ -1,4 +1,4 @@
use std::{os::fd::OwnedFd, time::Duration}; use std::{collections::HashMap, os::fd::OwnedFd, time::Duration};
use anyhow::Context; use anyhow::Context;
use futures_util::stream; use futures_util::stream;
@@ -66,7 +66,7 @@ pub enum VarlinkRwhodClientResponse {
Ruptime(VarlinkRuptimeResponse), Ruptime(VarlinkRuptimeResponse),
} }
pub type VarlinkRwhoResponse = Vec<(String, WhodUserEntry)>; pub type VarlinkRwhoResponse = HashMap<String, Vec<WhodUserEntry>>;
pub type VarlinkRuptimeResponse = Vec<WhodStatusUpdate>; pub type VarlinkRuptimeResponse = Vec<WhodStatusUpdate>;
#[derive(Debug, Clone, PartialEq, ReplyError)] #[derive(Debug, Clone, PartialEq, ReplyError)]
@@ -180,19 +180,19 @@ impl VarlinkRoowhoo2ClientServer {
tracing::debug!(?max_idle_time, "Handling Rwho request"); tracing::debug!(?max_idle_time, "Handling Rwho request");
let store = self.whod_status_store.read().await; let store = self.whod_status_store.read().await;
let mut all_user_entries = Vec::with_capacity(store.len()); store
for status_update in store.values() { .values()
all_user_entries.extend( .filter_map(|status_update| {
status_update let users: Vec<WhodUserEntry> = status_update
.users .users
.iter() .iter()
.filter(|user| max_idle_time.is_none_or(|max| user.idle_time < max)) .filter(|user| max_idle_time.is_none_or(|max| user.idle_time < max))
.cloned() .cloned()
.map(|user| (status_update.hostname.clone(), user)), .collect();
);
}
all_user_entries (!users.is_empty()).then(|| (status_update.hostname.clone(), users))
})
.collect()
} }
async fn handle_ruptime_request( async fn handle_ruptime_request(