xx 2.5.3

A collection of useful Rust macros and small functions.
Documentation
use crate::error::XXResult;
use std::env;
use std::path::{Path, PathBuf};

use crate::file::display_path;
use crate::hash::hash_to_str;
use crate::{XXError, file};

pub type OnLockedFn = Box<dyn Fn(&Path)>;

pub struct FSLock {
    path: PathBuf,
    on_locked: Option<OnLockedFn>,
}

impl FSLock {
    pub fn new(path: &Path) -> Self {
        let normalized = normalize_path(path);
        Self {
            path: env::temp_dir()
                .join("fslock")
                .join(hash_to_str(&normalized)),
            on_locked: None,
        }
    }

    pub fn with_callback<F>(mut self, cb: F) -> Self
    where
        F: Fn(&Path) + 'static,
    {
        self.on_locked = Some(Box::new(cb));
        self
    }

    pub fn lock(self) -> XXResult<fslock::LockFile> {
        if let Some(parent) = self.path.parent() {
            file::mkdirp(parent)?;
        }
        let mut lock = fslock::LockFile::open(&self.path)
            .map_err(|e| XXError::FSLockError(e, format!("lockfile {}", self.path.display())))?;
        if !lock
            .try_lock()
            .map_err(|e| XXError::FSLockError(e, format!("lockfile {}", self.path.display())))?
        {
            if let Some(f) = self.on_locked {
                f(&self.path)
            }
            lock.lock().map_err(|e| {
                XXError::FSLockError(e, format!("lockfile {}", self.path.display()))
            })?;
        }
        Ok(lock)
    }
}

fn normalize_path(path: &Path) -> PathBuf {
    if let Ok(canonical) = path.canonicalize() {
        return canonical;
    }
    if let Ok(absolute) = std::path::absolute(path) {
        return absolute;
    }
    path.to_path_buf()
}

pub fn get(path: &Path, force: bool) -> XXResult<Option<fslock::LockFile>> {
    let lock = if force {
        None
    } else {
        let lock = FSLock::new(path)
            .with_callback(|l| {
                debug!("waiting for lock on {}", display_path(l));
            })
            .lock()?;
        Some(lock)
    };
    Ok(lock)
}