octopush 0.1.0

Profile management for Git repositories.
Documentation
use std::{
    io,
    path::{Path, PathBuf},
    str::FromStr,
};

pub struct Project {
    pub path: PathBuf,
}

impl FromStr for Project {
    type Err = std::io::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Project::new(s)
    }
}

impl Project {
    pub fn new(path: impl AsRef<Path>) -> Result<Self, io::Error> {
        let p = path.as_ref();

        if !p.exists() {
            return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid path"));
        }

        Ok(Project {
            path: p.to_path_buf(),
        })
    }

    pub fn get_repo_name(&self) -> Result<String, io::Error> {
        match Self::resolve_git_repo_name(&self.path)? {
            Some(name) => Ok(name),
            None => Err(io::Error::other(
                "no git repository found for given project path",
            )),
        }
    }

    fn resolve_git_repo_name(start: &Path) -> Result<Option<String>, io::Error> {
        let mut cur = if start.is_file() {
            start
                .parent()
                .map(|p| p.to_path_buf())
                .unwrap_or_else(|| PathBuf::from("."))
        } else {
            start.to_path_buf()
        };

        loop {
            let git_dir = cur.join(".git");
            if git_dir.is_dir() {
                if let Some(name) = cur.file_name().and_then(|n| n.to_str()) {
                    return Ok(Some(name.to_string()));
                } else {
                    return Ok(None);
                }
            }

            if !cur.pop() {
                break;
            }
        }

        Ok(None)
    }
}