difftreelog
feat remove lock
in: trunk
Building is locked on nix store anyway
1 file changed
src/db/db.rsdiffbeforeafterboth1//! Small .toml based readable data store23use anyhow::{Context, Result};4use serde::{de::DeserializeOwned, Serialize};5use std::{6 cell::Cell,7 collections::HashSet,8 io::Write,9 ops::{Deref, DerefMut},10 path::Path,11 path::PathBuf,12 sync::{Arc, Mutex},13};1415struct DbInternal {16 root: PathBuf,17 locked_paths: HashSet<PathBuf>,18 _lockfile: lockfile::Lockfile,19}2021pub trait DbData: DeserializeOwned + Serialize + Default {22 const DB_NAME: &'static str;2324 fn open(db: &Db) -> Result<DbFile<Self>> {25 db.db::<Self>()26 }27}2829#[derive(Clone)]30pub struct Db(Arc<Mutex<DbInternal>>);31impl Db {32 pub fn new(root: impl AsRef<Path>) -> Result<Self> {33 let root: &Path = root.as_ref();34 std::fs::create_dir_all(&root).context("db root")?;35 let mut lockfile = root.to_owned();36 lockfile.push(".lock");37 let lockfile = lockfile::Lockfile::create(lockfile).context("db lock")?;38 Ok(Db(Arc::new(Mutex::new(DbInternal {39 root: root.to_owned(),40 locked_paths: HashSet::new(),41 _lockfile: lockfile,42 }))))43 }4445 pub fn db<T: DbData>(&self) -> Result<DbFile<T>> {46 let name = T::DB_NAME;47 assert!(!name.contains("/") && !name.contains("\\"));48 let mut db = self.0.lock().unwrap();49 let mut data_path = db.root.clone();50 data_path.push(format!("{}.toml", name));5152 if !db.locked_paths.insert(data_path.clone()) {53 anyhow::bail!("file is already open");54 }5556 let data = if data_path.exists() {57 let raw_data = std::fs::read(&data_path).context("reading file")?;58 toml::from_slice(&raw_data).context("parsing file")?59 } else {60 T::default()61 };6263 Ok(DbFile {64 db: self.clone(),65 root: db.root.clone(),66 path: data_path,67 data,68 dirty: Cell::new(false),69 })70 }71}7273pub struct DbFile<T: DbData> {74 db: Db,75 root: PathBuf,76 path: PathBuf,77 data: T,78 dirty: Cell<bool>,79}8081impl<T: DbData> Deref for DbFile<T> {82 type Target = T;8384 fn deref(&self) -> &Self::Target {85 &self.data86 }87}8889impl<T: DbData> DerefMut for DbFile<T> {90 fn deref_mut(&mut self) -> &mut Self::Target {91 self.dirty.set(true);92 &mut self.data93 }94}9596impl<T: DbData> DbFile<T> {97 pub fn write(&self) -> Result<()> {98 if !self.dirty.get() {99 return Ok(());100 }101 let mut temp = tempfile::Builder::new()102 .prefix("~")103 .suffix(".toml")104 .tempfile_in(&self.root)?;105 let mut out = String::new();106 let mut serializer = toml::Serializer::new(&mut out);107 serializer.pretty_array(true).pretty_string(true);108 self.data.serialize(&mut serializer)?;109 temp.write_all(&out.as_bytes())?;110 temp.persist(&self.path)?;111 self.dirty.set(false);112 Ok(())113 }114}115116impl<T: DbData> Drop for DbFile<T> {117 fn drop(&mut self) {118 let mut db = self.db.0.lock().unwrap();119 self.write().unwrap();120 db.locked_paths.remove(&self.path);121 }122}1//! Small .toml based readable data store23use anyhow::{Context, Result};4use serde::{de::DeserializeOwned, Serialize};5use std::{6 cell::Cell,7 collections::HashSet,8 io::Write,9 ops::{Deref, DerefMut},10 path::Path,11 path::PathBuf,12 sync::{Arc, Mutex},13};1415struct DbInternal {16 root: PathBuf,17 locked_paths: HashSet<PathBuf>,18}1920pub trait DbData: DeserializeOwned + Serialize + Default {21 const DB_NAME: &'static str;2223 fn open(db: &Db) -> Result<DbFile<Self>> {24 db.db::<Self>()25 }26}2728#[derive(Clone)]29pub struct Db(Arc<Mutex<DbInternal>>);30impl Db {31 pub fn new(root: impl AsRef<Path>) -> Result<Self> {32 let root: &Path = root.as_ref();33 std::fs::create_dir_all(&root).context("db root")?;34 Ok(Db(Arc::new(Mutex::new(DbInternal {35 root: root.to_owned(),36 locked_paths: HashSet::new(),37 }))))38 }3940 pub fn db<T: DbData>(&self) -> Result<DbFile<T>> {41 let name = T::DB_NAME;42 assert!(!name.contains("/") && !name.contains("\\"));43 let mut db = self.0.lock().unwrap();44 let mut data_path = db.root.clone();45 data_path.push(format!("{}.toml", name));4647 if !db.locked_paths.insert(data_path.clone()) {48 anyhow::bail!("file is already open");49 }5051 let data = if data_path.exists() {52 let raw_data = std::fs::read(&data_path).context("reading file")?;53 toml::from_slice(&raw_data).context("parsing file")?54 } else {55 T::default()56 };5758 Ok(DbFile {59 db: self.clone(),60 root: db.root.clone(),61 path: data_path,62 data,63 dirty: Cell::new(false),64 })65 }66}6768pub struct DbFile<T: DbData> {69 db: Db,70 root: PathBuf,71 path: PathBuf,72 data: T,73 dirty: Cell<bool>,74}7576impl<T: DbData> Deref for DbFile<T> {77 type Target = T;7879 fn deref(&self) -> &Self::Target {80 &self.data81 }82}8384impl<T: DbData> DerefMut for DbFile<T> {85 fn deref_mut(&mut self) -> &mut Self::Target {86 self.dirty.set(true);87 &mut self.data88 }89}9091impl<T: DbData> DbFile<T> {92 pub fn write(&self) -> Result<()> {93 if !self.dirty.get() {94 return Ok(());95 }96 let mut temp = tempfile::Builder::new()97 .prefix("~")98 .suffix(".toml")99 .tempfile_in(&self.root)?;100 let mut out = String::new();101 let mut serializer = toml::Serializer::new(&mut out);102 serializer.pretty_array(true).pretty_string(true);103 self.data.serialize(&mut serializer)?;104 temp.write_all(&out.as_bytes())?;105 temp.persist(&self.path)?;106 self.dirty.set(false);107 Ok(())108 }109}110111impl<T: DbData> Drop for DbFile<T> {112 fn drop(&mut self) {113 let mut db = self.db.0.lock().unwrap();114 self.write().unwrap();115 db.locked_paths.remove(&self.path);116 }117}