rust/hg-core/src/vfs.rs
author Pierre-Yves David <pierre-yves.david@octobus.net>
Tue, 11 Mar 2025 02:29:42 +0100
branchstable
changeset 53042 cdd7bf612c7b
parent 52345 a48c688d3e80
permissions -rw-r--r--
bundle-spec: properly format boolean parameter (issue6960) This was breaking automatic clone bundle generation. This changeset fixes it and add a test to catch it in the future.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
52180
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
     1
use crate::errors::{HgError, HgResultExt, IoErrorContext, IoResultExt};
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
     2
use crate::exit_codes;
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
     3
use crate::fncache::FnCache;
52178
bd8081e9fd62 rust: don't star export from the `revlog` module
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52168
diff changeset
     4
use crate::revlog::path_encode::path_encode;
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
     5
use crate::utils::files::{get_bytes_from_path, get_path_from_bytes};
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
     6
use dyn_clone::DynClone;
52180
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
     7
use format_bytes::format_bytes;
47955
e834b79def74 rust: Switch to the memmap2-rs crate
Simon Sapin <simon.sapin@octobus.net>
parents: 47952
diff changeset
     8
use memmap2::{Mmap, MmapOptions};
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
     9
use rand::distributions::{Alphanumeric, DistString};
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
    10
use std::fs::{File, Metadata, OpenOptions};
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
    11
use std::io::{ErrorKind, Read, Seek, Write};
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
    12
use std::os::fd::AsRawFd;
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    13
use std::os::unix::fs::{MetadataExt, PermissionsExt};
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    14
use std::path::{Path, PathBuf};
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
    15
#[cfg(test)]
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
    16
use std::sync::atomic::AtomicUsize;
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
    17
#[cfg(test)]
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
    18
use std::sync::atomic::Ordering;
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    19
use std::sync::OnceLock;
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    20
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    21
/// Filesystem access abstraction for the contents of a given "base" diretory
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
    22
#[derive(Clone)]
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
    23
pub struct VfsImpl {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
    24
    pub(crate) base: PathBuf,
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    25
    pub readonly: bool,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    26
    pub mode: Option<u32>,
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    27
}
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    28
48199
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    29
struct FileNotFound(std::io::Error, PathBuf);
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    30
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    31
/// Store the umask for the whole process since it's expensive to get.
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    32
static UMASK: OnceLock<u32> = OnceLock::new();
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    33
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    34
fn get_umask() -> u32 {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    35
    *UMASK.get_or_init(|| unsafe {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    36
        // TODO is there any way of getting the umask without temporarily
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    37
        // setting it? Doesn't this affect all threads in this tiny window?
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    38
        let mask = libc::umask(0);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    39
        libc::umask(mask);
52310
33d8cb64e9da rust: fix darwin build
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 52306
diff changeset
    40
        #[allow(clippy::useless_conversion)]
33d8cb64e9da rust: fix darwin build
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 52306
diff changeset
    41
        (mask & 0o777).into()
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    42
    })
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    43
}
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    44
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    45
/// Return the (unix) mode with which we will create/fix files
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    46
fn get_mode(base: impl AsRef<Path>) -> Option<u32> {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    47
    match base.as_ref().metadata() {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    48
        Ok(meta) => {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    49
            // files in .hg/ will be created using this mode
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    50
            let mode = meta.mode();
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    51
            // avoid some useless chmods
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    52
            if (0o777 & !get_umask()) == (0o777 & mode) {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    53
                None
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    54
            } else {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    55
                Some(mode)
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    56
            }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    57
        }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    58
        Err(_) => None,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    59
    }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    60
}
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    61
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
    62
