Some(number) of ways to calculate a Fibonacci Number in Rust

I write a fibonacci fn several ways, exploring the Rust and timing in different versions

A Fibonacci spiral found in nature, in a nautilus shell.
A Fibonacci number divided by the previous one approaches Phi, the Golden Ratio. That’s an Internet hole that can last for hours, if you let it! However, don’t believe everything you read about Phi.

Solving Fibonacci Numbers is the start of Chapter 3 on Dynamic Programming (more on that below) in Erickson’s Algorithm book. I had started with Peasant Multiplication, then the merge sort, and most fun – the n Queens Problem. With Fibonacci Number calculating though, there are a couple of obvious ways to code this and some presented questions for me with Rust.

I became interested in comparing a couple of these ways – both in thinking about the Rust code and looking at the timing. Then my eyes popped out when I saw the difference in the release build times!

Recursive Backtracing

Pseudo code, Page 97 from Algorithm by Jeff Erickson
Clean, Simple Recursion

First is probably closest to the pseudo code for the classic solution for Fibonacci. Define what 0 and 1 return and solve for the given number, recursing all the way back to 2 (which adds the fixed answers for 0 and 1):

fn backtrace_fib(fib_num: u128) -> u128 {
    if fib_num == 0 || fib_num == 1 {
        return fib_num;
    }
    backtrace_fib(fib_num - 1) + backtrace_fib(fib_num - 2)
}

Backtracing With Memoization

Next, especially important if your function will be called multiple times, is to add memoization. That is, once you calculate an answer for a given input, remember than answer so you can quickly respond with it later, skipping the math.

