Add (de-)serializable NoteId type
This commit is contained in:
parent
d19f98e852
commit
849400cf35
6 changed files with 160 additions and 3 deletions
58
Cargo.lock
generated
58
Cargo.lock
generated
|
|
@ -26,7 +26,7 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"version_check",
|
"version_check",
|
||||||
"zerocopy",
|
"zerocopy 0.7.35",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1459,6 +1459,7 @@ dependencies = [
|
||||||
name = "gdn-app"
|
name = "gdn-app"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"rand 0.9.0",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
|
|
@ -3993,7 +3994,7 @@ version = "0.2.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerocopy",
|
"zerocopy 0.7.35",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -4176,6 +4177,17 @@ dependencies = [
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
|
||||||
|
dependencies = [
|
||||||
|
"rand_chacha 0.9.0",
|
||||||
|
"rand_core 0.9.0",
|
||||||
|
"zerocopy 0.8.17",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_chacha"
|
name = "rand_chacha"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
|
@ -4196,6 +4208,16 @@ dependencies = [
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core 0.9.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_core"
|
name = "rand_core"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
|
@ -4214,6 +4236,16 @@ dependencies = [
|
||||||
"getrandom 0.2.15",
|
"getrandom 0.2.15",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.3.1",
|
||||||
|
"zerocopy 0.8.17",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_hc"
|
name = "rand_hc"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
|
@ -6616,7 +6648,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"zerocopy-derive",
|
"zerocopy-derive 0.7.35",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.8.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa91407dacce3a68c56de03abe2760159582b846c6a4acd2f456618087f12713"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive 0.8.17",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -6630,6 +6671,17 @@ dependencies = [
|
||||||
"syn 2.0.98",
|
"syn 2.0.98",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.8.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06718a168365cad3d5ff0bb133aad346959a2074bd4a85c121255a11304a8626"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.98",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerofrom"
|
name = "zerofrom"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ clap = { version = "4.5.28", features = ["derive", "deprecated"] }
|
||||||
directories = "6.0.0"
|
directories = "6.0.0"
|
||||||
gdn = { path = "gdn" }
|
gdn = { path = "gdn" }
|
||||||
gix = "0.70.0"
|
gix = "0.70.0"
|
||||||
|
rand = "0.9.0"
|
||||||
serde = { version = "1.0.217", features = ["derive"] }
|
serde = { version = "1.0.217", features = ["derive"] }
|
||||||
serde_json = "1.0.138"
|
serde_json = "1.0.138"
|
||||||
tauri = { version = "2.2.5", features = [] }
|
tauri = { version = "2.2.5", features = [] }
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ crate-type = ["staticlib", "cdylib", "rlib"]
|
||||||
tauri-build = { workspace = true }
|
tauri-build = { workspace = true }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
rand = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
tauri = { workspace = true }
|
tauri = { workspace = true }
|
||||||
|
|
|
||||||
99
gdn-app/src-tauri/src/ids.rs
Normal file
99
gdn-app/src-tauri/src/ids.rs
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
use std::{fmt, ops::Shl, str::FromStr, time::SystemTime};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
|
/// A timestamp- and randomness-based id.
|
||||||
|
///
|
||||||
|
/// The id consists of 8 bytes. The first 5 bytes are an epoch timestamp in
|
||||||
|
/// seconds, while the remaining 3 bytes are a random number, meant to avoid
|
||||||
|
/// collisions when two ids are created at the same time on different devices.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
struct TimestampId(u64);
|
||||||
|
|
||||||
|
impl TimestampId {
|
||||||
|
fn new() -> Self {
|
||||||
|
let secs = SystemTime::now()
|
||||||
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
.expect("duration is positive")
|
||||||
|
.as_secs();
|
||||||
|
|
||||||
|
let random = rand::random::<u64>();
|
||||||
|
|
||||||
|
// Zeroing the last three bytes just in case. They should already be
|
||||||
|
// zero under normal circumstances.
|
||||||
|
let first_part = secs.shl(3 * 8) & 0xFFFFFFFF_FF000000_u64;
|
||||||
|
let second_part = random & 0x00000000_00FFFFFF_u64;
|
||||||
|
|
||||||
|
Self(first_part | second_part)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for TimestampId {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{:016X}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for TimestampId {
|
||||||
|
type Err = ();
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
if s.len() != 16 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.chars().any(|c| c.is_lowercase()) {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let n = u64::from_str_radix(s, 16).map_err(|_| ())?;
|
||||||
|
|
||||||
|
Ok(Self(n))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The id for a single note.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct NoteId(TimestampId);
|
||||||
|
|
||||||
|
impl NoteId {
|
||||||
|
#[allow(clippy::new_without_default)] // Because side-effects
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self(TimestampId::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for NoteId {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "NoteId({self})")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for NoteId {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "n{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for NoteId {
|
||||||
|
type Err = ();
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let s = s.strip_prefix("n").ok_or(())?;
|
||||||
|
Ok(Self(s.parse()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for NoteId {
|
||||||
|
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
format!("{self}").serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for NoteId {
|
||||||
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
|
<&'de str as Deserialize<'de>>::deserialize(deserializer)?
|
||||||
|
.parse()
|
||||||
|
.map_err(|()| serde::de::Error::custom("invalid note id"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
mod ids;
|
||||||
|
|
||||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn greet(name: &str) -> String {
|
fn greet(name: &str) -> String {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
// This warning does not apply here - the lib uses the dependencies.
|
||||||
|
#![allow(unused_crate_dependencies)]
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
gdn_app_lib::run()
|
gdn_app_lib::run()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue