Aldonu datumbazon kaj sesia-administradon

This commit is contained in:
Savanni D'Gerinel 2021-12-30 16:40:14 -05:00
parent e48ed1f2dd
commit 823a701a2f
8 changed files with 431 additions and 70 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
node_modules node_modules
target target
*.db

82
Cargo.lock generated
View File

@ -2,6 +2,23 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom 0.2.3",
"once_cell",
"version_check",
]
[[package]]
name = "anyhow"
version = "1.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3"
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.0.1" version = "1.0.1"
@ -84,6 +101,18 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "fallible-iterator"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
name = "fallible-streaming-iterator"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
@ -219,6 +248,18 @@ name = "hashbrown"
version = "0.11.2" version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
dependencies = [
"ahash",
]
[[package]]
name = "hashlink"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf"
dependencies = [
"hashbrown",
]
[[package]] [[package]]
name = "headers" name = "headers"
@ -361,6 +402,10 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
name = "kampanja-kontrolada-servilo" name = "kampanja-kontrolada-servilo"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow",
"rusqlite",
"tempfile",
"thiserror",
"tokio", "tokio",
"uuid", "uuid",
"warp", "warp",
@ -378,6 +423,16 @@ version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
[[package]]
name = "libsqlite3-sys"
version = "0.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cafc7c74096c336d9d27145f7ebd4f4b6f95ba16aa5a282387267e6925cb58"
dependencies = [
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.5" version = "0.4.5"
@ -558,6 +613,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.10" version = "0.2.10"
@ -687,6 +748,21 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "rusqlite"
version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ba4d3462c8b2e4d7f4fcfcf2b296dc6b65404fbbc7b63daa37fd485c149daf7"
dependencies = [
"bitflags",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
"libsqlite3-sys",
"memchr",
"smallvec",
]
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.5" version = "1.0.5"
@ -1058,6 +1134,12 @@ dependencies = [
"getrandom 0.2.3", "getrandom 0.2.3",
] ]
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.3" version = "0.9.3"

View File

@ -6,6 +6,12 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
tokio = { version = "1", features = ["full"] } anyhow = { version = "1" }
uuid = { version = "0.8.2", features = ["v4"] } rusqlite = { version = "0.26" }
warp = { version = "0.3.1" } thiserror = { version = "1" }
tokio = { version = "1", features = ["full"] }
uuid = { version = "0.8", features = ["v4"] }
warp = { version = "0.3" }
[dev-dependencies]
tempfile = { version = "3" }

View File

