mirror of
https://github.com/sagiegurari/duckscript
synced 2024-10-14 11:53:05 +00:00
New ftp_get and ftp_get_in_memory commands
This commit is contained in:
parent
7142e94c4a
commit
cb3b2d2cbf
|
@ -2,6 +2,8 @@
|
|||
|
||||
### v0.5.1
|
||||
|
||||
* New ftp_get command.
|
||||
* New ftp_get_in_memory command.
|
||||
* New ftp_list command.
|
||||
* New ftp_nlst command.
|
||||
|
||||
|
|
68
docs/sdk.md
68
docs/sdk.md
|
@ -111,6 +111,8 @@
|
|||
* [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::process::Execute (exec)](#std__process__Execute)
|
||||
|
@ -4143,6 +4145,72 @@ 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
|
||||
|
|
26
duckscript_sdk/src/sdk/std/net/ftp/get/help.md
Normal file
26
duckscript_sdk/src/sdk/std/net/ftp/get/help.md
Normal 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
|
||||
```
|
97
duckscript_sdk/src/sdk/std/net/ftp/get/mod.rs
Executable file
97
duckscript_sdk/src/sdk/std/net/ftp/get/mod.rs
Executable 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(&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(),
|
||||
})
|
||||
}
|
12
duckscript_sdk/src/sdk/std/net/ftp/get/mod_test.rs
Normal file
12
duckscript_sdk/src/sdk/std/net/ftp/get/mod_test.rs
Normal 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");
|
||||
}
|
26
duckscript_sdk/src/sdk/std/net/ftp/get_in_memory/help.md
Normal file
26
duckscript_sdk/src/sdk/std/net/ftp/get_in_memory/help.md
Normal 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}
|
||||
```
|
80
duckscript_sdk/src/sdk/std/net/ftp/get_in_memory/mod.rs
Executable file
80
duckscript_sdk/src/sdk/std/net/ftp/get_in_memory/mod.rs
Executable 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(),
|
||||
})
|
||||
}
|
12
duckscript_sdk/src/sdk/std/net/ftp/get_in_memory/mod_test.rs
Normal file
12
duckscript_sdk/src/sdk/std/net/ftp/get_in_memory/mod_test.rs
Normal 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");
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
mod get;
|
||||
mod get_in_memory;
|
||||
mod list;
|
||||
mod nlst;
|
||||
|
||||
|
@ -12,6 +14,8 @@ 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))?;
|
||||
|
||||
|
@ -34,6 +38,8 @@ pub(crate) struct Options {
|
|||
password: Option<String>,
|
||||
path: Option<String>,
|
||||
transfer_type: Option<TransferType>,
|
||||
remote_file: Option<String>,
|
||||
local_file: Option<String>,
|
||||
}
|
||||
|
||||
impl Options {
|
||||
|
@ -45,6 +51,8 @@ impl Options {
|
|||
password: None,
|
||||
path: None,
|
||||
transfer_type: None,
|
||||
remote_file: None,
|
||||
local_file: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,17 +65,34 @@ enum LookingFor {
|
|||
Password,
|
||||
Path,
|
||||
TransferType,
|
||||
RemoteFile,
|
||||
LocalFile,
|
||||
}
|
||||
|
||||
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) => run_in_ftp_connection_context(
|
||||
&options,
|
||||
&mut |ftp_stream: &mut FtpStream| -> CommandResult { func(&options, ftp_stream) },
|
||||
),
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
@ -85,6 +110,8 @@ fn parse_common_options(arguments: &Vec<String>) -> Result<Options, String> {
|
|||
"--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,
|
||||
_ => (),
|
||||
},
|
||||
LookingFor::Host => {
|
||||
|
@ -135,6 +162,18 @@ fn parse_common_options(arguments: &Vec<String>) -> Result<Options, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
14
test/std/net/ftp/ftp_get_in_memory_test.ds
Normal file
14
test/std/net/ftp/ftp_get_in_memory_test.ds
Normal 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
|
||||
|
16
test/std/net/ftp/ftp_get_test.ds
Normal file
16
test/std/net/ftp/ftp_get_test.ds
Normal 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
|
||||
|
Loading…
Reference in a new issue