Handling lifetimes when decoding generic types using SQLx - rust

I am attempting to use a generic method to retrieve values from database tables that are structurally identical, but have different types for one of their columns. Simplified example below
async fn query<'a, 'r, T: DatabaseType<Item=T> + Decode<'r, Sqlite> + Type<Sqlite>>(&self, name: &'a str) -> Result<Vec<NamedValue<'a, T>>> {
let mut connection = self.pool.acquire().await?;
let mut rows = sqlx::query("Select id, value from table where name = $1")
.bind(name)
.fetch(&mut connection);
let mut results = Vec::new();
while let Some(row) = rows.try_next().await? {
results.push(NamedValue {
name,
value: row.try_get("value")?
})
}
Ok(results)
}
This will not compile, with the error: borrowed value does not live long enough, argument requires that 'row' is borrowed for 'r. The lifetime sqlx::Decode wants ('r), has to be declared as part of the query function's signature, but the resource the lifetime refers to does not exist yet, and only exists when the query executes and the stream is iterated over. I can't omit this bound on the generic, because the type does need to be decodable for try_get to work, so how do I tell the compiler that it is actually completely safe, and that the decoding is happening against a row that will definitely live longe enough for the try_get? Once the value is decoded, it will always have a static lifetime.
Rust playground doesn't include SQLx, an example that can be compiled at home is below:
[package]
name = "sqlx-minimal-example"
version = "0.1.0"
edition = "2021"
[dependencies]
tokio = { version = "1", features = ["full"] }
sqlx = { version = "0.6", features = ["runtime-tokio-rustls", "sqlite"] }
anyhow = "1.0"
futures = "0.3"
And the full application would be:
use anyhow::Result;
use sqlx::{Decode, Row, Sqlite, SqlitePool, Type};
use futures::TryStreamExt;
#[tokio::main]
async fn main() -> Result<()> {
println!("Hello, world!");
Ok(())
}
struct NamedValue<'a ,T> {
name: &'a str,
value: T
}
struct SqliteBackend {
pool: SqlitePool
}
trait DatabaseType {
type Item;
}
impl DatabaseType for f32 {
type Item = f32;
}
impl DatabaseType for i32 {
type Item = i32;
}
impl SqliteBackend {
async fn query<'a, 'r, T: DatabaseType<Item=T> + Decode<'r, Sqlite> + Type<Sqlite>>(&self, name: &'a str) -> Result<Vec<NamedValue<'a, T>>> {
let mut connection = self.pool.acquire().await?;
let mut rows = sqlx::query("Select id, value from table where name = $1")
.bind(name)
.fetch(&mut connection);
let mut results = Vec::new();
while let Some(row) = rows.try_next().await? {
results.push(NamedValue {
name,
value: row.try_get("value")?
})
}
Ok(results)
}
}

Higher-ranked trait bounds were the answer. This tells the compiler the type is decodable for all possible lifetimes.
Working function below:
async fn query<'a, T: DatabaseType<Item=T> + for<'r> Decode<'r, Sqlite> + Type<Sqlite>>(&self, name: &'a str) -> Result<Vec<NamedValue<'a, T>>> {
let mut connection = self.pool.acquire().await?;
let mut rows = sqlx::query("Select id, value from table where name = $1")
.bind(name)
.fetch(&mut connection);
let mut results = Vec::new();
while let Some(row) = rows.try_next().await? {
results.push(NamedValue {
name,
value: row.try_get("value")?
})
}
Ok(results)
}

Related

Rust, returns a value referencing data owned by the current function