I don’t like that, with the way I did this, I have to pass in my HashMap where I’m storing the answers. Is there any way to have some `static living private variable that only this function would see, but would last the whole run of the program?? There are other places I could store away data, like memcache and redis.

fn backtrace_memo_fib(memo: &mut HashMap<u128, u128>, fib_num: u128) -> u128 {
    match memo.get(&fib_num).map(|answer| answer.clone()) {
        Some(result) => result,
        None => {
            let result = match fib_num {
                0 | 1 => fib_num,
                n => backtrace_memo_fib(memo, n - 1) + backtrace_memo_fib(memo, n - 2),
            };
            memo.insert(fib_num, result.clone());
            result
        }
    }
}



Dynamic Programming

So, Jeff Erickson, author of the Algorithms book, explains another way solve for a Fibonacci Number involves using dynamic programming. But this IS your grandfathers “programming” (not ours).

The word ‘programming’ does not refer to writing code, but rather to the older sense of planning or scheduling, typically by filling in a table. For example, sports programs and theater programs are schedules of important events (with ads); television programming involves filling each available time slot with a show (and ads); degree programs are schedules of classes to be taken (with ads).

Erickson, Jeff (2018, December 29). Algorithms. pp. 100. Retrieved from Algorithms: http://algorithms.wtf

For this one, I just kept the memoization inside the function. We then purposely fill the HashMap on our way up, referencing already-calculated answers, and so never descend into levels of recursion.

Pseudo code, Page 99 from Algorithm by Jeff Erickson
fn dynamic_fib(fib_num: u128) -> u128 {
    let mut memo = HashMap::new();
    memo.insert(0, 0);
    memo.insert(1, 1);
    match fib_num {
        0 | 1 => {} // already set
        n => {
            for i in 2..=n {
                let result = *memo.get(&(i - 1)).unwrap() + *memo.get(&(i - 2)).unwrap();
                memo.insert(i, result);
            }
        }
    };
    *memo.get(&fib_num).unwrap()
}

Cached Backtracing Function

So, as I was trying to figure out how to store some data in a function for future calls (if that is even possible), I came across the cached crate. “cached provides implementations of several caching structures as well as a handy macro for defining memoized functions.” How interesting – just what I needed and the memoization is even hidden from ME. Ok, I’ll do the simple, recursive, backtracing function but cache the results.

#[cached(size = 200)]
fn cached_fib(fib_num: u128) -> u128 {
    if fib_num == 0 || fib_num == 1 {
        return fib_num;
    }
    cached_fib(fib_num - 1) + cached_fib(fib_num - 2)
}

Fibonacci Fight

So, I wrap all of those up into main() so I can start comparing them. That code is pretty boring, so check it out in the repo. Mostly I run each flavor of the algorithm, timing the result and print it all out. The first one looks like:

    let now = Instant::now();
    let _ = backtrace_fib(fib_num);
    let elapsed = now.elapsed();
    print_results(fib_num, "simple backtracing/recursion", elapsed);

I also added a test, just to be sure each is giving the correct answer.

#[test]
fn test_each_version() {
    assert_eq!(backtrace_fib(20), 6765);
    assert_eq!(backtrace_memo_fib(&mut HashMap::new(), 20), 6765);
    assert_eq!(dynamic_fib(20), 6765);
    assert_eq!(cached_fib(20), 6765);
}

Fibonacci Racing

And then some code to time each version, and call it a second and third time, so we can see the memoization at work.

Of course, your results will vary by CPU, but look at this run for Fibonacci Number 50 with the debug build (where the differences are huge). This is on an Amazon t2.medium EC2 server:

The first time solving will be the slowest

Solving fib:50 with simple backtracing/recursion took     287244288946 ns
Solving fib:50 with backtracing/recursion with memoization took 211289 ns
Solving fib:50 with dynamic programming with memoization took   167291 ns
Solving fib:50 with cached function took                        214175 ns

What about solving it a second or third time, anyone faster this time?

Solving fib:50 with simple backtracing/recursion took     287262389809 ns
Solving fib:50 with backtracing/recursion with memoization took 202481 ns
Solving fib:50 with dynamic programming with memoization took   162527 ns
Solving fib:50 with cached function took                          6285 ns

Solving fib:50 with simple backtracing/recursion took     287273113221 ns
Solving fib:50 with backtracing/recursion with memoization took 185609 ns
Solving fib:50 with dynamic programming with memoization took   158492 ns
Solving fib:50 with cached function took                          6496 ns

By the way, Fibonacci Number 50 is 12586269025 which (divided by Fib Num 49) approximates phi as 1.618033988749895

But wow, check out these numbers with a release build!!

The first time solving will be the slowest

Solving fib:50 with simple backtracing/recursion took              806 ns
Solving fib:50 with backtracing/recursion with memoization took  16128 ns
Solving fib:50 with dynamic programming with memoization took     8052 ns
Solving fib:50 with cached function took                         19495 ns

What about solving it a second or third time, anyone faster this time?

Solving fib:50 with simple backtracing/recursion took             596 ns
Solving fib:50 with backtracing/recursion with memoization took  8142 ns
Solving fib:50 with dynamic programming with memoization took    7576 ns
Solving fib:50 with cached function took                          724 ns

Solving fib:50 with simple backtracing/recursion took             596 ns
Solving fib:50 with backtracing/recursion with memoization took  7549 ns
Solving fib:50 with dynamic programming with memoization took    7327 ns
Solving fib:50 with cached function took                          746 ns

By the way, Fibonacci Number 50 is 12586269025 which (divided by Fib Num 49) approximates phi as 1.618033988749895

A Really Big Number

And it handles a Fibonacci Number of 186, though any higher and casting the answers as f64s to divide and get Phi breaks. The fastest “first” calculation for Fibonacci Number 186 was simple backtracing/recursion and took only 820 ns and it’s kinda big:

By the way, Fibonacci Number 186 is 332825110087067562321196029789634457848 which (divided by Fib Num 185) approximates phi as 1.6180339887498947

Update #1

unreliablebazooka on reddit points out my dynamic_fib is a poor use of a HashMap and offered a suggestion I now added as better_dynamic_fib. It uses a tuple (SO much easier and cleaner) and in the category of optimization, it only keeps the tuple around for the past 2 Fibonacci numbers in the chain, not the whole list. Much, much faster – the fasted first execution yet, in fact!

fn better_dynamic_fib(fib_num: u128) -> u128 {
    let mut memo = (0, 1);

    match fib_num {
        0 | 1 => fib_num,
        _ => {
            for _ in 2..=fib_num {
                memo = (memo.1, memo.0 + memo.1)
            }
            memo.1
        }
    }
}
First and additional runs:

Solving fib:20 with dynamic programming with memoization via tuple took 706 ns

(A Few) Advanced Variable Types in Rust

Get a firm grasp of each of these smart pointers and other advanced variables in Rust: Box, Cell, RefCell, Rc, Arc, RwLock, Mutex, OnceCell (and there are others!)

Programmer on laptop, presumably trying to figure out how to get access to his variable in several threads of his Rust program.
Keep one eye on your code at all times!

“I haven’t seen Evil Dead II yet”. Much is made about this simple question in the movie adaption of High Fidelity. Does “yet” mean the person does, indeed, intend to see the film? Jack Black’s character is having real trouble with the concept – not only does he know that the speaker, John Cusack’s character, has seen Evil Dead II, but what idiot wouldn’t see it, “because it’s a brilliant film. It’s so funny, and violent, and the soundtrack kicks so much ass.” I love this exchange, but I’m a fan of the film anyway. It is not always clear to me how to handle advanced variable types Rust, yet.

I think of these as wrappers that add abilities (and restrictions) to a variable. They give a variable super powers since the Rust compiler is so strict about what you can and can’t do with variables.


Box<T>

PROVIDES:
Smart pointer that forces your variable’s value to be stored on the heap instead of the stack. The Box<> variable itself is just a pointer so its size is obvious and can, itself, be stored on the stack.

RESTRICTIONS:

USEFUL WHEN:
If the size of an item cannot be determined at compile time it will complain if the default is to store it on the stack (where a calculable size is necessary). Using Box<> will force the storage on the heap where a fixed size is not needed. For example, a recursive data-structure, including enums, will not work on the stack because a concrete size cannot be calculated. Turning the recursive field into a Box<> means it stores a pointer which CAN be sized. The example in the docs being:

enum List<T> {
    Cons(T, Box<List<T>>),
    Nil,
}

Also useful if you have a very large-sized T, and want to transfer ownership of that variable without it being copied each time.

NOTABLY PROVIDES:
just see the rust-lang.org docs

EXAMPLES/DISCUSSION:
https://doc.rust-lang.org/stable/rust-by-example/std/box.html
https://www.koderhq.com/tutorial/rust/smart-pointer/
https://manishearth.github.io/blog/2017/01/10/rust-tidbits-box-is-special/

Setting the value of a simple Box<> variable is easy enough and getting the
value back looks very normal:

fn main() {
    let answer = Box::new(42);
    println!("The answer is : {}", answer);
}



Cell<T>

PROVIDES:
You can have multiple, shared references to the Cell<>(and thus, access to the value inside with .get()) and yet still mutate the value inside (with .set()). This is called interior mutability because the value inside can be changed but mut on the Cell<> itself is not needed. The inner value can only be set by calling a method on the Cell<>.

RESTRICTIONS:
It is not possible to get a reference to what is inside the Cell, only a copy of the value. Also, Cell does not implement sync, so it cannot be given to a different thread, which ensures safety.

USEFUL WHEN:
Usually used for small values, such as counters or flags, where you need multiple shared references to the value AND be allowed to mutate it at the same time, in a guaranteed safe way.

NOTABLY PROVIDES:
.set() to set the value inside
.get() to get a copy of the value inside
.take() to get a copy of the value inside AND reset the value inside to default.
see the rust-lang.org docs

EXAMPLES/DISCUSSION:
https://hub.packtpub.com/shared-pointers-in-rust-challenges-solutions/
https://ricardomartins.cc/2016/06/08/interior-mutability

Setting the inner value of a Cell<> is only possible with a method call which is how it maintains safety:

use std::cell::Cell;
fn main() {
    let answer = Cell::new(0);
    answer.set(42);
    println!("The answer is : {}", answer.get());
}

RefCell<T>

PROVIDES:
RefCell<> is very similar to Cell<> except it adds borrow checking, but at run-time instead of compile time! This means, unlike Cell<>, it is possible to write RefCell<> code which will panic!(). You borrow() a ref to the inner value for read-only or borrow_mut() in order to change it.

RESTRICTIONS:
borrow() will panic if a borrow_mut() is in place, and borrow_mut() will panic if either type is in place.

USEFUL WHEN:

NOTABLY PROVIDES:
.borrow() to get a copy of the value at the ref
.borrow_mut() to set the value at the ref
.try_borrow() and .try_borrow_mut() will return a Result<> or error instead of a panic!().
see the rust-lang.org docs

EXAMPLES/DISCUSSION:
https://ricardomartins.cc/2016/06/08/interior-mutability (again)

You must successfully borrow_mut() the RefCell<> in order to set the value (by dereferencing) and then simply borrow() it to retrieve the value:

use std::cell::RefCell;
fn main() {
    let answer = RefCell::new(0);
    *answer.borrow_mut() = 42;
    println!("The answer is : {}", answer.borrow());
}

whereas, something as simple as this compiles, but panics at run-time. Imagine how much more obscure this code could be. Remember, any number of read-only references or exactly 1 read-write reference and nothing else – although for RefCell, this is enforced at run-time:

use std::cell::RefCell;
fn main() {
    let answer = RefCell::new(0);
    let break_things = answer.borrow_mut();
    println!("The initial value is : {}", *break_things);
    *answer.borrow_mut() = 42;
    println!("The answer is : {}", answer.borrow());
}

Rc<T>

PROVIDES:
Adds the feature of run-time reference counting to your variable, but this is the simple, lower-cost version – it is not thread safe.

RESTRICTIONS:
Right from the docs “you cannot generally obtain a mutable reference to something inside an Rc. If you need mutability, put a Cell or RefCell inside the Rc“. So while there is a get_mut() method, it’s easy to just use a Cell<> inside.

USEFUL WHEN:
You need run-time reference counting of a variable so it hangs around until the last reference of it is gone.

NOTABLY PROVIDES:
.clone() – get a new copy of the pointer to the same value, upping the reference count by 1.
see the rust-lang.org docs

EXAMPLES/DISCUSSION:
https://blog.sentry.io/2018/04/05/you-cant-rust-that#refcounts-are-not-dirty

Note that in the example below, my_answer is still pointing to valid memory even when correct_answer is dropped, because the Rc<> had an internal count of “2” and drops it to “1”, leaving the storage of “42” still valid.

use std::rc::Rc;
fn main() {
    let correct_answer = Rc::new(42);
    let my_answer = Rc::clone(&correct_answer);

    println!("The correct answer is : {}", correct_answer);
    drop(correct_answer);

    println!("And you got : {}", my_answer);
}

Arc<T>

PROVIDES:
Arc<> is an atomic reference counter, very similar to Rc<> above but thread-safe.

RESTRICTIONS:
More expensive than Rc<>. Also note, the <T> you store must have the Send and Sync traits. So an Arc<RefCell<T>> will not work because RefCell<> is not Sync.

USEFUL WHEN:
Same as Rc<>, You need run-time reference counting of a variable so it hangs around until the last reference of it is gone, but safe across threads as long as the inner <T> is.

NOTABLY PROVIDES:
see the rust-lang.org docs

EXAMPLES/DISCUSSION:
https://medium.com/@DylanKerler1/how-arc-works-in-rust-b06192acd0a6

Same idea as with Rc<>, we just show it working across multiple threads (and then sleep for just 10ms to let those threads finish).

use std::sync::Arc;
use std::thread;
use std::time::Duration;
fn main() {
    let answer = Arc::new(42);

    for threadno in 0..5 {
        let answer = Arc::clone(&answer);
        thread::spawn(move || {
            println!("Thread {}, answer is: {}", threadno + 1, answer);
        });
    }
    let ten_ms = Duration::from_millis(10);
    thread::sleep(ten_ms);
}



Mutex<T>

PROVIDES:
Mutual exclusion lock protecting shared data, even across threads.

RESTRICTIONS:
Any thread which panics will “poison” the Mutex<> and make it inaccessible to all threads. The T stored must allow Send but Sync is not necessary.

USEFUL WHEN:
working on it!

NOTABLY PROVIDES:
see the rust-lang.org docs

EXAMPLES/DISCUSSION:
https://doc.rust-lang.org/book/ch16-03-shared-state.html

use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
fn main() {
    let answer = Arc::new(Mutex::new(42));

    for thread_no in 0..5 {
        let changer = Arc::clone(&answer);
        thread::spawn(move || {
            let mut changer = changer.lock().unwrap();
            println!("Setting answer to thread_no: {}", thread_no + 1,);
            *changer = thread_no + 1;
        });
    }
    let ten_ms = Duration::from_millis(10);
    thread::sleep(ten_ms);

    if answer.is_poisoned() {
        println!("Mutex was poisoned :(");
    } else {
        println!("Mutex survived :)");
        let final_answer = answer.lock().unwrap();
        println!("Ended with answer: {}", final_answer);
    }
}

RwLock<T>

PROVIDES:
Similar to RefCell, but thread safe. borrow() is read(), borrow_mut is write(). They don’t return an option, they will block until they do get the lock.

RESTRICTIONS:
Any thread which panics while a write lock is in place will “poison” the RwLock<> and make it inaccessible to all threads. A panic! during a read lock does not poison the RwLock. The T stored must allow both Send and Sync.

USEFUL WHEN:
working on it!

NOTABLY PROVIDES:
see the rust-lang.org docs

EXAMPLES/DISCUSSION:

Slightly fancier example, that shows getting both read() and write() locks on the value. If nothing panics, we should see the answer at the end.

use std::sync::{Arc, RwLock};
use std::thread;
use std::time::Duration;
fn main() {
    let answer = Arc::new(RwLock::new(42));

    for thread_no in 0..5 {
        if thread_no % 2 == 1 {
            let changer = Arc::clone(&answer);
            thread::spawn(move || {
                let mut changer = changer.write().unwrap();
                println!("Setting answer to thread_no: {}", thread_no + 1,);
                *changer = thread_no + 1;
            });
        } else {
            let reader = Arc::clone(&answer);
            thread::spawn(move || {
                let reader = reader.read().unwrap();
                println!(
                    "Checking  answer in thread_no: {}, value is {}",
                    thread_no + 1,
                    *reader
                );
            });
        }
    }
    let ten_ms = Duration::from_millis(10);
    thread::sleep(ten_ms);

    if answer.is_poisoned() {
        println!("Mutex was poisoned :(");
    } else {
        println!("Mutex survived :)");
        let final_answer = answer.read().unwrap();
        println!("Ended with answer: {}", final_answer);
    }
}
Checking answer in thread_no: 1, value is 42
Checking answer in thread_no: 3, value is 42
Setting answer to thread_no: 2
Checking answer in thread_no: 5, value is 2
Setting answer to thread_no: 4
Mutex survived :)
Ended with answer: 4

Summary

There are more, plus many custom types, some I’ve even used like the crate once_cell. I started using that for the web app I was (am?) working on and wrote a little about it. Also, as you saw in the last two examples, you can combine types when you need multiple functionalities. I have included these examples in a GitHub repo, pointers.

I’ll probably hear about or (much more slowly) learn about mistakes I’ve made in wording here or come up with much better examples and excuses for using these various types, so I’ll try to update this post as I do. I see using this myself as a reference until I am really familiar with each of these types. Obviously, any mistakes here are mine alone as I learn Rust and not from any of the links or sources I listed!

Also, lots of help from 3 YouTubers I’ve been watching – the best examples can been seen as they write code and explain why they need something inside an Rc<> or in a Mutex<>. Check out their streams and watch over their shoulder as they code!!

OAuth Requests, APIs, Diesel, and Sessions

Part of a Series: Designing a Full-Featured WebApp with Rust
Part 1: Piecing Together a Rust Web Application
Part 2: My Next Step in Rust Web Application Dev
Part 3: It’s Not a Web Application Without a Database
Part 4: Better Logging for the Web Application
Part 5: Rust Web App Session Management with AWS
Part 6: OAuth Requests, APIs, Diesel, and Sessions
Part 7: Scraping off the Dust: Redeploy of my Rust web app
Part 8: Giving My App Secrets to the AWS SecretManager

Intertwined woven basket material.. much like Oauth, accounts, APIs, diesel, and sessions are in this project (probably any project)
Things are starting to intertwine…

Some big changes in the repository just recently. I added Google Signin and Facebook Signin OAuth connections. I’m thinking I may not even configure an internal password on the site for users and instead just require one of those options. Probably I’ll add more, like 500px.com and/or Flickr, given the site’s purpose. A password field is still in my database though, so I haven’t given up the idea completely. Also, the OAuth requests create accounts using Diesel.

Users (now identified as shooters – we photographers haven’t given up that term) – are now written to the db. I really fought with one Diesel feature, so that bit is still commented out in the code. In addition, I added my first API to POST to – so another step with the Rocketcrate as well! I’d like to work my way into playing with a GraphQL endpoint so I can play with that as well!! (What’s the limit on Crate dependencies in a project anyway?!) I’m starting to think I won’t be able to tackle all of this in a single post – but let start!

OAuth vs Old and New Accounts

When a user arrives on the site, I check for a cookie with a session id (see my previous post). I decided, for now, I would use the User Agent (plus some random characters) as a fingerprint for creating the session id. So, when I am able to get a session_id from a cookie, I want to verify the User Agent is the same and that the session hasn’t expired. If the user arrives brand new, without a cookie, I immediately create an empty, no-user session for them. All of this is done, for now, right at the top of my index() route.

<src/routes.rs>

...
#[get("/")]
pub fn index(mut cookies: Cookies, nginx: Nginx) -> rocket_contrib::templates::Template {
    let session = get_or_setup_session(&mut cookies, &nginx);
    ...

After the index page loads, it shows the Google and Facebook Sign In buttons. Clicking one of those, they do the validation dance and get permission from the user. When that is granted, my app gets a token back which I send up to the server via a POST to /api/v1/tokensignin.

<src/api.rs>

use rocket::{http::Cookies, post, request::Form, FromForm};

use crate::oauth::*;
use crate::routes::Nginx;
use crate::session::*;

#[derive(FromForm)]
pub struct OAuthReq {
    pub g_token: Option<String>,  // google login req
    pub fb_token: Option<String>, // facebook login req`
    pub name: String,
    pub email: String,
}

