Getting Rusty

Rust

My current (and very much WIP) application is written in Python. I utilize Metrc’s API via GET requests to build a local database of facilities, packages, harvests, sales, and transfers for further data analysis and it also provides an extremely efficient way of interacting with the traceability system that isn’t a web app. Currently taking advantage of SQLite, a local database makes for a much smoother experience.

As of late, I’ve been going through the Rust book and the exercises provided by Rustlings to wet my whistle, as it were. I’ve got some light background in embedded programming and moving back to a lower level language has been attractive recently. Why not dive in to Rust?

It’s been a lot of fun getting familiar with Rust idioms and design patterns. The book has been amazing for grasping fundamentals and I’m constantly dreaming up all the new ways I can refactor what I’ve got so far.

All this fluff is really just to demonstrate my two Metrc “Hello, world.” style first-steps to a full rewrite, one using reqwest:

// The first attempt using reqwest.

use std::env;
use serde_json;

fn print_json(data: &str) {
    let v: serde_json::Value = serde_json::from_str(data).unwrap();

    println!("{:#?}", v);
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let user = env::var("METRC_VENDOR_API_KEY")?;
    let pass = env::var("METRC_USER_API_KEY")?;

    let client = reqwest::Client::new();

    let resp = client.get("https://api-mt.metrc.com/facilities/v1")
        .basic_auth(user.clone(), Some(pass.clone()))
        .send()
        .await?
        .text()
        .await?;

    print_json(&resp);

    Ok(())
}

…and another using the more bare-bones hyper:

use std::env;
use std::io::Write;
use hyper::{Client, Method, Request, Body};
use hyper_tls::HttpsConnector;
use base64::write::EncoderWriter as Base64Encoder;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let username = env::var("METRC_VENDOR_API_KEY")?;
    let password = env::var("METRC_USER_API_KEY")?;

    let mut header_value = b"Basic ".to_vec();
    let mut encoder = 
        Base64Encoder::new(&mut header_value, base64::STANDARD);
    
    write!(encoder, "{}:", username).unwrap();
    write!(encoder, "{}", password).unwrap();

    let req = Request::builder()
        .method(Method::GET)
        .uri("https://api-mt.metrc.com/facilities/v1/")
        .header("Authorization", header_value)
        .body(Body::empty())
        .expect("request builder");

    let https = HttpsConnector::new();
    let client = Client::builder()
        .build::<_, hyper::Body>(https);

    let response = client.request(req).await?;

    println!("Status: {}", response.status());

    let buf = hyper::body::to_bytes(response).await?;

    println!("Body: {:?}", buf);

    Ok(())
}

Hyper remains as advertised - a bit more involved as it’s more like the bricks to build custom libraries from the ground up. One has to set up the https connector, as well as generate the basic authorization header value. There is a const in the header struct that I have yet to piece together called AUTHORIZATION.

I’m still wrapping my head around ownership and returning results, options, and using traits but I can’t wait to keep plugging away!