I checked forum for this question, I found some answers, but any of this situation was different and don't work for me.
I have problem with this error:
let test = PasswordHash::new(&hashed_password).clone().unwrap();
| ---------------- `hashed_password` is borrowed here
53 | // Ok(PasswordHash::new(&hashed_password).unwrap())
54 | Ok(test)
| ^^^^^^^^ returns a value referencing data owned by the current function
I exactly know what this error means. I know where the problem is, and I even know what provide it. But have no idea how can I fix it.
My idea it don't repeat part of code which is responsible for generating PasswordHash. I decided create separate function to do this.
My code below:
use argon2::{
password_hash::{
rand_core::OsRng,
PasswordHash, PasswordHasher, PasswordVerifier, SaltString, Error
},
Argon2,
};
struct AlgorithmData {
version: u32,
params: String,
algorithm_type: String,
}
impl AlgorithmData {
fn new() -> Self {
let example_string = "example_string";
let password_hash = Hasher::get_passwordhash_object(&example_string).unwrap();
AlgorithmData { version: password_hash.version.unwrap(),
params: password_hash.params.to_string(),
algorithm_type: password_hash.algorithm.to_string() }
}
}
pub struct Hasher {}
impl Hasher {
pub fn hash_string(string_to_hash: &str) -> Result<String, Error> {
let parsed_hash_password = Hasher::get_passwordhash_object(string_to_hash)?;
let result = format!("{}${}",
parsed_hash_password.salt.unwrap().to_string(),
parsed_hash_password.hash.unwrap().to_string());
Ok(result)
}
fn get_passwordhash_object(string_to_hash: &str) -> Result<PasswordHash, Error> {
let bytes_to_hash = string_to_hash.as_bytes();
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
let hashed_password = argon2.hash_password(&bytes_to_hash,
&salt)?.to_string();
let test = PasswordHash::new(&hashed_password).clone().unwrap();
// Ok(PasswordHash::new(&hashed_password).unwrap()) --> here too is problem
Ok(test) // here is the same problem
}
pub fn compare_string_to_hash(string_to_compare: &String,
hash_to_compare: &String) -> Result<bool, Error> {
let bytes_to_compare = string_to_compare.as_bytes();
let bytes_from_hash = PasswordHash::new(&hash_to_compare).unwrap();
Ok(Argon2::default().verify_password(&bytes_to_compare,
&bytes_from_hash).is_ok())
}
}
Like you see main problem is variable: hashed_password and it reference in new object PasswordHash.
I was tried use copy function to clone hashed_password variable or object PasswordHash.
Second thing was trying to use lifetime parameter like <'a>.
Third idea was using Box::new to try access to memory directly.
I need help to resolve this problem. I have already read documentation. But still have no more ideas to resolve this.
Error number: E0515 explaining reason quite well.
You definitely have a lifetime issue here. In your case, the PasswordHash lifetime depends on the SaltString. So, if you want to send a reference of PasswordHash out to the caller, it's better to take SaltString as an argument to the function get_passwordhash_object.
Here is the minimal example derived from your code:
use argon2::{
password_hash::{rand_core::OsRng, Error, PasswordHash, PasswordHasher, SaltString},
Argon2,
};
pub struct Hasher {}
impl Hasher {
fn get_passwordhash_object<'a>(
bytes_to_hash: &'a [u8],
salt: &'a SaltString,
) -> Result<PasswordHash<'a>, Error> {
let argon2 = Argon2::default();
let hashed_password = argon2.hash_password(&bytes_to_hash, salt).unwrap();
Ok(hashed_password)
}
}
fn main() {
let salt = SaltString::generate(&mut OsRng);
let hash = Hasher::get_passwordhash_object("something".as_bytes(), &salt);
println!("{:?}", hash);
}
If you check the documentation for PasswordHash::new you will see that it returns a struct with the same lifetime of the parameter. In your case the the test variable lifetime depends on the lifetime of hashed_password, that itself depends on the lifetime of salt, that owns its data.
In the get_passwordhash_object, you can't return PasswordHash type and discard the salt, you need to return both, or transform the data in something else that owns its data.
One solution is to serialize the PasswordHash into PasswordHashString, because this type owns his data
pub fn hash_string(string_to_hash: &str) -> Result<String, Error> {
let parsed_hash_password =
Hasher::get_passwordhash_object(string_to_hash)?;
let result = format!(
"{}${}",
parsed_hash_password.salt().unwrap().to_string(),
parsed_hash_password.hash().unwrap().to_string()
);
Ok(result)
}
fn get_passwordhash_object(
string_to_hash: &str,
) -> Result<PasswordHashString, Error> {
let bytes_to_hash = string_to_hash.as_bytes();
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
let hashed_password =
argon2.hash_password(&bytes_to_hash, &salt)?.to_string();
let test = PasswordHash::new(&hashed_password).clone().unwrap();
Ok(test.serialize())
}
pub fn compare_string_to_hash(
string_to_compare: &String,
hash_to_compare: &String,
) -> Result<bool, Error> {
let bytes_to_compare = string_to_compare.as_bytes();
let bytes_from_hash = PasswordHash::new(&hash_to_compare).unwrap();
Ok(Argon2::default()
.verify_password(&bytes_to_compare, &bytes_from_hash)
.is_ok())
}

How can I get/store span duration with Rust tracing?