#[post("/api/v1/tokensignin", data = "<oauth_req>")]
pub fn tokensignin(mut cookies: Cookies, nginx: Nginx,
        oauth_req: Form<OAuthReq>) -> String
{
    let mut session = get_or_setup_session(&mut cookies, &nginx);

    if let Some(token) = &oauth_req.g_token {
        match verify_google_oauth(&mut session, &token,
            &oauth_req.name, &oauth_req.email)
        {
            true => {
                session.google_oauth = true;
                save_session_to_ddb(&mut session);
                "success".to_string()
            }
            false => {
                session.google_oauth = false;
                save_session_to_ddb(&mut session);
                "failed".to_string()
            }
        }
    } else if let Some(token) = &oauth_req.fb_token {
        match verify_facebook_oauth(&mut session, &token,
            &oauth_req.name, &oauth_req.email)
        {
            true => {
                session.facebook_oauth = true;
                save_session_to_ddb(&mut session);
                "success".to_string()
            }
            false => {
                session.facebook_oauth = false;
                save_session_to_ddb(&mut session);
                "failed".to_string()
            }
        }
    } else {
        "no token sent".to_string()
    }
}

OAuth Requests via HTTP POSTs

This is how you allow for a POST and form data to come in – you setup a struct (OAuthReq in my example) of what you expect and bring that in as an input param. Plus, I am also bringing in any cookies that arrive with the request plus some Nginx headers so I have access to UserAgent. In the code so far, I’m either verifying a Google or Facebook token. Let’s look at the Google example (the Facebook one is nearly the same). Here are the relevant parts, but I’ll break some pieces down and go through it:

