/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use std::cmp;
use std::fmt;
use std::marker::PhantomData;
use std::str::FromStr;

use serde::de::Error;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::Serializer;
use serde_bytes::ByteBuf;
use thiserror::Error;

/// An abstracted hash type.
///
/// To use this type, provide the name and length information.
/// See `HgId` for example.
pub struct AbstractHashType<I, const N: usize>([u8; N], PhantomData<I>);

/// Describe information about a hash type.
pub trait HashTypeInfo {
    /// The name of the hash type.
    const HASH_TYPE_NAME: &'static str;
}

#[derive(Debug, Error)]
#[error("expect {0} bytes but got {1}")]
pub struct LengthMismatchError(usize, usize);

#[derive(Debug, Error)]
#[error("{0:?} is not a {1}-byte hex string")]
pub struct HexError(Vec<u8>, usize);

// It's unfortunate that I and N are separate parameters. But that's
// limitation by rustc: array length cannot refer to generic types
// (https://github.com/rust-lang/rust/issues/43408), and `&str` cannot
// be used as a const genric, as of rust 1.54.

impl<'de, I, const N: usize> Deserialize<'de> for AbstractHashType<I, N> {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        // ByteBuf supports both list and bytes.
        let bytes: ByteBuf = serde_bytes::deserialize(deserializer)?;
        let bytes = bytes.as_ref();
        // Compatible with hex.
        if bytes.len() == Self::hex_len() {
            Self::from_hex(bytes).map_err(|e| {
                let msg = format!("invalid HgId: {} ({:?})", e, bytes);
                D::Error::custom(msg)
            })
        } else {
            Self::from_slice(bytes).map_err(|e| {
                let msg = format!("invalid HgId: {} ({:?})", e, bytes);
                D::Error::custom(msg)
            })
        }
    }
}

impl<I, const N: usize> Serialize for AbstractHashType<I, N> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_bytes(&self.0)
    }
}

static HEXIFY_LOOKUP_TABLE: [i8; 256] = [
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, 0, 10, 11, 12, 13, 14, 15, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 10,
    11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
];

#[inline]
fn hexify(nibble: u8, hex: &[u8]) -> Result<u8, HexError> {
    let res = HEXIFY_LOOKUP_TABLE[nibble as usize];
    if res < 0 {
        Err(HexError(hex.to_vec(), hex.len()))
    } else {
        Ok(res as u8)
    }
}

impl<I, const N: usize> AbstractHashType<I, N> {
    pub const fn len() -> usize {
        N
    }

    pub const fn hex_len() -> usize {
        N * 2
    }

    pub fn from_slice(bytes: &[u8]) -> Result<Self, LengthMismatchError> {
        if bytes.len() != Self::len() {
            return Err(LengthMismatchError(Self::len(), bytes.len()));
        }

        let mut fixed_bytes = [0u8; N];
        fixed_bytes.copy_from_slice(bytes);
        Ok(Self(fixed_bytes, PhantomData))
    }

    pub const fn from_byte_array(bytes: [u8; N]) -> Self {
        Self(bytes, PhantomData)
    }

    pub fn into_byte_array(self) -> [u8; N] {
        self.0
    }

    pub fn to_hex(&self) -> String {
        to_hex(self.0.as_ref())
    }

    /// Convert the hex string to a binary hash.
    ///
    /// Note: this function is performance sensitive, please benchmark it carefully while changing
    /// it.
    pub fn from_hex(hex: &[u8]) -> Result<Self, HexError> {
        if hex.len() != Self::hex_len() {
            return Err(HexError(hex.to_vec(), Self::hex_len()));
        }
        let mut bytes = [0u8; N];
        for (i, chunk) in hex.chunks_exact(2).enumerate() {
            let high = hexify(chunk[0], hex)?;
            let low = hexify(chunk[1], hex)?;
            bytes[i] = (high << 4) | low;
        }
        Ok(Self::from_byte_array(bytes))
    }
}

impl<I, const N: usize> fmt::Display for AbstractHashType<I, N> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(&self.to_hex())
    }
}

impl<I: HashTypeInfo, const N: usize> fmt::Debug for AbstractHashType<I, N> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}({:?})", I::HASH_TYPE_NAME, &self.to_hex())
    }
}

impl<I, const N: usize> AsRef<[u8]> for AbstractHashType<I, N> {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

impl<I, const N: usize> From<[u8; N]> for AbstractHashType<I, N> {
    fn from(bytes: [u8; N]) -> Self {
        Self::from_byte_array(bytes)
    }
}

impl<I, const N: usize> From<AbstractHashType<I, N>> for [u8; N] {
    fn from(id: AbstractHashType<I, N>) -> Self {
        id.into_byte_array()
    }
}

impl<I, const N: usize> FromStr for AbstractHashType<I, N> {
    type Err = HexError;

    fn from_str(s: &str) -> Result<Self, HexError> {
        Self::from_hex(s.as_bytes())
    }
}

#[cfg(any(test, feature = "for-tests"))]
impl<I: 'static, const N: usize> quickcheck::Arbitrary for AbstractHashType<I, N> {
    fn arbitrary(g: &mut quickcheck::Gen) -> Self {
        let mut bytes = [0u8; N];
        for b in bytes.iter_mut() {
            *b = u8::arbitrary(g);
        }
        Self::from_byte_array(bytes)
    }
}

// Boilerplate for common traits.
// These would ideally be just `#[derive(Default, Eq, PartialEq, ...)]`. However,
// using `#[derive(...)]` would put constraints on type parameter `I` undesirably.

impl<I, const N: usize> Default for AbstractHashType<I, N> {
    fn default() -> Self {
        Self([0; N], PhantomData)
    }
}

impl<I, const N: usize> PartialEq<Self> for AbstractHashType<I, N> {
    fn eq(&self, other: &Self) -> bool {
        self.0 == other.0
    }
}

impl<I, const N: usize> Eq for AbstractHashType<I, N> {}

impl<I, const N: usize> PartialOrd<Self> for AbstractHashType<I, N> {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        self.0.partial_cmp(&other.0)
    }
}

impl<I, const N: usize> Ord for AbstractHashType<I, N> {
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        self.0.cmp(&other.0)
    }
}

impl<I, const N: usize> std::hash::Hash for AbstractHashType<I, N> {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.0.hash(state);
    }
}

impl<I, const N: usize> Clone for AbstractHashType<I, N> {
    fn clone(&self) -> Self {
        Self(self.0, PhantomData)
    }
}

impl<I, const N: usize> Copy for AbstractHashType<I, N> {}

// Utilities

pub fn to_hex(slice: &[u8]) -> String {
    const HEX_CHARS: &[u8] = b"0123456789abcdef";
    let mut v = Vec::with_capacity(slice.len() * 2);
    for &byte in slice {
        v.push(HEX_CHARS[(byte >> 4) as usize]);
        v.push(HEX_CHARS[(byte & 0xf) as usize]);
    }
    unsafe { String::from_utf8_unchecked(v) }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

#[cfg(any(test, feature = "for-tests"))]
use std::collections::HashSet;
use std::io;
use std::io::Read;
use std::io::Write;

#[cfg(any(test, feature = "for-tests"))]
use rand::RngCore;
use sha1::Digest;
use sha1::Sha1;

use crate::hash::AbstractHashType;
use crate::hash::HashTypeInfo;
use crate::parents::Parents;

/// A 20-byte identifier, often a hash. Nodes are used to uniquely identify
/// commits, file versions, and many other things.
///
///
/// # Serde Serialization
///
/// The `serde_with` module allows customization on `HgId` serialization:
/// - `#[serde(with = "types::serde_with::hgid::bytes")]` (current default)
/// - `#[serde(with = "types::serde_with::hgid::hex")]`
/// - `#[serde(with = "types::serde_with::hgid::tuple")]`
///
/// Using them can change the size or the type of serialization result:
///
/// | lib \ serde_with | hgid::tuple         | hgid::bytes  | hgid::hex |
/// |------------------|---------------------|--------------|-----------|
/// | mincode          | 20 bytes            | 21 bytes     | 41 bytes  |
/// | cbor             | 21 to 41 bytes  [1] | 21 bytes     | 42 bytes  |
/// | json             | 41 to 81+ bytes [1] | invalid  [2] | 42 bytes  |
/// | python           | Tuple[int]          | bytes        | str       |
///
/// In general,
/// - `hgid::tuple` only works best for `mincode`.
/// - `hgid::bytes` works best for cbor, python.
/// - `hgid::hex` is useful for `json`, or other text-only formats.
///
/// Compatibility note:
/// - `hgid::tuple` cannot decode `hgid::bytes` or `hgid::hex` data.
/// - `hgid::hex` can decode `hgid::bytes` data, or vice-versa. They share a
///   same `deserialize` implementation.
/// - `hgid::hex` or `hgid::bytes` might be able to decode `hgid::tuple` data,
///   depending on how tuples are serialized. For example, mincode
///   does not add framing for tuples, so `hgid::bytes` cannot decode
///   `hgid::tuple` data; cbor adds framing for tuples, so `hgid::bytes`
///   can decode `hgid::tuple` data.
///
/// [1]: Depends on actual data of `HgId`.
/// [2]: JSON only supports utf-8 data.
pub type HgId = AbstractHashType<HgIdTypeInfo, 20>;

pub struct HgIdTypeInfo;

impl HashTypeInfo for HgIdTypeInfo {
    const HASH_TYPE_NAME: &'static str = "HgId";
}

/// The nullid (0x00) is used throughout Mercurial to represent "None".
/// (For example, a commit will have a nullid p2, if it has no second parent).
pub const NULL_ID: HgId = HgId::from_byte_array([0; HgId::len()]);

/// The hard-coded 'working copy parent' Mercurial id.
pub const WDIR_ID: HgId = HgId::from_byte_array([0xff; HgId::len()]);

impl HgId {
    pub fn null_id() -> &'static Self {
        &NULL_ID
    }

    pub fn is_null(&self) -> bool {
        self == &NULL_ID
    }

    pub const fn wdir_id() -> &'static Self {
        &WDIR_ID
    }

    pub fn is_wdir(&self) -> bool {
        self == &WDIR_ID
    }

    pub fn from_content(data: &[u8], parents: Parents) -> Self {
        // Parents must be hashed in sorted order.
        let (p1, p2) = match parents.into_nodes() {
            (p1, p2) if p1 > p2 => (p2, p1),
            (p1, p2) => (p1, p2),
        };

        let mut hasher = Sha1::new();
        hasher.update(p1.as_ref());
        hasher.update(p2.as_ref());
        hasher.update(data);
        let hash: [u8; 20] = hasher.finalize().into();

        HgId::from_byte_array(hash)
    }

    #[cfg(any(test, feature = "for-tests"))]
    pub fn random(rng: &mut dyn RngCore) -> Self {
        let mut bytes = [0; HgId::len()];
        rng.fill_bytes(&mut bytes);
        loop {
            let hgid = HgId::from(&bytes);
            if !hgid.is_null() {
                return hgid;
            }
        }
    }

    #[cfg(any(test, feature = "for-tests"))]
    pub fn random_distinct(rng: &mut dyn RngCore, count: usize) -> Vec<Self> {
        let mut nodes = Vec::new();
        let mut nodeset = HashSet::new();
        while nodes.len() < count {
            let hgid = HgId::random(rng);
            if !nodeset.contains(&hgid) {
                nodeset.insert(hgid.clone());
                nodes.push(hgid);
            }
        }
        nodes
    }
}

impl<'a> From<&'a [u8; HgId::len()]> for HgId {
    fn from(bytes: &[u8; HgId::len()]) -> HgId {
        HgId::from_byte_array(bytes.clone())
    }
}

pub trait WriteHgIdExt {
    /// Write a ``HgId`` directly to a stream.
    ///
    /// # Examples
    ///
    /// ```
    /// use types::hgid::{HgId, WriteHgIdExt};
    /// let mut v = vec![];
    ///
    /// let n = HgId::null_id();
    /// v.write_hgid(&n).expect("writing a hgid to a vec should work");
    ///
    /// assert_eq!(v, vec![0; HgId::len()]);
    /// ```
    fn write_hgid(&mut self, value: &HgId) -> io::Result<()>;
}