I want to capture the duration of execution of a span in rust tracing and send that as metric.
I have found that fmt() helps in printing that as mentioned here:How can I log span duration with Rust tracing?
I have also tried this example about creating layer and implementing on_new_span() and on_event(). I added on_close() as well to check what metadata do we get here. The code for that I wrote is:
use tracing::{info, info_span};
use tracing_subscriber::prelude::*;
mod custom_layer;
use custom_layer::CustomLayer;
fn main() {
tracing_subscriber::registry()
.with(CustomLayer)
.init();
let outer_span = info_span!("Outer", level = 0, other_field = tracing::field::Empty);
let _outer_entered = outer_span.enter();
outer_span.record("other_field", &7);
let inner_span = info_span!("inner", level = 1);
let _inner_entered = inner_span.enter();
info!(a_bool = true, answer = 42, message = "first example");
}
custom_layer.rs:
use std::collections::BTreeMap;
use tracing_subscriber::Layer;
pub struct CustomLayer;
impl<S> Layer<S> for CustomLayer
where
S: tracing::Subscriber,
S: for<'lookup> tracing_subscriber::registry::LookupSpan<'lookup>,
{
fn on_new_span(
&self,
attrs: &tracing::span::Attributes<'_>,
id: &tracing::span::Id,
ctx: tracing_subscriber::layer::Context<'_, S>,
) {
let span = ctx.span(id).unwrap();
let mut fields = BTreeMap::new();
let mut visitor = JsonVisitor(&mut fields);
attrs.record(&mut visitor);
let storage = CustomFieldStorage(fields);
let mut extensions = span.extensions_mut();
extensions.insert(storage);
}
fn on_record(
&self,
id: &tracing::span::Id,
values: &tracing::span::Record<'_>,
ctx: tracing_subscriber::layer::Context<'_, S>,
) {
// Get the span whose data is being recorded
let span = ctx.span(id).unwrap();
// Get a mutable reference to the data we created in new_span
let mut extensions_mut = span.extensions_mut();
let custom_field_storage: &mut CustomFieldStorage =
extensions_mut.get_mut::<CustomFieldStorage>().unwrap();
let json_data: &mut BTreeMap<String, serde_json::Value> = &mut custom_field_storage.0;
// And add to using our old friend the visitor!
let mut visitor = JsonVisitor(json_data);
values.record(&mut visitor);
}
fn on_event(&self, event: &tracing::Event<'_>, ctx: tracing_subscriber::layer::Context<'_, S>) {
// All of the span context
let scope = ctx.event_scope(event).unwrap();
let mut spans = vec![];
for span in scope.from_root() {
let extensions = span.extensions();
let storage = extensions.get::<CustomFieldStorage>().unwrap();
let field_data: &BTreeMap<String, serde_json::Value> = &storage.0;
spans.push(serde_json::json!({
"target": span.metadata().target(),
"name": span.name(),
"level": format!("{:?}", span.metadata().level()),
"fields": field_data,
}));
}
// The fields of the event
let mut fields = BTreeMap::new();
let mut visitor = JsonVisitor(&mut fields);
event.record(&mut visitor);
// And create our output
let output = serde_json::json!({
"target": event.metadata().target(),
"name": event.metadata().name(),
"level": format!("{:?}", event.metadata().level()),
"fields": fields,
"spans": spans,
});
println!("{}", serde_json::to_string_pretty(&output).unwrap());
}
fn on_close(
&self,
id: tracing::span::Id,
ctx: tracing_subscriber::layer::Context<'_, S>,
) {
// Get the span whose data is being recorded
let span = ctx.span(&id).unwrap();
let output = serde_json::json!({
"target": span.metadata().target(),
"name": span.name(),
"level": format!("{:?}", span.metadata().level()),
"fields": format!("{:?}", span.metadata().fields()),
});
println!("On_close{}", serde_json::to_string_pretty(&output).unwrap());
}
}
struct JsonVisitor<'a>(&'a mut BTreeMap<String, serde_json::Value>);
impl<'a> tracing::field::Visit for JsonVisitor<'a> {
fn record_f64(&mut self, field: &tracing::field::Field, value: f64) {
self.0
.insert(field.name().to_string(), serde_json::json!(value));
}
fn record_i64(&mut self, field: &tracing::field::Field, value: i64) {
self.0
.insert(field.name().to_string(), serde_json::json!(value));
}
fn record_u64(&mut self, field: &tracing::field::Field, value: u64) {
self.0
.insert(field.name().to_string(), serde_json::json!(value));
}
fn record_bool(&mut self, field: &tracing::field::Field, value: bool) {
self.0
.insert(field.name().to_string(), serde_json::json!(value));
}
fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
self.0
.insert(field.name().to_string(), serde_json::json!(value));
}
fn record_error(
&mut self,
field: &tracing::field::Field,
value: &(dyn std::error::Error + 'static),
) {
self.0.insert(
field.name().to_string(),
serde_json::json!(value.to_string()),
);
}
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
self.0.insert(
field.name().to_string(),
serde_json::json!(format!("{:?}", value)),
);
}
}
#[derive(Debug)]
struct CustomFieldStorage(BTreeMap<String, serde_json::Value>);
Cargo.toml
[package]
name = "tracing-custom-logging"
version = "0.1.0"
edition = "2021"
[dependencies]
serde_json = "1"
tracing = "0.1"
tracing-subscriber = "0.3.16"
snafu = "0.7.3"
thiserror = "1.0.31"
tracing-opentelemetry = "0.18.0"
Unfortunately I have not been able to get the data about duration of a span anywhere. Can you guys help me identify how/where can I get it from?
You cannot "get" the span duration from the tracing crate because it doesn't store it. It only stores the basic metadata and allows for hooking into framework events in a lightweight way. It is the job of the Subscriber to keep track of any additional data.
You could use the tracing-timing crate if you only need periodic histograms. Otherwise, you can't really use data from an existing layer which may already store timing data, because they often don't expose it. You'll have to keep track of it yourself.
Using the tracing-subscriber crate, you can create a Layer and store additional data using the Registry. Here's an example of how that can be done:
use std::time::Instant;
use tracing::span::{Attributes, Id};
use tracing::Subscriber;
use tracing_subscriber::layer::{Context, Layer};
use tracing_subscriber::registry::LookupSpan;
struct Timing {
started_at: Instant,
}
pub struct CustomLayer;
impl<S> Layer<S> for CustomLayer
where
S: Subscriber,
S: for<'lookup> LookupSpan<'lookup>,
{
fn on_new_span(&self, _attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
let span = ctx.span(id).unwrap();
span.extensions_mut().insert(Timing {
started_at: Instant::now(),
});
}
fn on_close(&self, id: Id, ctx: Context<'_, S>) {
let span = ctx.span(&id).unwrap();
let started_at = span.extensions().get::<Timing>().unwrap().started_at;
println!(
"span {} took {}",
span.metadata().name(),
(Instant::now() - started_at).as_micros(),
);
}
}
This just prints out the results where they are calculated, but you can emit the results elsewhere, or store it in some shared resource as you see fit.
Some example usage:
use std::time::Duration;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
#[tracing::instrument]
fn test(n: u64) {
std::thread::sleep(Duration::from_secs(n));
}
fn main() {
tracing_subscriber::registry::Registry::default()
.with(CustomLayer)
.init();
test(1);
test(2);
test(3);
}
span test took 1000081
span test took 2000106
span test took 3000127
You may also need to be aware of on_enter() and on_exit(), which are relevant when using async functions because their execution may be suspended and resumed later, and you can use those functions to be notified when that happens. Depending on what you're looking for, you may need to add filtering so that you only track the spans you're interested in (by name or target or whatever).