<src/oauth.rs>

...
pub fn verify_google_oauth(
    session: &mut Session,
    token: &String,
    name: &String,
    email: &String,
) -> bool {
    let mut google = google_signin::Client::new();
    google.audiences.push(CONFIG.google_api_client_id.clone());

    let id_info = google.verify(&token).expect("Expected token to be valid");
    let token = id_info.sub.clone();

    verify_token(session, "google".to_string(), &token, &name, &email)
}

Which leads right away to a big match:

fn verify_token(
    session: &mut Session,
    vendor: String,
    token: &String,
    name: &String,
    email: &String,
) -> bool {
    use crate::schema::oauth::dsl::*;
    use crate::schema::shooter::dsl::*;
    let connection = connect_pgsql();
    match oauth
        .filter(oauth_vendor.eq(&vendor))
        .filter(oauth_user.eq(&token))
        .first::<Oauth>(&connection)
    {

With the OK arm:

        // token WAS found in oauth table
        Ok(o) => {
            if let Some(id) = session.shooter_id {
                if id == o.shooter_id {
                    return true;
                } else {
                    return false;
                }
            } else {
                // log in user - what IS the problem with BelongsTo!?
                //if let Ok(s) = Shooter::belonging_to(&o)
                //    .load::<Shooter>(&connection)
                //{
                //    session.shooter_id = Some(shooter.shooter_id);
                //    session.shooter_name = Some(shooter.shooter_name);
                //    session.email_address = Some(shooter.email);
                return true;
                //} else {
                //    return false;
                //}
            }
        }

And the ERR arms:

        // token not found in oauth table
        Err(diesel::NotFound) => match session.shooter_id {
            Some(id) => {
                create_oauth(&connection, &vendor, token, id);
                true
            }
            None => match shooter
                .filter(shooter_email.eq(&email))
                .first::<Shooter>(&connection)
            {
                // email address WAS found in shooter table
                Ok(s) => {
                    create_oauth(&connection, &vendor, token, s.shooter_id);
                    true
                }
                // email address not found in shooter table
                Err(diesel::NotFound) => {
                    let this_shooter =
                        create_shooter(&connection, name, None,
                            email, &"active".to_string());
                    session.shooter_id = Some(this_shooter.shooter_id);
                    create_oauth(&connection, &vendor, token,
                        this_shooter.shooter_id);
                    true
                }
                Err(e) => {
                    panic!("Database error {}", e);
                }
            },
        },
        Err(e) => {
            panic!("Database error {}", e);
        }
    }
}