impl<W: Write + ?Sized> WriteHgIdExt for W {
    fn write_hgid(&mut self, value: &HgId) -> io::Result<()> {
        self.write_all(value.as_ref())
    }
}

pub trait ReadHgIdExt {
    /// Read a ``HgId`` directly from a stream.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::Cursor;
    /// use types::hgid::{HgId, ReadHgIdExt};
    /// let mut v = vec![0; HgId::len()];
    /// let mut c = Cursor::new(v);
    ///
    /// let n = c.read_hgid().expect("reading a hgid from a vec should work");
    ///
    /// assert_eq!(&n, HgId::null_id());
    /// ```
    fn read_hgid(&mut self) -> io::Result<HgId>;
}

impl<R: Read + ?Sized> ReadHgIdExt for R {
    fn read_hgid(&mut self) -> io::Result<HgId> {
        let mut bytes = [0; HgId::len()];
        self.read_exact(&mut bytes)?;
        Ok(HgId::from_byte_array(bytes))
    }
}

#[cfg(any(test, feature = "for-tests"))]
pub mod mocks {
    use super::HgId;

    pub const ONES: HgId = HgId::from_byte_array([0x11; HgId::len()]);
    pub const TWOS: HgId = HgId::from_byte_array([0x22; HgId::len()]);
    pub const THREES: HgId = HgId::from_byte_array([0x33; HgId::len()]);
    pub const FOURS: HgId = HgId::from_byte_array([0x44; HgId::len()]);
    pub const FIVES: HgId = HgId::from_byte_array([0x55; HgId::len()]);
    pub const SIXES: HgId = HgId::from_byte_array([0x66; HgId::len()]);
    pub const SEVENS: HgId = HgId::from_byte_array([0x77; HgId::len()]);
    pub const EIGHTS: HgId = HgId::from_byte_array([0x88; HgId::len()]);
    pub const NINES: HgId = HgId::from_byte_array([0x99; HgId::len()]);
    pub const AS: HgId = HgId::from_byte_array([0xAA; HgId::len()]);
    pub const BS: HgId = HgId::from_byte_array([0xAB; HgId::len()]);
    pub const CS: HgId = HgId::from_byte_array([0xCC; HgId::len()]);
    pub const DS: HgId = HgId::from_byte_array([0xDD; HgId::len()]);
    pub const ES: HgId = HgId::from_byte_array([0xEE; HgId::len()]);
    pub const FS: HgId = HgId::from_byte_array([0xFF; HgId::len()]);
}

#[cfg(test)]
mod tests {
    use quickcheck::quickcheck;
    use serde::Deserialize;
    use serde::Serialize;

    use super::*;

    #[test]
    fn test_incorrect_length() {
        HgId::from_slice(&[0u8; 25]).expect_err("bad slice length");
    }

    #[test]
    fn test_serde_with_using_cbor() {
        // Note: this test is for CBOR. Other serializers like mincode
        // or Thrift would have different backwards compatibility!
        use serde_cbor::de::from_slice as decode;
        use serde_cbor::ser::to_vec as encode;

        #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
        struct Orig(#[serde(with = "crate::serde_with::hgid::tuple")] HgId);

        #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
        struct Bytes(#[serde(with = "crate::serde_with::hgid::bytes")] HgId);

        #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
        struct Hex(#[serde(with = "crate::serde_with::hgid::hex")] HgId);

        let id: HgId = mocks::CS;
        let orig = Orig(id);
        let bytes = Bytes(id);
        let hex = Hex(id);

        let cbor_orig = encode(&orig).unwrap();
        let cbor_bytes = encode(&bytes).unwrap();
        let cbor_hex = encode(&hex).unwrap();

        assert_eq!(cbor_orig.len(), 41);
        assert_eq!(cbor_bytes.len(), 21);
        assert_eq!(cbor_hex.len(), 42);

        // Orig cannot decode bytes or hex.
        assert_eq!(decode::<Orig>(&cbor_orig).unwrap().0, id);
        decode::<Orig>(&cbor_bytes).unwrap_err();
        decode::<Orig>(&cbor_hex).unwrap_err();

        // Bytes can decode all 3 formats.
        assert_eq!(decode::<Bytes>(&cbor_orig).unwrap().0, id);
        assert_eq!(decode::<Bytes>(&cbor_bytes).unwrap().0, id);
        assert_eq!(decode::<Bytes>(&cbor_hex).unwrap().0, id);

        // Hex can decode all 3 formats.
        assert_eq!(decode::<Hex>(&cbor_orig).unwrap().0, id);
        assert_eq!(decode::<Hex>(&cbor_bytes).unwrap().0, id);
        assert_eq!(decode::<Hex>(&cbor_hex).unwrap().0, id);
    }

    quickcheck! {
        fn test_from_slice(hgid: HgId) -> bool {
            hgid == HgId::from_slice(hgid.as_ref()).expect("from_slice")
        }
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use std::fmt;

use serde_derive::Deserialize;
use serde_derive::Serialize;

use crate::hgid::HgId;
use crate::path::RepoPathBuf;

#[derive(
    Clone,
    Debug,
    Default,
    Eq,
    Hash,
    Ord,
    PartialEq,
    PartialOrd,
    Serialize,
    Deserialize
)]
#[cfg_attr(
    any(test, feature = "for-tests"),
    derive(quickcheck_arbitrary_derive::Arbitrary)
)]
pub struct Key {
    // Name is usually a file or directory path
    pub path: RepoPathBuf,
    // HgId is always a 20 byte hash. This will be changed to a fix length array later.
    #[serde(alias = "node")]
    #[serde(rename = "node")]
    pub hgid: HgId,
}

impl Key {
    pub fn new(path: RepoPathBuf, hgid: HgId) -> Self {
        Key { path, hgid }
    }
}

impl fmt::Display for Key {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} {}", &self.hgid, self.path)
    }
}

#[cfg(any(test, feature = "for-tests"))]
pub mod mocks {
    use lazy_static::lazy_static;

    use super::Key;
    use crate::hgid::mocks::ONES;
    use crate::hgid::mocks::THREES;
    use crate::hgid::mocks::TWOS;
    use crate::testutil::*;

    lazy_static! {
        pub static ref FOO_KEY: Key = Key::new(repo_path_buf("foo"), ONES);
        pub static ref BAR_KEY: Key = Key::new(repo_path_buf("bar"), TWOS);
        pub static ref BAZ_KEY: Key = Key::new(repo_path_buf("baz"), THREES);
    }
}

#[cfg(test)]
mod tests {
    use mocks::*;

    use super::*;

    #[test]
    fn display_key() {
        let foo = "1111111111111111111111111111111111111111 foo";
        let bar = "2222222222222222222222222222222222222222 bar";
        let baz = "3333333333333333333333333333333333333333 baz";
        assert_eq!(format!("{}", &*FOO_KEY), foo);
        assert_eq!(format!("{}", &*BAR_KEY), bar);
        assert_eq!(format!("{}", &*BAZ_KEY), baz);
    }

