New spawn command

This commit is contained in:
sagie gur ari 2020-02-19 17:51:11 +00:00
parent 31ebe87e59
commit 4d8e65215c
10 changed files with 198 additions and 28 deletions

View file

@ -2,6 +2,7 @@
### v0.2.1
* New spawn command.
* Update build makefile.
* Runtime - Add support for **Any** state type.
* SDK - Use fsio crate for file system apis.

View file

@ -76,6 +76,7 @@
* [std::process::Execute (exec)](#std__process__Execute)
* [std::process::Exit (exit, quit, q)](#std__process__Exit)
* [std::process::ProcessID (pid, process_id)](#std__process__ProcessID)
* [std::process::Spawn (spawn)](#std__process__Spawn)
* [std::process::Watchdog (watchdog)](#std__process__Watchdog)
* [std::scope::Clear (clear_scope)](#std__scope__Clear)
* [std::string::Base64 (base64)](#std__string__Base64)
@ -2906,6 +2907,35 @@ id = pid
#### Aliases:
pid, process_id
<a name="std__process__Spawn"></a>
## std::process::Spawn
```sh
pid = spawn command [args]*
```
Executes the provided native command and arguments.<br>
It will not wait for the process to finish and will return the process pid.
#### Parameters
The command to execute and its arguments.
#### Return Value
The process pid.
#### Examples
```sh
pid = spawn echo test
echo PID: ${pid}
```
#### Aliases:
spawn
<a name="std__process__Watchdog"></a>
## std::process::Watchdog
```sh

View file

@ -1,6 +1,7 @@
mod exec;
mod exit;
mod process_id;
mod spawn;
mod watchdog;
use crate::utils::pckg;
@ -15,6 +16,7 @@ pub(crate) fn load(commands: &mut Commands, parent: &str) -> Result<(), ScriptEr
commands.set(exec::create(&package))?;
commands.set(exit::create(&package))?;
commands.set(process_id::create(&package))?;
commands.set(spawn::create(&package))?;
commands.set(watchdog::create(&package))?;
Ok(())

View file

@ -0,0 +1,22 @@
```sh
pid = spawn command [args]*
```
Executes the provided native command and arguments.<br>
It will not wait for the process to finish and will return the process pid.
#### Parameters
The command to execute and its arguments.
#### Return Value
The process pid.
#### Examples
```sh
pid = spawn echo test
echo PID: ${pid}
```

View file

@ -0,0 +1,46 @@
use crate::utils::{exec, pckg};
use duckscript::types::command::{Command, CommandResult};
#[cfg(test)]
#[path = "./mod_test.rs"]
mod mod_test;
#[derive(Clone)]
pub(crate) struct CommandImpl {
package: String,
}
impl Command for CommandImpl {
fn name(&self) -> String {
pckg::concat(&self.package, "Spawn")
}
fn aliases(&self) -> Vec<String> {
vec!["spawn".to_string()]
}
fn help(&self) -> String {
include_str!("help.md").to_string()
}
fn clone_and_box(&self) -> Box<dyn Command> {
Box::new((*self).clone())
}
fn run(&self, arguments: Vec<String>) -> CommandResult {
match exec::spawn(&arguments, false, false, 0) {
Ok(child) => {
let pid = child.id();
CommandResult::Continue(Some(pid.to_string()))
}
Err(error) => CommandResult::Error(error),
}
}
}
pub(crate) fn create(package: &str) -> Box<dyn Command> {
Box::new(CommandImpl {
package: package.to_string(),
})
}

View file

@ -0,0 +1,22 @@
use super::*;
use crate::test;
use crate::test::CommandValidation;
#[test]
fn common_functions() {
test::test_common_command_functions(create(""));
}
#[test]
fn run_no_args() {
test::run_script_and_error(vec![create("")], "out = spawn", "out");
}
#[test]
fn run_valid() {
test::run_script_and_validate(
vec![create("")],
"out = spawn echo test",
CommandValidation::PositiveNumber("out".to_string()),
);
}

View file

@ -1,5 +1,5 @@
use crate::utils::pckg;
use crate::utils::state::get_handles_sub_state;
use crate::utils::state::remove_handle;
use duckscript::types::command::{Command, CommandResult, Commands};
use duckscript::types::instruction::Instruction;
use duckscript::types::runtime::StateValue;
@ -10,7 +10,7 @@ use std::collections::HashMap;
mod mod_test;
fn remove(state: &mut HashMap<String, StateValue>, key: &str, recursive: bool) -> bool {
match state.remove(key) {
match remove_handle(state, key.to_string()) {
Some(state_value) => {
if recursive {
match state_value {
@ -83,8 +83,6 @@ impl Command for CommandImpl {
if arguments.is_empty() {
CommandResult::Continue(Some("false".to_string()))
} else {
let state = get_handles_sub_state(state);
let (key, recursive) =
if arguments.len() > 1 && (arguments[0] == "-r" || arguments[0] == "--recursive") {
(arguments[1].to_string(), true)

View file

@ -1,5 +1,4 @@
use std::process::Command;
use std::process::Stdio;
use std::process::{Child, Command, Stdio};
#[cfg(test)]
#[path = "./exec_test.rs"]
@ -11,6 +10,49 @@ pub(crate) fn exec(
allow_input: bool,
start_index: usize,
) -> Result<(String, String, i32), String> {
let mut command = create_command(arguments, print_output, allow_input, start_index)?;
match command.output() {
Ok(ref output) => {
let stdout = String::from_utf8_lossy(&output.stdout).into_owned();
let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
let exit_code = match output.status.code() {
Some(value) => value,
None => {
return Err(format!(
"Unable to extract exit code for command: {}",
&arguments[0]
)
.to_string());
}
};
Ok((stdout, stderr, exit_code))
}
Err(error) => Err(error.to_string()),
}
}
pub(crate) fn spawn(
arguments: &Vec<String>,
print_output: bool,
allow_input: bool,
start_index: usize,
) -> Result<Child, String> {
let mut command = create_command(arguments, print_output, allow_input, start_index)?;
match command.spawn() {
Ok(child) => Ok(child),
Err(error) => Err(error.to_string()),
}
}
fn create_command(
arguments: &Vec<String>,
print_output: bool,
allow_input: bool,
start_index: usize,
) -> Result<Command, String> {
if arguments.len() <= start_index {
Err("Command not provided.".to_string())
} else {
@ -31,24 +73,6 @@ pub(crate) fn exec(
command.stdout(Stdio::inherit()).stderr(Stdio::inherit());
}
match command.output() {
Ok(ref output) => {
let stdout = String::from_utf8_lossy(&output.stdout).into_owned();
let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
let exit_code = match output.status.code() {
Some(value) => value,
None => {
return Err(format!(
"Unable to extract exit code for command: {}",
&arguments[0]
)
.to_string());
}
};
Ok((stdout, stderr, exit_code))
}
Err(error) => Err(error.to_string()),
}
Ok(command)
}
}

View file

@ -35,9 +35,7 @@ pub(crate) fn put_handle(state: &mut HashMap<String, StateValue>, value: StateVa
.collect();
key.insert_str(0, "handle:");
let handle_state = get_handles_sub_state(state);
handle_state.insert(key.clone(), value);
return_handle(state, key.clone(), value);
key
}
@ -51,6 +49,25 @@ pub(crate) fn get_handle(
handle_state.get(&key)
}
pub(crate) fn remove_handle(
state: &mut HashMap<String, StateValue>,
key: String,
) -> Option<StateValue> {
let handle_state = get_handles_sub_state(state);
handle_state.remove(&key)
}
pub(crate) fn return_handle(
state: &mut HashMap<String, StateValue>,
key: String,
value: StateValue,
) {
let handle_state = get_handles_sub_state(state);
handle_state.insert(key.clone(), value);
}
fn ensure_sub_state(key: &str, state: &mut HashMap<String, StateValue>) {
match state.get(key) {
Some(value) => match value {

View file

@ -0,0 +1,8 @@
fn test_echo
output = spawn echo hello world
empty = is_empty ${output}
assert_false ${empty}
end