Simple Queries with Diesel

Breaking all that code down to smaller bits: first, I query the PgSQL database for the given oauth user:

match oauth
    .filter(oauth_vendor.eq(&vendor))
    .filter(oauth_user.eq(&token))
    .first::<Oauth>(&connection) {
        Ok(o) => { ... }
        Err(diesel::NotFound) => { ... }
        Err(e) => { ... }
}

Check the oauth table for records WHERE (filter) the oauth_vendor is (google or facebook) AND I’ve already stored the same validated oauth_user. I will get back either Ok(o) or Err(diesel::NotFound) … (or some worse error message), so I make a pattern with those 3 arms.

If we did get a hit from the DB, that session id is already tied to a shooter_id (user id) unless something is very wrong. So, IF we also have a shooter_id defined in our current session, I just need to verify that they match and return true or false. But, if we don’t have a shooter_id in our session, we know the oauth is tied to a shooter in the db, so this will log them in. Diesel has an easy way to get that parent record, which is what this should do:

// if let Ok(s) = Shooter::belonging_to(&o).load::<Shooter>(&connection) {
   ...

I fought and fought to get this work, but you can see it is still commented out. From posts and chat around the Internet, I believe it can work – I think I either have a scope problem or my models aren’t setup correctly… this is how they look:

<src/model.rs>