    #[test]
    fn test_serde_with_using_cbor() {
        // Note: this test is for CBOR. Other serializers like mincode
        // or Thrift would have different backwards compatibility!
        use serde_cbor::de::from_slice as decode;
        use serde_cbor::ser::to_vec as encode;

        #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
        struct Orig(
            #[serde(with = "crate::serde_with::key::tuple")] Key,
            #[serde(with = "crate::serde_with::key::tuple")] Option<Key>,
        );

        #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
        struct Bytes(
            #[serde(with = "crate::serde_with::key::bytes")] Key,
            #[serde(with = "crate::serde_with::key::bytes")] Option<Key>,
        );

        #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
        struct Hex(
            #[serde(with = "crate::serde_with::key::hex")] Key,
            #[serde(with = "crate::serde_with::key::hex")] Option<Key>,
        );

        let key: Key = Key {
            path: RepoPathBuf::from_string("foo".to_string()).unwrap(),
            hgid: crate::hgid::mocks::CS,
        };
        let orig = Orig(key.clone(), Some(key.clone()));
        let bytes = Bytes(key.clone(), Some(key.clone()));
        let hex = Hex(key.clone(), Some(key.clone()));

        let cbor_orig = encode(&orig).unwrap();
        let cbor_bytes = encode(&bytes).unwrap();
        let cbor_hex = encode(&hex).unwrap();

        assert_eq!(cbor_orig.len(), 113);
        assert_eq!(cbor_bytes.len(), 73);
        assert_eq!(cbor_hex.len(), 115);

        // Orig cannot decode bytes or hex.
        assert_eq!(&decode::<Orig>(&cbor_orig).unwrap().0, &key);
        decode::<Orig>(&cbor_bytes).unwrap_err();
        decode::<Orig>(&cbor_hex).unwrap_err();

        // Bytes can decode all 3 formats.
        assert_eq!(&decode::<Bytes>(&cbor_orig).unwrap().0, &key);
        assert_eq!(&decode::<Bytes>(&cbor_bytes).unwrap().0, &key);
        assert_eq!(&decode::<Bytes>(&cbor_hex).unwrap().0, &key);

        // Hex can decode all 3 formats.
        assert_eq!(&decode::<Hex>(&cbor_orig).unwrap().0, &key);
        assert_eq!(&decode::<Hex>(&cbor_bytes).unwrap().0, &key);
        assert_eq!(&decode::<Hex>(&cbor_hex).unwrap().0, &key);
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

//! Common types used by sibling crates

pub mod errors;
pub mod hash;
pub mod hgid;
pub mod key;
pub mod mutation;
pub mod node;
pub mod nodeinfo;
pub mod parents;
pub mod path;
pub mod repo;
pub mod serde_with;
pub mod sha;

pub use crate::hgid::HgId;
pub use crate::key::Key;
pub use crate::node::Node;
pub use crate::nodeinfo::NodeInfo;
pub use crate::parents::Parents;
pub use crate::path::PathComponent;
pub use crate::path::PathComponentBuf;
pub use crate::path::RepoPath;
pub use crate::path::RepoPathBuf;
pub use crate::sha::Sha256;

pub type Id20 = HgId;

#[cfg(any(test, feature = "for-tests"))]
pub mod testutil;


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

//! Tracking of commit mutations (amends, rebases, etc.)

use std::io::Read;
use std::io::Write;

use anyhow::anyhow;
use anyhow::Result;
use byteorder::BigEndian;
use byteorder::ReadBytesExt;
use byteorder::WriteBytesExt;
use vlqencoding::VLQDecode;
use vlqencoding::VLQEncode;

use crate::node::Node;
use crate::node::ReadNodeExt;
use crate::node::WriteNodeExt;

#[derive(Clone, PartialEq, PartialOrd)]
pub struct MutationEntry {
    pub succ: Node,
    pub preds: Vec<Node>,
    pub split: Vec<Node>,
    pub op: String,
    pub user: String,
    pub time: i64,
    pub tz: i32,
    pub extra: Vec<(Box<[u8]>, Box<[u8]>)>,
}

/// Default size for a buffer that will be used for serializing a mutation entry.
///
/// This is:
///   * Version (1 byte)
///   * Successor hash (20 bytes)
///   * Predecessor count (1 byte)
///   * Predecessor hash (20 bytes) - most entries have only one predecessor
///   * Operation (~7 bytes) - e.g. amend, rebase
///   * User (~40 bytes)
///   * Time (4-8 bytes)
///   * Timezone (~2 bytes)
///   * Extra count (1 byte)
pub const DEFAULT_ENTRY_SIZE: usize = 100;

const DEFAULT_VERSION: u8 = 1;

impl MutationEntry {
    pub fn serialize(&self, w: &mut dyn Write) -> Result<()> {
        w.write_u8(DEFAULT_VERSION)?;
        w.write_node(&self.succ)?;
        w.write_vlq(self.preds.len())?;
        for pred in self.preds.iter() {
            w.write_node(pred)?;
        }
        w.write_vlq(self.split.len())?;
        for split in self.split.iter() {
            w.write_node(split)?;
        }
        w.write_vlq(self.op.len())?;
        w.write_all(self.op.as_bytes())?;
        w.write_vlq(self.user.len())?;
        w.write_all(&self.user.as_bytes())?;
        w.write_f64::<BigEndian>(self.time as f64)?;
        w.write_vlq(self.tz)?;
        w.write_vlq(self.extra.len())?;
        for (key, value) in self.extra.iter() {
            w.write_vlq(key.len())?;
            w.write_all(&key)?;
            w.write_vlq(value.len())?;
            w.write_all(&value)?;
        }
        Ok(())
    }

    pub fn deserialize(r: &mut dyn Read) -> Result<Self> {
        enum EntryFormat {
            FloatDate,
            Latest,
        }
        let format = match r.read_u8()? {
            0 => return Err(anyhow!("invalid mutation entry version: 0")),
            1..=4 => {
                // These versions stored the date as an f64.
                EntryFormat::FloatDate
            }
            5 => EntryFormat::Latest,
            v => return Err(anyhow!("unsupported mutation entry version: {}", v)),
        };
        let succ = r.read_node()?;
        let pred_count = r.read_vlq()?;
        let mut preds = Vec::with_capacity(pred_count);
        for _ in 0..pred_count {
            preds.push(r.read_node()?);
        }
        let split_count = r.read_vlq()?;
        let mut split = Vec::with_capacity(split_count);
        for _ in 0..split_count {
            split.push(r.read_node()?);
        }
        let op_len = r.read_vlq()?;
        let mut op = vec![0; op_len];
        r.read_exact(&mut op)?;
        let op = String::from_utf8(op)?;
        let user_len = r.read_vlq()?;
        let mut user = vec![0; user_len];
        r.read_exact(&mut user)?;
        let user = String::from_utf8(user)?;
        let time = match format {
            EntryFormat::FloatDate => {
                // The date was stored as a floating point number.  We
                // actually want an integer, so truncate and convert.
                r.read_f64::<BigEndian>()?.trunc() as i64
            }
            _ => r.read_vlq()?,
        };
        let tz = r.read_vlq()?;
        let extra_count = r.read_vlq()?;
        let mut extra = Vec::with_capacity(extra_count);
        for _ in 0..extra_count {
            let key_len = r.read_vlq()?;
            let mut key = vec![0; key_len];
            r.read_exact(&mut key)?;
            let value_len = r.read_vlq()?;
            let mut value = vec![0; value_len];
            r.read_exact(&mut value)?;
            extra.push((key.into_boxed_slice(), value.into_boxed_slice()));
        }
        Ok(MutationEntry {
            succ,
            preds,
            split,
            op,
            user,
            time,
            tz,
            extra,
        })
    }

    /// Return true if this represents an 1:1 commit replacement.
    /// A split or a fold are not 1:1 replacement.
    ///
    /// Note: Resolving divergence should use multiple 1:1 records
    /// and are considered 1:1 replacements.
    ///
    /// If this function returns true, `preds` is ensured to only
    /// have one item.
    pub fn is_one_to_one(&self) -> bool {
        self.split.is_empty() && self.preds.len() == 1
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use std::io;
use std::io::Read;
use std::io::Write;

use crate::hgid::HgId;
use crate::hgid::ReadHgIdExt;
use crate::hgid::WriteHgIdExt;

pub type Node = HgId;

pub trait WriteNodeExt {
    fn write_node(&mut self, value: &Node) -> io::Result<()>;
}

impl<W: Write + ?Sized> WriteNodeExt for W {
    fn write_node(&mut self, value: &Node) -> io::Result<()> {
        self.write_hgid(value)
    }
}

pub trait ReadNodeExt {
    fn read_node(&mut self) -> io::Result<Node>;
}

impl<R: Read + ?Sized> ReadNodeExt for R {
    fn read_node(&mut self) -> io::Result<Node> {
        self.read_hgid()
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use serde_derive::Deserialize;
use serde_derive::Serialize;

use crate::hgid::HgId;
use crate::key::Key;

#[derive(
    Clone,
    Debug,
    Default,
    Eq,
    Hash,
    Ord,
    PartialEq,
    PartialOrd,
    Serialize,
    Deserialize
)]
pub struct NodeInfo {
    pub parents: [Key; 2],
    pub linknode: HgId,
}

#[cfg(any(test, feature = "for-tests"))]
use quickcheck::Arbitrary;
#[cfg(any(test, feature = "for-tests"))]
use quickcheck::Gen;

#[cfg(any(test, feature = "for-tests"))]
impl Arbitrary for NodeInfo {
    fn arbitrary(g: &mut Gen) -> Self {
        NodeInfo {
            parents: [Key::arbitrary(g), Key::arbitrary(g)],
            linknode: HgId::arbitrary(g),
        }
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use serde_derive::Deserialize;
use serde_derive::Serialize;

use crate::hgid::HgId;
use crate::hgid::NULL_ID;

/// Enum representing a Mercurial hgid's parents.
///
/// A hgid may have zero, one, or two parents (referred to as p1 and p2 respectively).
/// Ordinarily, a non-existent parent is denoted by a null hash, consisting of all zeros.
/// A null p1 implies a null p2, so it is invalid for a hgid to have a p2 without a p1.
///
/// In Rust, these restrictions can be enforced with an enum that makes invalid
/// states unrepresentable.
#[derive(
    Copy,
    Clone,
    Debug,
    Eq,
    Hash,
    Ord,
    PartialEq,
    PartialOrd,
    Serialize,
    Deserialize
)]
#[cfg_attr(
    any(test, feature = "for-tests"),
    derive(quickcheck_arbitrary_derive::Arbitrary)
)]
#[serde(untagged)]
pub enum Parents {
    None,
    One(HgId),
    Two(HgId, HgId),
}

impl Parents {
    /// Construct a new Parents from two potentially null HgId hashes.
    /// This function will panic if an invalid combination of Nodes is given --
    /// namely, if p1 is null but p2 is not null.
    pub fn new(p1: HgId, p2: HgId) -> Self {
        match (p1.is_null(), p2.is_null()) {
            (true, true) => Parents::None,
            (false, true) => Parents::One(p1),
            (true, _) => panic!("invalid parents: non-null p2 with null p1"),
            (false, false) => Parents::Two(p1, p2),
        }
    }

    /// Convert this Parents into a tuple representation, with non-existent
    /// parents represented by NULL_ID.
    pub fn into_nodes(self) -> (HgId, HgId) {
        match self {
            Parents::None => (NULL_ID, NULL_ID),
            Parents::One(p1) => (p1, NULL_ID),
            Parents::Two(p1, p2) => (p1, p2),
        }
    }

    pub fn p1(&self) -> Option<&HgId> {
        match self {
            Parents::None => None,
            Parents::One(ref p1) => Some(p1),
            Parents::Two(ref p1, _) => Some(p1),
        }
    }

    pub fn p2(&self) -> Option<&HgId> {
        match self {
            Parents::None | Parents::One(_) => None,
            Parents::Two(_, ref p2) => Some(p2),
        }
    }
}

impl Default for Parents {
    fn default() -> Self {
        Parents::None
    }
}

impl FromIterator<HgId> for Parents {
    fn from_iter<I: IntoIterator<Item = HgId>>(iter: I) -> Self {
        let mut iter = iter.into_iter();
        let p1 = iter.next().unwrap_or(NULL_ID);
        let p2 = iter.next().unwrap_or(NULL_ID);
        Parents::new(p1, p2)
    }
}

impl IntoIterator for Parents {
    type IntoIter = ParentIter;
    type Item = HgId;

    fn into_iter(self) -> ParentIter {
        ParentIter(self)
    }
}

impl IntoIterator for &Parents {
    type IntoIter = ParentIter;
    type Item = HgId;

    fn into_iter(self) -> ParentIter {
        ParentIter(self.clone())
    }
}

#[derive(Debug)]
pub struct ParentIter(Parents);

impl Iterator for ParentIter {
    type Item = HgId;

    fn next(&mut self) -> Option<Self::Item> {
        match self.0 {
            Parents::None => None,
            Parents::One(p1) => {
                self.0 = Parents::None;
                Some(p1)
            }
            Parents::Two(p1, p2) => {
                self.0 = Parents::One(p2);
                Some(p1)
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use serde_json;
    use serde_json::json;

    use super::*;

    #[test]
    fn from_iter() {
        let none = Parents::from_iter(vec![NULL_ID, NULL_ID]);
        assert_eq!(none, Parents::None);

        let p1 = HgId::from_byte_array([0xAA; 20]);
        let one = Parents::from_iter(vec![p1, NULL_ID]);
        assert_eq!(one, Parents::One(p1));

        let p2 = HgId::from_byte_array([0xBB; 20]);
        let two = Parents::from_iter(vec![p1, p2]);
        assert_eq!(two, Parents::Two(p1, p2));
    }

    #[test]
    #[should_panic]
    fn from_iter_invalid() {
        let p2 = HgId::from_byte_array([0xAA; 20]);
        let _ = Parents::from_iter(vec![NULL_ID, p2]);
    }

    #[test]
    fn into_iter() {
        let parents = Parents::None;
        let none = parents.into_iter().collect::<Vec<_>>();
        assert_eq!(none, Vec::new());

        let p1 = HgId::from_byte_array([0xAA; 20]);
        let parents = Parents::One(p1);
        let one = parents.into_iter().collect::<Vec<_>>();
        assert_eq!(one, vec![p1]);

        let p2 = HgId::from_byte_array([0xBB; 20]);
        let parents = Parents::Two(p1, p2);
        let two = parents.into_iter().collect::<Vec<_>>();
        assert_eq!(two, vec![p1, p2]);
    }

    #[test]
    fn untagged_serialization() {
        let parents = Parents::None;
        let none = serde_json::to_value(&parents).unwrap();
        assert_eq!(none, json!(null));

        let p1 = HgId::from_byte_array([0x1; 20]);
        let parents = Parents::One(p1);
        let one = serde_json::to_value(&parents).unwrap();
        let expected = json!([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
        assert_eq!(one, expected);

        let p2 = HgId::from_byte_array([0x2; 20]);
        let parents = Parents::Two(p1, p2);
        let two = serde_json::to_value(&parents).unwrap();
        let p1_json = json!([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
        let p2_json = json!([2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]);
        assert_eq!(two, json!([p1_json, p2_json]));
    }

    #[test]
    fn serialized_size_cbor() {
        let parents = Parents::None;
        let none = serde_cbor::to_vec(&parents).unwrap();
        assert_eq!(none.len(), 1);

        let p1 = HgId::from_byte_array([0x1; 20]);
        let parents = Parents::One(p1);
        let one = serde_cbor::to_vec(&parents).unwrap();
        assert_eq!(one.len(), 21);

        let p2 = HgId::from_byte_array([0x2; 20]);
        let parents = Parents::Two(p1, p2);
        let two = serde_cbor::to_vec(&parents).unwrap();
        assert_eq!(two.len(), 43);
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

//! Here we have types for working with paths specialized for source control internals.
//! They are akin to str and String in high level behavior. `RepoPath` is an unsized type wrapping
//! a str so it can't be instantiated directly. `RepoPathBuf` represents the owned version of a
//! RepoPath and wraps a String.
//!
//! The inspiration for `RepoPath` and `RepoPathBuf` comes from the std::path crate however
//! we know that the internal representation of a path is consistently a utf8 string where
//! directories are delimited by the `SEPARATOR` (`/`) so our types can have a simpler
//! representation. It is because of the same reason that we can't use the abstractions in
//! `std::path` for internal uses where we need to apply the same algorithm for blobs we get from
//! the server across all systems.
//!
//! We could use `String` and `&str` directly however these types are inexpressive and have few
//! guarantees. Rust has a strong type system so we can leverage it to provide more safety.
//!
//! `PathComponent` and `PathComponentBuf` can be seen as specializations of `RepoPath` and
//! `RepoPathBuf` that do not have any `SEPARATOR` characters. The main operation that is done on
//! paths is iterating over its components. `PathComponents` are names of files or directories.
//! For the path: `foo/bar/baz.txt` we have 3 components: `foo`, `bar` and `baz.txt`.
//!
//! A lot of algorithms used in source control management operate on directories so having an
//! abstraction for individual components is going to increase readability in the long run.
//! A clear example for where we may want to use `PathComponentBuf` is in the treemanifest logic
//! where all indexing is done using components. The index in those cases must be able to own
//! component. Writing it in terms of `RepoPathBuf` would probably be less readable that
//! writing it in terms of `String`.

use std::borrow::Borrow;
use std::cmp::Ordering;
use std::fmt;
use std::ops::Deref;
use std::path::Path;
use std::path::PathBuf;
use std::str::Utf8Error;

use ref_cast::ref_cast_custom;
use ref_cast::RefCastCustom;
use serde_derive::Deserialize;
use serde_derive::Serialize;
use thiserror::Error;

/// An owned version of a `RepoPath`.
#[derive(Clone, Debug, Default, Eq, PartialEq, Hash)]
#[derive(Serialize, Deserialize)]
pub struct RepoPathBuf(String);

/// A normalized path starting from the root of the repository. Paths can be broken into
/// components by using `SEPARATOR`. Normalized means that it following the following rules:
///  * unicode is normalized
///  * does not end with a `SEPARATOR`
///  * does not contain:
///    * \0, null character - it is an illegal file name character on unix
///    * \1, CTRL-A - used as metadata separator
///    * \10, newline - used as metadata separator
///  * does not contain the following components:
///    * ``, empty, implies that paths can't start with, end or contain consecutive `SEPARATOR`s
///    * `.`, dot/period, unix current directory
///    * `..`, double dot, unix parent directory
/// TODO: There is more validation that could be done here. Windows has a broad list of illegal
/// characters and reseved words.
///
/// It should be noted that `RepoPathBuf` and `RepoPath` implement `AsRef<RepoPath>`.
#[derive(Debug, Eq, PartialEq, Hash, RefCastCustom, Serialize)]
#[repr(transparent)]
pub struct RepoPath(str);

/// An owned version of a `PathComponent`. Not intended for mutation. RepoPathBuf is probably
/// more appropriate for mutation.
#[derive(Clone, Debug, Default, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct PathComponentBuf(String);

/// A `RepoPath` is a series of `PathComponent`s joined together by a separator (`/`).
/// Names for directories or files.
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash, RefCastCustom)]
#[repr(transparent)]
pub struct PathComponent(str);

/// The One. The One Character We Use To Separate Paths Into Components.
pub const SEPARATOR: char = '/';

#[derive(Error, Debug)]
pub enum ParseError {
    ValidationError(String, ValidationError),
    InvalidUtf8(Vec<u8>, Utf8Error),
    // Prefer InvalidUtf8 if the actual UTF8 encoding is invalid. Use InvalidUnicode
    // if the string (e.g. OsString) cannot be interpreted as a valid sequence
    // of unicode codepoints. Accepts a lossy string conversion as its argument.
    InvalidUnicode(String),
}

impl ParseError {
    pub fn into_path_bytes(self) -> Vec<u8> {
        match self {
            ParseError::ValidationError(s, _) => s.into_bytes(),
            ParseError::InvalidUtf8(b, _) => b,
            ParseError::InvalidUnicode(s) => s.into_bytes(),
        }
    }
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self {
            ParseError::ValidationError(path, validation_error) => {
                write!(f, "Failed to validate {:?}. {}", path, validation_error)
            }
            ParseError::InvalidUtf8(bytes, utf8_error) => write!(
                f,
                "Failed to parse to Utf8: {:?}. {}",
                String::from_utf8_lossy(bytes),
                utf8_error
            ),
            ParseError::InvalidUnicode(lossy_string) => {
                write!(f, "Failed to parse 'unicode' string {:?}", lossy_string)
            }
        }
    }
}

#[derive(Error, Debug)]
pub enum ValidationError {
    #[error("Invalid component: \"{0}\".")]
    InvalidPathComponent(#[from] InvalidPathComponent),
    #[error("Invalid byte: {0:?}.")]
    InvalidByte(u8),
    #[error("Trailing slash.")]
    TrailingSlash,
}

#[derive(Error, Debug)]
pub enum InvalidPathComponent {
    #[error("")]
    Empty,
    #[error(".")]
    Current,
    #[error("..")]
    Parent,
}

impl RepoPathBuf {
    /// Constructs an empty RepoPathBuf. This path will have no
    /// components and will be equivalent to the root of the repository.
    pub const fn new() -> RepoPathBuf {
        Self(String::new())
    }

    /// Constructs a `RepoPathBuf` from a vector of bytes. It will fail when the bytes are are not
    /// valid utf8 or when the string does not respect the `RepoPathBuf` rules.
    pub fn from_utf8(vec: Vec<u8>) -> Result<Self, ParseError> {
        let utf8_string = String::from_utf8(vec).map_err(|e| {
            let utf8_error = e.utf8_error();
            ParseError::InvalidUtf8(e.into_bytes(), utf8_error)
        })?;
        RepoPathBuf::from_string(utf8_string)
    }

    /// Constructs a `RepoPathBuf` from a `String`. It can fail when the contents of String is
    /// deemed invalid. See `RepoPath` for validation rules.
    pub fn from_string(s: String) -> Result<Self, ParseError> {
        match validate_path(&s) {
            Ok(()) => Ok(RepoPathBuf(s)),
            Err(e) => Err(ParseError::ValidationError(s, e)),
        }
    }

    /// Consumes the current instance and returns a String with the contents of this `RepoPathBuf`.
    /// Intended for code that converts between different formats. FFI / serialization.
    pub fn into_string(self) -> String {
        self.0
    }

    /// Converts the `RepoPathBuf` in a `RepoPath`.
    pub fn as_repo_path(&self) -> &RepoPath {
        self
    }

    /// Returns whether the current `RepoPathBuf` has no components. Since `RepoPathBuf`
    /// represents the relative path from the start of the repository this is equivalent to
    /// checking whether the path is the root of the repository
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    /// Append a `RepoPath` to the end of `RepoPathBuf`. This function will add the `SEPARATOR`
    /// required by concatenation.
    pub fn push<P: AsRef<RepoPath>>(&mut self, path: P) {
        self.append(&path.as_ref().0);
    }

    /// Removed the last component from the `RepoPathBuf` and return it.
    pub fn pop(&mut self) -> Option<PathComponentBuf> {
        if self.0.is_empty() {
            return None;
        }
        match self.0.rfind(SEPARATOR) {
            None => {
                let result = PathComponentBuf::from_string_unchecked(self.0.clone());
                self.0 = String::new();
                Some(result)
            }
            Some(pos) => {
                let result = PathComponentBuf::from_string_unchecked(self.0.split_off(pos + 1));
                self.0.pop(); // remove SEPARATOR
                Some(result)
            }
        }
    }

    pub fn to_lower_case(&self) -> Self {
        Self(self.0.to_lowercase())
    }

    fn append(&mut self, s: &str) {
        if !self.0.is_empty() {
            self.0.push(SEPARATOR);
        }
        self.0.push_str(s);
    }
}

impl Ord for RepoPathBuf {
    fn cmp(&self, other: &RepoPathBuf) -> Ordering {
        self.as_repo_path().cmp(other.as_repo_path())
    }
}

impl PartialOrd for RepoPathBuf {
    fn partial_cmp(&self, other: &RepoPathBuf) -> Option<Ordering> {
        self.as_repo_path().partial_cmp(other.as_repo_path())
    }
}

impl Deref for RepoPathBuf {
    type Target = RepoPath;
    fn deref(&self) -> &Self::Target {
        RepoPath::from_str_unchecked(&self.0)
    }
}

impl AsRef<RepoPath> for RepoPathBuf {
    fn as_ref(&self) -> &RepoPath {
        self
    }
}

impl AsRef<str> for RepoPathBuf {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl AsRef<[u8]> for RepoPathBuf {
    fn as_ref(&self) -> &[u8] {
        self.as_byte_slice()
    }
}

impl Borrow<RepoPath> for RepoPathBuf {
    fn borrow(&self) -> &RepoPath {
        self
    }
}

impl fmt::Display for RepoPathBuf {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&*self.0, formatter)
    }
}

impl RepoPath {
    /// Returns an empty `RepoPath`. Parallel to `RepoPathBuf::new()`. This path will have no
    /// components and will be equivalent to the root of the repository.
    pub fn empty() -> &'static RepoPath {
        RepoPath::from_str_unchecked("")
    }

    /// Returns whether the current `RepoPath` has no components. Since `RepoPath`
    /// represents the relative path from the start of the repository this is equivalent to
    /// checking whether the path is the root of the repository
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    /// Constructs a `RepoPath` from a byte slice. It will fail when the bytes are are not valid
    /// utf8 or when the string does not respect the `RepoPath` rules.
    pub fn from_utf8<S: AsRef<[u8]> + ?Sized>(s: &S) -> Result<&RepoPath, ParseError> {
        let utf8_str = std::str::from_utf8(s.as_ref())
            .map_err(|e| ParseError::InvalidUtf8(s.as_ref().to_vec(), e))?;
        RepoPath::from_str(utf8_str)
    }

    /// Constructs a `RepoPath` from a `str` slice. It will fail when the string does not respect
    /// the `RepoPath` rules.
    pub fn from_str(s: &str) -> Result<&RepoPath, ParseError> {
        validate_path(s).map_err(|e| ParseError::ValidationError(s.to_string(), e))?;
        Ok(RepoPath::from_str_unchecked(s))
    }

    #[ref_cast_custom]
    fn from_str_unchecked(s: &str) -> &RepoPath;

    /// Returns the underlying bytes of the `RepoPath`.
    pub fn as_byte_slice(&self) -> &[u8] {
        self.0.as_bytes()
    }

    /// Returns the `str` interpretation of the `RepoPath`.
    pub fn as_str(&self) -> &str {
        &self.0
    }

    /// Return the parent of the path. The empty path, `RepoPath::empty()` does not have a
    /// parent so `None` is returned in that case.
    pub fn parent(&self) -> Option<&RepoPath> {
        self.split_last_component().map(|(parent, _)| parent)
    }

    /// Return the last component of the path. The empty path, `RepoPath::empty()` does not have
    /// any components so `None` is returned in that case.
    pub fn last_component(&self) -> Option<&PathComponent> {
        self.split_last_component().map(|(_, component)| component)
    }

    /// Tries to split the current `RepoPath` in a parent path and a component. If the current
    /// path is empty then None is returned. If the current path contains only one component then
    /// the pair that is returned is the empty repo path and a path component that will match the
    /// contents `self`.
    pub fn split_last_component(&self) -> Option<(&RepoPath, &PathComponent)> {
        if self.is_empty() {
            return None;
        }
        match self.0.rfind(SEPARATOR) {
            Some(pos) => Some((
                RepoPath::from_str_unchecked(&self.0[..pos]),
                PathComponent::from_str_unchecked(&self.0[(pos + 1)..]),
            )),
            None => Some((
                RepoPath::empty(),
                PathComponent::from_str_unchecked(&self.0),
            )),
        }
    }

    /// Returns an iterator over the parents of the current path.
    /// The `RepoPath` itself is not returned. The root of the repository represented by the empty
    /// `RepoPath` is always returned by this iterator except if the path is empty.
    ///
    /// For example for the path `"foo/bar/baz"` this iterator will return three items:
    /// `""`, `"foo"` and `"foo/bar"`.
    ///
    /// If you don't want to handle the empty path, then you can use `parents().skip(1)`.
    /// It is possible to get iterate over parents with elements in paralel using:
    /// `path.parents().zip(path.components())`.
    pub fn parents(&'_ self) -> Parents<'_> {
        Parents::new(self)
    }

    /// Returns an iterator over the parents of the current path.
    /// The `RepoPath` itself is not returned. The root of the repository represented by the empty
    /// `RepoPath` is always returned by this iterator except if the path is empty.
    ///
    /// For example for the path `"foo/bar/baz"` this iterator will return three items:
    /// `"foo/bar"`, `"foo"`, and `""`.
    pub fn reverse_parents(&'_ self) -> ReverseParents<'_> {
        ReverseParents::new(self)
    }

    /// Returns an iterator over the components of the path.
    pub fn components(&'_ self) -> Components<'_> {
        Components::new(self)
    }

    /// Returns an iterator over the ancestors of the current path.
    ///
    /// This should be strictly equivalent to: `self.parents().rev()`.
    pub fn ancestors(&'_ self) -> Ancestors<'_> {
        Ancestors::new(self)
    }

    pub fn to_lower_case(&self) -> RepoPathBuf {
        RepoPathBuf(self.0.to_lowercase())
    }

    /// Create a std::Path::PathBuf from this RepoPath. "/" will be
    /// converted to the system path separator.
    pub fn to_path(&self) -> PathBuf {
        self.components().map(PathComponent::as_str).collect()
    }
}

impl Ord for RepoPath {
    fn cmp(&self, other: &RepoPath) -> Ordering {
        self.components().cmp(other.components())
    }
}

impl PartialOrd for RepoPath {
    fn partial_cmp(&self, other: &RepoPath) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl AsRef<RepoPath> for RepoPath {
    fn as_ref(&self) -> &RepoPath {
        self
    }
}

impl AsRef<str> for RepoPath {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl AsRef<[u8]> for RepoPath {
    fn as_ref(&self) -> &[u8] {
        self.as_byte_slice()
    }
}

impl ToOwned for RepoPath {
    type Owned = RepoPathBuf;
    fn to_owned(&self) -> Self::Owned {
        RepoPathBuf(self.0.to_string())
    }
}

impl<'a> TryFrom<&'a str> for &'a RepoPath {
    type Error = ParseError;

    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
        RepoPath::from_str(s)
    }
}

impl TryFrom<String> for RepoPathBuf {
    type Error = ParseError;

    fn try_from(s: String) -> Result<Self, Self::Error> {
        RepoPathBuf::from_string(s)
    }
}

impl TryFrom<PathBuf> for RepoPathBuf {
    type Error = ParseError;

    fn try_from(p: PathBuf) -> Result<Self, Self::Error> {
        RepoPathBuf::from_string(
            p.into_os_string().into_string().map_err(|os_str| {
                ParseError::InvalidUnicode(os_str.to_string_lossy().to_string())
            })?,
        )
    }
}

impl fmt::Display for RepoPath {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, formatter)
    }
}

impl PathComponentBuf {
    /// Constructs an from a `String`. It can fail when the contents of `String` is deemed invalid.
    /// See `PathComponent` for validation rules.
    pub fn from_string(s: String) -> Result<Self, ParseError> {
        match validate_component(&s) {
            Ok(()) => Ok(PathComponentBuf(s)),
            Err(e) => Err(ParseError::ValidationError(s, e)),
        }
    }

    /// Consumes the current instance and returns a String with the contents of this
    /// `PathComponentBuf`.
    /// Intended for code that converts between different formats. FFI / serialization.
    pub fn into_string(self) -> String {
        self.0
    }

    /// Converts the `PathComponentBuf` in a `PathComponent`.
    pub fn as_path_component(&self) -> &PathComponent {
        self
    }

    fn from_string_unchecked(s: String) -> Self {
        PathComponentBuf(s)
    }
}

impl Deref for PathComponentBuf {
    type Target = PathComponent;
    fn deref(&self) -> &Self::Target {
        PathComponent::from_str_unchecked(&self.0)
    }
}

impl AsRef<PathComponent> for PathComponentBuf {
    fn as_ref(&self) -> &PathComponent {
        self
    }
}

impl Borrow<PathComponent> for PathComponentBuf {
    fn borrow(&self) -> &PathComponent {
        self
    }
}

impl fmt::Display for PathComponentBuf {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&*self.0, formatter)
    }
}

impl PathComponent {
    /// Constructs a `PathComponent` from a byte slice. It will fail when the bytes are are not
    /// valid utf8 or when the string does not respect the `PathComponent` rules.
    pub fn from_utf8(s: &[u8]) -> Result<&PathComponent, ParseError> {
        let utf8_str =
            std::str::from_utf8(s).map_err(|e| ParseError::InvalidUtf8(s.to_vec(), e))?;
        PathComponent::from_str(utf8_str)
    }

    /// Constructs a `PathComponent` from a `str` slice. It will fail when the string does not
    /// respect the `PathComponent` rules.
    pub fn from_str(s: &str) -> Result<&PathComponent, ParseError> {
        validate_component(s).map_err(|e| ParseError::ValidationError(s.to_string(), e))?;
        Ok(PathComponent::from_str_unchecked(s))
    }

    #[ref_cast_custom]
    fn from_str_unchecked(s: &str) -> &PathComponent;

    /// Returns the underlying bytes of the `PathComponent`.
    pub fn as_byte_slice(&self) -> &[u8] {
        self.0.as_bytes()
    }

    /// Returns the `str` interpretation of the `PathComponent`.
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl AsRef<PathComponent> for PathComponent {
    fn as_ref(&self) -> &PathComponent {
        self
    }
}

impl AsRef<RepoPath> for PathComponent {
    fn as_ref(&self) -> &RepoPath {
        RepoPath::from_str_unchecked(&self.0)
    }
}

impl AsRef<[u8]> for PathComponent {
    fn as_ref(&self) -> &[u8] {
        self.as_byte_slice()
    }
}

impl ToOwned for PathComponent {
    type Owned = PathComponentBuf;
    fn to_owned(&self) -> Self::Owned {
        PathComponentBuf(self.0.to_string())
    }
}

impl fmt::Display for PathComponent {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, formatter)
    }
}

fn validate_path(s: &str) -> Result<(), ValidationError> {
    if s.is_empty() {
        return Ok(());
    }
    if s.bytes().next_back() == Some(b'/') {
        return Err(ValidationError::TrailingSlash);
    }
    for component in s.split(SEPARATOR) {
        validate_component(component)?;
    }
    Ok(())
}

fn validate_component(s: &str) -> Result<(), ValidationError> {
    if s.is_empty() {
        return Err(InvalidPathComponent::Empty.into());
    }
    if s == "." {
        return Err(InvalidPathComponent::Current.into());
    }
    if s == ".." {
        return Err(InvalidPathComponent::Parent.into());
    }
    for b in s.bytes() {
        if b == 0u8 || b == 1u8 || b == b'\n' || b == b'\r' || b == b'/' {
            return Err(ValidationError::InvalidByte(b));
        }
    }
    Ok(())
}

pub struct Parents<'a> {
    path: &'a RepoPath,
    position: Option<usize>,
}

impl<'a> Parents<'a> {
    pub fn new(path: &'a RepoPath) -> Self {
        Parents {
            path,
            position: None,
        }
    }
}

impl<'a> Iterator for Parents<'a> {
    type Item = &'a RepoPath;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(ref mut position) = self.position {
            match self.path.0[*position..].find(SEPARATOR) {
                Some(delta) => {
                    let end = *position + delta;
                    let result = RepoPath::from_str_unchecked(&self.path.0[..end]);
                    *position = end + 1;
                    Some(result)
                }
                None => {
                    *position = self.path.0.len();
                    None
                }
            }
        } else {
            self.position = Some(0);
            if self.path.is_empty() {
                None
            } else {
                Some(RepoPath::empty())
            }
        }
    }
}

pub struct ReverseParents<'a> {
    path: &'a RepoPath,
    position: Option<usize>,
}

impl<'a> ReverseParents<'a> {
    pub fn new(path: &'a RepoPath) -> Self {
        ReverseParents {
            path,
            // Skip the last component since we only want the parents.
            position: if path.is_empty() {
                None
            } else {
                Some(path.0[..].rfind(SEPARATOR).unwrap_or(0))
            },
        }
    }
}

impl<'a> Iterator for ReverseParents<'a> {
    type Item = &'a RepoPath;

    fn next(&mut self) -> Option<Self::Item> {
        match self.position {
            Some(ref mut position) if *position != 0 => {
                let result = RepoPath::from_str_unchecked(&self.path.0[0..*position]);
                *position = self.path.0[..*position].rfind(SEPARATOR).unwrap_or(0);
                Some(result)
            }
            Some(_) => {
                self.position = None;
                Some(RepoPath::empty())
            }
            None => None,
        }
    }
}

pub struct Components<'a> {
    path: &'a RepoPath,
    position: usize,
}

impl<'a> Components<'a> {
    pub fn new(path: &'a RepoPath) -> Self {
        Components { path, position: 0 }
    }
}

impl<'a> Iterator for Components<'a> {
    type Item = &'a PathComponent;

