rust/hg-core/src/operations/list_tracked_files.rs
author Raphaël Gomès <rgomes@octobus.net>
Thu, 30 Jul 2020 16:55:44 +0200
changeset 45436 1b3197047f5c
parent 45359 0f5286ccf82c
child 45535 72b7d58d6e35
permissions -rw-r--r--
rhg: make output of `files` relative to the current directory and the root This matches the behavior of `hg files`. The util is added in `hg-core` instead of `rhg` because this operation could be useful for other external tools. (this was definitely not prompted by rust issue #50784, I swear) Differential Revision: https://phab.mercurial-scm.org/D8872

// list_tracked_files.rs
//
// Copyright 2020 Antoine Cezar <antoine.cezar@octobus.net>
//
// This software may be used and distributed according to the terms of the
// GNU General Public License version 2 or any later version.

use super::find_root;
use crate::dirstate::parsers::parse_dirstate;
use crate::utils::hg_path::HgPath;
use crate::{DirstateParseError, EntryState};
use rayon::prelude::*;
use std::convert::From;
use std::fmt;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

/// Kind of error encoutered by ListTrackedFiles
#[derive(Debug)]
pub enum ListTrackedFilesErrorKind {
    ParseError(DirstateParseError),
}

/// A ListTrackedFiles error
#[derive(Debug)]
pub struct ListTrackedFilesError {
    /// Kind of error encoutered by ListTrackedFiles
    pub kind: ListTrackedFilesErrorKind,
}

impl std::error::Error for ListTrackedFilesError {}

impl fmt::Display for ListTrackedFilesError {
    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
        unimplemented!()
    }
}

impl From<ListTrackedFilesErrorKind> for ListTrackedFilesError {
    fn from(kind: ListTrackedFilesErrorKind) -> Self {
        ListTrackedFilesError { kind }
    }
}

/// List files under Mercurial control in the working directory
pub struct ListTrackedFiles {
    root: PathBuf,
}

impl ListTrackedFiles {
    pub fn new() -> Result<Self, find_root::FindRootError> {
        let root = find_root::FindRoot::new().run()?;
        Ok(ListTrackedFiles { root })
    }

    /// Load the tracked files data from disk
    pub fn load(&self) -> Result<ListDirstateTrackedFiles, io::Error> {
        let dirstate = &self.root.join(".hg/dirstate");
        let content = fs::read(&dirstate)?;
        Ok(ListDirstateTrackedFiles { content })
    }

    /// Returns the repository root directory
    /// TODO I think this is a crutch that creates a dependency that should not
    /// be there. Operations that need the root of the repository should get
    /// it themselves, probably in a lazy fashion. But this would make the
    /// current series even larger, so this is simplified for now.
    pub fn get_root(&self) -> &Path {
        &self.root
    }
}

/// List files under Mercurial control in the working directory
/// by reading the dirstate
pub struct ListDirstateTrackedFiles {
    content: Vec<u8>,
}

impl ListDirstateTrackedFiles {
    pub fn run(&self) -> Result<Vec<&HgPath>, ListTrackedFilesError> {
        let (_, entries, _) = parse_dirstate(&self.content)
            .map_err(ListTrackedFilesErrorKind::ParseError)?;
        let mut files: Vec<&HgPath> = entries
            .into_iter()
            .filter_map(|(path, entry)| match entry.state {
                EntryState::Removed => None,
                _ => Some(path),
            })
            .collect();
        files.par_sort_unstable();
        Ok(files)
    }
}