...
#[derive(Identifiable, Queryable, Debug, PartialEq)]
#[table_name = "shooter"]
#[primary_key("shooter_id")]
pub struct Shooter {
    pub shooter_id: i32,
    pub shooter_name: String,
    pub shooter_password: String,
    pub shooter_status: String,
    pub shooter_email: String,
    pub shooter_real_name: String,
    pub shooter_create_time: chrono::NaiveDateTime,
    pub shooter_active_time: Option<chrono::NaiveDateTime>,
    pub shooter_inactive_time: Option<chrono::NaiveDateTime>,
    pub shooter_remove_time: Option<chrono::NaiveDateTime>,
    pub shooter_modify_time: chrono::NaiveDateTime,
}
...
#[derive(Identifiable, Associations, Queryable, Debug, PartialEq)]
#[belongs_to(Shooter, foreign_key = "shooter_id")]
#[table_name = "oauth"]
#[primary_key("oauth_id")]
pub struct Oauth {
    pub oauth_id: i32,
    pub oauth_vendor: String,
    pub oauth_user: String,
    pub shooter_id: i32,
    pub oauth_status: String,
    pub oauth_create_time: chrono::NaiveDateTime,
    pub oauth_last_use_time: chrono::NaiveDateTime,
    pub oauth_modify_time: chrono::NaiveDateTime,
}

