Feature: zoxide edit subcommand
Allows editing on the backing db by opening up $EDITOR with a human-editable format. Upon exit of the editor, changes are validated and written back to the DB. Fixes: #453
This commit is contained in:
parent
9d9bcfcac2
commit
635a97e812
|
|
@ -181,6 +181,20 @@ dependencies = [
|
|||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89eab4d20ce20cea182308bca13088fecea9c05f6776cf287205d41a0ed3c847"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"terminal_size",
|
||||
"unicode-width",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.11"
|
||||
|
|
@ -191,6 +205,17 @@ dependencies = [
|
|||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dialoguer"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a92e7e37ecef6857fdc0c0c5d42fd5b0938e46590c2183cc92dd310a6d078eb1"
|
||||
dependencies = [
|
||||
"console",
|
||||
"tempfile",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "difflib"
|
||||
version = "0.4.0"
|
||||
|
|
@ -235,6 +260,12 @@ version = "1.7.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.8.0"
|
||||
|
|
@ -674,6 +705,16 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termtree"
|
||||
version = "0.2.4"
|
||||
|
|
@ -730,6 +771,12 @@ version = "1.0.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
|
|
@ -814,6 +861,12 @@ dependencies = [
|
|||
"shell-words",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f"
|
||||
|
||||
[[package]]
|
||||
name = "zoxide"
|
||||
version = "0.8.3"
|
||||
|
|
@ -825,6 +878,7 @@ dependencies = [
|
|||
"clap",
|
||||
"clap_complete",
|
||||
"clap_complete_fig",
|
||||
"dialoguer",
|
||||
"dirs",
|
||||
"dunce",
|
||||
"fastrand",
|
||||
|
|
|
|||
|
|
@ -23,11 +23,13 @@ anyhow = "1.0.32"
|
|||
askama = { version = "0.11.0", default-features = false }
|
||||
bincode = "1.3.1"
|
||||
clap = { version = "3.1.0", features = ["derive"] }
|
||||
dialoguer = "0.10.2"
|
||||
dirs = "4.0.0"
|
||||
dunce = "1.0.1"
|
||||
fastrand = "1.7.0"
|
||||
glob = "0.3.0"
|
||||
serde = { version = "1.0.116", features = ["derive"] }
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
nix = { version = "0.24.1", default-features = false, features = [
|
||||
|
|
@ -47,7 +49,6 @@ clap_complete_fig = "3.1.0"
|
|||
assert_cmd = "2.0.0"
|
||||
rstest = { version = "0.15.0", default-features = false }
|
||||
rstest_reuse = "0.4.0"
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
|
|
|||
|
|
@ -37,6 +37,14 @@ _arguments "${_arguments_options[@]}" \
|
|||
'*::paths:_files -/' \
|
||||
&& ret=0
|
||||
;;
|
||||
(edit)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'-h[Print help information]' \
|
||||
'--help[Print help information]' \
|
||||
'-V[Print version information]' \
|
||||
'--version[Print version information]' \
|
||||
&& ret=0
|
||||
;;
|
||||
(import)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'--from=[Application to import from]:FROM:(autojump z)' \
|
||||
|
|
@ -97,6 +105,7 @@ esac
|
|||
_zoxide_commands() {
|
||||
local commands; commands=(
|
||||
'add:Add a new directory or increment its rank' \
|
||||
'edit:Modify list of paths and rankings in default editor' \
|
||||
'import:Import entries from another application' \
|
||||
'init:Generate shell configuration' \
|
||||
'query:Search for a directory in the database' \
|
||||
|
|
@ -109,6 +118,11 @@ _zoxide__add_commands() {
|
|||
local commands; commands=()
|
||||
_describe -t commands 'zoxide add commands' commands "$@"
|
||||
}
|
||||
(( $+functions[_zoxide__edit_commands] )) ||
|
||||
_zoxide__edit_commands() {
|
||||
local commands; commands=()
|
||||
_describe -t commands 'zoxide edit commands' commands "$@"
|
||||
}
|
||||
(( $+functions[_zoxide__import_commands] )) ||
|
||||
_zoxide__import_commands() {
|
||||
local commands; commands=()
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ Register-ArgumentCompleter -Native -CommandName 'zoxide' -ScriptBlock {
|
|||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version information')
|
||||
[CompletionResult]::new('add', 'add', [CompletionResultType]::ParameterValue, 'Add a new directory or increment its rank')
|
||||
[CompletionResult]::new('edit', 'edit', [CompletionResultType]::ParameterValue, 'Modify list of paths and rankings in default editor')
|
||||
[CompletionResult]::new('import', 'import', [CompletionResultType]::ParameterValue, 'Import entries from another application')
|
||||
[CompletionResult]::new('init', 'init', [CompletionResultType]::ParameterValue, 'Generate shell configuration')
|
||||
[CompletionResult]::new('query', 'query', [CompletionResultType]::ParameterValue, 'Search for a directory in the database')
|
||||
|
|
@ -39,6 +40,13 @@ Register-ArgumentCompleter -Native -CommandName 'zoxide' -ScriptBlock {
|
|||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version information')
|
||||
break
|
||||
}
|
||||
'zoxide;edit' {
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help information')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information')
|
||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version information')
|
||||
break
|
||||
}
|
||||
'zoxide;import' {
|
||||
[CompletionResult]::new('--from', 'from', [CompletionResultType]::ParameterName, 'Application to import from')
|
||||
[CompletionResult]::new('--merge', 'merge', [CompletionResultType]::ParameterName, 'Merge into existing database')
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ _zoxide() {
|
|||
add)
|
||||
cmd+="__add"
|
||||
;;
|
||||
edit)
|
||||
cmd+="__edit"
|
||||
;;
|
||||
import)
|
||||
cmd+="__import"
|
||||
;;
|
||||
|
|
@ -34,7 +37,7 @@ _zoxide() {
|
|||
|
||||
case "${cmd}" in
|
||||
zoxide)
|
||||
opts="-h -V --help --version add import init query remove"
|
||||
opts="-h -V --help --version add edit import init query remove"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
|
|
@ -61,6 +64,20 @@ _zoxide() {
|
|||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
;;
|
||||
zoxide__edit)
|
||||
opts="-h -V --help --version"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
;;
|
||||
zoxide__import)
|
||||
opts="-h -V --from --merge --help --version <PATH>"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ set edit:completion:arg-completer[zoxide] = {|@words|
|
|||
cand -V 'Print version information'
|
||||
cand --version 'Print version information'
|
||||
cand add 'Add a new directory or increment its rank'
|
||||
cand edit 'Modify list of paths and rankings in default editor'
|
||||
cand import 'Import entries from another application'
|
||||
cand init 'Generate shell configuration'
|
||||
cand query 'Search for a directory in the database'
|
||||
|
|
@ -34,6 +35,12 @@ set edit:completion:arg-completer[zoxide] = {|@words|
|
|||
cand -V 'Print version information'
|
||||
cand --version 'Print version information'
|
||||
}
|
||||
&'zoxide;edit'= {
|
||||
cand -h 'Print help information'
|
||||
cand --help 'Print help information'
|
||||
cand -V 'Print version information'
|
||||
cand --version 'Print version information'
|
||||
}
|
||||
&'zoxide;import'= {
|
||||
cand --from 'Application to import from'
|
||||
cand --merge 'Merge into existing database'
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
complete -c zoxide -n "__fish_use_subcommand" -s h -l help -d 'Print help information'
|
||||
complete -c zoxide -n "__fish_use_subcommand" -s V -l version -d 'Print version information'
|
||||
complete -c zoxide -n "__fish_use_subcommand" -f -a "add" -d 'Add a new directory or increment its rank'
|
||||
complete -c zoxide -n "__fish_use_subcommand" -f -a "edit" -d 'Modify list of paths and rankings in default editor'
|
||||
complete -c zoxide -n "__fish_use_subcommand" -f -a "import" -d 'Import entries from another application'
|
||||
complete -c zoxide -n "__fish_use_subcommand" -f -a "init" -d 'Generate shell configuration'
|
||||
complete -c zoxide -n "__fish_use_subcommand" -f -a "query" -d 'Search for a directory in the database'
|
||||
complete -c zoxide -n "__fish_use_subcommand" -f -a "remove" -d 'Remove a directory from the database'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from add" -s h -l help -d 'Print help information'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from add" -s V -l version -d 'Print version information'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from edit" -s h -l help -d 'Print help information'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from edit" -s V -l version -d 'Print version information'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from import" -l from -d 'Application to import from' -r -f -a "{autojump ,z }"
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from import" -l merge -d 'Merge into existing database'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from import" -s h -l help -d 'Print help information'
|
||||
|
|
|
|||
|
|
@ -21,6 +21,20 @@ const completion: Fig.Spec = {
|
|||
template: "folders",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "edit",
|
||||
description: "Modify list of paths and rankings in default editor",
|
||||
options: [
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help information",
|
||||
},
|
||||
{
|
||||
name: ["-V", "--version"],
|
||||
description: "Print version information",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "import",
|
||||
description: "Import entries from another application",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ const ENV_HELP: &str = "ENVIRONMENT VARIABLES:
|
|||
)]
|
||||
pub enum Cmd {
|
||||
Add(Add),
|
||||
Edit(Edit),
|
||||
Import(Import),
|
||||
Init(Init),
|
||||
Query(Query),
|
||||
|
|
@ -37,6 +38,10 @@ pub struct Add {
|
|||
pub paths: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
/// Modify list of paths and rankings in default editor
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Edit {}
|
||||
|
||||
/// Import entries from another application
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Import {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,181 @@
|
|||
use crate::cmd::{Edit, Run};
|
||||
use crate::db::{db_path, Database, DatabaseFile, Epoch, Rank};
|
||||
use crate::util::{rename, resolve_path};
|
||||
use crate::{config, util};
|
||||
use anyhow::Result;
|
||||
use std::fmt::Write as FmtWrite;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use core::mem;
|
||||
use dialoguer::{Editor, Input};
|
||||
use tempfile::tempdir;
|
||||
|
||||
const HEADER: &str = "\
|
||||
# Blank lines and lines prepended with '#' are ignored; Line order is insignificant
|
||||
# last_accessed,rank,path
|
||||
";
|
||||
|
||||
enum ValidationResult {
|
||||
Success,
|
||||
Retry,
|
||||
Exit,
|
||||
}
|
||||
|
||||
impl Run for Edit {
|
||||
fn run(&self) -> Result<()> {
|
||||
let temp_dir = tempdir()?;
|
||||
let temp_dir_path = temp_dir.path();
|
||||
while let Some(db_edits) = get_db_edits()? {
|
||||
let mut db_file = DatabaseFile::new(temp_dir_path);
|
||||
let mut db = db_file.open()?;
|
||||
let result = validate_db(&mut db, db_edits);
|
||||
match result {
|
||||
ValidationResult::Success => {
|
||||
db.save()?;
|
||||
mem::drop(db);
|
||||
mem::drop(db_file);
|
||||
rename(db_path(temp_dir_path), db_path(config::data_dir()?))?;
|
||||
return Ok(());
|
||||
}
|
||||
ValidationResult::Exit => break,
|
||||
ValidationResult::Retry => continue,
|
||||
}
|
||||
}
|
||||
println!("Zoxide database not altered");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn get_db_edits() -> Result<Option<String>> {
|
||||
let data_dir = config::data_dir()?;
|
||||
let mut db = DatabaseFile::new(data_dir);
|
||||
let mut db = db.open()?;
|
||||
let mut stream = db.stream(util::current_time().unwrap());
|
||||
let mut to_edit = String::from(HEADER);
|
||||
while let Some(dir) = stream.next() {
|
||||
writeln!(&mut to_edit, "{},{},{}", dir.last_accessed, dir.rank, dir.path)?;
|
||||
}
|
||||
Ok(Editor::new().edit(&to_edit)?)
|
||||
}
|
||||
|
||||
fn validate_db(db: &mut Database, db_edits: String) -> ValidationResult {
|
||||
let lines = db_edits.lines();
|
||||
|
||||
let mut errors: Vec<(usize, String)> = Vec::new();
|
||||
let mut warnings: Vec<(usize, String)> = Vec::new();
|
||||
|
||||
for (index, line) in lines.enumerate() {
|
||||
let line_number = index + 1;
|
||||
let first_char = line.trim().chars().next();
|
||||
if let Some(first_char) = first_char {
|
||||
if first_char == '#' {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
let mut split = line.split(',');
|
||||
let (last_accessed_txt, rank_txt, path_txt) = (split.next(), split.next(), split.next());
|
||||
if split.next().is_some() {
|
||||
errors.push((line_number, "Too many values on line".to_string()));
|
||||
}
|
||||
|
||||
let last_accessed: Option<Epoch> = match last_accessed_txt {
|
||||
Some(value) => match value.trim().parse::<Epoch>() {
|
||||
Ok(value) => Some(value),
|
||||
Err(e) => {
|
||||
errors.push((line_number, e.to_string()));
|
||||
None
|
||||
}
|
||||
},
|
||||
None => {
|
||||
errors.push((line_number, "Cannot parse 'last_accessed' field".to_string()));
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let rank: Option<Rank> = match rank_txt {
|
||||
Some(value) => match value.trim().parse::<Rank>() {
|
||||
Ok(value) => Some(value),
|
||||
Err(e) => {
|
||||
errors.push((line_number, e.to_string()));
|
||||
None
|
||||
}
|
||||
},
|
||||
None => {
|
||||
errors.push((line_number, "Cannot parse 'rank' field".to_string()));
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let path: Option<String> = match path_txt {
|
||||
Some(value) => {
|
||||
if value.trim() != value {
|
||||
warnings.push((line_number, "path contains trailing whitespace".to_string()));
|
||||
}
|
||||
match resolve_path(&PathBuf::from(value)) {
|
||||
Ok(v) => {
|
||||
if v.to_str().unwrap() != value {
|
||||
errors.push((line_number, "path must be an absolute path".to_string()));
|
||||
}
|
||||
Some(value.to_string())
|
||||
}
|
||||
Err(e) => {
|
||||
errors.push((line_number, e.to_string()));
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
errors.push((line_number, "Cannot parse 'path' field".to_string()));
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
if let (Some(path), Some(last_accessed), Some(rank)) = (path, last_accessed, rank) {
|
||||
db.add_raw(&path, last_accessed, rank);
|
||||
}
|
||||
}
|
||||
let has_warnings = !warnings.is_empty();
|
||||
let has_errors = !errors.is_empty();
|
||||
if has_warnings {
|
||||
println!("Warnings:");
|
||||
for (line_num, warning) in warnings {
|
||||
println!("{line_num}: {warning}");
|
||||
}
|
||||
println!();
|
||||
}
|
||||
if has_errors {
|
||||
println!("Errors:");
|
||||
for (line_num, error) in errors {
|
||||
println!("line {line_num}: {error}");
|
||||
}
|
||||
println!();
|
||||
}
|
||||
if has_warnings || has_errors {
|
||||
println!("You may:");
|
||||
println!("(e)dit the file again");
|
||||
println!("e(x)it without saving changes");
|
||||
if !has_errors {
|
||||
println!("(s)ave changes and exit (DANGER!)");
|
||||
}
|
||||
let selection = Input::new()
|
||||
.with_prompt("Choice")
|
||||
.validate_with(|input: &String| -> Result<(), &str> {
|
||||
if input == "e" || input == "x" || (input == "s" && !has_errors) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err("Invalid selection.")
|
||||
}
|
||||
})
|
||||
.interact()
|
||||
.unwrap();
|
||||
return match selection.as_str() {
|
||||
"e" => ValidationResult::Retry,
|
||||
"s" => ValidationResult::Success,
|
||||
"x" => ValidationResult::Exit,
|
||||
i => panic!("Expected 'e', 's', or 'x'. Received {i}"), // We already validated input above
|
||||
};
|
||||
}
|
||||
ValidationResult::Success
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
mod add;
|
||||
mod cmd;
|
||||
mod edit;
|
||||
mod import;
|
||||
mod init;
|
||||
mod query;
|
||||
|
|
@ -17,6 +18,7 @@ impl Run for Cmd {
|
|||
fn run(&self) -> Result<()> {
|
||||
match self {
|
||||
Cmd::Add(cmd) => cmd.run(),
|
||||
Cmd::Edit(cmd) => cmd.run(),
|
||||
Cmd::Import(cmd) => cmd.run(),
|
||||
Cmd::Init(cmd) => cmd.run(),
|
||||
Cmd::Query(cmd) => cmd.run(),
|
||||
|
|
|
|||
|
|
@ -32,15 +32,19 @@ impl<'file> Database<'file> {
|
|||
|
||||
/// Adds a new directory or increments its rank. Also updates its last accessed time.
|
||||
pub fn add<S: AsRef<str>>(&mut self, path: S, now: Epoch) {
|
||||
self.add_raw(path, now, 1.0)
|
||||
}
|
||||
|
||||
pub fn add_raw<S: AsRef<str>>(&mut self, path: S, last_accessed: Epoch, rank: Rank) {
|
||||
let path = path.as_ref();
|
||||
|
||||
match self.dirs.iter_mut().find(|dir| dir.path == path) {
|
||||
None => {
|
||||
self.dirs.push(Dir { path: path.to_string().into(), last_accessed: now, rank: 1.0 });
|
||||
self.dirs.push(Dir { path: path.to_string().into(), last_accessed, rank });
|
||||
}
|
||||
Some(dir) => {
|
||||
dir.last_accessed = now;
|
||||
dir.rank += 1.0;
|
||||
dir.last_accessed = last_accessed;
|
||||
dir.rank += rank;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -140,7 +144,7 @@ impl DatabaseFile {
|
|||
}
|
||||
}
|
||||
|
||||
fn db_path<P: AsRef<Path>>(data_dir: P) -> PathBuf {
|
||||
pub fn db_path<P: AsRef<Path>>(data_dir: P) -> PathBuf {
|
||||
const DB_FILENAME: &str = "db.zo";
|
||||
data_dir.as_ref().join(DB_FILENAME)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ fn tmpfile<P: AsRef<Path>>(dir: P) -> Result<(File, PathBuf)> {
|
|||
}
|
||||
|
||||
/// Similar to [`fs::rename`], but retries on Windows.
|
||||
fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<()> {
|
||||
pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<()> {
|
||||
const MAX_ATTEMPTS: usize = 5;
|
||||
let from = from.as_ref();
|
||||
let to = to.as_ref();
|
||||
|
|
|
|||
Loading…
Reference in New Issue