    fn next(&mut self) -> Option<Self::Item> {
        match self.path.0[self.position..].find(SEPARATOR) {
            Some(delta) => {
                let end = self.position + delta;
                let result = PathComponent::from_str_unchecked(&self.path.0[self.position..end]);
                self.position = end + 1;
                Some(result)
            }
            None => {
                if self.position < self.path.0.len() {
                    let result = PathComponent::from_str_unchecked(&self.path.0[self.position..]);
                    self.position = self.path.0.len();
                    Some(result)
                } else {
                    None
                }
            }
        }
    }
}

pub struct Ancestors<'a> {
    path: Option<&'a RepoPath>,
}

impl<'a> Ancestors<'a> {
    pub fn new(path: &'a RepoPath) -> Self {
        Ancestors {
            path: path.parent(),
        }
    }
}

impl<'a> Iterator for Ancestors<'a> {
    type Item = &'a RepoPath;

    fn next(&mut self) -> Option<Self::Item> {
        let path = self.path;
        self.path = path.and_then(RepoPath::parent);
        path
    }
}

enum RepoPathRelativizerConfig {
    // If the cwd is inside the repo, then Hg paths should be relativized against the cwd relative
    // to the repo root.
    CwdUnderRepo { relative_cwd: PathBuf },

    // If the cwd is outside the repo, then prefix is the cwd relative to the repo root: Hg paths
    // can simply be appended to this path.
    CwdOutsideRepo { prefix: PathBuf },
}