I’ll get it to work eventually – I really hope it isn’t failing because I didn’t specifically name my primary fields just id like in the examples Diesel gives in their guides. It seems like naming shooter_id in table oauth to match shooter_id in the shooter table should make things obvious. Hopefully we aren’t forced to always use id as the primary field… no, that can’t be it.

Anyway, back to verifying. The other main case is that an oauth record with this token is NOT found in the table. Which means it is a new connection we haven’t seen before. If the session is already logged in, we just need to attach this oauth token to the logged in user and return true!

Some(id) => {
    create_oauth(&connection, &vendor, token, id);
    true
}

Otherwise, two choices – we will try to match on an existing shooter via the email address. If we find a match, we log them in and again attach this oauth token to their shooter record.

 None => match shooter
     .filter(shooter_email.eq(&email))
     .first::<Shooter>(&connection)
 {
     // email address WAS found in shooter table
     Ok(s) => {
         create_oauth(&connection, &vendor, token, s.shooter_id);
         true
     }

Otherwise, we don’t get a hit; that is, we haven’t seen this oauth token before AND we haven’t seen this validated email address before. We have to call that a brand new shooter account. I mentioned we create accounts from the OAuth requests using Diesel – this is where that happens. In this case, we create both the shooter record and the oauth record, linking them together.

// email address not found in shooter table
Err(diesel::NotFound) => {
    let this_shooter =
        create_shooter(&connection, name, None, email,
            &"active".to_string());
    session.shooter_id = Some(this_shooter.shooter_id);
    create_oauth(&connection, &vendor, token, this_shooter.shooter_id);
    true
}

Using Diesel to Insert Records

As we fall back out of the stack of functions we’ve called, because we return true here the session will get updated with the shooter_id – they are now logged in. Also, the shooter and oauth records are saved, so if they come back, they can just validate and be logged into their same account again. Here are the two methods that create those records:

<src/shooter.rs>

...
pub fn create_shooter<'a>(
    connection: &PgConnection,
    name: &'a String,
    password: Option<&'a String>,
    email: &'a String,
    status: &'a String,
) -> Shooter {
    use crate::schema::shooter::dsl::*;

    let new_shooter = NewShooter {
        shooter_name: name.to_string(),
        shooter_password: match password {
            Some(p) => p.to_string(),
            None => thread_rng()
                .sample_iter(&Alphanumeric)
                .take(64)
                .collect::<String>(),
        },
        shooter_status: status.to_string(),
        shooter_email: email.to_string(),
        shooter_real_name: name.to_string(),
    };

    diesel::insert_into(shooter)
        .values(&new_shooter)
        .get_result(connection)
        .expect("Error saving new Shooter")
}
<src/oauth.rs>

