use super::args::{self, Args, is};
use super::table::Spec;
use crate::reply::Out;
use yo_common::{Code, Error, Result};
pub(super) fn execute(spec: &Spec, args: Args<'_>, out: &mut Out) -> Result<()> {
match spec.name {
"script" => script(args, out),
"function" => function(args, out),
other => unreachable!("scripting command with no body: {other}"),
}
}
fn script(args: Args<'_>, out: &mut Out) -> Result<()> {
let sub = args.get(1);
if is(sub, b"FLUSH") {
if args.len() > 3 || (args.len() == 3 && !mode(args.get(2))) {
return Err(Error::new(
Code::Invalid,
"SCRIPT FLUSH only support SYNC|ASYNC option",
));
}
out.ok();
} else if is(sub, b"EXISTS") {
if args.len() < 3 {
return Err(args::wrong_arity_sub("script", "exists"));
}
out.array(args.len() - 2);
for _ in 2..args.len() {
out.int(0);
}
} else if is(sub, b"HELP") {
super::server::help(out, SCRIPT_HELP);
} else {
return Err(args::unknown_subcommand(sub, "SCRIPT"));
}
Ok(())
}
fn function(args: Args<'_>, out: &mut Out) -> Result<()> {
let sub = args.get(1);
if is(sub, b"FLUSH") {
if args.len() > 3 {
return Err(unknown_or_arity("flush"));
}
if args.len() == 3 && !mode(args.get(2)) {
return Err(Error::new(
Code::Invalid,
"FUNCTION FLUSH only supports SYNC|ASYNC option",
));
}
out.ok();
} else if is(sub, b"LIST") {
let mut i = 2;
while i < args.len() {
let a = args.get(i);
if is(a, b"WITHCODE") {
i += 1;
} else if is(a, b"LIBRARYNAME") && i + 1 < args.len() {
i += 2;
} else {
return Err(yo_alloc::allow(|| {
Error::fmt(
Code::Invalid,
format_args!("Unknown argument {}", String::from_utf8_lossy(a)),
)
}));
}
}
out.array(0);
} else if is(sub, b"DELETE") {
if args.len() != 3 {
return Err(unknown_or_arity("delete"));
}
return Err(Error::new(Code::Unsupported, "Library not found"));
} else if is(sub, b"HELP") {
super::server::help(out, FUNCTION_HELP);
} else {
return Err(args::unknown_subcommand(sub, "FUNCTION"));
}
Ok(())
}
fn mode(arg: &[u8]) -> bool {
is(arg, b"SYNC") || is(arg, b"ASYNC")
}
fn unknown_or_arity(sub: &str) -> Error {
Error::fmt(
Code::Unsupported,
format_args!(
"unknown subcommand or wrong number of arguments for '{sub}'. Try FUNCTION HELP."
),
)
}
const SCRIPT_HELP: &[&str] = &[
"SCRIPT <subcommand> [<arg> [value] [opt] ...]. Subcommands are:",
"DEBUG (YES|SYNC|NO)",
" Set the debug mode for subsequent scripts executed.",
"EXISTS <sha1> [<sha1> ...]",
" Return information about the existence of the scripts in the script cache.",
"FLUSH [ASYNC|SYNC]",
" Flush the Lua scripts cache. Very dangerous on replicas.",
" When called without the optional mode argument, the behavior is determined by the",
" lazyfree-lazy-user-flush configuration directive. Valid modes are:",
" * ASYNC: Asynchronously flush the scripts cache.",
" * SYNC: Synchronously flush the scripts cache.",
"KILL",
" Kill the currently executing Lua script.",
"LOAD <script>",
" Load a script into the scripts cache without executing it.",
"HELP",
" Print this help.",
];
const FUNCTION_HELP: &[&str] = &[
"FUNCTION <subcommand> [<arg> [value] [opt] ...]. Subcommands are:",
"LOAD [REPLACE] <FUNCTION CODE>",
" Create a new library with the given library name and code.",
"DELETE <LIBRARY NAME>",
" Delete the given library.",
"LIST [LIBRARYNAME PATTERN] [WITHCODE]",
" Return general information on all the libraries:",
" * Library name",
" * The engine used to run the Library",
" * Functions list",
" * Library code (if WITHCODE is given)",
" It also possible to get only function that matches a pattern using LIBRARYNAME argument.",
"STATS",
" Return information about the current function running:",
" * Function name",
" * Command used to run the function",
" * Duration in MS that the function is running",
" If no function is running, return nil",
" In addition, returns a list of available engines.",
"KILL",
" Kill the current running function.",
"FLUSH [ASYNC|SYNC]",
" Delete all the libraries.",
" When called without the optional mode argument, the behavior is determined by the",
" lazyfree-lazy-user-flush configuration directive. Valid modes are:",
" * ASYNC: Asynchronously flush the libraries.",
" * SYNC: Synchronously flush the libraries.",
"DUMP",
" Return a serialized payload representing the current libraries, can be restored using FUNCTION RESTORE command",
"RESTORE <PAYLOAD> [FLUSH|APPEND|REPLACE]",
" Restore the libraries represented by the given payload, it is possible to give a restore policy to",
" control how to handle existing libraries (default APPEND):",
" * FLUSH: delete all existing libraries.",
" * APPEND: appends the restored libraries to the existing libraries. On collision, abort.",
" * REPLACE: appends the restored libraries to the existing libraries, On collision, replace the old",
" libraries with the new libraries (notice that even on this option there is a chance of failure",
" in case of functions name collision with another library).",
"HELP",
" Print this help.",
];