21 releases (stable)

3.0.0-beta.3 Jan 30, 2020
3.0.0-beta.2 Apr 6, 2019
3.0.0-beta Feb 17, 2019
2.1.3 Aug 22, 2017
0.7.0 Sep 30, 2015

#158 in Machine learning

MIT license

89KB
1.5K SLoC

Algorithmia Rust Client Library

A rust client library for the Algorithmia API.

Documentation

Build Status Crates.io

Getting started

The Algorithmia Rust client is published to crates.io. Add algorithmia = "3.0" to the dependencies in your Cargo.toml and run cargo install.

Instantiate an Algorithmia client using your API key:

use algorithmia::Algorithmia;

let client = Algorithmia::client("YOUR_API_KEY")?;

Now you're ready to call algorithms.

Calling algorithms

This client provides an Algorithm type (generally created by client.algo(..)) which provides methods for calling an algorithm hosted on the Algorithmia platform. The following examples of calling algorithms are organized by type of input/output which vary between algorithms.

Note: a single algorithm may have different input and output types, or accept multiple types of input, so consult the algorithm's description for usage examples specific to that algorithm.

Text input/output

Call an algorithm with text input by simply passing &str into Algorithm::pipe. If the algorithm output is text, call the as_string method on the response.

let algo = client.algo("algo://demo/Hello/0.1.1");
let response = algo.pipe("HAL 9000")?;
println!("{}", response.into_string()?);

JSON input/output

Call an algorithm with JSON input calling pipe with a reference to a type that implements serde::Serialize. If the algorithm output is JSON, you can call decode to deserialize the resonse into a type that implements serde::Deserialize.

This includes many primitive types, tuples, Vec, and other collection types from the standard library:

let algo = client.algo("algo://WebPredict/ListAnagrams/0.1.0");
let response = algo.pipe(vec!["transformer", "terraforms", "retransform"])?;
let output: Vec<String> = response.decode()?;
// -> ["transformer", "retransform"] as Vec<String>

Implementing Serialize or Deserialize for your custom types is generally as easy as adding a derive annotation.

#[derive(Deserialize, Serialize)]
struct MyStruct {
    some_field: String,
    other_field: u32,
}
// now you can call `pipe` with `&MyStruct` or `decode` into `MyStruct`

With serde_json, you can also sue the json! macro or implement custom serialization/deserialization. See serde.rs for more details on using serde.

Alternatively, you can work with raw JSON strings:

let response = algo.pipe_json(r#"["transformer", "terraforms", "retransform"]"#)?;
let output = response.to_json()?;
// -> "[\"transformer\", \"retransform\"]"

Binary input/output

Call an algorithm with binary input by calling the pipe method with a slice of bytes (&[u8]). If the algorithm response is binary data, then call the as_bytes method on the response to obtain a byte vector (Vec<u8>).

let mut input = Vec::new();
File::open("/path/to/bender.jpg").read_to_end(&mut input);
let response = client.algo("opencv/SmartThumbnail/0.1").pipe(&input)?;
let output = response.as_bytes()?;
// -> Vec<u8>

Error handling

True to the nature of explicit error handling in rust, the pipe and response parsing methods all return Result-wrapped types:

let algo = client.algo("algo://demo/Hello/0.1.1");
match algo.pipe(&[]) {
    Ok(response) => { /* success */ },
    Err(err) => println!("error calling demo/Hello: {}", err),
}
// -> error calling demo/Hello: apply() functions do not match input data

Note: this crate makes use of the error-chain crate, so for many error variants, there may be additional errors chained to err.source().

Request options

The client exposes options that can configure algorithm requests via a builder pattern. This includes support for changing the timeout or indicating that the API should include stdout in the response.

let mut algo = client.algo("algo://demo/Hello/0.1.1");
let algo = algo.timeout(10).stdout(true);
let response = algo.pipe(input)?;
if let Some(ref stdout) = response.metadata.stdout {
    println!("{}", stdout);
}

Note: stdout(true) is ignored if you do not have access to the algorithm source.

Managing data

The Algorithmia Rust client also provides a way to manage both Algorithmia hosted data and data from Dropbox or S3 accounts that you've connected to you Algorithmia account.

This client provides a DataFile type (generally created by client.file(uri)) and a DataDir type (generally created by client.dir(uri)) that provide methods for managing your data.

Create directories

Create directories by instantiating a DataDir object and calling create() with a DataAcl:

let robots = client.dir("data://.my/robots");
robots.create(DataAcl::default())

let robots = client.dir("dropbox://robots");
robots.create(DataAcl::default())

Upload files to a directory

Upload files by calling put on a DataFile object, or by calling put_file on a DataDir object.

let robots = client.dir("data://.my/robots");

// Upload local file
robots.put_file("/path/to/Optimus_Prime.png");
// Write a text file
robots.child::<DataFile>("Optimus_Prime.txt").put("Leader of the Autobots");
// Write a binary file
robots.child::<DataFile>("Optimus_Prime.key").put(b"transform");

Download contents of file

Download files by calling get on a DataFile object which returns a Result-wrapped DataResponse that implements Read:

// Download and locally save file
let mut t800_png_reader = client.file("data://.my/robots/T-800.png").get()?;
let mut t800_png = File::create("/path/to/save/t800.png")?;
std::io::copy(&mut t800_png_reader, &mut t800_png);

// Get the file's contents as a string
let mut t800_text_reader = robots.file("data://.my/robots/T-800.txt").get()?;
let mut t800_text = String::new();
t800_text_reader.read_to_string(&mut t800_text);

// Get the file's contents as a byte array
let mut t800_png_reader = robots.file("data://.my/robots/T-800.png").get()?;
let mut t800_bytes = Vec::new();
t800_png_reader.read_to_end(&mut t800_bytes);

Delete files and directories

Delete files and directories by calling delete on their respective DataFile or DataDir object. DataDirectories take a force parameter that indicates whether the directory should be deleted if it contains files or other directories.

client.file("data://.my/robots/C-3PO.txt").delete();
client.dir("data://.my/robots").delete(false);

List directory contents

Iterate over the contents of a directory using the iterator returned by calling list on a DataDir object:

let my_robots = client.dir("data://.my/robots");
for entry in my_robots.list() {
    match entry {
        Ok(DirEntry::Dir(dir)) => println!("Directory {}", dir.to_data_uri()),
        Ok(DirEntry::File(file)) => println!("File {}", file.to_data_uri()),
        Err(err) => println!("Error listing my robots: {}", err),
    }
}

Examples

For examples that use this client, see:

Build & Test

This project is built and tested with cargo:

cargo build
cargo test
cargo doc --no-deps

Dependencies

~14–24MB
~355K SLoC