Skip to content
Snippets Groups Projects
Commit a9ab3114 authored by Georgy Moshkin's avatar Georgy Moshkin :speech_balloon: Committed by Yaroslav Dynnikov
Browse files

feature: tarantool subcommand

parent aa18d6d7
No related branches found
No related tags found
1 merge request!47feature: tarantool subcommand
Pipeline #3611 passed
......@@ -31,6 +31,7 @@ features = ["schema"]
[dev-dependencies]
assert_cmd = "2.0"
predicates = "2.0"
tempfile = "3"
[build-dependencies]
......
use crate::traft::row::Peer;
use std::ffi::CString;
use std::{
borrow::Cow,
ffi::{CStr, CString},
};
use structopt::StructOpt;
use tarantool::tlua;
use tarantool::tlua::{self, c_str};
#[derive(Debug, StructOpt)]
#[structopt(name = "picodata", version = env!("CARGO_PKG_VERSION"))]
pub enum Picodata {
Run(Run),
Tarantool(Tarantool),
}
////////////////////////////////////////////////////////////////////////////////
// Run
////////////////////////////////////////////////////////////////////////////////
#[derive(Debug, StructOpt, tlua::Push)]
#[structopt(
about = "Run the picodata instance",
setting = clap::AppSettings::NoBinaryName,
)]
#[structopt(about = "Run the picodata instance")]
pub struct Run {
#[structopt(long, value_name = "name", env = "PICODATA_CLUSTER_ID")]
/// Name of the cluster
......@@ -80,12 +85,6 @@ pub struct Run {
pub autorun: bool,
}
macro_rules! c_str {
($s:literal) => {
unsafe { ::std::ffi::CStr::from_ptr(::std::concat!($s, "\0").as_ptr() as _) }
};
}
impl Run {
#[allow(dead_code)]
/// Returns argument matches as if the `argv` is empty
......@@ -96,15 +95,7 @@ impl Run {
/// Get the arguments that will be passed to `tarantool_main`
pub fn tt_args(&self) -> Result<Vec<CString>, String> {
let exe = CString::new(
std::env::current_exe()
.map_err(|e| format!("Failed getting current executable path: {e}"))?
.display()
.to_string(),
)
.map_err(|e| format!("Current executable path contains nul bytes: {e}"))?;
let mut res = vec![exe];
let mut res = vec![current_exe()?];
if let Some(script) = &self.tarantool_exec {
res.extend([c_str!("-e").into(), script.clone()]);
......@@ -114,6 +105,40 @@ impl Run {
}
}
////////////////////////////////////////////////////////////////////////////////
// Tarantool
////////////////////////////////////////////////////////////////////////////////
#[derive(Debug, StructOpt, tlua::Push)]
#[structopt(about = "Run tarantool")]
pub struct Tarantool {
#[structopt(raw = true, parse(try_from_str = CString::new))]
pub args: Vec<CString>,
}
impl Tarantool {
/// Get the arguments that will be passed to `tarantool_main`
pub fn tt_args(&self) -> Result<Vec<Cow<CStr>>, String> {
Ok(std::iter::once(current_exe()?.into())
.chain(self.args.iter().map(AsRef::as_ref).map(Cow::from))
.collect())
}
}
////////////////////////////////////////////////////////////////////////////////
// fns
////////////////////////////////////////////////////////////////////////////////
fn current_exe() -> Result<CString, String> {
CString::new(
std::env::current_exe()
.map_err(|e| format!("Failed getting current executable path: {e}"))?
.display()
.to_string(),
)
.map_err(|e| format!("Current executable path contains nul bytes: {e}"))
}
fn parse_peer(text: &str) -> Peer {
static mut CURRENT_RAFT_ID: u64 = 0;
......
......@@ -258,12 +258,66 @@ pub extern "C" fn raft_interact(_: FunctionCtx, args: FunctionArgs) -> c_int {
fn main() {
let res = match args::Picodata::from_args() {
args::Picodata::Run(r) => run(r),
args::Picodata::Tarantool(t) => tarantool(t),
};
if let Err(e) = res {
eprintln!("Fatal: {}", e)
}
}
macro_rules! tarantool_main {
(@impl $tt_args:expr, $cb:expr, $cb_data:expr) => {
{
extern "C" {
fn tarantool_main(
argc: i32,
argv: *mut *mut c_char,
cb: Option<extern "C" fn(*mut c_void)>,
cb_data: *mut c_void,
) -> c_int;
}
let tt_args = $tt_args;
// XXX: `argv` is a vec of pointers to data owned by `tt_args`, so
// make sure `tt_args` outlives `argv`, because the compiler is not
// gonna do that for you
let argv = tt_args.iter().map(|a| a.as_ptr()).collect::<Vec<_>>();
let rc = unsafe {
tarantool_main(
argv.len() as _,
argv.as_ptr() as _,
$cb,
$cb_data,
)
};
if rc == 0 {
Ok(())
} else {
Err(format!("return code {rc}"))
}
}
};
($tt_args:expr) => {
tarantool_main!(@impl $tt_args, None, std::ptr::null_mut())
};
($tt_args:expr, $pd_args:expr => $cb:expr) => {
{
extern "C" fn trampoline(data: *mut c_void) {
let args = unsafe { Box::from_raw(data as _) };
$cb(*args)
}
tarantool_main!(@impl
$tt_args,
Some(trampoline),
Box::into_raw(Box::new($pd_args)) as _
)
}
}
}
fn run(args: args::Run) -> Result<(), String> {
// Tarantool implicitly parses some environment variables.
// We don't want them to affect the behavior and thus filter them out.
......@@ -273,43 +327,14 @@ fn run(args: args::Run) -> Result<(), String> {
}
}
let tt_args = args.tt_args()?;
// XXX: `argv` is a vec of pointers to data owned by `tt_args`, so make sure
// `tt_args` outlives `argv`, because the compiler is not gonna do that for
// you
let argv = tt_args.iter().map(|a| a.as_ptr()).collect::<Vec<_>>();
let boxed_args: Box<args::Run> = Box::new(args);
let rc = unsafe {
tarantool_main(
argv.len() as _,
argv.as_ptr() as _,
trampoline,
Box::into_raw(boxed_args) as _,
)
};
return if rc == 0 {
Ok(())
} else {
Err(format!("return code {rc}"))
};
extern "C" fn trampoline(data: *mut c_void) {
let args: Box<args::Run> = unsafe { Box::from_raw(data as _) };
tarantool_main!(args.tt_args()?, args => |args: args::Run| {
if args.autorun {
start(&args);
}
picolib_setup(*args);
}
picolib_setup(args);
})
}
extern "C" {
fn tarantool_main(
argc: i32,
argv: *mut *mut c_char,
cb: extern "C" fn(*mut c_void),
cb_data: *mut c_void,
) -> i32;
}
fn tarantool(args: args::Tarantool) -> Result<(), String> {
tarantool_main!(args.tt_args()?)
}
......@@ -118,3 +118,16 @@ fn precedence() {
);
cmd.timeout(Duration::from_secs(1)).assert().success();
}
#[test]
fn tarantool_hello() {
Command::cargo_bin("picodata")
.unwrap()
.arg("tarantool")
.arg("--")
.arg("-e")
.arg(r#"print("it's alive!")"#)
.assert()
.stdout(&b"it's alive!\n"[..])
.success();
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment