use std::{borrow::Cow, ffi::OsString}; use anyhow::{Result, bail}; use clap::{Parser, Subcommand}; use yazi_shared::id::Id; #[derive(Parser)] #[command(name = "Ya", about, long_about = None)] pub(super) struct Args { #[command(subcommand)] pub(super) command: Command, /// Print version #[arg(short = 'V', long)] pub(super) version: bool, } #[derive(Subcommand)] pub(super) enum Command { /// Emit an action to be executed by the current instance. Emit(CommandEmit), /// Emit an action to be executed by the specified instance. EmitTo(CommandEmitTo), /// Execute an action on the current instance and print its result. Exec(CommandExec), /// Manage packages. #[command(subcommand)] Pkg(CommandPkg), /// Publish a message to the current instance. Pub(CommandPub), /// Publish a message to the specified instance. PubTo(CommandPubTo), /// Subscribe to messages from all remote instances. Sub(CommandSub), /// Manage the cache. #[command(subcommand)] Cache(CommandCache), /// Print environment and configuration information. Env, } #[derive(clap::Args)] pub(super) struct CommandEmit { /// Name of the action. pub(super) name: String, /// Arguments of the action. #[arg(allow_hyphen_values = true, trailing_var_arg = true)] pub(super) args: Vec, } #[derive(clap::Args)] pub(super) struct CommandEmitTo { /// Receiver ID. pub(super) receiver: Id, /// Name of the action. pub(super) name: String, /// Arguments of the action. #[arg(allow_hyphen_values = true, trailing_var_arg = true)] pub(super) args: Vec, } #[derive(clap::Args)] pub(super) struct CommandExec { /// Name of the action. pub(super) name: String, /// Arguments of the action. #[arg(allow_hyphen_values = true, trailing_var_arg = true)] pub(super) args: Vec, } #[derive(Subcommand)] pub(super) enum CommandPkg { /// Add packages. #[command(arg_required_else_help = true)] Add { /// Packages to add. #[arg(index = 1, num_args = 1..)] ids: Vec, }, /// Delete packages. #[command(arg_required_else_help = true)] Delete { /// Packages to delete. #[arg(index = 1, num_args = 1..)] ids: Vec, /// Discard local changes made to packages while deleting. #[arg(long)] discard: bool, }, /// Install all packages. Install { /// Discard local changes made to packages while installing. #[arg(long)] discard: bool, }, /// List all packages. List, /// Upgrade all packages. Upgrade { /// Packages to upgrade, upgrade all if unspecified. #[arg(index = 1, num_args = 0..)] ids: Vec, /// Discard local changes made to packages while upgrading. #[arg(long)] discard: bool, }, } #[derive(clap::Args)] pub(super) struct CommandPub { /// Kind of message. #[arg(index = 1)] pub(super) kind: String, /// Send the message with a string body. #[arg(long)] pub(super) str: Option, /// Send the message with a JSON body. #[arg(long)] pub(super) json: Option, /// Send the message as a list of strings. #[arg(long, num_args = 0..)] pub(super) list: Vec, } impl CommandPub { #[allow(dead_code)] pub(super) fn receiver() -> Result { if let Some(s) = std::env::var("YAZI_PID").ok().filter(|s| !s.is_empty()) { Ok(s.parse()?) } else { bail!("No `YAZI_ID` environment variable found.") } } } #[derive(clap::Args)] pub(super) struct CommandPubTo { /// Receiver ID. #[arg(index = 1)] pub(super) receiver: Id, /// Kind of message. #[arg(index = 2)] pub(super) kind: String, /// Send the message with a string body. #[arg(long)] pub(super) str: Option, /// Send the message with a JSON body. #[arg(long)] pub(super) json: Option, /// Send the message as a list of strings. #[arg(long, num_args = 0..)] pub(super) list: Vec, } #[derive(clap::Args)] pub(super) struct CommandSub { /// Kind of messages to subscribe to, separated by commas if multiple. #[arg(index = 1)] pub(super) kinds: String, } #[derive(Subcommand)] pub(super) enum CommandCache { /// Clear the cache directory. Clear, } // --- Macros macro_rules! impl_emit_body { ($name:ident) => { impl $name { #[allow(dead_code)] pub(super) fn body(self) -> Result { #[derive(serde::Serialize)] #[serde(untagged)] enum Elem { Name(String), Arg(Vec), } let action: Vec<_> = [Elem::Name(self.name)] .into_iter() .chain(self.args.into_iter().map(|s| Elem::Arg(s.into_encoded_bytes()))) .collect(); Ok(serde_json::to_string(&action)?) } } }; } macro_rules! impl_exec_body { ($name:ident) => { impl $name { #[allow(dead_code)] pub(super) fn body(self, reply_to: Id) -> Result { #[derive(serde::Serialize)] #[serde(untagged)] enum Elem { Id(Id), Name(String), Arg(Vec), } let action: Vec<_> = [Elem::Id(reply_to), Elem::Name(self.name)] .into_iter() .chain(self.args.into_iter().map(|s| Elem::Arg(s.into_encoded_bytes()))) .collect(); Ok(serde_json::to_string(&action)?) } } }; } macro_rules! impl_pub_body { ($name:ident) => { impl $name { #[allow(dead_code)] pub(super) fn body(&self) -> Result> { Ok(if let Some(json) = &self.json { json.into() } else if let Some(str) = &self.str { serde_json::to_string(str)?.into() } else if !self.list.is_empty() { serde_json::to_string(&self.list)?.into() } else { "".into() }) } } }; } impl_emit_body!(CommandEmit); impl_emit_body!(CommandEmitTo); impl_exec_body!(CommandExec); impl_pub_body!(CommandPub); impl_pub_body!(CommandPubTo);