Add rm, mv, promote, demote, refile, archive, lint, query to ork

Client verbs for the new server capabilities, each a thin wrapper over
the base 9P operations: the structural verbs write to /ctl, and query
writes to /query and prints the matches.
This commit is contained in:
Levi Neely 2026-09-30 21:52:33 +02:00
parent 12869fb9f0
commit 943c05b61c
1 changed files with 103 additions and 3 deletions

View File

@ -3,9 +3,9 @@
//! `ork` is only a 9P client: it connects to a running `ork-server` and
//! operates on its namespace. All org-mode intelligence lives in the server.
//! The base verbs (ls/read/write/rdwr) speak raw 9P; the friendly verbs
//! (docs/sections/todos/agenda/get/set/add/mkdoc/reload/tag/prop/todo/tree)
//! are thin compositions of those same 9P operations against the exposed
//! namespace.
//! (docs/sections/todos/agenda/get/set/add/mkdoc/reload/tag/prop/todo/tree/
//! rm/mv/promote/demote/refile/archive/lint/query) are thin compositions of
//! those same 9P operations against the exposed namespace.
//!
//! Address resolution: `--addr` flag, then `$ORK_ADDR`, then
//! `tcp://localhost:5640`. Accepts `tcp://host:port` or `unix:///path/to/sock`.
@ -172,6 +172,73 @@ enum Commands {
/// Document name
doc: String,
},
/// Delete a section and its subtree
Rm {
/// Document name
doc: String,
/// Section id
id: String,
},
/// Reorder a section among its siblings
Mv {
/// Document name
doc: String,
/// Section id
id: String,
/// Direction: up or down
dir: String,
},
/// Decrease a section's outline level
Promote {
/// Document name
doc: String,
/// Section id
id: String,
},
/// Increase a section's outline level
Demote {
/// Document name
doc: String,
/// Section id
id: String,
},
/// Move a section's subtree to another document (or elsewhere)
Refile {
/// Source document name
doc: String,
/// Section id
id: String,
/// Target document name
to: String,
/// Place under this section id instead of at top level
#[arg(long, short)]
parent: Option<String>,
},
/// Move a section's subtree to <doc>.org_archive
Archive {
/// Document name
doc: String,
/// Section id
id: String,
},
/// Validate documents (all, or one)
Lint {
/// Document name; omit to lint all
doc: Option<String>,
},
/// Find sections matching a query (tag:X+todo:Y+priority:Z)
Query {
/// Match expression, e.g. "tag:work+todo:TODO"
expr: String,
},
}
/// Section fields that may be written via `set`.
@ -499,6 +566,20 @@ fn print_tree(
Ok(())
}
/// Send a control command and echo the server's response.
fn ctl(client: &mut Client, command: &str) -> io::Result<()> {
let resp = client.rdwr("/ctl", command.as_bytes())?;
io::stdout().write_all(&resp)?;
Ok(())
}
/// `query <expr>` — write a match expression and print matching sections.
fn cmd_query(client: &mut Client, expr: &str) -> io::Result<()> {
let resp = client.rdwr("/query", expr.as_bytes())?;
io::stdout().write_all(&resp)?;
Ok(())
}
fn run(cli: Cli) -> io::Result<()> {
let addr = resolve_addr(cli.addr.as_deref());
let mut client = Client::connect(&addr)
@ -553,6 +634,25 @@ fn run(cli: Cli) -> io::Result<()> {
cmd_todo(&mut client, &doc, &id, state.as_deref())?
}
Commands::Tree { doc } => cmd_tree(&mut client, &doc)?,
Commands::Rm { doc, id } => ctl(&mut client, &format!("rm {} {}", doc, id))?,
Commands::Mv { doc, id, dir } => {
ctl(&mut client, &format!("move {} {} {}", doc, id, dir))?
}
Commands::Promote { doc, id } => ctl(&mut client, &format!("promote {} {}", doc, id))?,
Commands::Demote { doc, id } => ctl(&mut client, &format!("demote {} {}", doc, id))?,
Commands::Refile { doc, id, to, parent } => {
let cmd = match parent {
Some(p) => format!("refile {} {} {} {}", doc, id, to, p),
None => format!("refile {} {} {}", doc, id, to),
};
ctl(&mut client, &cmd)?
}
Commands::Archive { doc, id } => ctl(&mut client, &format!("archive {} {}", doc, id))?,
Commands::Lint { doc } => match doc {
Some(d) => ctl(&mut client, &format!("lint {}", d))?,
None => ctl(&mut client, "lint")?,
},
Commands::Query { expr } => cmd_query(&mut client, &expr)?,
}
Ok(())