cargo-xfmt 0.0.16

Safely use nightly fmt options without using unstable Rust
#![allow(dead_code)]
use std::path::Path;
use std::path::PathBuf;

use normalize_path::NormalizePath;

use super::os_string_ext::OsStringExt;

pub fn path_to_absolute(input: &Path) -> anyhow::Result<PathBuf> {
    if input.is_absolute() {
        Ok(input.normalize())
    } else {
        let cwd = std::env::current_dir()?;
        Ok(cwd.join(input).normalize())
    }
}

pub fn find_ancestor_file<P: AsRef<Path>, S: AsRef<Path>>(
    start_dir: P,
    file_name: S,
) -> anyhow::Result<Vec<PathBuf>> {
    let file_name = file_name.as_ref();
    let mut found = vec![];

    let mut current = 'block: {
        if std::fs::metadata(&start_dir)?.is_dir() {
            break 'block start_dir.as_ref().to_path_buf();
        }
        break 'block start_dir.as_ref().try_parent()?.to_path_buf();
    };

    loop {
        let possible = current.join(file_name);

        if std::fs::exists(&possible)? {
            found.push(possible)
        }

        let Some(next) = current.parent() else {
            break;
        };

        current = next.to_path_buf();
    }

    Ok(found)
}

pub trait PathExt {
    fn try_parent(&self) -> std::io::Result<&Path>;
    fn try_file_name(&self) -> std::io::Result<String>;
    fn try_file_stem(&self) -> std::io::Result<String>;
    fn try_to_string(&self) -> std::io::Result<String>;
}

impl PathExt for PathBuf {
    fn try_parent(&self) -> std::io::Result<&Path> {
        match self.parent() {
            Some(path) => Ok(path),
            None => Err(std::io::Error::other("Unable to find parent")),
        }
    }

    fn try_file_name(&self) -> std::io::Result<String> {
        match self.file_name() {
            Some(v) => Ok(v.try_to_string()?),
            None => Err(std::io::Error::other("Cannot get file name")),
        }
    }

    fn try_file_stem(&self) -> std::io::Result<String> {
        match self.file_stem() {
            Some(v) => Ok(v.try_to_string()?),
            None => Err(std::io::Error::other("Cannot get file stem")),
        }
    }

    fn try_to_string(&self) -> std::io::Result<String> {
        match self.to_str() {
            Some(v) => Ok(v.to_string()),
            None => Err(std::io::Error::other("Cannot convert Path to string")),
        }
    }
}

impl PathExt for Path {
    fn try_parent(&self) -> std::io::Result<&Path> {
        match self.parent() {
            Some(path) => Ok(path),
            None => Err(std::io::Error::other("Unable to find parent")),
        }
    }

    fn try_file_name(&self) -> std::io::Result<String> {
        match self.file_name() {
            Some(v) => Ok(v.try_to_string()?),
            None => Err(std::io::Error::other("Cannot get file name")),
        }
    }

    fn try_file_stem(&self) -> std::io::Result<String> {
        match self.file_stem() {
            Some(v) => Ok(v.try_to_string()?),
            None => Err(std::io::Error::other("Cannot get file stem")),
        }
    }

    fn try_to_string(&self) -> std::io::Result<String> {
        match self.to_str() {
            Some(v) => Ok(v.to_string()),
            None => Err(std::io::Error::other("Cannot convert Path to string")),
        }
    }
}