119 lines
4.1 KiB
Rust
119 lines
4.1 KiB
Rust
|
/// The types used to build the `help` documentation for a module, as well as determine the
|
||
|
/// arguments that can be automatically detected by Icepick.
|
||
|
pub mod help {
|
||
|
use serde::{Deserialize, Serialize};
|
||
|
|
||
|
/// An operation that can be exposed to a frontend.
|
||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||
|
pub struct Operation {
|
||
|
/// The name of the argument, in the format expected by the deserializer.
|
||
|
pub name: String,
|
||
|
|
||
|
/// A description of what the operation does.
|
||
|
pub description: String,
|
||
|
|
||
|
/// The arguments to be provided to the operation's deserializer.
|
||
|
pub arguments: Vec<Argument>,
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
/// The context of whether a signature is signed, needs to be signed, or has been signed.
|
||
|
#[derive(Serialize, Deserialize, Clone)]
|
||
|
pub enum SigningContext {
|
||
|
/// This operation accepts a signed blob.
|
||
|
Signed,
|
||
|
|
||
|
/// This operation accepts an unsigned blob and will return a signed blob.
|
||
|
Signer,
|
||
|
|
||
|
/// This operation will return an unsigned blob.
|
||
|
Unsigned,
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
||
|
#[serde(rename_all = "lowercase")]
|
||
|
pub enum ArgumentType {
|
||
|
Required,
|
||
|
Optional,
|
||
|
}
|
||
|
|
||
|
/// An argument to an operation.
|
||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||
|
pub struct Argument {
|
||
|
/// The name of the argument, in the format expected by the deserializer.
|
||
|
pub name: String,
|
||
|
|
||
|
/// A description of the format and parameters of an argument.
|
||
|
pub description: String,
|
||
|
|
||
|
/// The type of argument - this may affect how it displays in the frontend.
|
||
|
pub r#type: ArgumentType,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Implementation methods for Icepick Modules, performed over command I/O using JSON.
|
||
|
pub trait Module {
|
||
|
/// The error type returned by an operation.
|
||
|
type Error: std::error::Error + 'static;
|
||
|
|
||
|
/// The request type. See [`Module::handle_request`] for more information.
|
||
|
type Request: serde::de::DeserializeOwned + std::fmt::Debug + 'static;
|
||
|
|
||
|
/// Describe the operations interface of RPC calls. This information will be used to generate a
|
||
|
/// frontend for users to perform requests.
|
||
|
fn describe_operations() -> Vec<help::Operation>;
|
||
|
|
||
|
/// Handle an incoming request. Requests can often be formed using the following schema:
|
||
|
///
|
||
|
/// ```rust
|
||
|
/// #[derive(serde::Serialize, serde::Deserialize, Debug)]
|
||
|
/// #[serde(tag = "operation", rename_all="kebab-case")]
|
||
|
/// enum Request {
|
||
|
/// Transfer {
|
||
|
/// // This is the amount of the "primary" currency you'd be receiving, such as Ether,
|
||
|
/// // Bitcoin, or Sol.
|
||
|
/// amount: f64,
|
||
|
///
|
||
|
/// // This would be your native wallet address type. In this example, we'll just use a
|
||
|
/// // String.
|
||
|
/// to_address: String,
|
||
|
///
|
||
|
/// // This is the address of the derivation account.
|
||
|
/// from_account: u32,
|
||
|
/// }
|
||
|
///
|
||
|
/// Stake {
|
||
|
/// amount: f64,
|
||
|
/// from_account: u32,
|
||
|
/// }
|
||
|
///
|
||
|
/// Sign {
|
||
|
/// blob:
|
||
|
/// }
|
||
|
/// }
|
||
|
/// ```
|
||
|
fn handle_request(request: Self::Request) -> Result<serde_json::Value, Self::Error>;
|
||
|
|
||
|
fn run_responder() -> Result<(), Box<dyn std::error::Error>> {
|
||
|
let mut lines = std::io::stdin().lines();
|
||
|
while let Some(line) = lines.next().transpose()? {
|
||
|
let value: serde_json::Value = serde_json::from_str(&line)?;
|
||
|
if let Some(serde_json::Value::String(operation)) = value.get("operation") {
|
||
|
if operation == "help" {
|
||
|
println!("{}", serde_json::to_string(&Self::describe_operations())?);
|
||
|
continue;
|
||
|
}
|
||
|
if operation == "exit" {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
// TODO: error handling
|
||
|
let request: Self::Request = serde_json::from_value(value).expect("good value");
|
||
|
let response = Self::handle_request(request)?;
|
||
|
println!("{}", serde_json::to_string(&response).unwrap());
|
||
|
}
|
||
|
Ok(())
|
||
|
}
|
||
|
}
|