pub struct RepoPathRelativizer {
    config: RepoPathRelativizerConfig,
}

/// Utility for computing a relativized path for a file in an Hg repository given the user's cwd
/// and specified value for --repository/-R, if any.
///
/// Note: the caller is responsible for normalizing the repo_root and cwd parameters ahead of time.
/// If these are specified in different formats (e.g., on Windows one is a UNC path and the other
/// is not), then this function will not produce expected results.  Unfortunately the Rust library
/// does not provide a mechanism for normalizing paths.  The best thing for callers to do for now
/// is probably to call Path::canonicalize() if they expect that the paths do exist on disk.
impl RepoPathRelativizer {
    /// `cwd` corresponds to getcwd(2) while `repo_root` is the absolute path specified via
    /// --repository/-R, or failing that, the Hg repo that contains `cwd`.
    pub fn new(cwd: impl AsRef<Path>, repo_root: impl AsRef<Path>) -> Self {
        RepoPathRelativizer::new_impl(cwd.as_ref(), repo_root.as_ref())
    }

    fn new_impl(cwd: &Path, repo_root: &Path) -> Self {
        use self::RepoPathRelativizerConfig::*;
        let config = if cwd.starts_with(&repo_root) {
            CwdUnderRepo {
                relative_cwd: util::path::relativize(repo_root, cwd),
            }
        } else {
            CwdOutsideRepo {
                prefix: util::path::relativize(cwd, repo_root),
            }
        };
        RepoPathRelativizer { config }
    }

    /// Relativize the [`RepoPath`]. Returns a String that is suitable for display to the user.
    pub fn relativize(&self, path: impl AsRef<RepoPath>) -> String {
        fn inner(relativizer: &RepoPathRelativizer, path: &RepoPath) -> String {
            // TODO: directly operate on the RepoPath components.
            let path = path.to_path();

            use self::RepoPathRelativizerConfig::*;
            let output = match &relativizer.config {
                CwdUnderRepo { relative_cwd } => util::path::relativize(relative_cwd, &path),
                CwdOutsideRepo { prefix } => prefix.join(path),
            };
            output.display().to_string()
        }

        inner(self, path.as_ref())
    }
}

