git_historian/
types.rs

1//! Types common to the entire crate.
2
3use std::cell::RefCell;
4use std::collections::{HashMap, HashSet};
5use std::error::Error;
6use std::fmt::{self, Display, Formatter};
7use std::rc::Rc;
8
9/// A set of paths, used to track which files we care about
10pub type PathSet = HashSet<String>;
11
12/// A change to a file in Git (or at least the kinds we care about)
13///
14/// Copies and renames have additional info:
15/// how much of the file remained the same.
16#[derive(Debug, Copy, Clone)]
17pub enum Change {
18    Added,
19    Deleted,
20    Modified,
21    Renamed{ percent_changed: u8},
22    Copied{ percent_changed: u8},
23}
24
25/// A change made to a given file in a commit
26#[derive(Debug, Clone)]
27pub struct FileDelta {
28    /// The change type
29    pub change: Change,
30
31    /// The current path of the file
32    pub path: String,
33
34    /// The previous path of the file if the change is a rename or copy,
35    /// and an empty string otherwise
36    pub from: String,
37}
38
39/// A 20-byte SHA1 hash, used for identifying objects in Git.
40#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
41pub struct SHA1 {
42    bytes: [u8; 20]
43}
44
45#[derive(Copy, Clone, Debug)]
46pub enum SHA1ParseError {
47    IncorrectLength,
48    InvalidHexadecimal
49}
50
51impl Error for SHA1ParseError {
52    fn description(&self) -> &str {
53        match *self {
54            SHA1ParseError::IncorrectLength => "String is not 40 characters long",
55            SHA1ParseError::InvalidHexadecimal => "String is not valid hexadecimal",
56        }
57    }
58}
59
60impl Display for SHA1ParseError {
61    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
62        self.description().fmt(f)
63    }
64}
65
66impl SHA1 {
67    /// Parses a SHA1 from a 40 character hex string
68    pub fn parse(s: &str) -> Result<SHA1, SHA1ParseError> {
69        if s.len() != 40 { return Err(SHA1ParseError::IncorrectLength) }
70
71        let mut ret = SHA1::default();
72
73        for i in 0..20 {
74            let char_index = i * 2;
75            ret.bytes[i] = match u8::from_str_radix(&s[char_index .. char_index + 2], 16) {
76                    Ok(b) => b,
77                    _ => { return Err(SHA1ParseError::InvalidHexadecimal); },
78                };
79        }
80
81        Ok(ret)
82    }
83}
84
85impl Display for SHA1 {
86    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
87        for b in &self.bytes {
88            match write!(f, "{:02x}", b) {
89                Ok (()) => { },
90                err => { return err; }
91            };
92        }
93        Ok(())
94    }
95}
96
97impl Default for SHA1 {
98    fn default() -> SHA1 { SHA1{bytes: [0; 20]} }
99}
100
101/// Expresses an edge between `HistoryNodes` in a `HistoryTree`
102pub type Link<T> = Rc<RefCell<T>>;
103
104/// A change in a file through Git history
105pub struct HistoryNode<T> {
106    /// A callback is issued for each delta, allowing the user to store
107    /// whatever info they want about the change.
108    /// This is an `Option` for the sake of filtering---we can't omit the node
109    /// entirely (or the processing that generated it), as we could screw up
110    /// the history graph. Instead, we make the contents `None` for all nodes
111    /// from a filtered-out commit.
112    pub data: Option<Rc<T>>,
113
114    /// What's the previous change?
115    pub previous: Option<Link<HistoryNode<T>>>,
116}
117
118/// For each key in the map, the value is a branch of a tree
119/// (i.e. a linked list) of all changes.
120/// This extends past name changes
121pub type HistoryTree<T> = HashMap<String, Link<HistoryNode<T>>>;