Macro algorithmia::algo_entrypoint
[−]
[src]
macro_rules! algo_entrypoint { ($t:ty, $apply:ident, Algo::$method:ident) => { ... }; ($t:ty, $apply:ident, $p:path) => { ... }; (&str) => { ... }; (&[u8]) => { ... }; (&JsonValue) => { ... }; (AlgoInput) => { ... }; ($t:ty) => { ... }; (&str => Algo::$i:ident) => { ... }; (&[u8] => Algo::$i:ident) => { ... }; (&JsonValue => Algo::$i:ident) => { ... }; (AlgoInput => Algo::$i:ident) => { ... }; ($t:ty => Algo::$i:ident) => { ... }; (&str => $p:path) => { ... }; (&[u8] => $p:path) => { ... }; (&JsonValue => $p:path) => { ... }; (AlgoInput => $p:path) => { ... }; ($t:ty => $p:path) => { ... }; }
Macro for implementing Entrypoint
or DecodedEntryPoint
boilerplate in an algorithm
Algorithmia support for Rust algorithms is hard-coded to call Algo::apply(AlgoInput)
but for the sake of convenience the EntryPoint
trait provides a default implementation
of apply
that delegates to apply_str
, apply_bytes
, or apply_json
depending on
the variant of AlgoInput
. The DecodedEntryPoint
trait overrides the default apply
to provide a method that uses a deserialized associated type when calling apply_decoded
.
This pattern is incredibly flexible, but adds boilerplate and still requires
converting output and error types back.
The algo_entrypoint!
macro hides all that boilerplate code behind a single macro invocation.
algo_entrypoint(type => your_fn)
wires up the boilerplate for calling your_fn(type)
.
Additionally, you can use the macro as algo_entrypoint(type)
which is equivalent to
algo_entrypoint!(type => apply)
.
Use the following types:
&str
if your algorithm accepts text input&[u8]
if your algorithm accepts binary input- Any deserializeable type if your algorithm accepts JSON input
&JsonValue
is you want your algorithm to work directly with the typed JSON inputAlgoInput
if you want to work with the full enum of possible input types
In all cases, the return value of your_fn
should be Result<T, E>
where:
T
implementsInto<AlgoOutput>
which includesString
,Vec<u8>
,JsonValue
, and boxed* serializeable typesE
implementsInto<Box<::std::error::Error>>
which includesString
and allError
types
* support for unboxed serializeable depends on specialization implemented behind the 'nightly' feature.
Examples
Text Input/Output
To set the entrypoint to a function that receives and returns text:
algo_entrypoint!(&str); fn apply(input: &str) -> Result<String, String> { unimplemented!() }
which generates:
#[derive(Default)] pub struct Algo; impl EntryPoint for Algo { fn apply_str(&self, input: &str) -> Result<AlgoOutput, Box<::std::error::Error>> { apply(input).map(AlgoOutput::from).map_err(|err| err.into()) } } fn apply(input: &str) -> Result<String, String> { unimplemented!() }
Binary Input/Output
To set the entrypoint to a function that receives and returns binary data:
algo_entrypoint!(&[u8]); fn apply(input: &[u8]) -> Result<Vec<u8>, String> { unimplemented!() }
JSON Input/Output
To set the entrypoint to a function that receives and returns JSON data,
you can work directly with the JsonValue
:
algo_entrypoint!(&JsonValue); fn apply(input: &JsonValue) -> Result<JsonValue, String> { unimplemented!() }
Although, often the preferred way to work with JSON is to automatically deserialize into and serialize from a custom type.
#[derive(Deserialize)] pub struct MyInput { corpus: String, msg: String } #[derive(Serialize)] pub struct MyOutput { probabilities: Vec<(String, f32)> } algo_entrypoint!(MyInput); fn apply(input: MyInput) -> Result<Box<MyOutput>, String> { unimplemented!() }
Note: Box
for serializeable output is currently required for lack of specialization.
The specialization implementation already exists behind the nightly
feature.
Alternatively, you can return AlgoOutput
via AlgoOutput::from(&myOutput)
The previous example expands into:
#[derive(Deserialize)] pub struct MyInput { corpus: String, msg: String }; #[derive(Serialize)] pub struct MyOutput { probabilities: Vec<(String, f32)> }; #[derive(Default)] pub struct Algo; impl DecodedEntryPoint for Algo { type Input (String, String); fn apply_decoded(&self, input: MyInput) -> Result<AlgoOutput, Box<::std::error::Error>> { apply(input).map(AlgoOutput::from).map_err(|err| err.into()) } } fn apply(input: MyInput) -> Result<Box<MyOutput>, String> { unimplemented!() }
Customizing Default
And finally, it is possible to provide a custom Default
implementation
and pass that state into your method by prefixing your_fn
with
the Algo::
namespace.
pub struct Algo{ init_id: String } algo_entrypoint!(&str => Algo::hello_text); impl Algo { fn hello_text(&self, input: &str) -> Result<String, String> { unimplemented!() } } impl Default for Algo { fn default() -> Algo { Algo { init_id: "foo".into() } } }