//! The owner's memory commands over `memoryd`'s owner socket: `reindex`, and `memory log`, //! `undo`, `pending`, `accept`, `reject` (M5a spec, section 10). use std::io::Write; use std::os::unix::net::UnixStream; use std::path::{Path, PathBuf}; use std::time::Duration; use proto::{ Empty, Envelope, MemoryAdmin, MemoryCommand, Message, PROTOCOL_VERSION, read_frame, write_frame, }; use crate::cli::{Command, UsageError}; use crate::escape::escape_json_text; /// How long `bxctl` waits for `memoryd`: a reindex reads every session log. pub const MEMORY_TIMEOUT: Duration = Duration::from_secs(300); pub fn default_socket(home: &Path) -> PathBuf { home.join("run").join("owner-memory").join("admin.sock") } /// What to ask `memoryd`. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Ask { Reindex, Admin(MemoryCommand, String), } /// `bxctl reindex [--memory-socket PATH]`. pub fn parse_reindex(args: &[String], home: &Path) -> Result { // `socket_flag(args, home)?`; any argument left -> Err(UsageError). Ok(Command::Memory { // socket, ask: Ask::Reindex }). todo!() } /// `bxctl memory log [N] | undo | pending | accept | reject `, each with an optional /// `--memory-socket PATH`. pub fn parse_memory(args: &[String], home: &Path) -> Result { // `socket_flag(args, home)?`, then match what is left: ["log"] -> Admin(Log, ""); ["log", n] // where n parses as usize -> Admin(Log, n); ["pending"] -> Admin(Pending, ""); ["undo", id] -> // Admin(Undo, id); ["accept", id] -> Admin(Accept, id); ["reject", id] -> Admin(Reject, id); // anything else -> Err(UsageError). Ok(Command::Memory { socket, ask }). todo!() } /// Take `--memory-socket PATH` out of `args`, once at most; the rest in order. fn socket_flag(args: &[String], home: &Path) -> Result<(PathBuf, Vec), UsageError> { // Walk `args`: "--memory-socket" takes the next argument as the socket (none -> // Err(UsageError); a second --memory-socket -> Err(UsageError)); every other argument is kept // in order. The socket defaults to `default_socket(home)`. Ok((socket, rest)). todo!() } /// `n` with commas between thousands. fn grouped(n: u64) -> String { // As memoryd's `grouped`: the digits of n with a comma before each char at index i > 0 where // `(digits.len() - i).is_multiple_of(3)`. todo!() } /// Ask `memoryd` and print its answer. `Err` is the line for stderr. pub fn run(socket: &Path, ask: &Ask, timeout: Duration, out: &mut dyn Write) -> Result<(), String> { // 1. Connect to `socket` and set read and write timeouts of `timeout`; each error -> // format!("cannot reach memoryd at {}: {e}", socket.display()). // 2. The message: Reindex -> Message::Reindex(Empty {}); Admin(command, arg) -> // Message::MemoryAdmin(MemoryAdmin { command, arg }). `write_frame` it in an Envelope { v: // PROTOCOL_VERSION, id: 1, final: true } (an error: the same "cannot reach" sentence). // 3. `read_frame` (an error -> format!("memoryd did not answer: {e}")). Reindexed(r) -> // format!("reindexed: {} logs, {} records, {} notes") with each count `grouped`; // MemoryAdminReply(reply) -> reply.text; Error(e) -> Err(format!("memoryd: {}", // escape_json_text(&e.detail))); anything else -> Err("memoryd answered with the wrong // message"). // 4. `writeln!(out, "{text}")`, its error as a String. todo!() }