diff --git a/src/profile/identity.rs b/src/profile/identity.rs index d575bce8..ffc80b5f 100644 --- a/src/profile/identity.rs +++ b/src/profile/identity.rs @@ -37,21 +37,18 @@ impl Identity { } } - /// Get `pem` record match `request` - /// - /// https://geminiprotocol.net/docs/protocol-specification.gmi#client-certificates + /// Get `pem` record match `request` according to + /// [Gemini protocol specification](https://geminiprotocol.net/docs/protocol-specification.gmi#client-certificates) + /// * this function work with memory cache not database pub fn gemini(&self, request: &str) -> Option { + // @TODO apply protocol rules to certificate selection for profile_identity_gemini_id in self.gemini.auth.memory.starts_with(request) { - if let Ok(gemini_records) = self.gemini.database.records() { - for gemini_record in gemini_records { - if gemini_record.id == profile_identity_gemini_id { - return Some(gemini_record.pem); - } - } + if let Ok(pem) = self.gemini.memory.get(profile_identity_gemini_id) { + return Some(pem); } } None - } // @TODO apply protocol rules to selection + } } // Tools diff --git a/src/profile/identity/gemini.rs b/src/profile/identity/gemini.rs index 39577c15..8cb6c96f 100644 --- a/src/profile/identity/gemini.rs +++ b/src/profile/identity/gemini.rs @@ -1,8 +1,10 @@ mod auth; mod database; +mod memory; use auth::Auth; use database::Database; +use memory::Memory; use sqlite::{Connection, Transaction}; use std::{rc::Rc, sync::RwLock}; @@ -12,7 +14,8 @@ use std::{rc::Rc, sync::RwLock}; /// https://geminiprotocol.net/docs/protocol-specification.gmi#client-certificates pub struct Gemini { pub auth: Rc, - pub database: Rc, + // pub database: Rc, + pub memory: Rc, } impl Gemini { @@ -20,9 +23,27 @@ impl Gemini { /// Create new `Self` pub fn new(connection: Rc>, profile_identity_id: Rc) -> Self { + // Init children components + let auth = Rc::new(Auth::new(connection.clone())); + let database = Rc::new(Database::new(connection, profile_identity_id)); + let memory = Rc::new(Memory::new()); + + // Build initial index + match database.records() { + Ok(records) => { + for record in records { + if memory.add(record.id, record.pem).is_err() { + todo!() + } + } + } + Err(reason) => todo!("{reason}"), + } + Self { - auth: Rc::new(Auth::new(connection.clone())), - database: Rc::new(Database::new(connection, profile_identity_id)), + auth, + // database, + memory, } } diff --git a/src/profile/identity/gemini/memory.rs b/src/profile/identity/gemini/memory.rs new file mode 100644 index 00000000..1f6ee41f --- /dev/null +++ b/src/profile/identity/gemini/memory.rs @@ -0,0 +1,39 @@ +mod error; +use error::Error; + +use std::{cell::RefCell, collections::HashMap}; + +/// Reduce disk usage by cache index in memory +pub struct Memory { + index: RefCell>, +} + +impl Memory { + // Constructors + + /// Create new `Self` + pub fn new() -> Self { + Self { + index: RefCell::new(HashMap::new()), + } + } + + // Actions + + /// Add new record with `id` as key and `pem` as value + /// * validate record with same key does not exist yet + pub fn add(&self, id: i64, pem: String) -> Result<(), Error> { + match self.index.borrow_mut().insert(id, pem) { + Some(_) => Err(Error::Overwrite), // @TODO prevent? + None => Ok(()), + } + } + + /// Get `pem` clone by `id` from memory index + pub fn get(&self, id: i64) -> Result { + match self.index.borrow().get(&id) { + Some(pem) => Ok(pem.clone()), + None => Err(Error::NotFound), + } + } +} diff --git a/src/profile/identity/gemini/memory/error.rs b/src/profile/identity/gemini/memory/error.rs new file mode 100644 index 00000000..09edab81 --- /dev/null +++ b/src/profile/identity/gemini/memory/error.rs @@ -0,0 +1,5 @@ +#[derive(Debug)] +pub enum Error { + NotFound, + Overwrite, +}