#![allow(dead_code)]
use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::Display;
use std::process::Command;
use retry::delay::Fixed;
use serde::Deserialize;
use std::ffi::OsStr;
#[derive(Debug, Copy, Clone)]
pub enum Journal {
User,
System,
}
#[derive(Debug, PartialEq, Deserialize)]
#[serde(untagged)]
pub enum FieldValue {
Text(String),
Array(Vec<String>),
Binary(Vec<u8>),
}
impl FieldValue {
pub fn as_text(&self) -> Cow<'_, str> {
match self {
FieldValue::Text(v) => Cow::Borrowed(v.as_str()),
FieldValue::Binary(binary) => String::from_utf8_lossy(binary),
FieldValue::Array(v) => Cow::Borrowed(v.first().map_or("", |s| s.as_str())),
}
}
}
impl Display for FieldValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.as_text().fmt(f)
}
}
impl PartialEq<&str> for FieldValue {
fn eq(&self, other: &&str) -> bool {
match self {
FieldValue::Text(s) => s == other,
FieldValue::Binary(b) => b == other.as_bytes(),
FieldValue::Array(_) => false,
}
}
}
impl PartialEq<String> for FieldValue {
fn eq(&self, other: &String) -> bool {
self == &other.as_str()
}
}
impl PartialEq<&String> for FieldValue {
fn eq(&self, other: &&String) -> bool {
self == &other.as_str()
}
}
impl PartialEq<[u8]> for FieldValue {
fn eq(&self, other: &[u8]) -> bool {
match self {
FieldValue::Text(s) => s.as_bytes() == other,
FieldValue::Binary(data) => data == other,
FieldValue::Array(_) => false,
}
}
}
impl PartialEq<Vec<&str>> for FieldValue {
fn eq(&self, other: &std::vec::Vec<&str>) -> bool {
match self {
FieldValue::Text(_) => false,
FieldValue::Binary(_) => false,
FieldValue::Array(a) => a == other,
}
}
}
pub fn read<I, S>(journal: Journal, args: I) -> Vec<HashMap<String, FieldValue>>
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let mut command = Command::new("journalctl");
if matches!(journal, Journal::User) {
command.arg("--user");
}
let stdout = String::from_utf8(
command
.arg("--output=json")
.arg("--all")
.args(args)
.output()
.unwrap()
.stdout,
)
.unwrap();
stdout
.lines()
.map(|l| serde_json::from_str(l).unwrap())
.collect()
}
pub fn read_current_process(target: &str) -> Vec<HashMap<String, FieldValue>> {
read(
Journal::User,
vec![
format!("_PID={}", std::process::id()),
format!("TARGET={}", target),
],
)
}
pub fn read_one_entry(target: &str) -> HashMap<String, FieldValue> {
retry::retry(Fixed::from_millis(100).take(30), || {
let mut entries = read_current_process(target);
if entries.len() == 1 {
Ok(entries.pop().unwrap())
} else {
Err(format!("No entries in journal for target {}", target))
}
})
.unwrap()
}