#[cfg(any(test, feature = "for-tests"))]
impl quickcheck::Arbitrary for RepoPathBuf {
    fn arbitrary(g: &mut quickcheck::Gen) -> Self {
        let size = usize::arbitrary(g) % 8;
        let mut path_buf = RepoPathBuf::new();
        for _ in 0..size {
            path_buf.push(PathComponentBuf::arbitrary(g).as_ref());
        }
        path_buf
    }
}

#[cfg(any(test, feature = "for-tests"))]
impl quickcheck::Arbitrary for PathComponentBuf {
    fn arbitrary(g: &mut quickcheck::Gen) -> Self {
        // Most strings should be valid `PathComponent` so it is reasonable to loop until a valid
        // string is found. To note that generating Arbitrary Unicode on `char` is implemented
        // using a loop where random bytes are validated against the `char` constructor.
        loop {
            let size = usize::arbitrary(g) % 7 + 1;
            let mut s = String::with_capacity(size);
            for _ in 0..size {
                let x = (u64::arbitrary(g) % 25) as u8;
                let x = (b'a' + x) as char;
                s.push(x);
            }
            if let Ok(component) = PathComponentBuf::from_string(s) {
                return component;
            }
        }
    }

    fn shrink(&self) -> Box<dyn Iterator<Item = PathComponentBuf>> {
        Box::new(
            self.0
                .shrink()
                .filter_map(|s| PathComponentBuf::from_string(s).ok()),
        )
    }
}

#[cfg(any(test, feature = "for-tests"))]
pub mod mocks {
    use lazy_static::lazy_static;

    use super::*;

    lazy_static! {
        pub static ref FOO_PATH: RepoPathBuf =
            RepoPathBuf::from_string(String::from("foo")).unwrap();
        pub static ref BAR_PATH: RepoPathBuf =
            RepoPathBuf::from_string(String::from("bar")).unwrap();
        pub static ref BAZ_PATH: RepoPathBuf =
            RepoPathBuf::from_string(String::from("baz")).unwrap();
    }
}

#[cfg(test)]
mod tests {
    use quickcheck::quickcheck;

    use super::*;
    use crate::testutil::path_component;
    use crate::testutil::path_component_buf;
    use crate::testutil::repo_path;
    use crate::testutil::repo_path_buf;

    #[test]
    fn test_repo_path_initialization_with_invalid_utf8() {
        assert!(RepoPath::from_utf8(&vec![0x80, 0x80]).is_err());
        assert!(RepoPathBuf::from_utf8(vec![0x80, 0x80]).is_err());
    }

    #[test]
    fn test_path_display() {
        assert_eq!(
            format!("{}", RepoPath::from_utf8(b"slice").unwrap()),
            "slice"
        );
        assert_eq!(format!("{}", RepoPath::from_str("slice").unwrap()), "slice");
    }

    #[test]
    fn test_path_debug() {
        assert_eq!(
            format!("{:?}", RepoPath::from_utf8(b"slice").unwrap()),
            "RepoPath(\"slice\")"
        );
        assert_eq!(
            format!("{:?}", RepoPath::from_str("slice").unwrap()),
            "RepoPath(\"slice\")"
        );
    }

    #[test]
    fn test_pathbuf_display() {
        assert_eq!(format!("{}", RepoPathBuf::new()), "");
        assert_eq!(
            format!(
                "{}",
                RepoPathBuf::from_string(String::from("slice")).unwrap()
            ),
            "slice"
        );
        assert_eq!(
            format!("{}", RepoPathBuf::from_utf8(b"slice".to_vec()).unwrap()),
            "slice"
        );
    }

    #[test]
    fn test_pathbuf_debug() {
        assert_eq!(format!("{:?}", RepoPathBuf::new()), "RepoPathBuf(\"\")");
        assert_eq!(
            format!(
                "{:?}",
                RepoPathBuf::from_string(String::from("slice")).unwrap()
            ),
            "RepoPathBuf(\"slice\")"
        );
    }

    #[test]
    fn test_repo_path_conversions() {
        let repo_path_buf = RepoPathBuf::from_string(String::from("path_buf")).unwrap();
        assert_eq!(repo_path_buf.as_repo_path().to_owned(), repo_path_buf);

        let repo_path = RepoPath::from_str("path").unwrap();
        assert_eq!(repo_path.to_owned().as_repo_path(), repo_path);
    }

    #[test]
    fn test_repo_path_buf_push() {
        let mut repo_path_buf = RepoPathBuf::new();
        repo_path_buf.push(repo_path("one"));
        assert_eq!(repo_path_buf.as_repo_path(), repo_path("one"));
        repo_path_buf.push(repo_path("two"));
        assert_eq!(repo_path_buf.as_repo_path(), repo_path("one/two"));
    }

    #[test]
    fn test_repo_path_buf_pop() {
        let mut out = repo_path_buf("one/two/three");
        assert_eq!(out.pop(), Some(path_component_buf("three")));
        assert_eq!(out, repo_path_buf("one/two"));
        assert_eq!(out.pop(), Some(path_component_buf("two")));
        assert_eq!(out, repo_path_buf("one"));
        assert_eq!(out.pop(), Some(path_component_buf("one")));
        assert_eq!(out, RepoPathBuf::new());
        assert_eq!(out.pop(), None);
    }

    #[test]
    fn test_component_initialization_with_invalid_utf8() {
        assert!(PathComponent::from_utf8(&vec![0x80, 0x80]).is_err());
    }

    #[test]
    fn test_component_display() {
        assert_eq!(
            format!("{}", PathComponent::from_utf8(b"slice").unwrap()),
            "slice"
        );
        assert_eq!(
            format!("{}", PathComponent::from_str("slice").unwrap()),
            "slice"
        );
    }

    #[test]
    fn test_component_debug() {
        assert_eq!(
            format!("{:?}", PathComponent::from_utf8(b"slice").unwrap()),
            "PathComponent(\"slice\")"
        );
        assert_eq!(
            format!("{:?}", PathComponent::from_str("slice").unwrap()),
            "PathComponent(\"slice\")"
        )
    }

    #[test]
    fn test_componentbuf_display() {
        assert_eq!(
            format!(
                "{}",
                PathComponentBuf::from_string(String::from("slice")).unwrap()
            ),
            "slice",
        );
    }

    #[test]
    fn test_componentbuf_debug() {
        assert_eq!(
            format!(
                "{:?}",
                PathComponentBuf::from_string(String::from("slice")).unwrap()
            ),
            "PathComponentBuf(\"slice\")"
        );
    }

    #[test]
    fn test_component_conversions() {
        let componentbuf = PathComponentBuf::from_string(String::from("componentbuf")).unwrap();
        assert_eq!(componentbuf.as_ref().to_owned(), componentbuf);

        let component = PathComponent::from_str("component").unwrap();
        assert_eq!(component.to_owned().as_ref(), component);
    }

    #[test]
    fn test_path_components() {
        let mut iter = repo_path("foo/bar/baz.txt").components();
        assert_eq!(iter.next().unwrap(), path_component("foo"));
        assert_eq!(iter.next().unwrap(), path_component("bar"));
        assert_eq!(iter.next().unwrap(), path_component("baz.txt"));
        assert!(iter.next().is_none());
    }

    #[test]
    fn test_append_component_to_path() {
        let expected = RepoPath::from_str("foo/bar/baz.txt").unwrap();
        let mut pathbuf = RepoPathBuf::new();
        for component in expected.components() {
            pathbuf.push(component);
        }
        assert_eq!(pathbuf.deref(), expected);
    }

    #[test]
    fn test_validate_path() {
        assert_eq!(
            format!("{}", validate_path("\n").unwrap_err()),
            "Invalid byte: 10."
        );
        assert_eq!(
            format!("{}", validate_path("\r").unwrap_err()),
            "Invalid byte: 13."
        );
        assert_eq!(
            format!("{}", RepoPath::from_str("\n").unwrap_err()),
            "Failed to validate \"\\n\". Invalid byte: 10."
        );
        assert_eq!(
            format!("{}", validate_path("boo/").unwrap_err()),
            "Trailing slash."
        );
        assert_eq!(
            format!("{}", RepoPath::from_str("boo/").unwrap_err()),
            "Failed to validate \"boo/\". Trailing slash."
        );
    }

    #[test]
    fn test_validate_component() {
        assert_eq!(
            format!("{}", validate_component("foo/bar").unwrap_err()),
            "Invalid byte: 47."
        );
        assert_eq!(
            format!("{}", PathComponent::from_str("\n").unwrap_err()),
            "Failed to validate \"\\n\". Invalid byte: 10."
        );
        assert_eq!(
            format!("{}", validate_component("").unwrap_err()),
            "Invalid component: \"\"."
        );
        assert_eq!(
            format!("{}", PathComponent::from_str("").unwrap_err()),
            "Failed to validate \"\". Invalid component: \"\"."
        );
    }

    #[test]
    fn test_empty_path_components() {
        assert_eq!(RepoPathBuf::new().components().next(), None);
        assert_eq!(RepoPath::empty().components().next(), None);
    }

    #[test]
    fn test_empty_path_is_empty() {
        assert!(RepoPathBuf::new().is_empty());
        assert!(RepoPath::empty().is_empty());
    }

    #[test]
    fn test_parent() {
        assert_eq!(RepoPath::empty().parent(), None);
        assert_eq!(repo_path("foo").parent(), Some(RepoPath::empty()));
        assert_eq!(
            repo_path("foo/bar/baz").parent(),
            Some(repo_path("foo/bar"))
        );
    }

    #[test]
    fn test_last_component() {
        assert_eq!(RepoPath::empty().last_component(), None);
        assert_eq!(
            repo_path("foo").last_component(),
            Some(path_component("foo"))
        );
        assert_eq!(
            repo_path("foo/bar/baz").last_component(),
            Some(path_component("baz"))
        );
    }

    #[test]
    fn test_parents_on_regular_path() {
        let path = repo_path("foo/bar/baz/file.txt");
        let mut iter = path.parents();
        assert_eq!(iter.next(), Some(RepoPath::empty()));
        assert_eq!(iter.next(), Some(repo_path("foo")));
        assert_eq!(iter.next(), Some(repo_path("foo/bar")));
        assert_eq!(iter.next(), Some(repo_path("foo/bar/baz")));
        assert_eq!(iter.next(), None)
    }

    #[test]
    fn test_reverse_parents_on_regular_path() {
        let path = repo_path("foo/bar/baz/file.txt");
        let mut iter = path.reverse_parents();
        assert_eq!(iter.next(), Some(repo_path("foo/bar/baz")));
        assert_eq!(iter.next(), Some(repo_path("foo/bar")));
        assert_eq!(iter.next(), Some(repo_path("foo")));
        assert_eq!(iter.next(), Some(RepoPath::empty()));
        assert_eq!(iter.next(), None)
    }

    #[test]
    fn test_parents_on_empty_path() {
        assert_eq!(RepoPath::empty().parents().next(), None);
    }

    #[test]
    fn test_parents_and_components_in_parallel() {
        let path = repo_path("foo/bar/baz");
        let mut iter = path.parents().zip(path.components());
        assert_eq!(
            iter.next(),
            Some((RepoPath::empty(), path_component("foo")))
        );
        assert_eq!(iter.next(), Some((repo_path("foo"), path_component("bar"))));
        assert_eq!(
            iter.next(),
            Some((repo_path("foo/bar"), path_component("baz")))
        );
        assert_eq!(iter.next(), None);
    }

    #[test]
    fn test_ancestors_on_regular_path() {
        let path = repo_path("foo/bar/baz/file.txt");
        let mut iter = path.ancestors();
        assert_eq!(iter.next(), Some(repo_path("foo/bar/baz")));
        assert_eq!(iter.next(), Some(repo_path("foo/bar")));
        assert_eq!(iter.next(), Some(repo_path("foo")));
        assert_eq!(iter.next(), Some(RepoPath::empty()));
        assert_eq!(iter.next(), None)
    }

    #[test]
    fn test_ancestors_on_empty_path() {
        assert_eq!(RepoPath::empty().ancestors().next(), None)
    }