Access Impl field from closure before field is alloc'ed in Rust?

I am new to Rust, as will probably be obvious.
Basically I have this scenario you can see below where, I create a new type that has a closure added to it, but this closure needs to access data which has not yet been created. The data will be created by the time the closure gets called, but when the closure is initially created the data is not yet available.
What is the best way to do deal with?
I am also curious if my closure was not a closure, but rather a private function in my implementation, how would I access that data? This closure/function is a callback from WasmTime and requires an explicit method signature which does not allow me to add $self to it. So how could I get at the instance fields of the implementation without a reference to $self in the function parameters?
pub struct EmWasmNode {
wasmStore: Store<WasiCtx>,
wasmTable: Table,
}
impl EmWasmNode {
pub fn new(filePath: &str) -> Result<Self> {
let engine = Engine::default();
// let module = Module::from_file(&engine, "wasm/index.wast")?;
let module = Module::from_file(&engine, filePath)?;
let mut linker = Linker::new(&engine);
wasmtime_wasi::add_to_linker(&mut linker, |s| s)?;
let wasi = WasiCtxBuilder::new()
.inherit_stdio()
.inherit_args()?
.build();
let mut store = Store::new(&engine, wasi);
linker.func_wrap("env", "emscripten_set_main_loop", |p0: i32, p1: i32, p2: i32| {
println!("emscripten_set_main_loop {} {} {}", p0, p1, p2);
/*** How would I access wasmTable and wasmStore from here to execute more methods??? ***/
//let browserIterationFuncOption:Option<wasmtime::Val> = Self::wasmTable.get(&mut Self::wasmStore, p0 as u32);
// browserIterationFuncOption.unwrap().unwrap_funcref().call(&store, ());
})?;
let instance = linker.instantiate(&mut store, &module)?;
let table = instance
.get_export(&mut store, "__indirect_function_table")
.as_ref()
.and_then(extern_table)
.cloned();
let start = instance.get_typed_func::<(), (), _>(&mut store, "_start")?;
start.call(&mut store, ())?;
Ok(EmWasmNode {
wasmStore: store,
wasmTable: table.unwrap(),
})
}
You have to instantiate a struct before. I suggest the more simple code below to see my idea.
struct Atype
{
name: String,
}
impl Atype
{
pub fn new() -> Self
{
Self{ name: String::from("zeppi")}
}
pub fn test(&self) -> ()
{
let func = | x | { println!("{} {}", &self.name, x);};
func(3)
}
}
fn main() {
let o = Atype::new();
o.test();
}

How do I read CSV data without knowing the structure at compile time?

I'm pretty new to Rust and trying to implement some kind of database. Users should create tables by giving a table name, a vector of column names and a vector of column types (realized over an enum). Filling tables should be done by specifying csv files. However, this requires the structure of the table rows to be specified at compile time, like shown in the basic example:
#[derive(Debug, Deserialize, Eq, PartialEq)]
struct Row {
key: u32,
name: String,
comment: String
}
use std::error::Error;
use csv::ReaderBuilder;
use serde::Deserialize;
use std::fs;
fn read_from_file(path: &str) -> Result<(), Box<dyn Error>> {
let data = fs::read_to_string(path).expect("Unable to read file");
let mut rdr = ReaderBuilder::new()
.has_headers(false)
.delimiter(b'|')
.from_reader(data.as_bytes());
let mut iter = rdr.deserialize();
if let Some(result) = iter.next() {
let record:Row = result?;
println!("{:?}", record);
Ok(())
} else {
Err(From::from("expected at least one record but got none"))
}
}
Is there a possibility to use the generic table information instead of the "Row"-struct to cast the results from the deserialization? Is it possible to simply allocate memory according to the combined sizes of the column types and parse the records in? I would do something like this in C...
Is there a possibility to use the generic table information instead of the "Row"-struct to cast the results from the deserialization?
All generics replaced with concrete types at compile time. If you do not know types you will need in runtime, "generics" is not what you need.
Is it possible to simply allocate memory according to the combined sizes of the column types and parse the records in? I would do something like this in C...
I suggest using Box<dyn Any> instead, to be able to store reference of any type and, still, know what type it is.
Maintenance cost for this approach is pretty high. You have to manage each possible value type everywhere you want to use a cell's value. On the other hand, you do not need to parse value each time, just make some type checks in runtime.
I have used std::any::TypeId to identify type, but it can not be used in match expressions. You can consider using custom enum as type identifier.
use std::any::{Any, TypeId};
use std::io::Read;
use csv::Reader;
#[derive(Default)]
struct Table {
name: String,
headers: Vec<(String, TypeId)>,
data: Vec<Vec<Box<dyn Any>>>,
}
impl Table {
fn add_header(&mut self, header: String, _type: TypeId) {
self.headers.push((header, _type));
}
fn populate_data<R: Read>(
&mut self,
rdr: &mut Reader<R>,
) -> Result<(), Box<dyn std::error::Error>> {
for record in rdr.records() {
let record = record?;
let mut row: Vec<Box<dyn Any>> = vec![];
for (&(_, type_id), value) in self.headers.iter().zip(record.iter()) {
if type_id == TypeId::of::<u32>() {
row.push(Box::new(value.parse::<u32>()?));
} else if type_id == TypeId::of::<String>() {
row.push(Box::new(value.to_owned()));
}
}
self.data.push(row);
}
Ok(())
}
}
impl std::fmt::Display for Table {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Table: {}", self.name)?;
for (name, _) in self.headers.iter() {
write!(f, "{}, ", name)?;
}
writeln!(f)?;
for row in self.data.iter() {
for cell in row.iter() {
if let Some(&value) = cell.downcast_ref::<u32>() {
write!(f, "{}, ", value)?;
} else if let Some(value) = cell.downcast_ref::<String>() {
write!(f, "{}, ", value)?;
}
}
writeln!(f)?;
}
Ok(())
}
}
fn main() {
let mut table: Table = Default::default();
table.name = "Foo".to_owned();
table.add_header("key".to_owned(), TypeId::of::<u32>());
table.add_header("name".to_owned(), TypeId::of::<String>());
table.add_header("comment".to_owned(), TypeId::of::<String>());
let data = "\
key,name,comment
1,foo,foo comment
2,bar,bar comment
";
let mut rdr = Reader::from_reader(data.as_bytes());
table.populate_data(&mut rdr).unwrap();
print!("{}", table);
}

