1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

use chrono::prelude::*;
use chrono::NaiveDateTime;

use crate::client::HglibError;

#[derive(Debug, PartialEq)]
pub struct Revision {
    pub rev: u64,
    pub node: String,
    pub tags: String,
    pub branch: String,
    pub author: String,
    pub desc: String,
    pub date: DateTime<Utc>,
}

pub const CHANGESETS_TEMPLATE: &str =
    "{rev}\\0{node}\\0{tags}\\0{branch}\\0{author}\\0{desc}\\0{date}\\0";

pub fn parserevs(data: Vec<u8>) -> Result<Vec<Revision>, HglibError> {
    let mut count = 0;
    let mut parts: Vec<&[u8]> = vec![&[]; 6];
    let mut res = Vec::new();
    let mut rev: u64 = 0;

    for buf in data.split(|x| *x == b'\0') {
        if count == 0 {
            rev = buf.iter().fold(0, |r, x| r * 10 + u64::from(*x - b'0'));
            count += 1;
        } else if count == 6 {
            count = 0;
            let timestamp = buf
                .iter()
                .take_while(|x| **x != b'.')
                .fold(0, |r, x| r * 10 + i64::from(*x - b'0'));
            res.push(Revision {
                rev,
                node: String::from_utf8(parts[1].to_vec())?,
                tags: String::from_utf8(parts[2].to_vec())?,
                branch: String::from_utf8(parts[3].to_vec())?,
                author: String::from_utf8(parts[4].to_vec())?,
                desc: String::from_utf8(parts[5].to_vec())?,
                date: DateTime::from_utc(NaiveDateTime::from_timestamp(timestamp, 0), Utc),
            });
            rev = 0;
        } else {
            parts[count] = &buf;
            count += 1;
        }
    }
    Ok(res)
}

pub fn eatlines(buf: &[u8], n: u32) -> &[u8] {
    let mut iter = buf.iter();
    let mut count = 0;
    while let Some(c) = iter.next() {
        if *c == b'\n' {
            if count == n - 1 {
                return iter.as_slice();
            } else {
                count += 1;
            }
        }
    }
    &[]
}

pub fn handle_err(x: Result<(Vec<u8>, i32), HglibError>) -> Result<bool, HglibError> {
    match x {
        Ok((_, code)) => Ok(code == 0),
        Err(err) => {
            if err.code == 0 {
                Ok(true)
            } else if err.code == 1 {
                Ok(false)
            } else {
                Err(err)
            }
        }
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_eatlines() {
        let s = "rust
is
an
amazing
programming
language";
        let s = s.as_bytes();
        let r = eatlines(s, 1);
        assert!(&r[..2] == b"is");

        let r = eatlines(s, 2);
        assert!(&r[..2] == b"an");

        let r = eatlines(s, 4);
        assert!(&r[..2] == b"pr");
    }
}