    quickcheck! {
       fn test_parents_equal_components(path: RepoPathBuf) -> bool {
           path.deref().parents().count() == path.deref().components().count()
        }

       fn test_ancestors_equal_parents(path: RepoPathBuf) -> bool {
           path.deref().ancestors().count() == path.deref().parents().count()
       }
    }

    #[test]
    fn test_split_last_component() {
        assert_eq!(RepoPath::empty().split_last_component(), None);

        assert_eq!(
            repo_path("foo").split_last_component(),
            Some((RepoPath::empty(), path_component("foo")))
        );

        assert_eq!(
            repo_path("foo/bar/baz").split_last_component(),
            Some((repo_path("foo/bar"), path_component("baz")))
        );
    }

    #[test]
    fn test_to_owned() {
        assert_eq!(RepoPath::empty().to_owned(), RepoPathBuf::new());
        assert_eq!(repo_path("foo/bar").to_owned(), repo_path_buf("foo/bar"));
        assert_eq!(path_component("foo").to_owned(), path_component_buf("foo"));
    }

    #[test]
    fn test_sort_order() {
        assert!(RepoPath::empty() == RepoPath::empty());
        assert!(RepoPath::empty() < repo_path("foo"));
        assert!(repo_path("foo").cmp(RepoPath::empty()) == Ordering::Greater);
        assert!(repo_path("foo/bar") < repo_path("foo/baz"));
        assert!(repo_path("foo/bar") < repo_path("foo-bar"));
        assert!(repo_path("foo/bar") < repo_path("foobar"));
        assert!(repo_path("foo/bar") < repo_path("fooBar"));
        assert!(repo_path("foo/bar") < repo_path("fooo/bar"));
        assert!(repo_path("foo/bar") < repo_path("txt"));
        assert!(repo_path("foo/bar") == repo_path("foo/bar"));
        assert!(repo_path("foo/bar") > repo_path("bar"));
        assert!(repo_path("foo/bar") > repo_path("foo"));
    }

    // Convert to ["foo", "bar"] to "foo\bar" on Windows, else "foo/bar".
    fn os_path(parts: &[&str]) -> String {
        parts.iter().collect::<PathBuf>().to_string_lossy().into()
    }

    #[test]
    fn test_relativize_path_from_repo_when_cwd_is_repo_root() {
        let repo_root = Path::new("/home/zuck/tfb");
        let cwd = Path::new("/home/zuck/tfb");
        let relativizer = RepoPathRelativizer::new(cwd, repo_root);
        let check = |path, expected| {
            assert_eq!(relativizer.relativize(repo_path(path)), expected);
        };
        check("foo/bar.txt", os_path(&["foo", "bar.txt"]));
    }

    #[test]
    fn test_relativize_path_from_repo_when_cwd_is_descendant_of_repo_root() {
        let repo_root = Path::new("/home/zuck/tfb");
        let cwd = Path::new("/home/zuck/tfb/foo");
        let relativizer = RepoPathRelativizer::new(cwd, repo_root);
        let check = |path, expected| {
            assert_eq!(relativizer.relativize(repo_path(path)), expected);
        };
        check("foo/bar.txt", "bar.txt");
    }

    #[test]
    fn test_relativize_path_from_repo_when_cwd_is_ancestor_of_repo_root() {
        let repo_root = PathBuf::from("/home/zuck/tfb");
        let cwd = PathBuf::from("/home/zuck");
        let relativizer = RepoPathRelativizer::new(cwd, repo_root);
        let check = |path, expected| {
            assert_eq!(relativizer.relativize(repo_path(path)), expected);
        };
        check("foo/bar.txt", os_path(&["tfb", "foo", "bar.txt"]));
    }