impl VfsImpl {
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    63
    pub fn new(base: PathBuf, readonly: bool) -> Self {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    64
        let mode = get_mode(&base);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    65
        Self {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    66
            base,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    67
            readonly,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    68
            mode,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    69
        }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    70
    }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    71
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    72
    // XXX these methods are probably redundant with VFS trait?
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    73
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    74
    pub fn join(&self, relative_path: impl AsRef<Path>) -> PathBuf {
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    75
        self.base.join(relative_path)
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    76
    }
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    77
48345
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    78
    pub fn symlink_metadata(
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    79
        &self,
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    80
        relative_path: impl AsRef<Path>,
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    81
    ) -> Result<std::fs::Metadata, HgError> {
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    82
        let path = self.join(relative_path);
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    83
        std::fs::symlink_metadata(&path).when_reading_file(&path)
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    84
    }
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    85
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    86
    pub fn read_link(
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    87
        &self,
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    88
        relative_path: impl AsRef<Path>,
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    89
    ) -> Result<PathBuf, HgError> {
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    90
        let path = self.join(relative_path);
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    91
        std::fs::read_link(&path).when_reading_file(&path)
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    92
    }
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    93
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    94
    pub fn read(
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    95
        &self,
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    96
        relative_path: impl AsRef<Path>,
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    97
    ) -> Result<Vec<u8>, HgError> {
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    98
        let path = self.join(relative_path);
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    99
        std::fs::read(&path).when_reading_file(&path)
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   100
    }
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   101
49485
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   102
    /// Returns `Ok(None)` if the file does not exist.
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   103
    pub fn try_read(
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   104
        &self,
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   105
        relative_path: impl AsRef<Path>,
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   106
    ) -> Result<Option<Vec<u8>>, HgError> {
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   107
        match self.read(relative_path) {
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   108
            Err(e) => match &e {
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   109
                HgError::IoError { error, .. } => match error.kind() {
49914
58074252db3c rust: run `cargo clippy`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   110
                    ErrorKind::NotFound => Ok(None),
49485
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   111
                    _ => Err(e),
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   112
                },
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   113
                _ => Err(e),
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   114
            },
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   115
            Ok(v) => Ok(Some(v)),
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   116
        }
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   117
    }
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   118
48199
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   119
    fn mmap_open_gen(
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   120
        &self,
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   121
        relative_path: impl AsRef<Path>,
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   122
    ) -> Result<Result<Mmap, FileNotFound>, HgError> {
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   123
        let path = self.join(relative_path);
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   124
        let file = match std::fs::File::open(&path) {
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   125
            Err(err) => {
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   126
                if let ErrorKind::NotFound = err.kind() {
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   127
                    return Ok(Err(FileNotFound(err, path)));
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   128
                };
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   129
                return (Err(err)).when_reading_file(&path);
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   130
            }
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   131
            Ok(file) => file,
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   132
        };
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   133
        // Safety is "enforced" by locks and assuming other processes are
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   134
        // well-behaved. If any misbehaving or malicious process does touch
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   135
        // the index, it could lead to corruption. This is inherent
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   136
        // to file-based `mmap`, though some platforms have some ways of
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   137
        // mitigating.
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   138
        // TODO linux: set the immutable flag with `chattr(1)`?
48199
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   139
        let mmap = unsafe { MmapOptions::new().map(&file) }
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   140
            .when_reading_file(&path)?;
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   141
        Ok(Ok(mmap))
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   142
    }
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   143
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   144
    pub fn mmap_open_opt(
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   145
        &self,
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   146
        relative_path: impl AsRef<Path>,
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   147
    ) -> Result<Option<Mmap>, HgError> {
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   148
        self.mmap_open_gen(relative_path).map(|res| res.ok())
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   149
    }
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   150
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   151
    pub fn mmap_open(
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   152
        &self,
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   153
        relative_path: impl AsRef<Path>,
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   154
    ) -> Result<Mmap, HgError> {
48199
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   155
        match self.mmap_open_gen(relative_path)? {
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   156
            Err(FileNotFound(err, path)) => Err(err).when_reading_file(&path),
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   157
            Ok(res) => Ok(res),
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   158
        }
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   159
    }
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   160
48417
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   161
    #[cfg(unix)]
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   162
    pub fn create_symlink(
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   163
        &self,
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   164
        relative_link_path: impl AsRef<Path>,
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   165
        target_path: impl AsRef<Path>,
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   166
    ) -> Result<(), HgError> {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   167
        let link_path = self.join(relative_link_path);
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   168
        std::os::unix::fs::symlink(target_path, &link_path)
48418
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   169
            .when_writing_file(&link_path)
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   170
    }
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   171
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   172
    /// Write `contents` into a temporary file, then rename to `relative_path`.
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   173
    /// This makes writing to a file "atomic": a reader opening that path will
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   174
    /// see either the previous contents of the file or the complete new
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   175
    /// content, never a partial write.
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   176
    pub fn atomic_write(
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   177
        &self,
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   178
        relative_path: impl AsRef<Path>,
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   179
        contents: &[u8],
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   180
    ) -> Result<(), HgError> {
52345
a48c688d3e80 rhg: set the expected dirstate permissions (0o666 minus umask)
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 52310
diff changeset
   181
        let mut tmp = tempfile::Builder::new()
a48c688d3e80 rhg: set the expected dirstate permissions (0o666 minus umask)
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 52310
diff changeset
   182
            .permissions(std::fs::Permissions::from_mode(0o666))
a48c688d3e80 rhg: set the expected dirstate permissions (0o666 minus umask)
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 52310
diff changeset
   183
            .tempfile_in(&self.base)
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   184
            .when_writing_file(&self.base)?;
48418
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   185
        tmp.write_all(contents)
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   186
            .and_then(|()| tmp.flush())
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   187
            .when_writing_file(tmp.path())?;
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   188
        let path = self.join(relative_path);
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   189
        tmp.persist(&path)
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   190
            .map_err(|e| e.error)
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   191
            .when_writing_file(&path)?;
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   192
        Ok(())
48417
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   193
    }
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   194
}
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   195
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   196
fn fs_metadata(
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   197
    path: impl AsRef<Path>,
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   198
) -> Result<Option<std::fs::Metadata>, HgError> {
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   199
    let path = path.as_ref();
52180
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   200
    match path.metadata() {
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   201
        Ok(meta) => Ok(Some(meta)),
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   202
        Err(error) => match error.kind() {
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   203
            // TODO: when we require a Rust version where `NotADirectory` is
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   204
            // stable, invert this logic and return None for it and `NotFound`
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   205
            // and propagate any other error.
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   206
            ErrorKind::PermissionDenied => Err(error).with_context(|| {
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   207
                IoErrorContext::ReadingMetadata(path.to_owned())
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   208
            }),
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   209
            _ => Ok(None),
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   210
        },
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   211
    }
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   212
}
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   213
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   214
/// Abstraction over the files handled by a [`Vfs`].
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   215
#[derive(Debug)]
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   216
pub enum VfsFile {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   217
    Atomic(AtomicFile),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   218
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   219
    Normal {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   220
        file: File,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   221
        path: PathBuf,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   222
        /// If `Some`, check (and maybe fix) this file's timestamp ambiguity.
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   223
        /// See [`is_filetime_ambiguous`].
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   224
        check_ambig: Option<Metadata>,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   225
    },
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   226
}
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   227
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   228
impl VfsFile {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   229
    pub fn normal(file: File, path: PathBuf) -> Self {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   230
        Self::Normal {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   231
            file,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   232
            check_ambig: None,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   233
            path,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   234
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   235
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   236
    pub fn normal_check_ambig(
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   237
        file: File,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   238
        path: PathBuf,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   239
    ) -> Result<Self, HgError> {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   240
        Ok(Self::Normal {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   241
            file,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   242
            check_ambig: Some(path.metadata().when_reading_file(&path)?),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   243
            path,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   244
        })
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   245
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   246
    pub fn try_clone(&self) -> Result<VfsFile, HgError> {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   247
        Ok(match self {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   248
            VfsFile::Atomic(AtomicFile {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   249
                fp,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   250
                temp_path,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   251
                check_ambig,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   252
                target_name,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   253
                is_open,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   254
            }) => Self::Atomic(AtomicFile {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   255
                fp: fp.try_clone().when_reading_file(temp_path)?,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   256
                temp_path: temp_path.clone(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   257
                check_ambig: *check_ambig,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   258
                target_name: target_name.clone(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   259
                is_open: *is_open,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   260
            }),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   261
            VfsFile::Normal {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   262
                file,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   263
                check_ambig,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   264
                path,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   265
            } => Self::Normal {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   266
                file: file.try_clone().when_reading_file(path)?,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   267
                check_ambig: check_ambig.clone(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   268
                path: path.to_owned(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   269
            },
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   270
        })
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   271
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   272
    pub fn set_len(&self, len: u64) -> Result<(), std::io::Error> {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   273
        match self {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   274
            VfsFile::Atomic(atomic_file) => atomic_file.fp.set_len(len),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   275
            VfsFile::Normal { file, .. } => file.set_len(len),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   276
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   277
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   278
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   279
    pub fn metadata(&self) -> Result<std::fs::Metadata, std::io::Error> {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   280
        match self {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   281
            VfsFile::Atomic(atomic_file) => atomic_file.fp.metadata(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   282
            VfsFile::Normal { file, .. } => file.metadata(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   283
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   284
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   285
}
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   286
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   287
impl AsRawFd for VfsFile {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   288
    fn as_raw_fd(&self) -> std::os::unix::prelude::RawFd {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   289
        match self {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   290
            VfsFile::Atomic(atomic_file) => atomic_file.fp.as_raw_fd(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   291
            VfsFile::Normal { file, .. } => file.as_raw_fd(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   292
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   293
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   294
}
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   295
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   296
impl Seek for VfsFile {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   297
    fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   298
        match self {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   299
            VfsFile::Atomic(atomic_file) => atomic_file.seek(pos),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   300
            VfsFile::Normal { file, .. } => file.seek(pos),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   301
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   302
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   303
}
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   304
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   305
impl Read for VfsFile {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   306
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   307
        match self {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   308
            VfsFile::Atomic(atomic_file) => atomic_file.fp.read(buf),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   309
            VfsFile::Normal { file, .. } => file.read(buf),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   310
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   311
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   312
}
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   313
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   314
impl Write for VfsFile {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   315
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   316
        match self {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   317
            VfsFile::Atomic(atomic_file) => atomic_file.fp.write(buf),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   318
            VfsFile::Normal { file, .. } => file.write(buf),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   319
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   320
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   321
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   322
    fn flush(&mut self) -> std::io::Result<()> {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   323
        match self {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   324
            VfsFile::Atomic(atomic_file) => atomic_file.fp.flush(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   325
            VfsFile::Normal { file, .. } => file.flush(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   326
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   327
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   328
}
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   329
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   330
impl Drop for VfsFile {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   331
    fn drop(&mut self) {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   332
        if let VfsFile::Normal {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   333
            path,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   334
            check_ambig: Some(old),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   335
            ..
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   336
        } = self
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   337
        {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   338
            avoid_timestamp_ambiguity(path, old)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   339
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   340
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   341
}
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   342
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   343
/// Records the number of times we've fixed a timestamp ambiguity, only
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   344
/// applicable for tests.
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   345
#[cfg(test)]
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   346
static TIMESTAMP_FIXES_CALLS: AtomicUsize = AtomicUsize::new(0);
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   347
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   348
fn avoid_timestamp_ambiguity(path: &Path, old: &Metadata) {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   349
    if let Ok(new) = path.metadata() {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   350
        let is_ambiguous = is_filetime_ambiguous(&new, old);
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   351
        if is_ambiguous {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   352
            let advanced =
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   353
                filetime::FileTime::from_unix_time(old.mtime() + 1, 0);
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   354
            if filetime::set_file_times(path, advanced, advanced).is_ok() {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   355
                #[cfg(test)]
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   356
                {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   357
                    TIMESTAMP_FIXES_CALLS.fetch_add(1, Ordering::Relaxed);
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   358
                }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   359
            }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   360
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   361
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   362
}
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   363
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   364
/// Examine whether new stat is ambiguous against old one
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   365
///
52306
a876ab6c3fd5 rust: fix `cargo doc` warnings
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52294
diff changeset
   366
/// `S[N]` below means stat of a file at `N`-th change:
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   367
///
52306
a876ab6c3fd5 rust: fix `cargo doc` warnings
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52294
diff changeset
   368
/// - `S[n-1].ctime  < S[n].ctime`: can detect change of a file
a876ab6c3fd5 rust: fix `cargo doc` warnings
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52294
diff changeset
   369
/// - `S[n-1].ctime == S[n].ctime`
a876ab6c3fd5 rust: fix `cargo doc` warnings
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52294
diff changeset
   370
///   - `S[n-1].ctime  < S[n].mtime`: means natural advancing (*1)
a876ab6c3fd5 rust: fix `cargo doc` warnings
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52294
diff changeset
   371
///   - `S[n-1].ctime == S[n].mtime`: is ambiguous (*2)
a876ab6c3fd5 rust: fix `cargo doc` warnings
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52294
diff changeset
   372
///   - `S[n-1].ctime  > S[n].mtime`: never occurs naturally (don't care)
a876ab6c3fd5 rust: fix `cargo doc` warnings
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52294
diff changeset
   373
/// - `S[n-1].ctime  > S[n].ctime`: never occurs naturally (don't care)
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   374
///
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   375
/// Case (*2) above means that a file was changed twice or more at
52306
a876ab6c3fd5 rust: fix `cargo doc` warnings
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52294
diff changeset
   376
/// same time in sec (= `S[n-1].ctime`), and comparison of timestamp
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   377
/// is ambiguous.
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   378
///
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   379
/// Base idea to avoid such ambiguity is "advance mtime 1 sec, if
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   380
/// timestamp is ambiguous".
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   381
///
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   382
/// But advancing mtime only in case (*2) doesn't work as
52306
a876ab6c3fd5 rust: fix `cargo doc` warnings
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52294
diff changeset
   383
/// expected, because naturally advanced `S[n].mtime` in case (*1)
a876ab6c3fd5 rust: fix `cargo doc` warnings
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52294
diff changeset
   384
/// might be equal to manually advanced `S[n-1 or earlier].mtime`.
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   385
///
52306
a876ab6c3fd5 rust: fix `cargo doc` warnings
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52294
diff changeset
   386
/// Therefore, all `S[n-1].ctime == S[n].ctime` cases should be
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   387
/// treated as ambiguous regardless of mtime, to avoid overlooking
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   388
/// by confliction between such mtime.
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   389
///
52306
a876ab6c3fd5 rust: fix `cargo doc` warnings
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52294
diff changeset
   390
/// Advancing mtime `if isambig(new, old)` ensures `S[n-1].mtime !=
a876ab6c3fd5 rust: fix `cargo doc` warnings
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52294
diff changeset
   391
/// S[n].mtime`, even if size of a file isn't changed.
a876ab6c3fd5 rust: fix `cargo doc` warnings
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52294
diff changeset
   392
pub fn is_filetime_ambiguous(new: &Metadata, old: &Metadata) -> bool {
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   393
    new.ctime() == old.ctime()
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   394
}
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   395
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   396
/// Writable file object that atomically updates a file
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   397
///
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   398
/// All writes will go to a temporary copy of the original file. Call
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   399
/// [`Self::close`] when you are done writing, and [`Self`] will rename
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   400
/// the temporary copy to the original name, making the changes
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   401
/// visible. If the object is destroyed without being closed, all your
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   402
/// writes are discarded.
52180
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   403
#[derive(Debug)]
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   404
pub struct AtomicFile {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   405
    /// The temporary file to write to
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   406
    fp: std::fs::File,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   407
    /// Path of the temp file
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   408
    temp_path: PathBuf,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   409
    /// Used when stat'ing the file, is useful only if the target file is
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   410
    /// guarded by any lock (e.g. repo.lock or repo.wlock).
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   411
    check_ambig: bool,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   412
    /// Path of the target file
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   413
    target_name: PathBuf,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   414
    /// Whether the file is open or not
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   415
    is_open: bool,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   416
}
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   417
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   418
impl AtomicFile {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   419
    pub fn new(
52180
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   420
        target_path: impl AsRef<Path>,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   421
        empty: bool,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   422
        check_ambig: bool,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   423
    ) -> Result<Self, HgError> {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   424
        let target_path = target_path.as_ref().to_owned();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   425
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   426
        let random_id =
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   427
            Alphanumeric.sample_string(&mut rand::thread_rng(), 12);
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   428
        let filename =
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   429
            target_path.file_name().expect("target has no filename");
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   430
        let filename = get_bytes_from_path(filename);
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   431
        let temp_filename =
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   432
            format_bytes!(b".{}-{}~", filename, random_id.as_bytes());
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   433
        let temp_path =
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   434
            target_path.with_file_name(get_path_from_bytes(&temp_filename));
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   435
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   436
        if !empty {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   437
            std::fs::copy(&target_path, &temp_path)
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   438
                .with_context(|| IoErrorContext::CopyingFile {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   439
                    from: target_path.to_owned(),
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   440
                    to: temp_path.to_owned(),
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   441
                })
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   442
                // If it doesn't exist, create it on open
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   443
                .io_not_found_as_none()?;
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   444
        }
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   445
        let fp = std::fs::OpenOptions::new()
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   446
            .write(true)
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   447
            .create(true)
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   448
            .truncate(empty)
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   449
            .open(&temp_path)
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   450
            .when_writing_file(&temp_path)?;
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   451
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   452
        Ok(Self {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   453
            fp,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   454
            temp_path,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   455
            check_ambig,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   456
            target_name: target_path,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   457
            is_open: true,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   458
        })
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   459
    }
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   460
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   461
    pub fn from_file(
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   462
        fp: std::fs::File,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   463
        check_ambig: bool,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   464
        temp_name: PathBuf,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   465
        target_name: PathBuf,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   466
    ) -> Self {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   467
        Self {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   468
            fp,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   469
            check_ambig,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   470
            temp_path: temp_name,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   471
            target_name,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   472
            is_open: true,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   473
        }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   474
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   475
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   476
    /// Write `buf` to the temporary file
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   477
    pub fn write_all(&mut self, buf: &[u8]) -> Result<(), std::io::Error> {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   478
        self.fp.write_all(buf)
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   479
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   480
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   481
    fn target(&self) -> PathBuf {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   482
        self.temp_path
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   483
            .parent()
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   484
            .expect("should not be at the filesystem root")
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   485
            .join(&self.target_name)
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   486
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   487
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   488
    /// Close the temporary file and rename to the target
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   489
    pub fn close(mut self) -> Result<(), std::io::Error> {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   490
        self.fp.flush()?;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   491
        let target = self.target();
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   492
        if self.check_ambig {
52180
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   493
            if let Ok(stat) = target.metadata() {
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   494
                std::fs::rename(&self.temp_path, &target)?;
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   495
                avoid_timestamp_ambiguity(&target, &stat);
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   496
            } else {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   497
                std::fs::rename(&self.temp_path, target)?;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   498
            }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   499
        } else {
52180
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   500
            std::fs::rename(&self.temp_path, target)?;
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   501
        }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   502
        self.is_open = false;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   503
        Ok(())
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   504
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   505
}
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   506
52180
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   507
impl Seek for AtomicFile {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   508
    fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   509
        self.fp.seek(pos)
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   510
    }
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   511
}
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   512
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   513
impl Drop for AtomicFile {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   514
    fn drop(&mut self) {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   515
        if self.is_open {
52179
82007b8c189e rust-vfs: delete the temp file and not the target on drop
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52178
diff changeset
   516
            std::fs::remove_file(&self.temp_path).ok();
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   517
        }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   518
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   519
}
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   520
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   521
/// Abstracts over the VFS to allow for different implementations of the
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   522
/// filesystem layer (like passing one from Python).
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   523
pub trait Vfs: Sync + Send + DynClone {
52294
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   524
    /// Open a [`VfsFile::Normal`] for reading the file at `filename`,
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   525
    /// relative to this VFS's root.
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   526
    fn open(&self, filename: &Path) -> Result<VfsFile, HgError>;
52182
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   527
    /// Open a [`VfsFile::Normal`] for writing and reading the file at
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   528
    /// `filename`, relative to this VFS's root.
52294
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   529
    fn open_write(&self, filename: &Path) -> Result<VfsFile, HgError>;
52182
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   530
    /// Open a [`VfsFile::Normal`] for reading and writing the file at
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   531
    /// `filename`, relative to this VFS's root. This file will be checked
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   532
    /// for an ambiguous mtime on [`drop`]. See [`is_filetime_ambiguous`].
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   533
    fn open_check_ambig(&self, filename: &Path) -> Result<VfsFile, HgError>;
52182
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   534
    /// Create a [`VfsFile::Normal`] for reading and writing the file at
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   535
    /// `filename`, relative to this VFS's root. If the file already exists,
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   536
    /// it will be truncated to 0 bytes.
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   537
    fn create(
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   538
        &self,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   539
        filename: &Path,
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   540
        check_ambig: bool,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   541
    ) -> Result<VfsFile, HgError>;
52182
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   542
    /// Create a [`VfsFile::Atomic`] for reading and writing the file at
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   543
    /// `filename`, relative to this VFS's root. If the file already exists,
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   544
    /// it will be truncated to 0 bytes.
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   545
    fn create_atomic(
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   546
        &self,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   547
        filename: &Path,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   548
        check_ambig: bool,
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   549
    ) -> Result<VfsFile, HgError>;
52182
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   550
    /// Return the total file size in bytes of the open `file`. Errors are
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   551
    /// usual IO errors (invalid file handle, permissions, etc.)
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   552
    fn file_size(&self, file: &VfsFile) -> Result<u64, HgError>;
52182
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   553
    /// Return `true` if `filename` exists relative to this VFS's root. Errors
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   554
    /// will coerce to `false`, to this also returns `false` if there are
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   555
    /// IO problems. This is fine because any operation that actually tries
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   556
    /// to do anything with this path will get the same error.
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   557
    fn exists(&self, filename: &Path) -> bool;
52182
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   558
    /// Remove the file at `filename` relative to this VFS's root. Errors
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   559
    /// are the usual IO errors (lacking permission, file does not exist, etc.)
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   560
    fn unlink(&self, filename: &Path) -> Result<(), HgError>;
52182
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   561
    /// Rename the file `from` to `to`, both relative to this VFS's root.
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   562
    /// Errors are the usual IO errors (lacking permission, file does not
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   563
    /// exist, etc.). If `check_ambig` is `true`, the VFS will check for an
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   564
    /// ambiguous mtime on rename. See [`is_filetime_ambiguous`].
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   565
    fn rename(
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   566
        &self,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   567
        from: &Path,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   568
        to: &Path,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   569
        check_ambig: bool,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   570
    ) -> Result<(), HgError>;
52182
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   571
    /// Rename the file `from` to `to`, both relative to this VFS's root.
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   572
    /// Errors are the usual IO errors (lacking permission, file does not
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   573
    /// exist, etc.). If `check_ambig` is passed, the VFS will check for an
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   574
    /// ambiguous mtime on rename. See [`is_filetime_ambiguous`].
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   575
    fn copy(&self, from: &Path, to: &Path) -> Result<(), HgError>;
52182
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   576
    /// Returns the absolute root path of this VFS, relative to which all
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   577
    /// operations are done.
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   578
    fn base(&self) -> &Path;
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   579
}
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   580
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   581
/// These methods will need to be implemented once `rhg` (and other) non-Python
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   582
/// users of `hg-core` start doing more on their own, like writing to files.
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   583
impl Vfs for VfsImpl {
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   584
    fn open(&self, filename: &Path) -> Result<VfsFile, HgError> {
52294
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   585
        // TODO auditpath
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   586
        let path = self.base.join(filename);
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   587
        Ok(VfsFile::normal(
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   588
            std::fs::File::open(&path).when_reading_file(&path)?,
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   589
            filename.to_owned(),
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   590
        ))
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   591
    }
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   592
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   593
    fn open_write(&self, filename: &Path) -> Result<VfsFile, HgError> {
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   594
        if self.readonly {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   595
            return Err(HgError::abort(
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   596
                "write access in a readonly vfs",
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   597
                exit_codes::ABORT,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   598
                None,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   599
            ));
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   600
        }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   601
        // TODO auditpath
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   602
        let path = self.base.join(filename);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   603
        copy_in_place_if_hardlink(&path)?;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   604
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   605
        Ok(VfsFile::normal(
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   606
            OpenOptions::new()
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   607
                .create(false)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   608
                .create_new(false)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   609
                .write(true)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   610
                .read(true)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   611
                .open(&path)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   612
                .when_writing_file(&path)?,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   613
            path.to_owned(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   614
        ))
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   615
    }
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   616
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   617
    fn open_check_ambig(&self, filename: &Path) -> Result<VfsFile, HgError> {
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   618
        if self.readonly {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   619
            return Err(HgError::abort(
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   620
                "write access in a readonly vfs",
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   621
                exit_codes::ABORT,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   622
                None,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   623
            ));
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   624
        }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   625
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   626
        let path = self.base.join(filename);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   627
        copy_in_place_if_hardlink(&path)?;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   628
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   629
        // TODO auditpath
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   630
        VfsFile::normal_check_ambig(
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   631
            OpenOptions::new()
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   632
                .write(true)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   633
                .read(true) // Can be used for reading to save on `open` calls
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   634
                .create(false)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   635
                .open(&path)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   636
                .when_reading_file(&path)?,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   637
            path.to_owned(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   638
        )
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   639
    }
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   640
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   641
    fn create(
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   642
        &self,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   643
        filename: &Path,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   644
        check_ambig: bool,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   645
    ) -> Result<VfsFile, HgError> {
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   646
        if self.readonly {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   647
            return Err(HgError::abort(
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   648
                "write access in a readonly vfs",
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   649
                exit_codes::ABORT,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   650
                None,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   651
            ));
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   652
        }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   653
        // TODO auditpath
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   654
        let path = self.base.join(filename);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   655
        let parent = path.parent().expect("file at root");
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   656
        std::fs::create_dir_all(parent).when_writing_file(parent)?;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   657
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   658
        let file = OpenOptions::new()
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   659
            .create(true)
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   660
            .truncate(true)
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   661
            .write(true)
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   662
            .read(true)
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   663
            .open(&path)
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   664
            .when_writing_file(&path)?;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   665
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   666
        if let Some(mode) = self.mode {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   667
            // Creating the file with the right permission (with `.mode()`)
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   668
            // may not work since umask takes effect for file creation.
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   669
            // So we need to fix the permission after creating the file.
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   670
            fix_directory_permissions(&self.base, &path, mode)?;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   671
            let perm = std::fs::Permissions::from_mode(mode & 0o666);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   672
            std::fs::set_permissions(&path, perm).when_writing_file(&path)?;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   673
        }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   674
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   675
        Ok(VfsFile::Normal {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   676
            file,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   677
            check_ambig: if check_ambig {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   678
                Some(path.metadata().when_reading_file(&path)?)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   679
            } else {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   680
                None
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   681
            },
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   682
            path: path.to_owned(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   683
        })
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   684
    }
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   685
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   686
    fn create_atomic(
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   687
        &self,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   688
        _filename: &Path,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   689
        _check_ambig: bool,
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   690
    ) -> Result<VfsFile, HgError> {
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   691
        todo!()
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   692
    }
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   693
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   694
    fn file_size(&self, file: &VfsFile) -> Result<u64, HgError> {
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   695
        Ok(file
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   696
            .metadata()
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   697
            .map_err(|e| {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   698
                HgError::abort(
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   699
                    format!("Could not get file metadata: {}", e),
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   700
                    exit_codes::ABORT,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   701
                    None,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   702
                )
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   703
            })?
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   704
            .size())
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   705
    }
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   706
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   707
    fn exists(&self, filename: &Path) -> bool {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   708
        self.base.join(filename).exists()
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   709
    }
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   710
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   711
    fn unlink(&self, filename: &Path) -> Result<(), HgError> {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   712
        if self.readonly {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   713
            return Err(HgError::abort(
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   714
                "write access in a readonly vfs",
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   715
                exit_codes::ABORT,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   716
                None,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   717
            ));
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   718
        }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   719
        let path = self.base.join(filename);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   720
        std::fs::remove_file(&path)
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   721
            .with_context(|| IoErrorContext::RemovingFile(path))
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   722
    }
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   723
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   724
    fn rename(
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   725
        &self,
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   726
        from: &Path,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   727
        to: &Path,
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   728
        check_ambig: bool,
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   729
    ) -> Result<(), HgError> {
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   730
        if self.readonly {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   731
            return Err(HgError::abort(
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   732
                "write access in a readonly vfs",
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   733
                exit_codes::ABORT,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   734
                None,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   735
            ));
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   736
        }
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   737
        let old_stat = if check_ambig {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   738
            Some(
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   739
                from.metadata()
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   740
                    .when_reading_file(from)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   741
                    .io_not_found_as_none()?,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   742
            )
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   743
        } else {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   744
            None
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   745
        };
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   746
        let from = self.base.join(from);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   747
        let to = self.base.join(to);
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   748
        std::fs::rename(&from, &to).with_context(|| {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   749
            IoErrorContext::RenamingFile {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   750
                from,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   751
                to: to.to_owned(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   752
            }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   753
        })?;
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   754
        if let Some(Some(old)) = old_stat {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   755
            avoid_timestamp_ambiguity(&to, &old);
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   756
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   757
        Ok(())
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   758
    }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   759
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   760
    fn copy(&self, from: &Path, to: &Path) -> Result<(), HgError> {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   761
        let from = self.base.join(from);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   762
        let to = self.base.join(to);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   763
        std::fs::copy(&from, &to)
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   764
            .with_context(|| IoErrorContext::CopyingFile { from, to })
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   765
            .map(|_| ())
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   766
    }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   767
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   768
    fn base(&self) -> &Path {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   769
        &self.base
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   770
    }
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   771
}
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   772
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   773
fn fix_directory_permissions(
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   774
    base: &Path,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   775
    path: &Path,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   776
    mode: u32,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   777
) -> Result<(), HgError> {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   778
    let mut ancestors = path.ancestors();
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   779
    ancestors.next(); // yields the path itself
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   780
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   781
    for ancestor in ancestors {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   782
        if ancestor == base {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   783
            break;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   784
        }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   785
        let perm = std::fs::Permissions::from_mode(mode);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   786
        std::fs::set_permissions(ancestor, perm)
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   787
            .when_writing_file(ancestor)?;
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   788
    }
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   789
    Ok(())
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   790
}
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   791
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   792
/// A VFS that understands the `fncache` store layout (file encoding), and
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   793
/// adds new entries to the `fncache`.
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   794
/// TODO Only works when using from Python for now.
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   795
pub struct FnCacheVfs {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   796
    inner: VfsImpl,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   797
    fncache: Box<dyn FnCache>,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   798
}
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   799
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   800
impl Clone for FnCacheVfs {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   801
    fn clone(&self) -> Self {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   802
        Self {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   803
            inner: self.inner.clone(),
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   804
            fncache: dyn_clone::clone_box(&*self.fncache),
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   805
        }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   806
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   807
}
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   808
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   809
impl FnCacheVfs {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   810
    pub fn new(
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   811
        base: PathBuf,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   812
        readonly: bool,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   813
        fncache: Box<dyn FnCache>,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   814
    ) -> Self {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   815
        let inner = VfsImpl::new(base, readonly);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   816
        Self { inner, fncache }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   817
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   818
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   819
    fn maybe_add_to_fncache(
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   820
        &self,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   821
        filename: &Path,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   822
        encoded_path: &Path,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   823
    ) -> Result<(), HgError> {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   824
        let relevant_file = (filename.starts_with("data/")
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   825
            || filename.starts_with("meta/"))
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   826
            && is_revlog_file(filename);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   827
        if relevant_file {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   828
            let not_load = !self.fncache.is_loaded()
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   829
                && (self.exists(filename)
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   830
                    && self
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   831
                        .inner
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   832
                        .join(encoded_path)
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   833
                        .metadata()
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   834
                        .when_reading_file(encoded_path)?
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   835
                        .size()
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   836
                        != 0);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   837
            if !not_load {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   838
                self.fncache.add(filename);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   839
            }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   840
        };
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   841
        Ok(())
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   842
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   843
}
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   844
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   845
impl Vfs for FnCacheVfs {
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   846
    fn open(&self, filename: &Path) -> Result<VfsFile, HgError> {
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   847
        let encoded = path_encode(&get_bytes_from_path(filename));
52294
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   848
        let filename = get_path_from_bytes(&encoded);
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   849
        self.inner.open(filename)
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   850
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   851
52294
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   852
    fn open_write(&self, filename: &Path) -> Result<VfsFile, HgError> {
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   853
        let encoded = path_encode(&get_bytes_from_path(filename));
52294
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   854
        let encoded_path = get_path_from_bytes(&encoded);
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   855
        self.maybe_add_to_fncache(filename, encoded_path)?;
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   856
        self.inner.open_write(encoded_path)
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   857
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   858
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   859
    fn open_check_ambig(&self, filename: &Path) -> Result<VfsFile, HgError> {
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   860
        let encoded = path_encode(&get_bytes_from_path(filename));
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   861
        let filename = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   862
        self.inner.open_check_ambig(filename)
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   863
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   864
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   865
    fn create(
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   866
        &self,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   867
        filename: &Path,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   868
        check_ambig: bool,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   869
    ) -> Result<VfsFile, HgError> {
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   870
        let encoded = path_encode(&get_bytes_from_path(filename));
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   871
        let encoded_path = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   872
        self.maybe_add_to_fncache(filename, encoded_path)?;
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   873
        self.inner.create(encoded_path, check_ambig)
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   874
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   875
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   876
    fn create_atomic(
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   877
        &self,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   878
        filename: &Path,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   879
        check_ambig: bool,
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   880
    ) -> Result<VfsFile, HgError> {
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   881
        let encoded = path_encode(&get_bytes_from_path(filename));
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   882
        let filename = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   883
        self.inner.create_atomic(filename, check_ambig)
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   884
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   885
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   886
    fn file_size(&self, file: &VfsFile) -> Result<u64, HgError> {
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   887
        self.inner.file_size(file)
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   888
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   889
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   890
    fn exists(&self, filename: &Path) -> bool {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   891
        let encoded = path_encode(&get_bytes_from_path(filename));
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   892
        let filename = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   893
        self.inner.exists(filename)
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   894
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   895
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   896
    fn unlink(&self, filename: &Path) -> Result<(), HgError> {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   897
        let encoded = path_encode(&get_bytes_from_path(filename));
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   898
        let filename = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   899
        self.inner.unlink(filename)
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   900
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   901
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   902
    fn rename(
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   903
        &self,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   904
        from: &Path,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   905
        to: &Path,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   906
        check_ambig: bool,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   907
    ) -> Result<(), HgError> {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   908
        let encoded = path_encode(&get_bytes_from_path(from));
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   909
        let from = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   910
        let encoded = path_encode(&get_bytes_from_path(to));
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   911
        let to = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   912
        self.inner.rename(from, to, check_ambig)
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   913
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   914
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   915
    fn copy(&self, from: &Path, to: &Path) -> Result<(), HgError> {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   916
        let encoded = path_encode(&get_bytes_from_path(from));
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   917
        let from = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   918
        let encoded = path_encode(&get_bytes_from_path(to));
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   919
        let to = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   920
        self.inner.copy(from, to)
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   921
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   922
    fn base(&self) -> &Path {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   923
        self.inner.base()
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   924
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   925
}
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   926
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   927
/// Detects whether `path` is a hardlink and does a tmp copy + rename erase
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   928
/// to turn it into its own file. Revlogs are usually hardlinked when doing
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   929
/// a local clone, and we don't want to modify the original repo.
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   930
fn copy_in_place_if_hardlink(path: &Path) -> Result<(), HgError> {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   931
    let metadata = path.metadata().when_writing_file(path)?;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   932
    if metadata.nlink() > 0 {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   933
        // If it's hardlinked, copy it and rename it back before changing it.
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   934
        let tmpdir = path.parent().expect("file at root");
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   935
        let name = Alphanumeric.sample_string(&mut rand::thread_rng(), 16);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   936
        let tmpfile = tmpdir.join(name);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   937
        std::fs::create_dir_all(tmpfile.parent().expect("file at root"))
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   938
            .with_context(|| IoErrorContext::CopyingFile {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   939
                from: path.to_owned(),
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   940
                to: tmpfile.to_owned(),
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   941
            })?;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   942
        std::fs::copy(path, &tmpfile).with_context(|| {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   943
            IoErrorContext::CopyingFile {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   944
                from: path.to_owned(),
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   945
                to: tmpfile.to_owned(),
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   946
            }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   947
        })?;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   948
        std::fs::rename(&tmpfile, path).with_context(|| {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   949
            IoErrorContext::RenamingFile {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   950
                from: tmpfile,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   951
                to: path.to_owned(),
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   952
            }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   953
        })?;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   954
    }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   955
    Ok(())
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   956
}
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   957
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   958
pub fn is_revlog_file(path: impl AsRef<Path>) -> bool {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   959
    path.as_ref()
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   960
        .extension()
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   961
        .map(|ext| {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   962
            ["i", "idx", "d", "dat", "n", "nd", "sda"]
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   963
                .contains(&ext.to_string_lossy().as_ref())
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   964
        })
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   965
        .unwrap_or(false)
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   966
}
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   967
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   968
pub(crate) fn is_dir(path: impl AsRef<Path>) -> Result<bool, HgError> {
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   969
    Ok(fs_metadata(path)?.map_or(false, |meta| meta.is_dir()))
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   970
}
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   971
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   972
pub(crate) fn is_file(path: impl AsRef<Path>) -> Result<bool, HgError> {
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   973
    Ok(fs_metadata(path)?.map_or(false, |meta| meta.is_file()))
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   974
}
50180
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   975
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   976
/// Returns whether the given `path` is on a network file system.
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   977
/// Taken from `cargo`'s codebase.
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   978
#[cfg(target_os = "linux")]
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   979
pub(crate) fn is_on_nfs_mount(path: impl AsRef<Path>) -> bool {
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   980
    use std::ffi::CString;
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   981
    use std::mem;
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   982
    use std::os::unix::prelude::*;
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   983
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   984
    let path = match CString::new(path.as_ref().as_os_str().as_bytes()) {
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   985
        Ok(path) => path,
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   986
        Err(_) => return false,
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   987
    };
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   988
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   989
    unsafe {
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   990
        let mut buf: libc::statfs = mem::zeroed();
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   991
        let r = libc::statfs(path.as_ptr(), &mut buf);
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   992
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   993
        r == 0 && buf.f_type as u32 == libc::NFS_SUPER_MAGIC as u32
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   994
    }
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   995
}
50274
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
   996
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
   997
/// Similar to what Cargo does; although detecting NFS (or non-local
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
   998
/// file systems) _should_ be possible on other operating systems,
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
   999
/// we'll just assume that mmap() works there, for now; after all,
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
  1000
/// _some_ functionality is better than a compile error, i.e. none at
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
  1001
/// all
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
  1002
#[cfg(not(target_os = "linux"))]
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
  1003
pub(crate) fn is_on_nfs_mount(_path: impl AsRef<Path>) -> bool {
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
  1004
    false
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
  1005
}
52180
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1006
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1007
#[cfg(test)]
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1008
mod tests {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1009
    use super::*;
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1010
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1011
    #[test]
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1012
    fn test_atomic_file() {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1013
        let dir = tempfile::tempdir().unwrap().into_path();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1014
        let target_path = dir.join("sometargetname");
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1015
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1016
        for empty in [true, false] {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1017
            let file = AtomicFile::new(&target_path, empty, false).unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1018
            assert!(file.is_open);
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1019
            let filename =
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1020
                file.temp_path.file_name().unwrap().to_str().unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1021
            // Make sure we have a coherent temp name
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1022
            assert_eq!(filename.len(), 29, "{}", filename);
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1023
            assert!(filename.contains("sometargetname"));
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1024
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1025
            // Make sure the temp file is created in the same folder
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1026
            assert_eq!(target_path.parent(), file.temp_path.parent());
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1027
        }
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1028
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1029
        assert!(!target_path.exists());
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1030
        std::fs::write(&target_path, "version 1").unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1031
        let mut file = AtomicFile::new(&target_path, false, false).unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1032
        file.write_all(b"version 2!").unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1033
        assert_eq!(
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1034
            std::fs::read(&target_path).unwrap(),
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1035
            b"version 1".to_vec()
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1036
        );
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1037
        let temp_path = file.temp_path.to_owned();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1038
        // test that dropping the file should discard the temp file and not
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1039
        // affect the target path.
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1040
        drop(file);
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1041
        assert_eq!(
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1042
            std::fs::read(&target_path).unwrap(),
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1043
            b"version 1".to_vec()
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1044
        );
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1045
        assert!(!temp_path.exists());
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1046
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1047
        let mut file = AtomicFile::new(&target_path, false, false).unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1048
        file.write_all(b"version 2!").unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1049
        assert_eq!(
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1050
            std::fs::read(&target_path).unwrap(),
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1051
            b"version 1".to_vec()
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1052
        );
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1053
        file.close().unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1054
        assert_eq!(
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1055
            std::fs::read(&target_path).unwrap(),
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1056
            b"version 2!".to_vec(),
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1057
            "{}",
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1058
            std::fs::read_to_string(&target_path).unwrap()
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1059
        );
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1060
        assert!(target_path.exists());
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1061
        assert!(!temp_path.exists());
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1062
    }
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1063
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1064
    #[test]
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1065
    fn test_vfs_file_check_ambig() {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1066
        let dir = tempfile::tempdir().unwrap().into_path();
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1067
        let file_path = dir.join("file");
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1068
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1069
        fn vfs_file_write(file_path: &Path, check_ambig: bool) {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1070
            let file = std::fs::OpenOptions::new()
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1071
                .write(true)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1072
                .open(file_path)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1073
                .unwrap();
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1074
            let old_stat = if check_ambig {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1075
                Some(file.metadata().unwrap())
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1076
            } else {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1077
                None
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1078
            };
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1079
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1080
            let mut vfs_file = VfsFile::Normal {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1081
                file,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1082
                path: file_path.to_owned(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1083
                check_ambig: old_stat,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1084
            };
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1085
            vfs_file.write_all(b"contents").unwrap();
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1086
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1087
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1088
        std::fs::OpenOptions::new()
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1089
            .write(true)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1090
            .create(true)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1091
            .truncate(false)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1092
            .open(&file_path)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1093
            .unwrap();
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1094
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1095
        let number_of_writes = 3;
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1096
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1097
        // Try multiple times, because reproduction of an ambiguity depends
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1098
        // on "filesystem time"
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1099
        for _ in 0..5 {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1100
            TIMESTAMP_FIXES_CALLS.store(0, Ordering::Relaxed);
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1101
            vfs_file_write(&file_path, false);
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1102
            let old_stat = file_path.metadata().unwrap();
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1103
            if old_stat.ctime() != old_stat.mtime() {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1104
                // subsequent changing never causes ambiguity
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1105
                continue;
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1106
            }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1107
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1108
            // Repeat atomic write with `check_ambig == true`, to examine
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1109
            // whether the mtime is advanced multiple times as expected
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1110
            for _ in 0..number_of_writes {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1111
                vfs_file_write(&file_path, true);
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1112
            }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1113
            let new_stat = file_path.metadata().unwrap();
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1114
            if !is_filetime_ambiguous(&new_stat, &old_stat) {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1115
                // timestamp ambiguity was naturally avoided while repetition
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1116
                continue;
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1117
            }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1118
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1119
            assert_eq!(
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1120
                TIMESTAMP_FIXES_CALLS.load(Ordering::Relaxed),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1121
                number_of_writes
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1122
            );
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1123
            assert_eq!(
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1124
                old_stat.mtime() + number_of_writes as i64,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1125
                file_path.metadata().unwrap().mtime()
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1126
            );
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1127
            break;
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1128
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1129
        // If we've arrived here without breaking, we might not have
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1130
        // tested anything because the platform is too slow. This test will
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1131
        // still work on fast platforms.
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1132
    }
52180
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1133
}