How do I use PickleDB with Rocket/Juniper Context?

I'm trying to write a Rocket / Juniper / Rust based GraphQL Server using PickleDB - an in-memory key/value store.
The pickle db is created / loaded at the start and given to rocket to manage:
fn rocket() -> Rocket {
let pickle_path = var_os(String::from("PICKLE_PATH")).unwrap_or(OsString::from("pickle.db"));
let pickle_db_dump_policy = PickleDbDumpPolicy::PeriodicDump(Duration::from_secs(120));
let pickle_serialization_method = SerializationMethod::Bin;
let pickle_db: PickleDb = match Path::new(&pickle_path).exists() {
false => PickleDb::new(pickle_path, pickle_db_dump_policy, pickle_serialization_method),
true => PickleDb::load(pickle_path, pickle_db_dump_policy, pickle_serialization_method).unwrap(),
};
rocket::ignite()
.manage(Schema::new(Query, Mutation))
.manage(pickle_db)
.mount(
"/",
routes![graphiql, get_graphql_handler, post_graphql_handler],
)
}
And I want to retrieve the PickleDb instance from the Rocket State in my Guard:
pub struct Context {
pickle_db: PickleDb,
}
impl juniper::Context for Context {}
impl<'a, 'r> FromRequest<'a, 'r> for Context {
type Error = ();
fn from_request(_request: &'a Request<'r>) -> request::Outcome<Context, ()> {
let pickle_db = _request.guard::<State<PickleDb>>()?.inner();
Outcome::Success(Context { pickle_db })
}
}
This does not work because the State only gives me a reference:
26 | Outcome::Success(Context { pickle_db })
| ^^^^^^^^^ expected struct `pickledb::pickledb::PickleDb`, found `&pickledb::pickledb::PickleDb`
When I change my Context struct to contain a reference I get lifetime issues which I'm not yet familiar with:
15 | pickle_db: &PickleDb,
| ^ expected named lifetime parameter
I tried using 'static which does make rust quite unhappy and I tried to use the request lifetime (?) 'r of the FromRequest, but that does not really work either...
How do I get this to work? As I'm quite new in rust, is this the right way to do things?
I finally have a solution, although the need for unsafe indicates it is sub-optimal :)
#![allow(unsafe_code)]
use pickledb::{PickleDb, PickleDbDumpPolicy, SerializationMethod};
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::env;
use std::path::Path;
use std::time::Duration;
pub static mut PICKLE_DB: Option<PickleDb> = None;
pub fn cache_init() {
let pickle_path = env::var(String::from("PICKLE_PATH")).unwrap_or(String::from("pickle.db"));
let pickle_db_dump_policy = PickleDbDumpPolicy::PeriodicDump(Duration::from_secs(120));
let pickle_serialization_method = SerializationMethod::Json;
let pickle_db = match Path::new(&pickle_path).exists() {
false => PickleDb::new(
pickle_path,
pickle_db_dump_policy,
pickle_serialization_method,
),
true => PickleDb::load(
pickle_path,
pickle_db_dump_policy,
pickle_serialization_method,
)
.unwrap(),
};
unsafe {
PICKLE_DB = Some(pickle_db);
}
}
pub fn cache_get<V>(key: &str) -> Option<V>
where
V: DeserializeOwned + std::fmt::Debug,
{
unsafe {
let pickle_db = PICKLE_DB
.as_ref()
.expect("cache uninitialized - call cache_init()");
pickle_db.get::<V>(key)
}
}
pub fn cache_set<V>(key: &str, value: &V) -> Result<(), pickledb::error::Error>
where
V: Serialize,
{
unsafe {
let pickle_db = PICKLE_DB
.as_mut()
.expect("cache uninitialized - call cache_init()");
pickle_db.set::<V>(key, value)?;
Ok(())
}
}
This can be simply imported and used as expected, but I think I'll run into issues when the load gets to high...

Resources