Skip to content

Commit

Permalink
Copy owner from previous database file (#364)
Browse files Browse the repository at this point in the history
  • Loading branch information
ajeetdsouza committed Mar 9, 2022
1 parent 90e781a commit de16c49
Show file tree
Hide file tree
Showing 13 changed files with 224 additions and 199 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fzf: added `--keep-right` option by default, upgraded minimum version to v0.21.0.
- Bash: only enable completions on 4.4+.
- Fzf: bypass `ls` alias in preview window.
- Retain ownership of database file.

## [0.8.0] - 2021-12-25

Expand Down
82 changes: 36 additions & 46 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 5 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,13 @@ bincode = "1.3.1"
clap = { version = "3.1.0", features = ["derive"] }
dirs = "4.0.0"
dunce = "1.0.1"
fastrand = "1.7.0"
glob = "0.3.0"
ordered-float = "2.0.0"
serde = { version = "1.0.116", features = ["derive"] }
tempfile = "3.1.0"
thiserror = "1.0.30"

[target.'cfg(windows)'.dependencies]
rand = { version = "0.8.4", features = [
"getrandom",
"small_rng",
], default-features = false }
[target.'cfg(unix)'.dependencies]
nix = "0.23.1"

[build-dependencies]
clap = { version = "3.1.0", features = ["derive"] }
Expand All @@ -44,10 +40,11 @@ clap_complete_fig = "3.1.0"
assert_cmd = "2.0.0"
rstest = "0.12.0"
rstest_reuse = "0.3.0"
tempfile = "3.1.0"

[features]
default = []
nix = []
nix-dev = []

[profile.release]
codegen-units = 1
Expand Down
4 changes: 2 additions & 2 deletions src/cmd/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ use std::io::{self, Write};
use anyhow::{Context, Result};

use crate::cmd::{Query, Run};
use crate::config;
use crate::db::{Database, DatabaseFile};
use crate::error::BrokenPipeHandler;
use crate::fzf::Fzf;
use crate::{config, util};
use crate::util::{self, Fzf};

impl Run for Query {
fn run(&self) -> Result<()> {
Expand Down
4 changes: 2 additions & 2 deletions src/cmd/remove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ use std::io::{self, Write};
use anyhow::{bail, Context, Result};

use crate::cmd::{Remove, Run};
use crate::config;
use crate::db::DatabaseFile;
use crate::fzf::Fzf;
use crate::{config, util};
use crate::util::{self, Fzf};

impl Run for Remove {
fn run(&self) -> Result<()> {
Expand Down
55 changes: 4 additions & 51 deletions src/db/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ mod dir;
mod stream;

use std::fs;
use std::io::{self, Write};
use std::io;
use std::path::{Path, PathBuf};

use anyhow::{Context, Result};
pub use dir::{Dir, DirList, Epoch, Rank};
pub use stream::Stream;
use tempfile::{NamedTempFile, PersistError};

use crate::util;

#[derive(Debug)]
pub struct Database<'file> {
Expand All @@ -24,18 +25,8 @@ impl<'file> Database<'file> {
}

let buffer = self.dirs.to_bytes()?;
let mut file = NamedTempFile::new_in(self.data_dir)
.with_context(|| format!("could not create temporary database in: {}", self.data_dir.display()))?;

// Preallocate enough space on the file, preventing copying later on. This optimization may
// fail on some filesystems, but it is safe to ignore it and proceed.
let _ = file.as_file().set_len(buffer.len() as _);
file.write_all(&buffer)
.with_context(|| format!("could not write to temporary database: {}", file.path().display()))?;

let path = db_path(&self.data_dir);
persist(file, &path).with_context(|| format!("could not replace database: {}", path.display()))?;

util::write(&path, &buffer).context("could not write to database")?;
self.modified = false;
Ok(())
}
Expand Down Expand Up @@ -120,44 +111,6 @@ impl<'file> Database<'file> {
}
}

#[cfg(unix)]
fn persist<P: AsRef<Path>>(file: NamedTempFile, path: P) -> Result<(), PersistError> {
file.persist(path)?;
Ok(())
}

#[cfg(windows)]
fn persist<P: AsRef<Path>>(mut file: NamedTempFile, path: P) -> Result<(), PersistError> {
use std::thread;
use std::time::Duration;

use rand::distributions::{Distribution, Uniform};
use rand::rngs::SmallRng;
use rand::SeedableRng;

// File renames on Windows are not atomic and sometimes fail with `PermissionDenied`. This is
// extremely unlikely unless it's running in a loop on multiple threads. Nevertheless, we guard
// against it by retrying the rename a fixed number of times.
const MAX_TRIES: usize = 10;
let mut rng = None;

for _ in 0..MAX_TRIES {
match file.persist(&path) {
Ok(_) => break,
Err(e) if e.error.kind() == io::ErrorKind::PermissionDenied => {
let mut rng = rng.get_or_insert_with(SmallRng::from_entropy);
let between = Uniform::from(50..150);
let duration = Duration::from_millis(between.sample(&mut rng));
thread::sleep(duration);
file = e.file;
}
Err(e) => return Err(e),
}
}

Ok(())
}

pub struct DatabaseFile {
buffer: Vec<u8>,
data_dir: PathBuf,
Expand Down
15 changes: 8 additions & 7 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
use std::fmt::{self, Display, Formatter};
use std::io;

use anyhow::{bail, Context, Result};
use thiserror::Error;

#[derive(Debug, Error)]
#[error("could not find fzf, is it installed?")]
pub struct FzfNotFound;

/// Custom error type for early exit.
#[derive(Debug, Error)]
#[error("")]
#[derive(Debug)]
pub struct SilentExit {
pub code: i32,
}

impl Display for SilentExit {
fn fmt(&self, _: &mut Formatter<'_>) -> fmt::Result {
Ok(())
}
}

pub trait BrokenPipeHandler {
fn pipe_exit(self, device: &str) -> Result<()>;
}
Expand Down
Loading

0 comments on commit de16c49

Please sign in to comment.