@ -1,3 +1,5 @@
use crate::datumbazo::Datumbazo;
use rusqlite::OptionalExtension;
use std::{ use std::{
collections::HashMap, collections::HashMap,
convert::Infallible, convert::Infallible,
@ -30,6 +32,21 @@ impl From<Ĵetono> for String {
} }
} }
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct UzantIdentigilo(String);
impl From<&str> for UzantIdentigilo {
fn from(s: &str) -> Self {
UzantIdentigilo(s.to_owned())
}
}
impl From<UzantIdentigilo> for String {
fn from(s: UzantIdentigilo) -> Self {
s.0.clone()
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Uzantnomo(String); pub struct Uzantnomo(String);
@ -45,33 +62,95 @@ impl From<Uzantnomo> for String {
} }
} }
pub trait AŭtentigoDB: Clone + Send + Sync { pub trait AŭtentigoDB: Send {
fn aŭtentigu(&self, ĵetono: Ĵetono) -> Option<Uzantnomo>; fn aŭtentigu(&self, ĵetono: Ĵetono) -> Option<(UzantIdentigilo, Uzantnomo)>;
fn kreu_ĵetono(&mut self, uzantnomo: Uzantnomo) -> Ĵetono; fn kreu_ĵetono(&mut self, uzantnomo: Uzantnomo) -> Ĵetono;
} }
#[derive(Clone)]
pub struct MemAŭtentigo { pub struct MemAŭtentigo {
mem: HashMap<Ĵetono, Uzantnomo>, sesioj: HashMap<Ĵetono, UzantIdentigilo>,
uzantoj: HashMap<UzantIdentigilo, Uzantnomo>,
inversa_uzantoj: HashMap<Uzantnomo, UzantIdentigilo>,
} }
impl MemAŭtentigo { impl MemAŭtentigo {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
mem: HashMap::new(), sesioj: HashMap::new(),
uzantoj: HashMap::new(),
inversa_uzantoj: HashMap::new(),
} }
} }
} }
impl AŭtentigoDB for MemAŭtentigo { impl AŭtentigoDB for MemAŭtentigo {
fn aŭtentigu(&self, ĵetono: Ĵetono) -> Option<Uzantnomo> { fn aŭtentigu(&self, ĵetono: Ĵetono) -> Option<(UzantIdentigilo, Uzantnomo)> {
self.mem.get(&ĵetono).cloned() let identigilo = self.sesioj.get(&ĵetono).cloned()?;
let uzantnomo = self.uzantoj.get(&identigilo).cloned()?;
Some((identigilo, uzantnomo))
}
fn kreu_ĵetono(&mut self, uzantnomo: Uzantnomo) -> Ĵetono {
let identigilo = self.inversa_uzantoj.get(&uzantnomo).cloned().unwrap();
let ĵetono = Ĵetono::from(format!("{}", Hyphenated::from_uuid(Uuid::new_v4())).as_str());
self.sesioj.insert(ĵetono.clone(), identigilo);
ĵetono
}
}
pub struct DBAŭtentigo {
pool: Datumbazo,
}
impl DBAŭtentigo {
pub fn kreu(pool: Datumbazo) -> Self {
DBAŭtentigo { pool }
}
}
impl AŭtentigoDB for DBAŭtentigo {
fn aŭtentigu(&self, ĵetono: Ĵetono) -> Option<(UzantIdentigilo, Uzantnomo)> {
let konekto = self.pool.konektu().unwrap();
konekto
.query_row(
"SELECT uzantoj.id, uzantoj.nomo FROM sesioj INNER JOIN uzantoj on sesioj.uzanto == uzantoj.id WHERE sesioj.id = ?",
[String::from(ĵetono)],
|row| {
let identigilo = row.get("id")
.map(|s: String| UzantIdentigilo::from(s.as_str())).unwrap();
let nomo = row.get("nomo")
.map(|s: String| Uzantnomo::from(s.as_str())).unwrap();
Ok((identigilo, nomo))
},
)
.optional()
.unwrap()
} }
fn kreu_ĵetono(&mut self, uzantnomo: Uzantnomo) -> Ĵetono { fn kreu_ĵetono(&mut self, uzantnomo: Uzantnomo) -> Ĵetono {
let ĵetono = Ĵetono::from(format!("{}", Hyphenated::from_uuid(Uuid::new_v4())).as_str()); let ĵetono = Ĵetono::from(format!("{}", Hyphenated::from_uuid(Uuid::new_v4())).as_str());
self.mem.insert(ĵetono.clone(), uzantnomo); let mut konekto = self.pool.konektu().unwrap();
let tr = konekto.transaction().unwrap();
let uzanta_id: Option<String> = tr
.query_row(
"SELECT id FROM uzantoj WHERE nomo = ?",
[String::from(uzantnomo.clone())],
|row| row.get("id"),
)
.optional()
.unwrap();
match uzanta_id {
None => panic!("uzanto ne ekzistas"),
Some(id) => {
tr.execute(
"INSERT INTO sesioj VALUES(?, ?)",
[String::from(ĵetono.clone()), id],
)
.unwrap();
}
}
tr.commit().unwrap();
ĵetono ĵetono
} }
} }
@ -82,8 +161,8 @@ pub struct AŭtentigoPostulas;
impl Reject for AŭtentigoPostulas {} impl Reject for AŭtentigoPostulas {}
pub fn kun_aŭtentigo( pub fn kun_aŭtentigo(
auth_ctx: Arc<RwLock<impl AŭtentigoDB>>, auth_ctx: Arc<RwLock<impl AŭtentigoDB + Sync>>,
) -> impl Filter<Extract = (Uzantnomo,), Error = Rejection> + Clone { ) -> impl Filter<Extract = ((UzantIdentigilo, Uzantnomo),), Error = Rejection> + Clone {
let auth_ctx = auth_ctx.clone(); let auth_ctx = auth_ctx.clone();
warp::header("authentication").and_then({ warp::header("authentication").and_then({
let auth_ctx = auth_ctx.clone(); let auth_ctx = auth_ctx.clone();
@ -91,7 +170,7 @@ pub fn kun_aŭtentigo(
let auth_ctx = auth_ctx.clone(); let auth_ctx = auth_ctx.clone();
async move { async move {
match auth_ctx.read().unwrap().aŭtentigu(text) { match auth_ctx.read().unwrap().aŭtentigu(text) {
Some(uzantnomo) => Ok(uzantnomo), Some(salutiloj) => Ok(salutiloj),
None => Err(reject::custom(AŭtentigoPostulas)), None => Err(reject::custom(AŭtentigoPostulas)),
} }
} }

116
servilo/src/datumbazo.rs Normal file
View File

@ -0,0 +1,116 @@
use rusqlite::{params, Connection};
use std::{
ops::{Deref, DerefMut},
path::PathBuf,
sync::{Arc, Mutex},
};
pub struct ManagedConnection<'a> {
pool: &'a Datumbazo,
konekto: Option<Connection>,
}
#[derive(Clone)]
pub struct Datumbazo {
dosierindiko: PathBuf,
pool: Arc<Mutex<Vec<Connection>>>,
}
impl Datumbazo {
pub fn kreu(dosierindiko: PathBuf) -> Result<Datumbazo, anyhow::Error> {
let mut konekto = Connection::open(dosierindiko.clone())?;
let tx = konekto.transaction()?;
let versio: i32 = tx.pragma_query_value(None, "user_version", |r| r.get(0))?;
println!("versio: {}", versio);
if versio == 0 {
tx.execute_batch(
"CREATE TABLE uzantoj (id string primary key, nomo text);
CREATE TABLE rajtoj (uzanto string, rajto string, foreign key(uzanto) references uzanto(id));
CREATE TABLE sesioj (id string primary key not null, uzanto string, foreign key(uzanto) references uzantoj(id));
PRAGMA user_version = 1;",
)?;
}
let versio: i32 = tx.pragma_query_value(None, "user_version", |r| r.get(0))?;
println!("versio: {}", versio);
tx.commit()?;
Ok(Datumbazo {
dosierindiko,
pool: Arc::new(Mutex::new(vec![konekto])),
})
}
pub fn konektu<'a>(&'a self) -> Result<ManagedConnection<'a>, anyhow::Error> {
let mut pool = self.pool.lock().unwrap();
match pool.pop() {
Some(konekto) => Ok(ManagedConnection {
pool: &self,
konekto: Some(konekto),
}),
None => {
let konekto = Connection::open(self.dosierindiko.clone())?;
Ok(ManagedConnection {
pool: &self,
konekto: Some(konekto),
})
}
}
}
pub fn revenu(&self, konekto: Connection) {
let mut pool = self.pool.lock().unwrap();
pool.push(konekto);
}
}
impl Deref for ManagedConnection<'_> {
type Target = Connection;
fn deref(&self) -> &Connection {
self.konekto.as_ref().unwrap()
}
}
impl DerefMut for ManagedConnection<'_> {
fn deref_mut(&mut self) -> &mut Connection {
self.konekto.as_mut().unwrap()
}
}
impl Drop for ManagedConnection<'_> {
fn drop(&mut self) {
self.pool.revenu(self.konekto.take().unwrap());
}
}
#[cfg(test)]
mod test {
use super::*;
use tempfile::NamedTempFile;
#[test]
fn povas_krei_uzanton() {
let vojo = NamedTempFile::new().unwrap().into_temp_path();
let datumbazo = Datumbazo::kreu(vojo.to_path_buf()).unwrap();
let mut konekto = datumbazo.konektu().unwrap();
let tr = konekto.transaction().unwrap();
tr.execute(
"INSERT INTO uzantoj VALUES(?, ?)",
params![1, String::from("mia-uzantnomo")],
)
.unwrap();
tr.commit().unwrap();
let konekto = datumbazo.konektu().unwrap();
let id: Option<u32> = konekto
.query_row(
"SELECT id FROM uzantoj WHERE nomo = ?",
[String::from("mia-uzantnomo")],
|row| row.get("id"),
)
.unwrap();
assert_eq!(id, Some(1));
}
}

View File

@ -1,21 +1,27 @@
use std::convert::Infallible; use std::convert::Infallible;
use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::PathBuf;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use warp::{Filter, Rejection}; use warp::{Filter, Rejection};
#[path = "aŭtentigo.rs"] #[path = "aŭtentigo.rs"]
mod aŭtentigo; mod aŭtentigo;
use aŭtentigo::{kun_aŭtentigo, AŭtentigoDB, AŭtentigoPostulas, MemAŭtentigo, Uzantnomo}; use aŭtentigo::{kun_aŭtentigo, AŭtentigoDB, AŭtentigoPostulas, Uzantnomo};
mod datumbazo;
use datumbazo::Datumbazo;
use crate::rajtigo::{MemRajtigo, Rajtigo, Rajto};
mod rajtigo; mod rajtigo;
use rajtigo::{DBRajtigo, Rajtigo, Rajto};
use crate::aŭtentigo::{DBAŭtentigo, UzantIdentigilo};
async fn traktilo_de_listigu_ludantojn( async fn traktilo_de_listigu_ludantojn(
uzantnomo: Uzantnomo, identigilo: UzantIdentigilo,
rajtigo: Arc<RwLock<impl Rajtigo>>, rajtigo: Arc<RwLock<impl Rajtigo>>,
) -> Result<impl warp::Reply, Rejection> { ) -> Result<impl warp::Reply, Rejection> {
if rajtigo.read().unwrap().havas_rajton(uzantnomo, |rajtoj| { if rajtigo.read().unwrap().havas_rajton(identigilo, |rajtoj| {
rajtoj.fold(false, |acc, r| acc || *r == Rajto::from("admin")) rajtoj.fold(false, |acc, r| acc || r == Rajto::from("admin"))
}) { }) {
Ok(warp::reply::json(&vec!["Alice", "Betty", "Charles"])) Ok(warp::reply::json(&vec!["Alice", "Betty", "Charles"]))
} else { } else {
@ -42,17 +48,15 @@ async fn traktilu_erarojn(err: Rejection) -> Result<impl warp::Reply, Infallible
#[tokio::main] #[tokio::main]
pub async fn main() { pub async fn main() {
let auth_ctx = Arc::new(RwLock::new(MemAŭtentigo::new())); let db = Datumbazo::kreu(PathBuf::from("../servilo.db")).unwrap();
let auth_ctx = Arc::new(RwLock::new(DBAŭtentigo::kreu(db.clone())));
let ĵetono = auth_ctx let ĵetono = auth_ctx
.write() .write()
.unwrap() .unwrap()
.kreu_ĵetono(Uzantnomo::from("Savanni")); .kreu_ĵetono(Uzantnomo::from("savanni"));
println!("ĵetono: {}", String::from(ĵetono)); println!("ĵetono: {}", String::from(ĵetono));
let rajtigo = Arc::new(RwLock::new(MemRajtigo::new())); let rajtigo = Arc::new(RwLock::new(DBRajtigo::kreu(db)));
rajtigo.write().unwrap().aldonu_rajtoj(
Uzantnomo::from("Savanni"),
&mut (vec![Rajto::from("admin")].into_iter()),
);
let listigu_ludantojn = { let listigu_ludantojn = {
let auth_ctx = auth_ctx.clone(); let auth_ctx = auth_ctx.clone();
@ -60,9 +64,14 @@ pub async fn main() {
.and(warp::path!("api" / "ludantoj")) .and(warp::path!("api" / "ludantoj"))
.and_then({ .and_then({
let rajtigo = rajtigo.clone(); let rajtigo = rajtigo.clone();
move |uzantnomo| { move |(identigilo, uzantnomo): (UzantIdentigilo, Uzantnomo)| {
println!(
"[{}] {}",
String::from(identigilo.clone()),
String::from(uzantnomo.clone())
);
let rajtigo = rajtigo.clone(); let rajtigo = rajtigo.clone();
traktilo_de_listigu_ludantojn(uzantnomo, rajtigo) traktilo_de_listigu_ludantojn(identigilo, rajtigo)
} }
}) })
}; };

View File

@ -1,4 +1,5 @@
use crate::aŭtentigo::Uzantnomo; use crate::{aŭtentigo::UzantIdentigilo, datumbazo::Datumbazo};
use rusqlite::OptionalExtension;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
@ -17,58 +18,124 @@ impl From<Rajto> for String {
} }
pub trait Rajtigo { pub trait Rajtigo {
fn havas_rajton<'a, F>(&'a self, uzantnomo: Uzantnomo, provo: F) -> bool fn havas_rajton<'a, F>(&'a self, identigilo: UzantIdentigilo, provo: F) -> bool
where where
F: FnOnce(&mut (dyn Iterator<Item = &'a Rajto> + 'a)) -> bool; F: FnOnce(&mut (dyn Iterator<Item = Rajto>)) -> bool;
fn rajtoj(&self, uzantnomo: &Uzantnomo) -> Vec<Rajto>; fn rajtoj(&self, identigilo: &UzantIdentigilo) -> Vec<Rajto>;
fn aldonu_rajtoj(&mut self, uzantnomo: Uzantnomo, rajtoj: &mut (dyn Iterator<Item = Rajto>)); fn aldonu_rajtoj(
fn foriru_rajtoj(&mut self, uzantnomo: Uzantnomo, rajtoj: &mut (dyn Iterator<Item = Rajto>)); &mut self,
fn foriru_uzanto(&mut self, uzantnomo: Uzantnomo); identigilo: UzantIdentigilo,
rajtoj: &mut (dyn Iterator<Item = Rajto>),
);
fn foriru_rajtoj(
&mut self,
identigilo: UzantIdentigilo,
rajtoj: &mut (dyn Iterator<Item = Rajto>),
);
fn foriru_uzanto(&mut self, identigilo: UzantIdentigilo);
} }
/*
pub struct MemRajtigo { pub struct MemRajtigo {
rajtoj: HashMap<Uzantnomo, HashSet<Rajto>>, rajtoj: HashMap<UzantIdentigilo, HashSet<Rajto>>,
} }
impl MemRajtigo { impl MemRajtigo {
pub fn new() -> Self { pub fn new() -> Self {
MemRajtigo { MemRajtigo {
rajtoj: HashMap::new(), rajtoj: HashMap::new(),
} }
} }
} }
impl Rajtigo for MemRajtigo { impl Rajtigo for MemRajtigo {
fn havas_rajton<'a, F>(&'a self, uzantnomo: Uzantnomo, provo: F) -> bool fn havas_rajton<'a, F>(&'a self, identigilo: UzantIdentigilo, provo: F) -> bool
where where
F: FnOnce(&mut (dyn Iterator<Item = &'a Rajto> + 'a)) -> bool, F: FnOnce(&mut (dyn Iterator<Item = Rajto> + 'a)) -> bool,
{ {
match self.rajtoj.get(&uzantnomo) { match self.rajtoj.get(&identigilo) {
Some(rajtoj) => provo(&mut rajtoj.iter()), Some(rajtoj) => provo(&mut rajtoj.clone().into_iter()),
None => false, None => false,
} }
} }
fn rajtoj(&self, uzantnomo: &Uzantnomo) -> Vec<Rajto> { fn rajtoj(&self, identigilo: &UzantIdentigilo) -> Vec<Rajto> {
self.rajtoj self.rajtoj
.get(&uzantnomo) .get(&identigilo)
.map(|r| r.into_iter().cloned().collect::<Vec<Rajto>>()) .map(|r| r.into_iter().cloned().collect::<Vec<Rajto>>())
.unwrap_or(vec![]) .unwrap_or(vec![])
} }
fn aldonu_rajtoj(&mut self, uzantnomo: Uzantnomo, rajtoj: &mut (dyn Iterator<Item = Rajto>)) { fn aldonu_rajtoj(
let valoro = self.rajtoj.entry(uzantnomo).or_insert(HashSet::new()); &mut self,
identigilo: UzantIdentigilo,
rajtoj: &mut (dyn Iterator<Item = Rajto>),
) {
let valoro = self.rajtoj.entry(identigilo).or_insert(HashSet::new());
while let Some(r) = rajtoj.next() { while let Some(r) = rajtoj.next() {
valoro.insert(r); valoro.insert(r);
} }
} }
fn foriru_rajtoj(&mut self, uzantnomo: Uzantnomo, rajtoj: &mut (dyn Iterator<Item = Rajto>)) { fn foriru_rajtoj(
let valoro = self.rajtoj.entry(uzantnomo).or_insert(HashSet::new()); &mut self,
identigilo: UzantIdentigilo,
rajtoj: &mut (dyn Iterator<Item = Rajto>),
) {
let valoro = self.rajtoj.entry(identigilo).or_insert(HashSet::new());
while let Some(r) = rajtoj.next() { while let Some(r) = rajtoj.next() {
valoro.remove(&r); valoro.remove(&r);
} }
} }
fn foriru_uzanto(&mut self, uzantnomo: Uzantnomo) { fn foriru_uzanto(&mut self, identigilo: UzantIdentigilo) {
let _ = self.rajtoj.remove(&uzantnomo); let _ = self.rajtoj.remove(&identigilo);
}
}
*/
pub struct DBRajtigo {
pool: Datumbazo,
}
impl DBRajtigo {
pub fn kreu(pool: Datumbazo) -> Self {
DBRajtigo { pool }
} }
} }
impl Rajtigo for DBRajtigo {
fn havas_rajton<'a, F>(&'a self, identigilo: UzantIdentigilo, provo: F) -> bool
where
F: FnOnce(&mut (dyn Iterator<Item = Rajto>)) -> bool,
{
let konekto = self.pool.konektu().unwrap();
let mut stmt = konekto
.prepare("SELECT rajto FROM rajtoj WHERE uzanto = ?")
.unwrap();
let mut rajtoj = stmt
.query_map([String::from(identigilo)], |row| {
row.get("rajto").map(|s: String| Rajto::from(s.as_ref()))
})
.unwrap()
.map(|r| r.unwrap());
provo(&mut rajtoj)
}
fn rajtoj(&self, identigilo: &UzantIdentigilo) -> Vec<Rajto> {
vec![]
}
fn aldonu_rajtoj(
&mut self,
identigilo: UzantIdentigilo,
rajtoj: &mut (dyn Iterator<Item = Rajto>),
) {
}
fn foriru_rajtoj(
&mut self,
identigilo: UzantIdentigilo,
rajtoj: &mut (dyn Iterator<Item = Rajto>),
) {
}
fn foriru_uzanto(&mut self, identigilo: UzantIdentigilo) {}
}

View File

@ -14,6 +14,7 @@ in pkgs.mkShell {
pkgs.nodejs pkgs.nodejs
pkgs.openssl pkgs.openssl
pkgs.pkg-config pkgs.pkg-config
pkgs.sqlite
pkgs.wrapGAppsHook pkgs.wrapGAppsHook
rust rust
unstable.rust-analyzer unstable.rust-analyzer