Writing a Rust library crate.

#WHO

Matt Gathu

Software Engineer at JUMO

Pythonista and Rust Enthusiast

Rust is a systems  language that pursues the trifecta of:

  • speed

  • safety

  • concurrency

Speed

  • No Garbage Collection
  • LLVM
  • Zero cost abstractions
  • Minimal Runtime
  • Generics monomorphization

Safety

  • Memory safety
    • You never explicitly free memory.
  • Automatic Resource Management
    • You never explicitly release resources.
  • Ownership Model

 

Rust means never having to close a socket
~ Yehuda Katz

Concurrency

Rust’s Package Manager, Build tool and test runner.

$ cargo new africastalking_gateway 

Project Layout

$ cd africastalking_gateway
$ tree .
.
├── Cargo.toml
└── src
    └── lib.rs

1 directory, 2 files

Cargo.toml

[package]
name = "africastalking_gateway"
version = "0.1.0"
authors = ["Matt Gathu <mattgathu@gmail.com>"]

[dependencies]
$ cargo test 

Running Tests

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

   Doc-tests africastalking_gateway

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
$ cargo doc 

Generating Docs

.....
Documenting tokio-core v0.1.10
Documenting tokio-tls v0.1.3
Documenting tokio-proto v0.1.1
Documenting hyper v0.11.6
Documenting hyper-tls v0.1.2
Documenting reqwest v0.8.1
Documenting africastalking_gateway v0.1.0 (...)
 Finished dev [unoptimized + debuginfo] target(s) in 35.19 secs
$ cargo doc --open

Viewing Docs

AT's Rust Crate

  • messaging endpoints
  • subscription endpoints
  • voice endpoints
  • airtime endpoints
  • payments endpoints
  • user data endpoint

Implementation

  • Gateway Struct
  • endpoints urls as attributes
  • endpoints logic as methods
#[derive(Debug)]
pub struct AfricasTalkingGateway {
    username: String,
    api_key: String,
    env: String,
    user_data_url: String,
    sms_url: String,
    voice_url: String,
    sms_subscription_url: String,
    send_airtime_url: String,
    mobi_payment_checkout_url: String,
    mobi_payment_b2c_url: String,
    mobi_payment_b2b_url: String,
}
impl AfricasTalkingGateway {
    pub fn new(
      username: &str, 
      api_key: &str, 
      env: &str) -> Self {
      
      Self {
        username: username.into(),
        api_key: api_key.into(),
        env: env.into(),
        user_data_url: ...,
        sms_url: ...,
        ...
      }
    }

}
pub fn get_user_data(&self) -> Result<json::Value> {
  let url = format!("{}?username={}", self.user_data_url, self.username);
  let val: json::Value = self.send_request(&url, None)?.json()?;

  Ok(val)
}

Testing and Docs

#[cfg(test)]
mod tests {
  use super::*;
  use std::env;

  #[test]
  fn it_works() {}

  #[test]
  fn fetch_user_data() {
    let username = env::var("AFRICAS_TALKING_USERNAME").unwrap();
    let apikey = env::var("AFRICAS_TALKING_APIKEY").unwrap();
    let gway = AfricasTalkingGateway::new(&username, &apikey, "sandbox");

    let data: json::Value = gway.get_user_data().unwrap();
    assert!(data["UserData"].is_object());
  }
}
/// Sends airtime. [docs reference](http://docs.africastalking.com/airtime/sending)
///
/// `recipients` is a json array of the format
///
/// ```json,ignore
/// [ 
///   {
///     "phoneNumber":"+254711XXXYYY",
///     "amount":"KES X"
///   },
///   {
///     "phoneNumber":"+254733YYYZZZ",
///     "amount":"KES Y"
///   }
/// ]
/// ```
pub fn send_airtime(&self, recipients: json::Value) 
  -> Result<json::Value> {
  ...
}

Publishing to Crates.io

$ cargo publish
Made with Slides.com