...
pub fn create_oauth<'a>(
    connection: &PgConnection,
    vendor: &'a String,
    user_id: &'a String,
    shooterid: i32,
) -> Oauth {
    use crate::schema::oauth::dsl::*;

    let new_oauth = NewOauth {
        oauth_vendor: vendor.to_string(),
        oauth_user: user_id.to_string(),
        shooter_id: shooterid,
    };

    diesel::insert_into(oauth)
        .values(&new_oauth)
        .get_result(connection)
        .expect("Error saving new Oauth")
}

As far as writing these new records to PgSQL – in both cases, we have NewShooter and NewOauth structs that allow us to set the bare minimum of fields without having to worry about the fields that PgSQL will default for us (like the create_date fields). We setup the appropriate struct and pass it to insert_into(). Adding .get_result() will return the newly created record to us, so we have access to the brand new shooter_id or oauth_id.

Complexity

If a user comes to the site, signs in with one OAuth (which creates their shooter record and attaches that oauth token) and then signs in with the other, this logic figures out they are validated to be the same person, so creates just a single shooter record with two oauth records, and both point to the one user. If they come back, they can authenticate via either third-party and are allowed back in.

Ok, more to come as I figure out other problems. I haven’t gone through that logic tightly enough to make sure I don’t have any holes – and it wouldn’t surprise me to find some. It doesn’t really matter – this is certainly teaching me Rust! Give it a try at PinpointShooting.com – but don’t be surprised if you shooter account gets deleted, constantly.