    #[test]
    fn test_relativize_path_from_repo_when_cwd_is_cousin_of_repo_root() {
        let relativizer = RepoPathRelativizer::new("/home/schrep/tfb", "/home/zuck/tfb");
        let check = |path, expected| {
            assert_eq!(relativizer.relativize(repo_path(path)), expected);
        };
        check(
            "foo/bar.txt",
            os_path(&["..", "..", "zuck", "tfb", "foo", "bar.txt"]),
        );
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

#[derive(PartialEq, Debug, Clone, Copy)]
pub enum StorageFormat {
    Revlog,
    RemoteFilelog,
    Eagerepo,
    Git,
}

impl StorageFormat {
    pub fn is_git(self) -> bool {
        self == Self::Git
    }

    pub fn is_eager(self) -> bool {
        self == Self::Eagerepo
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use std::io;
use std::io::Read;
use std::io::Write;

use crate::hash::AbstractHashType;
use crate::hash::HashTypeInfo;

/// A Sha256 hash.
pub type Sha256 = AbstractHashType<Sha256TypeInfo, 32>;

pub struct Sha256TypeInfo;

impl HashTypeInfo for Sha256TypeInfo {
    const HASH_TYPE_NAME: &'static str = "Sha256";
}

impl Sha256 {
    pub fn into_inner(self) -> [u8; Self::len()] {
        self.into_byte_array()
    }
}

impl<'a> From<&'a [u8; Sha256::len()]> for Sha256 {
    fn from(bytes: &[u8; Sha256::len()]) -> Sha256 {
        Sha256::from_byte_array(bytes.clone())
    }
}

pub trait WriteSha256Ext {
    /// Write a `Sha256` to a stream.
    fn write_sha256(&mut self, value: &Sha256) -> io::Result<()>;
}

impl<W: Write + ?Sized> WriteSha256Ext for W {
    fn write_sha256(&mut self, value: &Sha256) -> io::Result<()> {
        self.write_all(value.as_ref())
    }
}

pub trait ReadSha256Ext {
    /// Read a `Sha256` from a stream.
    fn read_sha256(&mut self) -> io::Result<Sha256>;
}

impl<R: Read + ?Sized> ReadSha256Ext for R {
    fn read_sha256(&mut self) -> io::Result<Sha256> {
        let mut bytes = [0u8; Sha256::len()];
        self.read_exact(&mut bytes)?;
        let sha256 = Sha256::from_byte_array(bytes);
        Ok(sha256)
    }
}

// Some users dependent on it.
pub use crate::hash::to_hex;

#[cfg(test)]
mod tests {
    use quickcheck::quickcheck;
    use serde::Deserialize;
    use serde::Serialize;

    use super::*;

    #[test]
    fn test_incorrect_length() {
        Sha256::from_slice(&[0u8; 25]).expect_err("bad slice length");
    }

    #[test]
    fn test_from_hex() {
        assert_eq!(
            Sha256::from_hex(b"abcd").unwrap_err().to_string(),
            "[97, 98, 99, 100] is not a 64-byte hex string"
        );
        assert_eq!(
            Sha256::from_hex(&[b'z'; 64]).unwrap_err().to_string(),
            "[122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122] is not a 64-byte hex string"
        );
        let hex = "434b1eeccd0fef2bad68f3c4f5dcbb2feb90b9465628a544cae3730ddf36310f";
        assert_eq!(Sha256::from_hex(hex.as_bytes()).unwrap().to_hex(), hex);
    }

    #[test]
    fn test_serde_with_using_cbor() {
        // Note: this test is for CBOR. Other serializers like mincode
        // or Thrift would have different backwards compatibility!
        use serde_cbor::de::from_slice as decode;
        use serde_cbor::ser::to_vec as encode;

        #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
        struct Orig(#[serde(with = "crate::serde_with::sha256::tuple")] Sha256);

        #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
        struct Bytes(#[serde(with = "crate::serde_with::sha256::bytes")] Sha256);

        #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
        struct Hex(#[serde(with = "crate::serde_with::sha256::hex")] Sha256);

        let id: Sha256 = Sha256::from_byte_array([0xcc; Sha256::len()]);
        let orig = Orig(id);
        let bytes = Bytes(id);
        let hex = Hex(id);

        let cbor_orig = encode(&orig).unwrap();
        let cbor_bytes = encode(&bytes).unwrap();
        let cbor_hex = encode(&hex).unwrap();

        assert_eq!(cbor_orig.len(), 66);
        assert_eq!(cbor_bytes.len(), 34);
        assert_eq!(cbor_hex.len(), 66);

        // Orig cannot decode bytes or hex.
        assert_eq!(decode::<Orig>(&cbor_orig).unwrap().0, id);
        decode::<Orig>(&cbor_bytes).unwrap_err();
        decode::<Orig>(&cbor_hex).unwrap_err();

        // Bytes can decode all 3 formats.
        assert_eq!(decode::<Bytes>(&cbor_orig).unwrap().0, id);
        assert_eq!(decode::<Bytes>(&cbor_bytes).unwrap().0, id);
        assert_eq!(decode::<Bytes>(&cbor_hex).unwrap().0, id);

        // Hex can decode all 3 formats.
        assert_eq!(decode::<Hex>(&cbor_orig).unwrap().0, id);
        assert_eq!(decode::<Hex>(&cbor_bytes).unwrap().0, id);
        assert_eq!(decode::<Hex>(&cbor_hex).unwrap().0, id);
    }
    quickcheck! {
        fn test_from_slice(sha256: Sha256) -> bool {
            sha256 == Sha256::from_slice(sha256.as_ref()).expect("from_slice")
        }

        fn test_from_str(sha256: Sha256) -> bool {
            let hex = sha256.to_hex();
            sha256 == hex.parse().expect("FromStr")
        }
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use std::collections::HashSet;
use std::str::FromStr;

use quickcheck::Arbitrary;
use quickcheck::Gen;

use crate::hgid::HgId;
use crate::key::Key;
use crate::path::PathComponent;
use crate::path::PathComponentBuf;
use crate::path::RepoPath;
use crate::path::RepoPathBuf;

pub fn repo_path(s: &str) -> &RepoPath {
    if s == "" {
        RepoPath::empty()
    } else {
        RepoPath::from_str(s).unwrap()
    }
}

pub fn repo_path_buf(s: &str) -> RepoPathBuf {
    if s == "" {
        RepoPathBuf::new()
    } else {
        RepoPathBuf::from_string(s.to_owned()).unwrap()
    }
}

pub fn path_component(s: &str) -> &PathComponent {
    PathComponent::from_str(s).unwrap()
}

pub fn path_component_buf(s: &str) -> PathComponentBuf {
    PathComponentBuf::from_string(s.to_owned()).unwrap()
}

pub fn hgid(hex: &str) -> HgId {
    if hex.len() > HgId::hex_len() {
        panic!("invalid length for hex hgid: {}", hex);
    }
    if hex == "0" {
        panic!("hgid 0 is special, use HgId::null_id() to build");
    }
    let mut buffer = String::new();
    for _i in 0..HgId::hex_len() - hex.len() {
        buffer.push('0');
    }
    buffer.push_str(hex);
    HgId::from_str(&buffer).unwrap()
}

pub fn key(path: &str, hexnode: &str) -> Key {
    Key::new(repo_path_buf(path), hgid(hexnode))
}

/// The null hgid id is special and it's semantics vary. A null key contains a null hgid id.
pub fn null_key(path: &str) -> Key {
    Key::new(repo_path_buf(path), HgId::null_id().clone())
}

pub fn generate_repo_paths(count: usize, qc_gen: &mut Gen) -> Vec<RepoPathBuf> {
    struct Generator<'a> {
        current_path: RepoPathBuf,
        current_component_length: usize,
        min_files_per_dir: usize,
        directory_component_min: usize,
        directory_component_max: usize,
        generated_paths: Vec<RepoPathBuf>,
        generate_paths_cnt: usize,
        qc_gen: &'a mut Gen,
    }
    impl<'a> Generator<'a> {
        fn generate_directory(&mut self) {
            let dir_components_cnt = if self.current_component_length == 0 {
                std::usize::MAX
            } else {
                self.directory_component_min
                    + usize::arbitrary(&mut self.qc_gen)
                        % (self.directory_component_max - self.directory_component_min)
            };
            let mut component_hash = HashSet::new();
            for i in 0..dir_components_cnt {
                if self.generate_paths_cnt <= self.generated_paths.len() {
                    break;
                }
                let component = PathComponentBuf::arbitrary(self.qc_gen);
                if component_hash.contains(&component) {
                    continue;
                }
                self.current_path.push(component.as_ref());
                component_hash.insert(component);
                self.current_component_length += 1;

                // Decide if this is a directory. As we nest more and more directories, the
                // probabilty of having directories decreses.
                let u = self.current_component_length as u32;
                if i < self.min_files_per_dir
                    || ((u64::arbitrary(self.qc_gen) % ((u + 2) as u64)) as u32) < u + 1
                {
                    self.generated_paths.push(self.current_path.clone());
                } else {
                    self.generate_directory();
                }
                self.current_path.pop();
                self.current_component_length -= 1;
            }
        }
    }

    let mut generator = Generator {
        current_path: RepoPathBuf::new(),
        current_component_length: 0,
        min_files_per_dir: 2,
        directory_component_min: 3,
        directory_component_max: 20,
        generated_paths: Vec::with_capacity(count),
        generate_paths_cnt: count,
        qc_gen,
    };

    generator.generate_directory();
    generator.generated_paths
}

#[cfg(test)]
mod tests {
    use quickcheck::Gen;

    use super::*;

    #[test]
    fn test_generate_repo_paths() {
        let mut qc_gen = Gen::new(10);
        let count = 10000;
        let paths = generate_repo_paths(count, &mut qc_gen);
        assert_eq!(paths.len(), count);
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use super::SerdeWith;
use crate::serde_with_mod;

macro_rules! key_type {
    ($typename:ident, $with:tt) => {
        #[derive(::serde::Serialize, ::serde::Deserialize)]
        pub struct $typename {
            pub path: $crate::path::RepoPathBuf,
            #[serde(alias = "node", rename = "node", with = $with)]
            pub hgid: $crate::HgId,
        }

        impl From<$crate::Key> for $typename {
            fn from(key: $crate::Key) -> Self {
                Self {
                    path: key.path,
                    hgid: key.hgid,
                }
            }
        }

        impl Into<$crate::Key> for $typename {
            fn into(self) -> $crate::Key {
                $crate::Key {
                    path: self.path,
                    hgid: self.hgid,
                }
            }
        }

        impl SerdeWith<$typename> for $crate::Key {
            fn serialize_with<S: ::serde::Serializer>(
                &self,
                serializer: S,
            ) -> Result<S::Ok, S::Error> {
                let value: $typename = self.clone().into();
                serde::Serialize::serialize(&value, serializer)
            }
            fn deserialize_with<'de, D: ::serde::Deserializer<'de>>(
                deserializer: D,
            ) -> Result<Self, D::Error> {
                let value: $typename = ::serde::Deserialize::deserialize(deserializer)?;
                Ok(value.into())
            }
        }

        impl SerdeWith<$typename> for Option<$crate::Key> {
            fn serialize_with<S: ::serde::Serializer>(
                &self,
                serializer: S,
            ) -> Result<S::Ok, S::Error> {
                let value: Option<$typename> = self.clone().map(Into::into);
                serde::Serialize::serialize(&value, serializer)
            }
            fn deserialize_with<'de, D: ::serde::Deserializer<'de>>(
                deserializer: D,
            ) -> Result<Self, D::Error> {
                let value: Option<$typename> = ::serde::Deserialize::deserialize(deserializer)?;
                let value = value.map(Into::into);
                Ok(value)
            }
        }

        impl SerdeWith<$typename> for [$crate::Key; 2] {
            fn serialize_with<S: ::serde::Serializer>(
                &self,
                serializer: S,
            ) -> Result<S::Ok, S::Error> {
                let [k1, k2] = self.clone();
                let value: [$typename; 2] = [k1.into(), k2.into()];
                serde::Serialize::serialize(&value, serializer)
            }
            fn deserialize_with<'de, D: ::serde::Deserializer<'de>>(
                deserializer: D,
            ) -> Result<Self, D::Error> {
                let value: [$typename; 2] = ::serde::Deserialize::deserialize(deserializer)?;
                let [k1, k2] = value;
                let value = [k1.into(), k2.into()];
                Ok(value)
            }
        }
    };
}

key_type!(BytesKey, "crate::serde_with::hgid::bytes");
key_type!(HexKey, "crate::serde_with::hgid::hex");
key_type!(TupleKey, "crate::serde_with::hgid::tuple");

serde_with_mod!(bytes, super::BytesKey);
serde_with_mod!(hex, super::HexKey);
serde_with_mod!(tuple, super::TupleKey);


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

pub mod hgid;
pub mod key;
pub mod nodeinfo;
pub mod sha256;

/// Use `SerdeType` as the type talking to serde to serialize or deserialize
/// `Self`. Practically, this makes the `serde_with` functions work for both
/// `Option<Key>` and `Key`.
pub trait SerdeWith<SerdeType> {
    fn serialize_with<S: ::serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>;
    fn deserialize_with<'de, D: ::serde::Deserializer<'de>>(
        deserializer: D,
    ) -> Result<Self, D::Error>
    where
        Self: Sized;
}

#[macro_export]
macro_rules! serde_with_mod {
    ($modname:ident, $serdetype:ty) => {
        pub mod $modname {
            use ::serde::Deserializer;
            use ::serde::Serializer;
            type SerdeType = $serdetype;

            pub fn serialize<S, T>(key: &T, serializer: S) -> Result<S::Ok, S::Error>
            where
                S: Serializer,
                T: super::SerdeWith<SerdeType>,
            {
                key.serialize_with(serializer)
            }

            pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
            where
                D: Deserializer<'de>,
                T: super::SerdeWith<SerdeType>,
            {
                T::deserialize_with(deserializer)
            }
        }
    };
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use crate::serde_with::SerdeWith;
use crate::serde_with_mod;

macro_rules! nodeinfo_type {
    ($typename:ident, $with_key:tt, $with_hgid:tt) => {
        #[derive(::serde::Serialize, ::serde::Deserialize)]
        pub struct $typename {
            #[serde(with = $with_key)]
            pub parents: [$crate::Key; 2],
            #[serde(with = $with_hgid)]
            pub linknode: $crate::HgId,
        }

        impl From<$crate::NodeInfo> for $typename {
            fn from(this: $crate::NodeInfo) -> Self {
                Self {
                    parents: this.parents,
                    linknode: this.linknode,
                }
            }
        }

        impl Into<$crate::NodeInfo> for $typename {
            fn into(self) -> $crate::NodeInfo {
                $crate::NodeInfo {
                    parents: self.parents,
                    linknode: self.linknode,
                }
            }
        }

        impl SerdeWith<$typename> for $crate::NodeInfo {
            fn serialize_with<S: ::serde::Serializer>(
                &self,
                serializer: S,
            ) -> Result<S::Ok, S::Error> {
                let value: $typename = self.clone().into();
                serde::Serialize::serialize(&value, serializer)
            }
            fn deserialize_with<'de, D: ::serde::Deserializer<'de>>(
                deserializer: D,
            ) -> Result<Self, D::Error> {
                let value: $typename = ::serde::Deserialize::deserialize(deserializer)?;
                Ok(value.into())
            }
        }
    };
}

nodeinfo_type!(
    BytesNodeInfo,
    "crate::serde_with::key::bytes",
    "crate::serde_with::hgid::bytes"
);
nodeinfo_type!(
    HexNodeInfo,
    "crate::serde_with::key::hex",
    "crate::serde_with::hgid::hex"
);
nodeinfo_type!(
    TupleNodeInfo,
    "crate::serde_with::key::tuple",
    "crate::serde_with::hgid::tuple"
);

serde_with_mod!(bytes, super::BytesNodeInfo);
serde_with_mod!(hex, super::HexNodeInfo);
serde_with_mod!(tuple, super::TupleNodeInfo);


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use serde::de::Error;
use serde::Deserializer;
use serde::Serializer;

use crate::HgId;

/// Serde `serialize_with` function to serialize `HgId` as bytes.
pub fn serialize<S>(id: &HgId, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    serializer.serialize_bytes(id.as_ref())
}

/// Serde `deserialize_with` function to deserialize `HgId` as list of u8s,
/// or bytes, or a hex string.
pub fn deserialize<'de, D>(deserializer: D) -> Result<HgId, D::Error>
where
    D: Deserializer<'de>,
{
    // ByteBuf supports both list and bytes.
    let bytes: serde_bytes::ByteBuf = serde_bytes::deserialize(deserializer)?;
    let bytes = bytes.as_ref();
    // Compatible with hex.
    if bytes.len() == HgId::hex_len() {
        HgId::from_hex(bytes).map_err(|e| {
            let msg = format!("invalid HgId: {} ({:?})", e, bytes);
            D::Error::custom(msg)
        })
    } else {
        HgId::from_slice(bytes).map_err(|e| {
            let msg = format!("invalid HgId: {} ({:?})", e, bytes);
            D::Error::custom(msg)
        })
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use serde::Serializer;

use crate::HgId;

/// Serde `serialize_with` function to serialize `HgId` as hex string.
pub fn serialize<S>(id: &HgId, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    let hex = id.to_hex();
    serializer.serialize_str(&hex)
}

// bytes::deserialize can decode hex.
pub use super::bytes::deserialize;


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

pub mod bytes;
pub mod hex;
pub mod tuple;


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use serde::ser::SerializeTuple;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serializer;

use crate::HgId;

/// Serialize `HgId` as a tuple of 20 `u8`s.
pub fn serialize<S>(id: &HgId, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    let bytes: &[u8] = id.as_ref();
    let mut tuple = serializer.serialize_tuple(bytes.len())?;
    for i in id.as_ref() {
        tuple.serialize_element(&i)?;
    }
    tuple.end()
}

/// Deserialize `HgId` as a tuple of 20 `u8`s.
pub fn deserialize<'de, D>(deserializer: D) -> Result<HgId, D::Error>
where
    D: Deserializer<'de>,
{
    Deserialize::deserialize(deserializer).map(HgId::from_byte_array)
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use serde::de::Error;
use serde::Deserializer;
use serde::Serializer;

use crate::sha::Sha256;

/// Serde `serialize_with` function to serialize `Sha256` as bytes.
pub fn serialize<S>(id: &Sha256, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    serializer.serialize_bytes(id.as_ref())
}

/// Serde `deserialize_with` function to deserialize `Sha256` as list of u8s,
/// or bytes, or a hex string.
pub fn deserialize<'de, D>(deserializer: D) -> Result<Sha256, D::Error>
where
    D: Deserializer<'de>,
{
    // ByteBuf supports both list and bytes.
    let bytes: serde_bytes::ByteBuf = serde_bytes::deserialize(deserializer)?;
    let bytes = bytes.as_ref();
    // Compatible with hex.
    if bytes.len() == Sha256::hex_len() {
        Sha256::from_hex(bytes).map_err(|e| {
            let msg = format!("invalid Sha256: {} ({:?})", e, bytes);
            D::Error::custom(msg)
        })
    } else {
        Sha256::from_slice(bytes).map_err(|e| {
            let msg = format!("invalid Sha256: {} ({:?})", e, bytes);
            D::Error::custom(msg)
        })
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use serde::Serializer;

use crate::Sha256;

/// Serde `serialize_with` function to serialize `Sha256` as hex string.
pub fn serialize<S>(id: &Sha256, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    let hex = id.to_hex();
    serializer.serialize_str(&hex)
}

// bytes::deserialize can decode hex.
pub use super::bytes::deserialize;


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use serde::ser::SerializeTuple;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serializer;

use crate::Sha256;

/// Serialize `Sha256` as a tuple of 32 `u8`s.
pub fn serialize<S>(id: &Sha256, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    let bytes: &[u8] = id.as_ref();
    let mut tuple = serializer.serialize_tuple(bytes.len())?;
    for i in id.as_ref() {
        tuple.serialize_element(&i)?;
    }
    tuple.end()
}

/// Deserialize `Sha256` as a tuple of 32 `u8`s.
pub fn deserialize<'de, D>(deserializer: D) -> Result<Sha256, D::Error>
where
    D: Deserializer<'de>,
{
    Deserialize::deserialize(deserializer).map(Sha256::from_byte_array)
}