protofetch 0.1.17

A source dependency management tool for Protobuf.
Documentation
use std::{
    fs::File,
    path::Path,
    time::{Duration, Instant},
};

use fs4::fs_std::FileExt;
use log::debug;
use thiserror::Error;

pub struct FileLock {
    _file: File,
}

#[derive(Error, Debug)]
#[error(transparent)]
pub struct Error(#[from] std::io::Error);

impl FileLock {
    pub fn new(path: &Path) -> Result<Self, Error> {
        let file = File::create(path)?;
        let start = Instant::now();
        loop {
            match file.try_lock_exclusive().or_else(|error| {
                if error.raw_os_error() == fs4::lock_contended_error().raw_os_error() {
                    Ok(false)
                } else {
                    Err(error)
                }
            }) {
                Ok(true) => {
                    return Ok(Self { _file: file });
                }
                Ok(false) if start.elapsed().as_secs() < 300 => {
                    debug!("Failed to acquire a lock on {}, retrying", path.display());
                    std::thread::sleep(Duration::from_secs(1));
                }
                Ok(false) => {
                    return Err(Error(std::io::Error::other(format!(
                        "Failed to acquire a lock on {}",
                        path.display()
                    ))))
                }
                Err(error) => return Err(error.into()),
            }
        }
    }
}