aidoku/imports/
defaults.rs

1//! Module for interacting with user preferences.
2use super::{
3	FFIResult, Ptr,
4	std::{encode, free_result, read},
5};
6use crate::{
7	alloc::{String, Vec},
8	imports::std::destroy,
9};
10use crate::{prelude::format, structs::HashMap};
11use serde::{Serialize, de::DeserializeOwned};
12
13#[link(wasm_import_module = "defaults")]
14unsafe extern "C" {
15	fn get(key: *const u8, len: usize) -> FFIResult;
16	fn set(key: *const u8, len: usize, kind: u8, value: Ptr) -> FFIResult;
17}
18
19/// A default value that can be stored in UserDefaults.
20pub enum DefaultValue {
21	Bool(bool),
22	Int(i32),
23	Float(f32),
24	String(String),
25	StringArray(Vec<String>),
26	Null,
27	HashMap(HashMap<String, String>),
28}
29
30impl DefaultValue {
31	fn as_byte(&self) -> u8 {
32		match self {
33			Self::Bool(_) => 1,
34			Self::Int(_) => 2,
35			Self::Float(_) => 3,
36			Self::String(_) => 4,
37			Self::StringArray(_) => 5,
38			Self::Null => 6,
39			Self::HashMap(_) => 0, // not a valid default value
40		}
41	}
42}
43
44/// Returns the UserDefaults value associated with the specified key.
45pub fn defaults_get<Value: DeserializeOwned>(key: &str) -> Option<Value> {
46	let rid = unsafe { get(key.as_ptr(), key.len()) };
47	if rid < 0 {
48		return None;
49	}
50	let result = read::<Value>(rid).ok();
51	unsafe { destroy(rid) };
52	result
53}
54
55/// Returns a HashMap stored in UserDefaults with the specified key.
56pub fn defaults_get_map(key: &str) -> Option<HashMap<String, String>> {
57	let keys = defaults_get::<Vec<String>>(&format!("{key}.keys"))?;
58	let values = defaults_get::<Vec<String>>(&format!("{key}.values"))?;
59	Some(keys.into_iter().zip(values).collect())
60}
61
62/// Returns the UserDefaults value associated with the specified key, deserialized into a JSON object.
63#[cfg(feature = "json")]
64pub fn defaults_get_json<Value: DeserializeOwned>(key: &str) -> super::error::Result<Value> {
65	let data: String = defaults_get(key).unwrap_or_default();
66	let value = serde_json::from_slice(data.as_bytes())?;
67	Ok(value)
68}
69
70/// Sets the UserDefaults value of the specified key.
71pub fn defaults_set(key: &str, value: DefaultValue) {
72	let value_ptr: Ptr = unsafe {
73		match value {
74			DefaultValue::Bool(ref value) => encode(value),
75			DefaultValue::Int(ref value) => encode(value),
76			DefaultValue::Float(ref value) => encode(value),
77			DefaultValue::String(ref value) => encode(value),
78			DefaultValue::StringArray(ref value) => encode(value),
79			DefaultValue::Null => 0,
80			DefaultValue::HashMap(value) => {
81				// handle hashmap specially
82				// keys and values are stored separately as string arrays
83				let keys = value.keys().cloned().collect::<Vec<_>>();
84				let values = keys
85					.iter()
86					.map(|k| value.get(k).unwrap().clone())
87					.collect::<Vec<_>>();
88				defaults_set(&format!("{key}.keys"), DefaultValue::StringArray(keys));
89				defaults_set(&format!("{key}.values"), DefaultValue::StringArray(values));
90				return;
91			}
92		}
93	};
94	let kind = value.as_byte();
95	unsafe {
96		set(key.as_ptr(), key.len(), kind, value_ptr);
97	};
98	if value_ptr != 0 {
99		unsafe { free_result(value_ptr) };
100	}
101}
102
103/// Sets the UserDefaults value of the specified key with serialized data.
104pub fn defaults_set_data<T: Serialize>(key: &str, value: T) {
105	let value_ptr: i32 = unsafe { encode(&value) };
106	unsafe {
107		set(key.as_ptr(), key.len(), 0, value_ptr);
108	};
109	unsafe { free_result(value_ptr) };
110}