store-flows 0.3.2

Store extension for flows.network
Documentation
//! Give the flow function the ability to store key/value in [Flows.network](https://flows.network)
//!
//! # Quick Start
//!
//! To get started, let's write a very tiny flow function.
//!
//! ```rust
//! use serde_json::json;
//! use lambda_flows::{request_received, send_response};
//! use store_flows::{get, set, Expire, ExpireKind};
//!
//! #[no_mangle]
//! pub fn run() {
//!     if let Some((_qry, _body)) = request_received() {
//!         let mut c = match get("count") {
//!             Some(v) => v.as_u64().unwrap_or_default(),
//!             None => 0,
//!         };
//!
//!         c = c + 1;
//!
//!         set(
//!             "count",
//!             json!(c),
//!             Some(Expire {
//!                 kind: ExpireKind::Ex,
//!                 value: 20,
//!             }),
//!         );
//!
//!         send_response(
//!             200,
//!             vec![(String::from("content-type"), String::from("text/html"))],
//!             c.to_string().as_bytes().to_vec(),
//!         );
//!     }
//! }
//! ```
//!
//! In this [lambda](https://docs.rs/lambda-flows) service flow function,
//! use [set()] and [get()] to store and return the times it has been called.
use http_req::request;
use lazy_static::lazy_static;
use serde_json::Value;

lazy_static! {
    static ref STORE_API_PREFIX: String = String::from(
        std::option_env!("STORE_API_PREFIX").unwrap_or("https://store.flows.network/api")
    );
}

extern "C" {
    fn get_flows_user(p: *mut u8) -> i32;
    fn get_flow_id(p: *mut u8) -> i32;
    fn set_error_log(p: *const u8, len: i32);
}

/// The kind for setting the expire
///
/// `Ex` is for setting the specified expire time, in milliseconds.
/// 'ExAt` is for setting the specified Unix time at which the key will expire, in seconds.
pub enum ExpireKind {
    Ex,
    ExAt,
}

/// The expire object to pass to [set()] function
///
/// If kind is [ExpireKind::Ex], value should be the seconds before expiration.
/// If kind is [ExpireKind::ExAt], value should be the
/// timestamp-seconds at which the key will expire.
pub struct Expire {
    pub kind: ExpireKind,
    pub value: i64,
}

/// Store the key/value pair.
///
/// If the key exists, old value will be replaced by the new value.
///
/// You can also pass expire to set the when then key will expire.
///
pub fn set(key: &str, value: Value, expire: Option<Expire>) {
    unsafe {
        let mut flows_user = Vec::<u8>::with_capacity(100);
        let c = get_flows_user(flows_user.as_mut_ptr());
        flows_user.set_len(c as usize);
        let flows_user = String::from_utf8(flows_user).unwrap();

        let mut flow_id = Vec::<u8>::with_capacity(100);
        let c = get_flow_id(flow_id.as_mut_ptr());
        if c == 0 {
            panic!("Failed to get flow id");
        }
        flow_id.set_len(c as usize);
        let flow_id = String::from_utf8(flow_id).unwrap();

        let mut writer = Vec::new();
        let mut x = serde_json::json!({
            "key": key,
            "value": value
        });

        if let Some(exp) = expire {
            match exp.kind {
                ExpireKind::Ex => {
                    x.as_object_mut()
                        .unwrap()
                        .insert(String::from("ex"), Value::from(exp.value));
                }
                ExpireKind::ExAt => {
                    x.as_object_mut()
                        .unwrap()
                        .insert(String::from("exat"), Value::from(exp.value));
                }
            }
        }

        let x = serde_json::to_vec(&x).unwrap();

        let res = request::post(
            format!(
                "{}/{}/{}/set",
                STORE_API_PREFIX.as_str(),
                flows_user,
                flow_id
            ),
            &x,
            &mut writer,
        )
        .unwrap();

        match res.status_code().is_success() {
            true => {}
            false => {
                set_error_log(writer.as_ptr(), writer.len() as i32);
            }
        }
    }
}

/// Retrieve the stored value by the key.
pub fn get(key: &str) -> Option<Value> {
    unsafe {
        let mut flows_user = Vec::<u8>::with_capacity(100);
        let c = get_flows_user(flows_user.as_mut_ptr());
        flows_user.set_len(c as usize);
        let flows_user = String::from_utf8(flows_user).unwrap();

        let mut flow_id = Vec::<u8>::with_capacity(100);
        let c = get_flow_id(flow_id.as_mut_ptr());
        if c == 0 {
            panic!("Failed to get flow id");
        }
        flow_id.set_len(c as usize);
        let flow_id = String::from_utf8(flow_id).unwrap();

        let mut writer = Vec::new();
        let res = request::post(
            format!(
                "{}/{}/{}/get",
                STORE_API_PREFIX.as_str(),
                flows_user,
                flow_id
            ),
            key.as_bytes(),
            &mut writer,
        )
        .unwrap();

        match res.status_code().is_success() {
            true => match serde_json::from_slice::<Value>(&writer) {
                Ok(v) => Some(v),
                Err(_) => None,
            },
            false => None,
        }
    }
}

/// Delete the stored key/value pair.
///
/// Return the old value if the key exists.
pub fn del(key: &str) -> Option<Value> {
    unsafe {
        let mut flows_user = Vec::<u8>::with_capacity(100);
        let c = get_flows_user(flows_user.as_mut_ptr());
        flows_user.set_len(c as usize);
        let flows_user = String::from_utf8(flows_user).unwrap();

        let mut flow_id = Vec::<u8>::with_capacity(100);
        let c = get_flow_id(flow_id.as_mut_ptr());
        if c == 0 {
            panic!("Failed to get flow id");
        }
        flow_id.set_len(c as usize);
        let flow_id = String::from_utf8(flow_id).unwrap();

        let mut writer = Vec::new();
        let res = request::post(
            format!(
                "{}/{}/{}/del",
                STORE_API_PREFIX.as_str(),
                flows_user,
                flow_id
            ),
            key.as_bytes(),
            &mut writer,
        )
        .unwrap();

        match res.status_code().is_success() {
            true => match serde_json::from_slice::<Value>(&writer) {
                Ok(v) => Some(v),
                Err(_) => None,
            },
            false => None,
        }
    }
}