I just want to paste all the notes or snippet here.
no explanation at all, just put many references and how I implement it.
use axum::{
Json, Router,
extract::State,
http::StatusCode,
routing::{get, post},
};
use rusqlite::Connection;
use serde::{Deserialize, Serialize};
use std::sync::{Arc, Mutex};
// use std::thread;
use tokio::task;
// Ref: how to use app state: https://users.rust-lang.org/t/how-to-pass-app-state-in-axum/104458/3
// define local module. the file at app_infra/mod.rs
mod app_infra;
#[derive(Debug, Clone)]
struct AppState {
// Ref: how to make un-clonable object in external mod implementing Clone traits
// here the link: https://itsallaboutthebit.com/arc-mutex/
// Arc: will let the data / object shared between threads
// Mutex: ensure only 1 thread at a time can access the data / object
db: Arc<Mutex<Connection>>,
}
#[tokio::main]
async fn main() {
// initialize tracing
tracing_subscriber::fmt::init();
let db = match app_infra::create_db_connection() {
Ok(conn) => conn,
Err(err) => panic!("error to connect to db {}", err.to_string()),
};
let cloneable_db = Arc::new(Mutex::new(db));
let app_state = AppState { db: cloneable_db };
// build our application with a route
let app = Router::new()
// `GET /` goes to `root`
.route("/", get(root))
// `POST /users` goes to `create_user`
.route("/users", post(create_user))
.with_state(app_state);
// run our app with hyper, listening globally on port 3000
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
// basic handler that responds with a static string
async fn root(State(AppState { db }): State<AppState>) -> &'static str {
let outside_int = 213123i32;
// let mut all_item: Vec<String> = Vec::new();
// ref: https://itsallaboutthebit.com/arc-mutex/
// this is how to lock our mutex object
// ref: use tokio:task instead: https://stackoverflow.com/a/75840352
// let t1 = thread::spawn(move || -> Result<(), rusqlite::Error> {
// let locked_db = db.lock().unwrap();
// let mut stmt = locked_db.prepare("select * from memo limit 1")?;
// let mut rows = stmt.query([])?;
// let _row = match rows.next() {
// Ok(it) => {
// println!("{:?}", it);
// }
// Err(_) => (),
// };
// println!("testing outsider variable {:?}", all_item);
// println!("testing outsider variable {:?}", outside_int);
// all_item.push("Mantab".to_string());
// // let mut names = Vec::new();
// // let Some(row) = rows.next()
// // ref: https://itsallaboutthebit.com/arc-mutex/
// // after locked_user goes out of scope, mutex will be unlocked again,
// // but you can also explicitly unlock it with:
// // drop(locked_user);
// Ok(())
// });
// let _ = t1.join().unwrap();
// println!("testing modify variable {:?}", all_item); // Error!
// ref: spawn vs spawn_blocking https://stackoverflow.com/a/74547875
// we use spawn_blocking here because it related to IO files like sqlite db.
let res = task::spawn_blocking(move || -> Result<Vec<String>, rusqlite::Error> {
let mut all_item: Vec<String> = Vec::new();
let locked_db = db.lock().unwrap();
let mut stmt = locked_db.prepare("select * from memo limit 1")?;
let mut rows = stmt.query([])?;
let _row = match rows.next() {
Ok(it) => {
println!("{:?}", it);
}
Err(_) => (),
};
println!("testing outsider variable {:?}", outside_int);
all_item.push("Mantab".to_string());
// let mut names = Vec::new();
// let Some(row) = rows.next()
// after locked_user goes out of scope, mutex will be unlocked again,
// but you can also explicitly unlock it with:
// drop(locked_user);
Ok(all_item)
})
.await
.unwrap();
let _ = match res {
Ok(it) => {
println!("testing modify variable {:?}", it);
it;
}
Err(_) => {
let empty_vec: Vec<String> = Vec::new();
empty_vec;
}
};
"Hello, World!"
}
async fn create_user(
// this argument tells axum to parse the request body
// as JSON into a `CreateUser` type
Json(payload): Json<CreateUser>,
) -> (StatusCode, Json<User>) {
// insert your application logic here
let user = User {
id: 1337,
username: payload.username,
};
// this will be converted into a JSON response
// with a status code of `201 Created`
(StatusCode::CREATED, Json(user))
}
// the input to our `create_user` handler
#[derive(Deserialize)]
struct CreateUser {
username: String,
}
// the output to our `create_user` handler
#[derive(Serialize)]
struct User {
id: u64,
username: String,
}
Maybe someone can just suggest me, or correct me.
Iteration 2
This is next iteration.
the sqlite db is come from use memos db. one of the best open source note app
use axum::{
Json, Router,
extract::State,
http::StatusCode,
routing::{get, post},
};
use rusqlite::Connection;
use serde::{Deserialize, Serialize};
use std::{
sync::{Arc, Mutex},
vec,
};
// use std::thread;
use tokio::task;
// Ref: how to use app state: https://users.rust-lang.org/t/how-to-pass-app-state-in-axum/104458/3
// define local module. the file at app_infra/mod.rs
mod app_infra;
#[derive(Debug, Clone)]
struct AppState {
// Ref: how to make un-clonable object in external mod implementing Clone traits
// here the link: https://itsallaboutthebit.com/arc-mutex/
// Arc: will let the data / object shared between threads
// Mutex: ensure only 1 thread at a time can access the data / object
db: Arc<Mutex<Connection>>,
}
#[tokio::main]
async fn main() {
// initialize tracing
tracing_subscriber::fmt::init();
let db = match app_infra::create_db_connection() {
Ok(conn) => conn,
Err(err) => panic!("error to connect to db {}", err.to_string()),
};
let cloneable_db = Arc::new(Mutex::new(db));
let app_state = AppState { db: cloneable_db };
// build our application with a route
let app = Router::new()
// `GET /` goes to `root`
.route("/", get(root))
.route("/memos", get(get_memo_list))
.route("/basic-json", get(get_with_response_json))
// `POST /users` goes to `create_user`
.route("/users", post(create_user))
.with_state(app_state);
// run our app with hyper, listening globally on port 3000
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
// basic handler that responds with a static string
async fn root(State(AppState { db }): State<AppState>) -> &'static str {
let outside_int = 213123i32;
// let mut all_item: Vec<String> = Vec::new();
// ref: use tokio:task instead: https://stackoverflow.com/a/75840352
// ref: spawn vs spawn_blocking https://stackoverflow.com/a/74547875
// we use spawn_blocking here because it related to IO files like sqlite db.
let res = task::spawn_blocking(move || -> Result<Vec<String>, rusqlite::Error> {
let mut all_item: Vec<String> = Vec::new();
// ref: https://itsallaboutthebit.com/arc-mutex/
// this is how to lock our mutex object
let locked_db = db.lock().unwrap();
let mut stmt = locked_db.prepare("select * from memo limit 1")?;
let mut rows = stmt.query([])?;
let _row = match rows.next() {
Ok(it) => {
println!("{:?}", it);
}
Err(_) => (),
};
println!("testing outsider variable {:?}", outside_int);
all_item.push("Mantab".to_string());
// after locked_user goes out of scope, mutex will be unlocked again,
// but you can also explicitly unlock it with:
// drop(locked_user);
Ok(all_item)
})
.await
.unwrap();
let _ = match res {
Ok(it) => {
println!("testing modify variable {:?}", it);
drop(it);
}
Err(_) => {
let empty_vec: Vec<String> = Vec::new();
drop(empty_vec);
}
};
"Hello, World!"
}
async fn get_with_response_json() -> (StatusCode, Json<Vec<Memo>>) {
let mut memo_list: Vec<Memo> = Vec::new();
let mut tag_list: Vec<String> = Vec::new();
tag_list.push("tag".to_string());
let memo = Memo {
id: 1,
created_ts: 1,
updated_ts: 1,
content: "content".to_string(),
payload: "".to_string(),
};
memo_list.push(memo);
(StatusCode::OK, Json(memo_list))
}
async fn get_memo_list(State(AppState { db }): State<AppState>) -> (StatusCode, Json<Vec<Memo>>) {
// let mut all_item: Vec<String> = Vec::new();
// ref: use tokio:task instead: https://stackoverflow.com/a/75840352
// ref: spawn vs spawn_blocking https://stackoverflow.com/a/74547875
// we use spawn_blocking here because it related to IO files like sqlite db.
let res = task::spawn_blocking(move || -> Result<Vec<Memo>, rusqlite::Error> {
let mut all_item: Vec<Memo> = Vec::new();
// ref: https://itsallaboutthebit.com/arc-mutex/
// this is how to lock our mutex object
let locked_db = db.lock().unwrap();
let mut stmt = locked_db.prepare("select * from memo limit 1")?;
let mut rows = stmt.query([])?;
// copied form smt.query docs. just click it.
while let Some(row) = rows.next()? {
println!("{:?}", row);
let memo_item = Memo {
id: match row.get::<&str, i64>("id") {
Ok(it) => it,
Err(_) => 0i64,
},
created_ts: match row.get::<&str, i64>("created_ts") {
Ok(it) => it,
Err(_) => 0i64,
},
updated_ts: match row.get::<&str, i64>("updated_ts") {
Ok(it) => it,
Err(_) => 0i64,
},
content: match row.get::<&str, String>("content") {
Ok(it) => it,
Err(_) => "".to_string(),
},
payload: match row.get::<&str, String>("payload") {
Ok(it) => it,
Err(_) => "".to_string(),
},
};
all_item.push(memo_item);
}
// after locked_user goes out of scope, mutex will be unlocked again,
// but you can also explicitly unlock it with:
// drop(locked_user);
Ok(all_item)
})
.await
.unwrap();
match res {
Ok(it) => {
return (StatusCode::OK, Json(it));
}
Err(_) => {
let memo_list: Vec<Memo> = Vec::new();
return (StatusCode::INTERNAL_SERVER_ERROR, Json(memo_list));
}
};
}
async fn create_user(
// this argument tells axum to parse the request body
// as JSON into a `CreateUser` type
Json(payload): Json<CreateUser>,
) -> (StatusCode, Json<User>) {
// insert your application logic here
let user = User {
id: 1337,
username: payload.username,
};
// this will be converted into a JSON response
// with a status code of `201 Created`
(StatusCode::CREATED, Json(user))
}
// the output to our `memo`
#[derive(Debug, Serialize)]
struct Memo {
id: i64,
created_ts: i64,
updated_ts: i64,
content: String,
payload: String,
}
// the input to our `create_user` handler
#[derive(Deserialize)]
struct CreateUser {
username: String,
}
// the output to our `create_user` handler
#[derive(Serialize)]
struct User {
id: u64,
username: String,
}
I forget to put Cargo.toml. here the Cargo.toml
[package]
name = "bosnotes"
version = "0.1.0"
edition = "2024"
[dependencies]
time = "0.3.44"
# `bundled` causes us to automatically compile and link in an up to date
# version of SQLite for you. This avoids many common build issues, and
# avoids depending on the version of SQLite on the users system (or your
# system), which may be old or missing. It's the right choice for most
# programs that control their own SQLite databases.
#
# That said, it's not ideal for all scenarios and in particular, generic
# libraries built around `rusqlite` should probably not enable it, which
# is why it is not a default feature -- it could become hard to disable.
rusqlite = { version = "0.38.0", features = ["bundled"] }
axum = { version = "0.8.8" }
# ref: https://stackoverflow.com/a/79605363
tokio = { version = "1.49.0", features = ["full"] }
# ref: https://users.rust-lang.org/t/serde-cannot-find-derive-macro-deserialize-serialize-in-this-scope/79402
serde = { version = "1.0.228", features = ["std", "derive", "serde_derive"] }
tracing-subscriber = "0.3.22"
tower = "0.5.2"
[dependencies.uuid]
version = "1.19.0"
# Lets you generate random UUIDs
features = [
"v4",
]
and when I try to find how much the resource usages, here the result on idle state:

Top comments (0)