Remote Database Operations

To persist data remotely in the PostgreSQL database, modify the src/db_ops.rs file and add thew following code

File: src/db_ops.rs

// -- code snippet --
use crate::{
    synching_to_server, Command, MemDB, MyTodos, MyTodosActiveModel, MyTodosModel, TodoList,
};

pub async fn get_fruits() -> anyhow::Result<Vec<String>> {
    // Get the fruits first
    let get_fruits = Command::ListFruits;
    let serialized_command = bincode::serialize(&get_fruits)?;
    let mut fruits_list: Vec<String>;

    let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
    stream.write_all(&serialized_command).await?;

    let mut fruits_buf = vec![0u8; 4096];
    loop {
        let n = stream.read(&mut fruits_buf).await?;
        let rx: Vec<_> = bincode::deserialize(&fruits_buf).unwrap();

        fruits_list = rx;

        if n != 0 {
            break;
        }
    }

    Ok(fruits_list)
}

pub async fn store(db: &DatabaseConnection, quantity: &str, todo_name: &str) -> anyhow::Result<()> {
    let my_todo = MyTodosActiveModel {
        todo_name: Set(todo_name.to_owned()),
        quantity: Set(quantity.to_owned()),
        status: Set(0),
        ..Default::default()
    };

    MyTodos::insert(my_todo).exec(db).await?;

    Ok(())
}

pub async fn get(db: &DatabaseConnection) -> Result<Vec<MyTodosModel>, sea_orm::DbErr> {
    MyTodos::find().all(db).await
}

pub async fn edit(
    db: &DatabaseConnection,
    todo_model: &MyTodosModel,
    quantity: String,
) -> Result<MyTodosModel, sea_orm::DbErr> {
    let mut todos_active_model: MyTodosActiveModel = todo_model.to_owned().into();
    todos_active_model.quantity = Set(quantity);

    Ok(todos_active_model.update(db).await?)
}

pub async fn done(
    db: &DatabaseConnection,
    todo_model: &MyTodosModel,
) -> Result<MyTodosModel, sea_orm::DbErr> {
    let mut todos_active_model: MyTodosActiveModel = todo_model.to_owned().into();
    todos_active_model.status = Set(1);

    Ok(todos_active_model.update(db).await?)
}

pub async fn undo(
    db: &DatabaseConnection,
    todo_model: &MyTodosModel,
) -> Result<MyTodosModel, sea_orm::DbErr> {
    let mut todos_active_model: MyTodosActiveModel = todo_model.to_owned().into();
    todos_active_model.status = Set(0);

    Ok(todos_active_model.update(db).await?)
}

pub(crate) async fn load_sqlite_cache(
    db: &DatabaseConnection,
    memdb: &mut MemDB,
) -> Result<(), sea_orm::DbErr> {
    let sqlite_cache = get(&db).await?;
    memdb.lock().await.clear();
    for mytodo_model in sqlite_cache {
        memdb
            .lock()
            .await
            .insert(mytodo_model.todo_name.clone(), mytodo_model);
    }

    Ok(())
}

pub async fn update_remote_storage(memdb: &MemDB, username: &str) -> anyhow::Result<()> {
    let mut temp_list = TodoList::default();
    memdb.lock().await.values().for_each(|todo| {
        if todo.status == 0 {
            temp_list.queued.push(todo.to_owned());
        } else {
            temp_list.completed.push(todo.to_owned());
        }
    });

    synching_to_server();

    // Update a todo_list
    let update_todo = Command::UpdateTodoList {
        username: username.to_owned(),
        todo_list: serde_json::to_string(&temp_list)?,
    };
    let serialized_command = bincode::serialize(&update_todo)?;

    let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
    stream.write_all(&serialized_command).await?;

    let mut buffer = vec![0u8; 4096];
    stream.read(&mut buffer).await?;

    bincode::deserialize::<String>(&buffer)?;

    Ok(())
}

pub async fn get_user_remote_storage(username: &str) -> anyhow::Result<Option<String>> {
    let get_user = Command::Get(username.to_owned());
    let serialized_command = bincode::serialize(&get_user)?;

    let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
    stream.write_all(&serialized_command).await?;

    let mut buffer = vec![0u8; 4096];
    stream.read(&mut buffer).await?;

    Ok(bincode::deserialize::<Option<String>>(&buffer)?)
}

pub async fn create_new_user(username: &str) -> anyhow::Result<String> {
    let create_user = Command::CreateUser(username.to_owned());
    let serialized_command = bincode::serialize(&create_user)?;

    let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
    stream.write_all(&serialized_command).await?;

    let mut buffer = vec![0u8; 4096];
    stream.read(&mut buffer).await?;

    Ok(bincode::deserialize::<String>(&buffer)?)
}

get_fruits() queries the list of fruits from the remote database.

store() will persist the contents of the in-memory database to local SQLite cache.

load_sqlite_cache() queries the local database a list of TODOs. This is useful when the client starts, since it fetches the cached TODOs and loads them into the in-memory database MemDB.

edit() persists the edits to the TODOs to the SQLite cache.

done() persists the state of the in-memory database with the completed TODOs in the SQLite cache.

undo() persists the state of the in-memory database with the queued TODOs in the SQLite cache reflecting the TODOs which have been moved from the completed Vector to the queued Vector.

update_remote_storage() updates the remote PostgreSQL database with the new changes in the TODO list.

get_user_remote_storage() checks if the username provided is in the remote PostgreSQL database.

create_new_user() creates a new user in the remote PostgreSQL database with the given username.

Up next is reading from the terminal and performing database operations bases on the command.