Merge pull request #119 from sagiegurari/0.6.0

0.6.0
This commit is contained in:
Sagie Gur-Ari 2020-07-03 14:22:04 +03:00 committed by GitHub
commit 0fe8f0b2de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 1694 additions and 111 deletions

View file

@ -1,5 +1,16 @@
## CHANGELOG
### v0.6.0
* New ftp_put command.
* New fpt_put_in_memory command.
* New ftp_get command.
* New ftp_get_in_memory command.
* New ftp_list command.
* New ftp_nlst command.
* New noop command.
* \[Breaking Change\] Conditions to support function calls #116
### v0.5.0 (2020-06-06)
* Fixed end command bug which directed execution to wrong sub command when multiple script contexts are running #110

View file

@ -2,6 +2,7 @@
* [std::Echo (echo)](#std__Echo)
* [std::Eval (eval)](#std__Eval)
* [std::IsDefined (is_defined)](#std__IsDefined)
* [std::Noop (noop)](#std__Noop)
* [std::Not (not)](#std__Not)
* [std::ReadUserInput (read)](#std__ReadUserInput)
* [std::Release (release)](#std__Release)
@ -111,6 +112,12 @@
* [std::net::Hostname (hostname)](#std__net__Hostname)
* [std::net::HttpClient (http_client)](#std__net__HttpClient)
* [std::net::WGet (wget)](#std__net__WGet)
* [std::net::ftp::Get (ftp_get)](#std__net__ftp__Get)
* [std::net::ftp::GetInMemory (ftp_get_in_memory)](#std__net__ftp__GetInMemory)
* [std::net::ftp::List (ftp_list)](#std__net__ftp__List)
* [std::net::ftp::NLst (ftp_nlst)](#std__net__ftp__NLst)
* [std::net::ftp::Put (ftp_put)](#std__net__ftp__Put)
* [std::net::ftp::PutInMemory (ftp_put_in_memory)](#std__net__ftp__PutInMemory)
* [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)
@ -238,6 +245,32 @@ exists = is_defined key
#### Aliases:
is_defined
<a name="std__Noop"></a>
## std::Noop
```sh
noop
```
Empty function that does nothing and returns none.
#### Parameters
All parameters are ignored
#### Return Value
None
#### Examples
```sh
noop
```
#### Aliases:
noop
<a name="std__Not"></a>
## std::Not
```sh
@ -4141,6 +4174,208 @@ http_client --method "${scope::wget::method}" --output-file "${scope::wget::file
#### Aliases:
wget
<a name="std__net__ftp__Get"></a>
## std::net::ftp::Get
```sh
result = ftp_get --host <hostname> [--port 21] [--username <user name>] [--password <password>] [--path <path>] [--type <A/I>] --remote-file <file name> --local-file <file name>
```
Invokes the FTP GET command from the given connection and file details.
#### Parameters
* --host - The host name or IP to connect to
* --port - Optional port number to use (by default 21)
* --username - Optional user name used to login (if not user or password provided, no login operation will be invoked)
* --password - Optional password used to login (if not user or password provided, no login operation will be invoked)
* --path - Optional path on the remote server to invoke operation on
* --type - Optional setting of the transfer type as A (ascii) I (image, binary)
* --remote-file - The remote file to download
* --local-file - The target local file name
#### Return Value
true if operation was completed.
#### Examples
```sh
ftp_get --host myhost --username someuser --password 12345 --remote-file README.md --local-file README.md
```
#### Aliases:
ftp_get
<a name="std__net__ftp__GetInMemory"></a>
## std::net::ftp::GetInMemory
```sh
handle = ftp_get_in_memory --host <hostname> [--port 21] [--username <user name>] [--password <password>] [--path <path>] [--type <A/I>] --remote-file <file name>
```
Invokes the FTP GET command from the given connection and file details.
#### Parameters
* --host - The host name or IP to connect to
* --port - Optional port number to use (by default 21)
* --username - Optional user name used to login (if not user or password provided, no login operation will be invoked)
* --password - Optional password used to login (if not user or password provided, no login operation will be invoked)
* --path - Optional path on the remote server to invoke operation on
* --type - Optional setting of the transfer type as A (ascii) I (image, binary)
* --remote-file - The remote file to download
#### Return Value
The binary data handle.
#### Examples
```sh
handle = ftp_get_in_memory --host myhost --username someuser --password 12345 --remote-file README.md
text = bytes_to_string ${handle}
```
#### Aliases:
ftp_get_in_memory
<a name="std__net__ftp__List"></a>
## std::net::ftp::List
```sh
handle = ftp_list --host <hostname> [--port 21] [--username <user name>] [--password <password>] [--path <path>]
```
Invokes the FTP LIST command from the given connection details and path.<br>
Returns a handle to an array of all response entries.
#### Parameters
* --host - The host name or IP to connect to
* --port - Optional port number to use (by default 21)
* --username - Optional user name used to login (if not user or password provided, no login operation will be invoked)
* --password - Optional password used to login (if not user or password provided, no login operation will be invoked)
* --path - Optional path on the remote server to invoke operation on
#### Return Value
A handle to an array holding all entries.
#### Examples
```sh
handle = ftp_list --host myhost --username someuser --password 12345
for entry in ${handle}
echo ${entry}
end
```
#### Aliases:
ftp_list
<a name="std__net__ftp__NLst"></a>
## std::net::ftp::NLst
```sh
handle = ftp_nlst --host <hostname> [--port 21] [--username <user name>] [--password <password>] [--path <path>]
```
Invokes the FTP NLST command from the given connection details and path.<br>
Returns a handle to an array of all response entries.
#### Parameters
* --host - The host name or IP to connect to
* --port - Optional port number to use (by default 21)
* --username - Optional user name used to login (if not user or password provided, no login operation will be invoked)
* --password - Optional password used to login (if not user or password provided, no login operation will be invoked)
* --path - Optional path on the remote server to invoke operation on
#### Return Value
A handle to an array holding all entries.
#### Examples
```sh
handle = ftp_nlst --host myhost --username someuser --password 12345
for entry in ${handle}
echo ${entry}
end
```
#### Aliases:
ftp_nlst
<a name="std__net__ftp__Put"></a>
## std::net::ftp::Put
```sh
result = ftp_put --host <hostname> [--port 21] [--username <user name>] [--password <password>] [--path <path>] [--type <A/I>] --remote-file <file name> --local-file <file name>
```
Invokes the FTP PUT command from the given connection and file details.
#### Parameters
* --host - The host name or IP to connect to
* --port - Optional port number to use (by default 21)
* --username - Optional user name used to login (if not user or password provided, no login operation will be invoked)
* --password - Optional password used to login (if not user or password provided, no login operation will be invoked)
* --path - Optional path on the remote server to invoke operation on
* --type - Optional setting of the transfer type as A (ascii) I (image, binary)
* --remote-file - The remote file to upload
* --local-file - The source local file to upload
#### Return Value
true if operation was completed.
#### Examples
```sh
ftp_put --host myhost --username someuser --password 12345 --remote-file README.md --local-file README.md
```
#### Aliases:
ftp_put
<a name="std__net__ftp__PutInMemory"></a>
## std::net::ftp::PutInMemory
```sh
result = ftp_put_in_memory --host <hostname> [--port 21] [--username <user name>] [--password <password>] [--path <path>] [--type <A/I>] --remote-file <file name> --content <content>
```
Invokes the FTP PUT command from the given connection and file details.
#### Parameters
* --host - The host name or IP to connect to
* --port - Optional port number to use (by default 21)
* --username - Optional user name used to login (if not user or password provided, no login operation will be invoked)
* --password - Optional password used to login (if not user or password provided, no login operation will be invoked)
* --path - Optional path on the remote server to invoke operation on
* --type - Optional setting of the transfer type as A (ascii) I (image, binary)
* --remote-file - The remote file to upload
* --content - The textual content to upload
#### Return Value
true if operation was completed.
#### Examples
```sh
ftp_put_in_memory --host myhost --username someuser --password 12345 --remote-file README.md --content "This is the README content"
```
#### Aliases:
ftp_put_in_memory
<a name="std__process__Execute"></a>
## std::process::Execute
```sh

View file

@ -4,9 +4,13 @@
anonymous_parameters,
arithmetic_overflow,
array_into_iter,
asm_sub_register,
bindings_with_variant_name,
cenum_impl_drop_cast,
clashing_extern_declarations,
coherence_leak_check,
conflicting_repr_hints,
confusable_idents,
const_err,
dead_code,
deprecated,
@ -17,7 +21,9 @@
ill_formed_attribute_input,
illegal_floating_point_literal_pattern,
improper_ctypes,
improper_ctypes_definitions,
incomplete_features,
incomplete_include,
indirect_structural_match,
inline_no_sanitize,
invalid_codeblock_attribute,
@ -32,6 +38,7 @@
missing_crate_level_docs,
missing_docs,
missing_fragment_specifier,
mixed_script_confusables,
mutable_borrow_reservation_conflict,
mutable_transmutes,
no_mangle_const_items,
@ -59,6 +66,7 @@
trivial_numeric_casts,
type_alias_bounds,
tyvar_behind_raw_pointer,
unaligned_references,
uncommon_codepoints,
unconditional_panic,
unconditional_recursion,
@ -75,6 +83,7 @@
unused_attributes,
unused_braces,
unused_comparisons,
unused_crate_dependencies,
unused_doc_comments,
unused_extern_crates,
unused_features,

View file

@ -4,9 +4,13 @@
anonymous_parameters,
arithmetic_overflow,
array_into_iter,
asm_sub_register,
bindings_with_variant_name,
cenum_impl_drop_cast,
clashing_extern_declarations,
coherence_leak_check,
conflicting_repr_hints,
confusable_idents,
const_err,
dead_code,
deprecated,
@ -17,7 +21,9 @@
ill_formed_attribute_input,
illegal_floating_point_literal_pattern,
improper_ctypes,
improper_ctypes_definitions,
incomplete_features,
incomplete_include,
indirect_structural_match,
inline_no_sanitize,
invalid_codeblock_attribute,
@ -32,6 +38,7 @@
missing_crate_level_docs,
missing_docs,
missing_fragment_specifier,
mixed_script_confusables,
mutable_borrow_reservation_conflict,
mutable_transmutes,
no_mangle_const_items,
@ -59,6 +66,7 @@
trivial_numeric_casts,
type_alias_bounds,
tyvar_behind_raw_pointer,
unaligned_references,
uncommon_codepoints,
unconditional_panic,
unconditional_recursion,
@ -75,6 +83,7 @@
unused_attributes,
unused_braces,
unused_comparisons,
unused_crate_dependencies,
unused_doc_comments,
unused_extern_crates,
unused_features,

View file

@ -29,6 +29,7 @@ cfg-if = "^0.1"
duckscript = { version = "^0.5.0", path = "../duckscript" }
fs_extra = "^1"
fsio = { version = "^0.1", features = ["temp-path"] }
ftp = "^3"
glob = "^0.3"
home = "^0.5"
java-properties = "^1"

View file

@ -4,9 +4,13 @@
anonymous_parameters,
arithmetic_overflow,
array_into_iter,
asm_sub_register,
bindings_with_variant_name,
cenum_impl_drop_cast,
clashing_extern_declarations,
coherence_leak_check,
conflicting_repr_hints,
confusable_idents,
const_err,
dead_code,
deprecated,
@ -17,7 +21,9 @@
ill_formed_attribute_input,
illegal_floating_point_literal_pattern,
improper_ctypes,
improper_ctypes_definitions,
incomplete_features,
incomplete_include,
indirect_structural_match,
inline_no_sanitize,
invalid_codeblock_attribute,
@ -32,6 +38,7 @@
missing_crate_level_docs,
missing_docs,
missing_fragment_specifier,
mixed_script_confusables,
mutable_borrow_reservation_conflict,
mutable_transmutes,
no_mangle_const_items,
@ -59,6 +66,7 @@
trivial_numeric_casts,
type_alias_bounds,
tyvar_behind_raw_pointer,
unaligned_references,
uncommon_codepoints,
unconditional_panic,
unconditional_recursion,
@ -75,6 +83,7 @@
unused_attributes,
unused_braces,
unused_comparisons,
unused_crate_dependencies,
unused_doc_comments,
unused_extern_crates,
unused_features,

View file

@ -524,8 +524,14 @@ impl Command for ReturnCommand {
None => (),
};
let output = if arguments.is_empty() {
None
} else {
Some(arguments[0].clone())
};
let next_line = call_info.call_line + 1;
CommandResult::GoTo(None, GoToValue::Line(next_line))
CommandResult::GoTo(output, GoToValue::Line(next_line))
} else {
push_to_call_stack(state, &call_info);
CommandResult::Continue(None)

View file

@ -344,7 +344,13 @@ impl Command for IfCommand {
self.package.clone(),
) {
Ok(if_else_info) => {
match condition::eval_condition(arguments, state, variables, commands) {
match condition::eval_condition(
arguments,
instructions,
state,
variables,
commands,
) {
Ok(passed) => {
if passed {
let next_line = if if_else_info.else_lines.is_empty() {
@ -422,7 +428,7 @@ impl Command for ElseIfCommand {
state: &mut HashMap<String, StateValue>,
variables: &mut HashMap<String, String>,
_output_variable: Option<String>,
_instructions: &Vec<Instruction>,
instructions: &Vec<Instruction>,
commands: &mut Commands,
line: usize,
) -> CommandResult {
@ -436,7 +442,7 @@ impl Command for ElseIfCommand {
CommandResult::GoTo(None, GoToValue::Line(next_line))
} else {
let if_else_info = call_info.meta_info.clone();
match condition::eval_condition(arguments, state, variables, commands) {
match condition::eval_condition(arguments, instructions, state, variables, commands) {
Ok(passed) => {
if passed {
let next_line = if call_info.else_line_index + 1 < if_else_info.else_lines.len() {

View file

@ -10,6 +10,7 @@ mod lib;
mod man;
mod math;
mod net;
mod noop;
mod not;
pub(crate) mod on_error;
mod process;
@ -32,6 +33,7 @@ pub(crate) fn load(commands: &mut Commands) -> Result<(), ScriptError> {
commands.set(eval::create(PACKAGE))?;
commands.set(is_defined::create(PACKAGE))?;
commands.set(man::create(PACKAGE))?;
commands.set(noop::create(PACKAGE))?;
commands.set(not::create(PACKAGE))?;
commands.set(read::create(PACKAGE))?;
commands.set(release::create(PACKAGE))?;

View file

@ -0,0 +1,26 @@
```sh
result = ftp_get --host <hostname> [--port 21] [--username <user name>] [--password <password>] [--path <path>] [--type <A/I>] --remote-file <file name> --local-file <file name>
```
Invokes the FTP GET command from the given connection and file details.
#### Parameters
* --host - The host name or IP to connect to
* --port - Optional port number to use (by default 21)
* --username - Optional user name used to login (if not user or password provided, no login operation will be invoked)
* --password - Optional password used to login (if not user or password provided, no login operation will be invoked)
* --path - Optional path on the remote server to invoke operation on
* --type - Optional setting of the transfer type as A (ascii) I (image, binary)
* --remote-file - The remote file to download
* --local-file - The target local file name
#### Return Value
true if operation was completed.
#### Examples
```sh
ftp_get --host myhost --username someuser --password 12345 --remote-file README.md --local-file README.md
```

View file

@ -0,0 +1,97 @@
use crate::sdk::std::net::ftp::{validate_and_run_with_connection, Options};
use crate::utils::io::create_empty_file;
use crate::utils::pckg;
use duckscript::types::command::{Command, CommandResult};
use ftp::{FtpError, FtpStream};
use std::fs::OpenOptions;
use std::io::{BufWriter, Error, Read, Write};
#[cfg(test)]
#[path = "./mod_test.rs"]
mod mod_test;
fn write_file(reader: &mut dyn Read, target_file: &str) -> Result<(), Error> {
let mut file = OpenOptions::new().append(true).open(target_file)?;
{
let mut writer = BufWriter::new(&mut file);
let mut buffer = [0; 10240];
loop {
let read_size = reader.read(&mut buffer)?;
if read_size > 0 {
writer.write_all(&buffer[0..read_size])?;
} else {
break;
}
}
writer.flush()?;
}
file.sync_all()?;
Ok(())
}
#[derive(Clone)]
pub(crate) struct CommandImpl {
package: String,
}
impl Command for CommandImpl {
fn name(&self) -> String {
pckg::concat(&self.package, "Get")
}
fn aliases(&self) -> Vec<String> {
vec!["ftp_get".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 {
validate_and_run_with_connection(
&arguments,
&|options: &Options| -> Result<(), String> {
if options.remote_file.is_none() {
Err("Missing remote file name".to_string())
} else if options.local_file.is_none() {
Err("Missing local file name.".to_string())
} else {
Ok(())
}
},
&mut |options: &Options, ftp_stream: &mut FtpStream| -> CommandResult {
let options_clone = options.clone();
let remote_file = options_clone.remote_file.unwrap();
let local_file = options_clone.local_file.unwrap();
match create_empty_file(&local_file) {
Ok(_) => {
match ftp_stream.retr(&remote_file, |reader| {
match write_file(reader, &local_file) {
Ok(_) => Ok(()),
Err(error) => Err(FtpError::InvalidResponse(error.to_string())),
}
}) {
Ok(_) => CommandResult::Continue(Some(true.to_string())),
Err(error) => CommandResult::Error(error.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,12 @@
use super::*;
use crate::test;
#[test]
fn common_functions() {
test::test_common_command_functions(create(""));
}
#[test]
fn run_no_args() {
test::run_script_and_error(vec![create("")], "out = ftp_get", "out");
}

View file

@ -0,0 +1,26 @@
```sh
handle = ftp_get_in_memory --host <hostname> [--port 21] [--username <user name>] [--password <password>] [--path <path>] [--type <A/I>] --remote-file <file name>
```
Invokes the FTP GET command from the given connection and file details.
#### Parameters
* --host - The host name or IP to connect to
* --port - Optional port number to use (by default 21)
* --username - Optional user name used to login (if not user or password provided, no login operation will be invoked)
* --password - Optional password used to login (if not user or password provided, no login operation will be invoked)
* --path - Optional path on the remote server to invoke operation on
* --type - Optional setting of the transfer type as A (ascii) I (image, binary)
* --remote-file - The remote file to download
#### Return Value
The binary data handle.
#### Examples
```sh
handle = ftp_get_in_memory --host myhost --username someuser --password 12345 --remote-file README.md
text = bytes_to_string ${handle}
```

View file

@ -0,0 +1,80 @@
use crate::sdk::std::net::ftp::{validate_and_run_with_connection, Options};
use crate::utils::pckg;
use crate::utils::state::put_handle;
use duckscript::types::command::{Command, CommandResult, Commands};
use duckscript::types::instruction::Instruction;
use duckscript::types::runtime::StateValue;
use ftp::FtpStream;
use std::collections::HashMap;
#[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, "GetInMemory")
}
fn aliases(&self) -> Vec<String> {
vec!["ftp_get_in_memory".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 requires_context(&self) -> bool {
true
}
fn run_with_context(
&self,
arguments: Vec<String>,
state: &mut HashMap<String, StateValue>,
_variables: &mut HashMap<String, String>,
_output_variable: Option<String>,
_instructions: &Vec<Instruction>,
_commands: &mut Commands,
_line: usize,
) -> CommandResult {
validate_and_run_with_connection(
&arguments,
&|options: &Options| -> Result<(), String> {
if options.remote_file.is_none() {
Err("Missing remote file name".to_string())
} else {
Ok(())
}
},
&mut |options: &Options, ftp_stream: &mut FtpStream| -> CommandResult {
let options_clone = options.clone();
let remote_file = options_clone.remote_file.unwrap();
match ftp_stream.simple_retr(&remote_file) {
Ok(binary) => {
let key = put_handle(state, StateValue::ByteArray(binary.into_inner()));
CommandResult::Continue(Some(key))
}
Err(error) => CommandResult::Error(error.to_string()),
}
},
)
}
}
pub(crate) fn create(package: &str) -> Box<dyn Command> {
Box::new(CommandImpl {
package: package.to_string(),
})
}

View file

@ -0,0 +1,12 @@
use super::*;
use crate::test;
#[test]
fn common_functions() {
test::test_common_command_functions(create(""));
}
#[test]
fn run_no_args() {
test::run_script_and_error(vec![create("")], "out = ftp_get_in_memory", "out");
}

View file

@ -0,0 +1,28 @@
```sh
handle = ftp_list --host <hostname> [--port 21] [--username <user name>] [--password <password>] [--path <path>]
```
Invokes the FTP LIST command from the given connection details and path.<br>
Returns a handle to an array of all response entries.
#### Parameters
* --host - The host name or IP to connect to
* --port - Optional port number to use (by default 21)
* --username - Optional user name used to login (if not user or password provided, no login operation will be invoked)
* --password - Optional password used to login (if not user or password provided, no login operation will be invoked)
* --path - Optional path on the remote server to invoke operation on
#### Return Value
A handle to an array holding all entries.
#### Examples
```sh
handle = ftp_list --host myhost --username someuser --password 12345
for entry in ${handle}
echo ${entry}
end
```

View file

@ -0,0 +1,75 @@
use crate::sdk::std::net::ftp::{run_with_connection, Options};
use crate::utils::pckg;
use crate::utils::state::put_handle;
use duckscript::types::command::{Command, CommandResult, Commands};
use duckscript::types::instruction::Instruction;
use duckscript::types::runtime::StateValue;
use ftp::FtpStream;
use std::collections::HashMap;
#[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, "List")
}
fn aliases(&self) -> Vec<String> {
vec!["ftp_list".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 requires_context(&self) -> bool {
true
}
fn run_with_context(
&self,
arguments: Vec<String>,
state: &mut HashMap<String, StateValue>,
_variables: &mut HashMap<String, String>,
_output_variable: Option<String>,
_instructions: &Vec<Instruction>,
_commands: &mut Commands,
_line: usize,
) -> CommandResult {
run_with_connection(&arguments, &mut |_options: &Options,
ftp_stream: &mut FtpStream|
-> CommandResult {
match ftp_stream.list(None) {
Ok(output) => {
let mut array = vec![];
for item in output {
array.push(StateValue::String(item));
}
let key = put_handle(state, StateValue::List(array));
CommandResult::Continue(Some(key))
}
Err(error) => CommandResult::Error(error.to_string()),
}
})
}
}
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 = ftp_list", "out");
}
#[test]
fn run_valid() {
test::run_script_and_validate(
vec![create("")],
"out = ftp_list --host test.rebex.net --username demo --password password",
CommandValidation::Contains("out".to_string(), "handle:".to_string()),
);
}

View file

@ -0,0 +1,252 @@
mod get;
mod get_in_memory;
mod list;
mod nlst;
mod put;
mod put_in_memory;
use crate::utils::pckg;
use duckscript::types::command::{CommandResult, Commands};
use duckscript::types::error::ScriptError;
use ftp::types::{FileType, FormatControl};
use ftp::FtpStream;
static PACKAGE: &str = "ftp";
pub(crate) fn load(commands: &mut Commands, parent: &str) -> Result<(), ScriptError> {
let package = pckg::concat(parent, PACKAGE);
commands.set(get::create(&package))?;
commands.set(get_in_memory::create(&package))?;
commands.set(list::create(&package))?;
commands.set(nlst::create(&package))?;
commands.set(put::create(&package))?;
commands.set(put_in_memory::create(&package))?;
Ok(())
}
#[derive(Clone)]
pub(crate) enum TransferType {
// ascii
Ascii,
// image/binary
Binary,
}
#[derive(Clone)]
pub(crate) struct Options {
host: Option<String>,
port: u32,
user_name: Option<String>,
password: Option<String>,
path: Option<String>,
transfer_type: Option<TransferType>,
remote_file: Option<String>,
local_file: Option<String>,
content: Option<String>,
}
impl Options {
fn new() -> Options {
Options {
host: None,
port: 21,
user_name: None,
password: None,
path: None,
transfer_type: None,
remote_file: None,
local_file: None,
content: None,
}
}
}
enum LookingFor {
Flag,
Host,
Port,
UserName,
Password,
Path,
TransferType,
RemoteFile,
LocalFile,
Content,
}
pub(crate) fn run_with_connection(
arguments: &Vec<String>,
func: &mut FnMut(&Options, &mut FtpStream) -> CommandResult,
) -> CommandResult {
validate_and_run_with_connection(
arguments,
&|_options: &Options| -> Result<(), String> { Ok(()) },
func,
)
}
pub(crate) fn validate_and_run_with_connection(
arguments: &Vec<String>,
validate_input: &Fn(&Options) -> Result<(), String>,
func: &mut FnMut(&Options, &mut FtpStream) -> CommandResult,
) -> CommandResult {
match parse_common_options(&arguments) {
Ok(options) => match validate_input(&options) {
Ok(_) => run_in_ftp_connection_context(
&options,
&mut |ftp_stream: &mut FtpStream| -> CommandResult { func(&options, ftp_stream) },
),
Err(error) => CommandResult::Error(error),
},
Err(error) => CommandResult::Error(error),
}
}
fn parse_common_options(arguments: &Vec<String>) -> Result<Options, String> {
let mut options = Options::new();
let mut looking_for = LookingFor::Flag;
for argument in arguments {
match looking_for {
LookingFor::Flag => match argument.as_str() {
"--host" => looking_for = LookingFor::Host,
"--port" => looking_for = LookingFor::Port,
"--username" => looking_for = LookingFor::UserName,
"--password" => looking_for = LookingFor::Password,
"--path" => looking_for = LookingFor::Path,
"--type" => looking_for = LookingFor::TransferType,
"--remote-file" => looking_for = LookingFor::RemoteFile,
"--local-file" => looking_for = LookingFor::LocalFile,
"--content" => looking_for = LookingFor::Content,
_ => (),
},
LookingFor::Host => {
if !argument.is_empty() {
options.host = Some(argument.to_string());
}
looking_for = LookingFor::Flag;
}
LookingFor::Port => {
if !argument.is_empty() {
options.port = match argument.parse::<u32>() {
Ok(value) => value,
Err(error) => return Err(error.to_string()),
};
}
looking_for = LookingFor::Flag;
}
LookingFor::UserName => {
if !argument.is_empty() {
options.user_name = Some(argument.to_string());
}
looking_for = LookingFor::Flag;
}
LookingFor::Password => {
if !argument.is_empty() {
options.password = Some(argument.to_string());
}
looking_for = LookingFor::Flag;
}
LookingFor::Path => {
if !argument.is_empty() {
options.path = Some(argument.to_string());
}
looking_for = LookingFor::Flag;
}
LookingFor::TransferType => {
if !argument.is_empty() {
match argument.as_str() {
"A" => options.transfer_type = Some(TransferType::Ascii),
"I" => options.transfer_type = Some(TransferType::Binary),
_ => {
return Err(
"Invalid transfer type provided, only A or I are supported."
.to_string(),
)
}
}
looking_for = LookingFor::Flag;
}
}
LookingFor::RemoteFile => {
if !argument.is_empty() {
options.remote_file = Some(argument.to_string());
}
looking_for = LookingFor::Flag;
}
LookingFor::LocalFile => {
if !argument.is_empty() {
options.local_file = Some(argument.to_string());
}
looking_for = LookingFor::Flag;
}
LookingFor::Content => {
if !argument.is_empty() {
options.content = Some(argument.to_string());
}
looking_for = LookingFor::Flag;
}
}
}
Ok(options)
}
fn run_in_ftp_connection_context(
options: &Options,
func: &mut FnMut(&mut FtpStream) -> CommandResult,
) -> CommandResult {
match options.host {
Some(ref host) => {
let mut connection_string = String::new();
connection_string.push_str(host);
connection_string.push(':');
connection_string.push_str(&options.port.to_string());
match FtpStream::connect(&connection_string) {
Ok(mut ftp_stream) => {
let options_cloned = options.clone();
// login if needed
if options.user_name.is_some() && options.password.is_some() {
let user_name = options_cloned.user_name.unwrap();
let password = options_cloned.password.unwrap();
if let Err(error) = ftp_stream.login(&user_name, &password) {
return CommandResult::Error(error.to_string());
}
}
// move to another directory
if let Some(path) = options_cloned.path {
if let Err(error) = ftp_stream.cwd(path.as_str()) {
return CommandResult::Error(error.to_string());
}
}
// set transfer type
if let Some(transfer_type) = options_cloned.transfer_type {
let file_type = match transfer_type {
TransferType::Ascii => FileType::Ascii(FormatControl::Default),
TransferType::Binary => FileType::Image,
};
if let Err(error) = ftp_stream.transfer_type(file_type) {
return CommandResult::Error(error.to_string());
}
}
let result = func(&mut ftp_stream);
ftp_stream.quit().unwrap_or(());
result
}
Err(error) => CommandResult::Error(error.to_string()),
}
}
None => CommandResult::Error("No host provided.".to_string()),
}
}

View file

@ -0,0 +1,28 @@
```sh
handle = ftp_nlst --host <hostname> [--port 21] [--username <user name>] [--password <password>] [--path <path>]
```
Invokes the FTP NLST command from the given connection details and path.<br>
Returns a handle to an array of all response entries.
#### Parameters
* --host - The host name or IP to connect to
* --port - Optional port number to use (by default 21)
* --username - Optional user name used to login (if not user or password provided, no login operation will be invoked)
* --password - Optional password used to login (if not user or password provided, no login operation will be invoked)
* --path - Optional path on the remote server to invoke operation on
#### Return Value
A handle to an array holding all entries.
#### Examples
```sh
handle = ftp_nlst --host myhost --username someuser --password 12345
for entry in ${handle}
echo ${entry}
end
```

View file

@ -0,0 +1,75 @@
use crate::sdk::std::net::ftp::{run_with_connection, Options};
use crate::utils::pckg;
use crate::utils::state::put_handle;
use duckscript::types::command::{Command, CommandResult, Commands};
use duckscript::types::instruction::Instruction;
use duckscript::types::runtime::StateValue;
use ftp::FtpStream;
use std::collections::HashMap;
#[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, "NLst")
}
fn aliases(&self) -> Vec<String> {
vec!["ftp_nlst".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 requires_context(&self) -> bool {
true
}
fn run_with_context(
&self,
arguments: Vec<String>,
state: &mut HashMap<String, StateValue>,
_variables: &mut HashMap<String, String>,
_output_variable: Option<String>,
_instructions: &Vec<Instruction>,
_commands: &mut Commands,
_line: usize,
) -> CommandResult {
run_with_connection(&arguments, &mut |_options: &Options,
ftp_stream: &mut FtpStream|
-> CommandResult {
match ftp_stream.nlst(None) {
Ok(output) => {
let mut array = vec![];
for item in output {
array.push(StateValue::String(item));
}
let key = put_handle(state, StateValue::List(array));
CommandResult::Continue(Some(key))
}
Err(error) => CommandResult::Error(error.to_string()),
}
})
}
}
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 = ftp_nlst", "out");
}
#[test]
fn run_valid() {
test::run_script_and_validate(
vec![create("")],
"out = ftp_nlst --host test.rebex.net --username demo --password password",
CommandValidation::Contains("out".to_string(), "handle:".to_string()),
);
}

View file

@ -0,0 +1,26 @@
```sh
result = ftp_put --host <hostname> [--port 21] [--username <user name>] [--password <password>] [--path <path>] [--type <A/I>] --remote-file <file name> --local-file <file name>
```
Invokes the FTP PUT command from the given connection and file details.
#### Parameters
* --host - The host name or IP to connect to
* --port - Optional port number to use (by default 21)
* --username - Optional user name used to login (if not user or password provided, no login operation will be invoked)
* --password - Optional password used to login (if not user or password provided, no login operation will be invoked)
* --path - Optional path on the remote server to invoke operation on
* --type - Optional setting of the transfer type as A (ascii) I (image, binary)
* --remote-file - The remote file to upload
* --local-file - The source local file to upload
#### Return Value
true if operation was completed.
#### Examples
```sh
ftp_put --host myhost --username someuser --password 12345 --remote-file README.md --local-file README.md
```

View file

@ -0,0 +1,81 @@
use crate::sdk::std::net::ftp::{validate_and_run_with_connection, Options};
use crate::utils::pckg;
use duckscript::types::command::{Command, CommandResult};
use fsio::path::as_path::AsPath;
use ftp::FtpStream;
use std::fs::File;
#[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, "Put")
}
fn aliases(&self) -> Vec<String> {
vec!["ftp_put".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 {
validate_and_run_with_connection(
&arguments,
&|options: &Options| -> Result<(), String> {
if options.remote_file.is_none() {
Err("Missing remote file name".to_string())
} else {
let options_clone = options.clone();
match options_clone.local_file {
Some(local_file) => {
let file_path = local_file.as_path();
if file_path.exists() {
if file_path.is_file() {
Ok(())
} else {
Err("Local path is a directory.".to_string())
}
} else {
Err("Local file not found.".to_string())
}
}
None => Err("Missing local file name.".to_string()),
}
}
},
&mut |options: &Options, ftp_stream: &mut FtpStream| -> CommandResult {
let options_clone = options.clone();
let remote_file = options_clone.remote_file.unwrap();
let local_file = options_clone.local_file.unwrap();
match File::open(local_file) {
Ok(mut file) => match ftp_stream.put(&remote_file, &mut file) {
Ok(_) => CommandResult::Continue(Some(true.to_string())),
Err(error) => CommandResult::Error(error.to_string()),
},
Err(error) => CommandResult::Error(error.to_string()),
}
},
)
}
}
pub(crate) fn create(package: &str) -> Box<dyn Command> {
Box::new(CommandImpl {
package: package.to_string(),
})
}

View file

@ -0,0 +1,12 @@
use super::*;
use crate::test;
#[test]
fn common_functions() {
test::test_common_command_functions(create(""));
}
#[test]
fn run_no_args() {
test::run_script_and_error(vec![create("")], "out = ftp_put", "out");
}

View file

@ -0,0 +1,26 @@
```sh
result = ftp_put_in_memory --host <hostname> [--port 21] [--username <user name>] [--password <password>] [--path <path>] [--type <A/I>] --remote-file <file name> --content <content>
```
Invokes the FTP PUT command from the given connection and file details.
#### Parameters
* --host - The host name or IP to connect to
* --port - Optional port number to use (by default 21)
* --username - Optional user name used to login (if not user or password provided, no login operation will be invoked)
* --password - Optional password used to login (if not user or password provided, no login operation will be invoked)
* --path - Optional path on the remote server to invoke operation on
* --type - Optional setting of the transfer type as A (ascii) I (image, binary)
* --remote-file - The remote file to upload
* --content - The textual content to upload
#### Return Value
true if operation was completed.
#### Examples
```sh
ftp_put_in_memory --host myhost --username someuser --password 12345 --remote-file README.md --content "This is the README content"
```

View file

@ -0,0 +1,64 @@
use crate::sdk::std::net::ftp::{validate_and_run_with_connection, Options};
use crate::utils::pckg;
use duckscript::types::command::{Command, CommandResult};
use ftp::FtpStream;
use std::io::Cursor;
#[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, "PutInMemory")
}
fn aliases(&self) -> Vec<String> {
vec!["ftp_put_in_memory".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 {
validate_and_run_with_connection(
&arguments,
&|options: &Options| -> Result<(), String> {
if options.remote_file.is_none() {
Err("Missing remote file name".to_string())
} else if options.content.is_none() {
Err("Missing content".to_string())
} else {
Ok(())
}
},
&mut |options: &Options, ftp_stream: &mut FtpStream| -> CommandResult {
let options_clone = options.clone();
let remote_file = options_clone.remote_file.unwrap();
let content = options_clone.content.unwrap();
let mut reader = Cursor::new(content.as_bytes());
match ftp_stream.put(&remote_file, &mut reader) {
Ok(_) => CommandResult::Continue(Some(true.to_string())),
Err(error) => CommandResult::Error(error.to_string()),
}
},
)
}
}
pub(crate) fn create(package: &str) -> Box<dyn Command> {
Box::new(CommandImpl {
package: package.to_string(),
})
}

View file

@ -0,0 +1,12 @@
use super::*;
use crate::test;
#[test]
fn common_functions() {
test::test_common_command_functions(create(""));
}
#[test]
fn run_no_args() {
test::run_script_and_error(vec![create("")], "out = ftp_put_in_memory", "out");
}

View file

@ -1,3 +1,4 @@
mod ftp;
mod hostname;
mod http_client;
mod wget;
@ -15,5 +16,7 @@ pub(crate) fn load(commands: &mut Commands, parent: &str) -> Result<(), ScriptEr
commands.set(http_client::create(&package))?;
commands.set(wget::create(&package)?)?;
ftp::load(commands, &package)?;
Ok(())
}

View file

@ -0,0 +1,19 @@
```sh
noop
```
Empty function that does nothing and returns none.
#### Parameters
All parameters are ignored
#### Return Value
None
#### Examples
```sh
noop
```

View file

@ -0,0 +1,39 @@
use crate::utils::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, "Noop")
}
fn aliases(&self) -> Vec<String> {
vec!["noop".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 {
CommandResult::Continue(None)
}
}
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_validate(vec![create("")], "out = noop", CommandValidation::None);
}
#[test]
fn run_multiple_args() {
test::run_script_and_validate(
vec![create("")],
"out = noop 1 2 \"3 4\"",
CommandValidation::None,
);
}

View file

@ -40,14 +40,14 @@ impl Command for CommandImpl {
state: &mut HashMap<String, StateValue>,
variables: &mut HashMap<String, String>,
_output_variable: Option<String>,
_instructions: &Vec<Instruction>,
instructions: &Vec<Instruction>,
commands: &mut Commands,
_line: usize,
) -> CommandResult {
if arguments.is_empty() {
CommandResult::Error("Missing condition".to_string())
} else {
match condition::eval_condition(arguments, state, variables, commands) {
match condition::eval_condition(arguments, instructions, state, variables, commands) {
Ok(passed) => {
let output = !passed;
CommandResult::Continue(Some(output.to_string()))

View file

@ -1,11 +1,12 @@
use crate::types::scope::clear;
use crate::types::scope::set_line_context_name;
use crate::utils::eval;
use crate::utils::state::{get_handles_sub_state, put_handle};
use duckscript::types::command::{Command, CommandResult, Commands, GoToValue};
use duckscript::parser;
use duckscript::types::command::{Command, CommandResult, Commands};
use duckscript::types::error::ScriptError;
use duckscript::types::instruction::{Instruction, InstructionType};
use duckscript::types::instruction::Instruction;
use duckscript::types::runtime::StateValue;
use duckscript::{parser, runner};
use std::collections::HashMap;
#[derive(Clone)]
@ -119,77 +120,8 @@ impl Command for AliasCommand {
handle_option = Some(handle);
}
let mut line = 0;
let mut flow_output = None;
let mut flow_result = None;
loop {
let instruction = if self.instructions.len() > line {
self.instructions[line].clone()
} else {
break;
};
match instruction.instruction_type {
InstructionType::Script(ref script_instruction) => {
let (command_result, _) = runner::run_instruction(
commands,
variables,
state,
&self.instructions,
instruction.clone(),
line,
);
match command_result {
CommandResult::Exit(output) => {
flow_result = Some(CommandResult::Exit(output));
break;
}
CommandResult::Error(error) => {
flow_result = Some(CommandResult::Error(error));
break;
}
CommandResult::Crash(error) => {
flow_result = Some(CommandResult::Crash(error));
break;
}
CommandResult::GoTo(output, goto_value) => {
flow_output = output.clone();
match goto_value {
GoToValue::Label(_) => {
flow_result = Some(CommandResult::Error(
"goto label result not supported in alias command flow.".to_string(),
));
break;
}
GoToValue::Line(line_number) => line = line_number,
}
}
CommandResult::Continue(output) => {
flow_output = output.clone();
match script_instruction.output {
Some(ref output_variable) => {
match output {
Some(value) => {
variables.insert(output_variable.to_string(), value)
}
None => variables.remove(output_variable),
};
}
None => (),
};
line = line + 1;
}
};
}
_ => {
line = line + 1;
}
};
}
let (flow_result, flow_output) =
eval::eval_instructions(&self.instructions, commands, state, variables, 0);
match handle_option {
Some(handle) => {

View file

@ -1,5 +1,6 @@
use crate::utils::eval;
use duckscript::types::command::{CommandResult, Commands};
use duckscript::types::instruction::Instruction;
use duckscript::types::runtime::StateValue;
use std::collections::HashMap;
@ -28,6 +29,7 @@ pub(crate) fn is_true(value: Option<String>) -> bool {
pub(crate) fn eval_condition(
arguments: Vec<String>,
instructions: &Vec<Instruction>,
state: &mut HashMap<String, StateValue>,
variables: &mut HashMap<String, String>,
commands: &mut Commands,
@ -38,7 +40,8 @@ pub(crate) fn eval_condition(
let eval_statement = commands.exists(&arguments[0]);
if eval_statement {
match eval::eval_with_error(&arguments, state, variables, commands) {
match eval::eval_with_instructions(&arguments, instructions, state, variables, commands)
{
CommandResult::Continue(value) => {
let passed = is_true(value);

View file

@ -61,6 +61,7 @@ fn is_true_valid() {
fn eval_condition_empty() {
let result = eval_condition(
vec![],
&vec![],
&mut HashMap::new(),
&mut HashMap::new(),
&mut Commands::new(),
@ -77,6 +78,7 @@ fn eval_condition_empty() {
fn eval_condition_value_true() {
let result = eval_condition(
vec!["true".to_string()],
&vec![],
&mut HashMap::new(),
&mut HashMap::new(),
&mut Commands::new(),
@ -93,6 +95,7 @@ fn eval_condition_value_true() {
fn eval_condition_value_false() {
let result = eval_condition(
vec!["false".to_string()],
&vec![],
&mut HashMap::new(),
&mut HashMap::new(),
&mut Commands::new(),
@ -115,6 +118,7 @@ fn eval_condition_command_true() {
let result = eval_condition(
vec!["test_set".to_string(), "true".to_string()],
&vec![],
&mut HashMap::new(),
&mut HashMap::new(),
&mut commands,
@ -137,6 +141,7 @@ fn eval_condition_command_false() {
let result = eval_condition(
vec!["test_set".to_string(), "false".to_string()],
&vec![],
&mut HashMap::new(),
&mut HashMap::new(),
&mut commands,
@ -159,6 +164,7 @@ fn eval_condition_command_error() {
let result = eval_condition(
vec!["test_error".to_string()],
&vec![],
&mut HashMap::new(),
&mut HashMap::new(),
&mut commands,

View file

@ -1,6 +1,7 @@
use duckscript::parser;
use duckscript::runner;
use duckscript::types::command::{CommandResult, Commands};
use duckscript::types::command::{CommandResult, Commands, GoToValue};
use duckscript::types::instruction::{Instruction, InstructionType};
use duckscript::types::runtime::StateValue;
use std::collections::HashMap;
@ -8,6 +9,30 @@ use std::collections::HashMap;
#[path = "./eval_test.rs"]
mod eval_test;
fn parse(arguments: &Vec<String>) -> Result<Instruction, String> {
let mut line_buffer = String::new();
for argument in arguments {
if argument.contains(" ") {
line_buffer.push('"');
}
line_buffer.push_str(argument);
if argument.contains(" ") {
line_buffer.push('"');
}
line_buffer.push(' ');
}
let line_str = line_buffer
.replace("\r", "")
.replace("\n", "")
.replace("\\", "\\\\");
match parser::parse_text(&line_str) {
Ok(instructions) => Ok(instructions[0].clone()),
Err(error) => Err(error.to_string()),
}
}
pub(crate) fn eval(
arguments: &Vec<String>,
state: &mut HashMap<String, StateValue>,
@ -17,33 +42,10 @@ pub(crate) fn eval(
if arguments.is_empty() {
Ok(CommandResult::Continue(None))
} else {
let mut line_buffer = String::new();
for argument in arguments {
if argument.contains(" ") {
line_buffer.push('"');
}
line_buffer.push_str(argument);
if argument.contains(" ") {
line_buffer.push('"');
}
line_buffer.push(' ');
}
let line_str = line_buffer
.replace("\r", "")
.replace("\n", "")
.replace("\\", "\\\\");
match parser::parse_text(&line_str) {
Ok(instructions) => {
let (command_result, _) = runner::run_instruction(
commands,
variables,
state,
&vec![],
instructions[0].clone(),
0,
);
match parse(arguments) {
Ok(instruction) => {
let (command_result, _) =
runner::run_instruction(commands, variables, state, &vec![], instruction, 0);
Ok(command_result)
}
@ -66,3 +68,121 @@ pub(crate) fn eval_with_error(
Err(error) => CommandResult::Error(error.to_string()),
}
}
pub(crate) fn eval_with_instructions(
arguments: &Vec<String>,
instructions: &Vec<Instruction>,
state: &mut HashMap<String, StateValue>,
variables: &mut HashMap<String, String>,
commands: &mut Commands,
) -> CommandResult {
if arguments.is_empty() {
CommandResult::Continue(None)
} else {
match parse(arguments) {
Ok(instruction) => {
let mut all_instructions = instructions.clone();
all_instructions.push(instruction);
let (flow_result, flow_output) = eval_instructions(
&all_instructions,
commands,
state,
variables,
all_instructions.len() - 1,
);
match flow_result {
Some(result) => match result.clone() {
CommandResult::Crash(error) => CommandResult::Error(error),
_ => result,
},
None => CommandResult::Continue(flow_output),
}
}
Err(error) => CommandResult::Error(error),
}
}
}
pub(crate) fn eval_instructions(
instructions: &Vec<Instruction>,
commands: &mut Commands,
state: &mut HashMap<String, StateValue>,
variables: &mut HashMap<String, String>,
start_line: usize,
) -> (Option<CommandResult>, Option<String>) {
let mut line = start_line;
let mut flow_output = None;
let mut flow_result = None;
loop {
let instruction = if instructions.len() > line {
instructions[line].clone()
} else {
break;
};
match instruction.instruction_type {
InstructionType::Script(ref script_instruction) => {
let (command_result, _) = runner::run_instruction(
commands,
variables,
state,
&instructions,
instruction.clone(),
line,
);
match command_result {
CommandResult::Exit(output) => {
flow_result = Some(CommandResult::Exit(output));
break;
}
CommandResult::Error(error) => {
flow_result = Some(CommandResult::Error(error));
break;
}
CommandResult::Crash(error) => {
flow_result = Some(CommandResult::Crash(error));
break;
}
CommandResult::GoTo(output, goto_value) => {
flow_output = output.clone();
match goto_value {
GoToValue::Label(_) => {
flow_result = Some(CommandResult::Error(
"goto label result not supported in alias command flow."
.to_string(),
));
break;
}
GoToValue::Line(line_number) => line = line_number,
}
}
CommandResult::Continue(output) => {
flow_output = output.clone();
match script_instruction.output {
Some(ref output_variable) => {
match output {
Some(value) => {
variables.insert(output_variable.to_string(), value)
}
None => variables.remove(output_variable),
};
}
None => (),
};
line = line + 1;
}
};
}
_ => {
line = line + 1;
}
};
}
(flow_result, flow_output)
}

View file

@ -7,7 +7,7 @@ fn clear_not_empty
result = array_is_empty ${handle}
assert_false ${result}
result array_clear ${handle}
result = array_clear ${handle}
assert ${result}
result = array_is_empty ${handle}
@ -19,7 +19,7 @@ end
fn clear_empty
handle = array
result array_clear ${handle}
result = array_clear ${handle}
assert ${result}
result = array_is_empty ${handle}

View file

@ -117,3 +117,46 @@ fn test_nested_if2
assert ${valid}
end
fn test_if_call_to_functions
value = _test_return_true
valid = set false
if ${value}
valid = set true
end
assert ${valid}
value = _test_return_false
valid = set true
if ${value}
valid = set false
end
assert ${valid}
valid = set false
if _test_return_true
valid = set true
end
assert ${valid}
valid = set true
if _test_return_false
valid = set false
end
assert ${valid}
valid = set false
if not _test_return_false
valid = set true
end
assert ${valid}
end
fn _test_return_true
return true
end
fn _test_return_false
return false
end

View file

@ -0,0 +1,14 @@
fn test_valid
handle = ftp_get_in_memory --host test.rebex.net --username demo --password password --remote-file readme.txt
text = bytes_to_string ${handle}
release ${handle}
empty = is_empty ${text}
assert_false ${empty}
found = contains ${text} Welcome
assert ${found}
end

View file

@ -0,0 +1,16 @@
fn test_valid
filename = set ./target/_duckscript_test/net/ftp/get/readme.txt
result = ftp_get --host test.rebex.net --username demo --password password --remote-file readme.txt --local-file ${filename}
assert ${result}
text = readfile ${filename}
empty = is_empty ${text}
assert_false ${empty}
found = contains ${text} Welcome
assert ${found}
end

View file

@ -0,0 +1,27 @@
fn test_valid
arr = ftp_list --host test.rebex.net --username demo --password password
empty = array_is_empty ${arr}
assert_false ${empty}
merged = array_join ${arr} ,
found = contains ${merged} pub
assert ${found}
release ${arr}
end
fn test_with_path
arr = ftp_list --host test.rebex.net --username demo --password password --path pub
empty = array_is_empty ${arr}
assert_false ${empty}
merged = array_join ${arr} ,
found = contains ${merged} example
assert ${found}
release ${arr}
end

View file

@ -0,0 +1,27 @@
fn test_valid
arr = ftp_nlst --host test.rebex.net --username demo --password password
empty = array_is_empty ${arr}
assert_false ${empty}
merged = array_join ${arr} ,
found = contains ${merged} pub
assert ${found}
release ${arr}
end
fn test_with_path
arr = ftp_nlst --host test.rebex.net --username demo --password password --path pub
empty = array_is_empty ${arr}
assert_false ${empty}
merged = array_join ${arr} ,
found = contains ${merged} example
assert ${found}
release ${arr}
end

View file

@ -0,0 +1,14 @@
fn test_auth_error
test_enabled = equals ${TEST_FTP_PUT} true
if ${test_enabled}
ftp_put_in_memory --host test.rebex.net --username demo --password password --remote-file readme2.txt --content "test content"
assert_false ${result}
last_error = get_last_error
found = contains "${last_error}" 550
assert ${found}
end
end

View file

@ -0,0 +1,17 @@
fn test_auth_error
test_enabled = equals ${TEST_FTP_PUT} true
if ${test_enabled}
filename = set ./target/_duckscript_test/net/ftp/put/readme.txt
writefile ${filename} "test content"
result = ftp_put --host test.rebex.net --username demo --password password --remote-file readme2.txt --local-file ${filename}
assert_false ${result}
last_error = get_last_error
found = contains "${last_error}" 550
assert ${found}
end
end

17
test/std/noop_test.ds Normal file
View file

@ -0,0 +1,17 @@
fn test_no_args
value = noop
defined = is_defined value
assert_false ${value}
end
fn test_multiple_args
value = noop 1 2 3 4
defined = is_defined value
assert_false ${value}
end