// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.

use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
use core::ops::Add;

use digest::core_api::BlockSizeUser;
use digest::OutputSizeUser;
use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, U256};
use generic_array::ArrayLength;
use serde_json::Value;

use crate::tests::mock_rng::CycleRng;
use crate::tests::parser::*;
use crate::{
    BlindedElement, CipherSuite, EvaluationElement, Group, OprfClient, OprfServer, PoprfClient,
    PoprfServer, PoprfServerBatchEvaluateFinishResult, PoprfServerBatchEvaluatePrepareResult,
    Proof, Result, VoprfClient, VoprfServer, VoprfServerBatchEvaluateFinishResult,
};

#[derive(Debug)]
struct VOPRFTestVectorParameters {
    seed: Vec<u8>,
    sksm: Vec<u8>,
    pksm: Vec<u8>,
    input: Vec<Vec<u8>>,
    info: Vec<u8>,
    key_info: Vec<u8>,
    blind: Vec<Vec<u8>>,
    blinded_element: Vec<Vec<u8>>,
    evaluation_element: Vec<Vec<u8>>,
    proof: Vec<u8>,
    proof_random_scalar: Vec<u8>,
    output: Vec<Vec<u8>>,
}

fn populate_test_vectors(values: &Value) -> VOPRFTestVectorParameters {
    VOPRFTestVectorParameters {
        seed: decode(values, "Seed"),
        sksm: decode(values, "skSm"),
        pksm: decode(values, "pkSm"),
        input: decode_vec(values, "Input"),
        info: decode(values, "Info"),
        key_info: decode(values, "KeyInfo"),
        blind: decode_vec(values, "Blind"),
        blinded_element: decode_vec(values, "BlindedElement"),
        evaluation_element: decode_vec(values, "EvaluationElement"),
        proof: decode(values, "Proof"),
        proof_random_scalar: decode(values, "ProofRandomScalar"),
        output: decode_vec(values, "Output"),
    }
}

fn decode(values: &Value, key: &str) -> Vec<u8> {
    values[key]
        .as_str()
        .and_then(|s| hex::decode(s).ok())
        .unwrap_or_default()
}

fn decode_vec(values: &Value, key: &str) -> Vec<Vec<u8>> {
    let s = values[key].as_str().unwrap();
    let res = match s.contains(',') {
        true => Some(s.split(',').map(|x| hex::decode(x).unwrap()).collect()),
        false => Some(vec![hex::decode(s).unwrap()]),
    };
    res.unwrap()
}

macro_rules! json_to_test_vectors {
    ( $v:ident, $cs:expr, $mode:expr ) => {
        $v[$cs][$mode]
            .as_array()
            .into_iter()
            .flatten()
            .map(populate_test_vectors)
            .collect::<Vec<VOPRFTestVectorParameters>>()
    };
}

#[test]
fn test_vectors() -> Result<()> {
    use p256::NistP256;
    use p384::NistP384;

    let rfc: Value = serde_json::from_str(rfc_to_json(super::cfrg_vectors::VECTORS).as_str())
        .expect("Could not parse json");

    #[cfg(feature = "ristretto255")]
    {
        use crate::Ristretto255;

        let ristretto_oprf_tvs = json_to_test_vectors!(
            rfc,
            String::from("ristretto255-SHA512"),
            String::from("OPRF")
        );
        assert_ne!(ristretto_oprf_tvs.len(), 0);
        test_oprf_seed_to_key::<Ristretto255>(&ristretto_oprf_tvs)?;
        test_oprf_blind::<Ristretto255>(&ristretto_oprf_tvs)?;
        test_oprf_blind_evaluate::<Ristretto255>(&ristretto_oprf_tvs)?;
        test_oprf_finalize::<Ristretto255>(&ristretto_oprf_tvs)?;
        test_oprf_evaluate::<Ristretto255>(&ristretto_oprf_tvs)?;

        let ristretto_voprf_tvs = json_to_test_vectors!(
            rfc,
            String::from("ristretto255-SHA512"),
            String::from("VOPRF")
        );
        assert_ne!(ristretto_voprf_tvs.len(), 0);
        test_voprf_seed_to_key::<Ristretto255>(&ristretto_voprf_tvs)?;
        test_voprf_blind::<Ristretto255>(&ristretto_voprf_tvs)?;
        test_voprf_blind_evaluate::<Ristretto255>(&ristretto_voprf_tvs)?;
        test_voprf_finalize::<Ristretto255>(&ristretto_voprf_tvs)?;
        test_voprf_evaluate::<Ristretto255>(&ristretto_voprf_tvs)?;

        let ristretto_poprf_tvs = json_to_test_vectors!(
            rfc,
            String::from("ristretto255-SHA512"),
            String::from("POPRF")
        );
        assert_ne!(ristretto_poprf_tvs.len(), 0);
        test_poprf_seed_to_key::<Ristretto255>(&ristretto_poprf_tvs)?;
        test_poprf_blind::<Ristretto255>(&ristretto_poprf_tvs)?;
        test_poprf_blind_evaluate::<Ristretto255>(&ristretto_poprf_tvs)?;
        test_poprf_finalize::<Ristretto255>(&ristretto_poprf_tvs)?;
        test_poprf_evaluate::<Ristretto255>(&ristretto_poprf_tvs)?;
    }

    let p256_oprf_tvs =
        json_to_test_vectors!(rfc, String::from("P256-SHA256"), String::from("OPRF"));
    assert_ne!(p256_oprf_tvs.len(), 0);
    test_oprf_seed_to_key::<NistP256>(&p256_oprf_tvs)?;
    test_oprf_blind::<NistP256>(&p256_oprf_tvs)?;
    test_oprf_blind_evaluate::<NistP256>(&p256_oprf_tvs)?;
    test_oprf_finalize::<NistP256>(&p256_oprf_tvs)?;
    test_oprf_evaluate::<NistP256>(&p256_oprf_tvs)?;

    let p256_voprf_tvs =
        json_to_test_vectors!(rfc, String::from("P256-SHA256"), String::from("VOPRF"));
    assert_ne!(p256_voprf_tvs.len(), 0);
    test_voprf_seed_to_key::<NistP256>(&p256_voprf_tvs)?;
    test_voprf_blind::<NistP256>(&p256_voprf_tvs)?;
    test_voprf_blind_evaluate::<NistP256>(&p256_voprf_tvs)?;
    test_voprf_finalize::<NistP256>(&p256_voprf_tvs)?;
    test_voprf_evaluate::<NistP256>(&p256_voprf_tvs)?;

    let p256_poprf_tvs =
        json_to_test_vectors!(rfc, String::from("P256-SHA256"), String::from("POPRF"));
    assert_ne!(p256_poprf_tvs.len(), 0);
    test_poprf_seed_to_key::<NistP256>(&p256_poprf_tvs)?;
    test_poprf_blind::<NistP256>(&p256_poprf_tvs)?;
    test_poprf_blind_evaluate::<NistP256>(&p256_poprf_tvs)?;
    test_poprf_finalize::<NistP256>(&p256_poprf_tvs)?;
    test_poprf_evaluate::<NistP256>(&p256_poprf_tvs)?;

    let p384_oprf_tvs =
        json_to_test_vectors!(rfc, String::from("P384-SHA384"), String::from("OPRF"));
    assert_ne!(p384_oprf_tvs.len(), 0);
    test_oprf_seed_to_key::<NistP384>(&p384_oprf_tvs)?;
    test_oprf_blind::<NistP384>(&p384_oprf_tvs)?;
    test_oprf_blind_evaluate::<NistP384>(&p384_oprf_tvs)?;
    test_oprf_finalize::<NistP384>(&p384_oprf_tvs)?;
    test_oprf_evaluate::<NistP384>(&p384_oprf_tvs)?;

    let p384_voprf_tvs =
        json_to_test_vectors!(rfc, String::from("P384-SHA384"), String::from("VOPRF"));
    assert_ne!(p384_voprf_tvs.len(), 0);
    test_voprf_seed_to_key::<NistP384>(&p384_voprf_tvs)?;
    test_voprf_blind::<NistP384>(&p384_voprf_tvs)?;
    test_voprf_blind_evaluate::<NistP384>(&p384_voprf_tvs)?;
    test_voprf_finalize::<NistP384>(&p384_voprf_tvs)?;
    test_voprf_evaluate::<NistP384>(&p384_voprf_tvs)?;

    let p384_poprf_tvs =
        json_to_test_vectors!(rfc, String::from("P384-SHA384"), String::from("POPRF"));
    assert_ne!(p384_poprf_tvs.len(), 0);
    test_poprf_seed_to_key::<NistP384>(&p384_poprf_tvs)?;
    test_poprf_blind::<NistP384>(&p384_poprf_tvs)?;
    test_poprf_blind_evaluate::<NistP384>(&p384_poprf_tvs)?;
    test_poprf_finalize::<NistP384>(&p384_poprf_tvs)?;
    test_poprf_evaluate::<NistP384>(&p384_poprf_tvs)?;

    Ok(())
}

fn test_oprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
    <CS::Hash as OutputSizeUser>::OutputSize:
        IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
    for parameters in tvs {
        let server = OprfServer::<CS>::new_from_seed(&parameters.seed, &parameters.key_info)?;

        assert_eq!(
            &parameters.sksm,
            &CS::Group::serialize_scalar(server.get_private_key()).to_vec()
        );
    }
    Ok(())
}

fn test_voprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
    <CS::Hash as OutputSizeUser>::OutputSize:
        IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
    for parameters in tvs {
        let server = VoprfServer::<CS>::new_from_seed(&parameters.seed, &parameters.key_info)?;

        assert_eq!(
            &parameters.sksm,
            &CS::Group::serialize_scalar(server.get_private_key()).to_vec()
        );
        assert_eq!(
            &parameters.pksm,
            CS::Group::serialize_elem(server.get_public_key()).as_slice()
        );
    }
    Ok(())
}

fn test_poprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
    <CS::Hash as OutputSizeUser>::OutputSize:
        IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
    for parameters in tvs {
        let server = PoprfServer::<CS>::new_from_seed(&parameters.seed, &parameters.key_info)?;

        assert_eq!(
            &parameters.sksm,
            &CS::Group::serialize_scalar(server.get_private_key()).to_vec()
        );
        assert_eq!(
            &parameters.pksm,
            CS::Group::serialize_elem(server.get_public_key()).as_slice()
        );
    }
    Ok(())
}

// Tests input -> blind, blinded_element
fn test_oprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
    <CS::Hash as OutputSizeUser>::OutputSize:
        IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
    for parameters in tvs {
        for i in 0..parameters.input.len() {
            let blind = CS::Group::deserialize_scalar(&parameters.blind[i])?;
            let client_result =
                OprfClient::<CS>::deterministic_blind_unchecked(&parameters.input[i], blind)?;

            assert_eq!(
                &parameters.blind[i],
                &CS::Group::serialize_scalar(client_result.state.blind).to_vec()
            );
            assert_eq!(
                parameters.blinded_element[i].as_slice(),
                client_result.message.serialize().as_slice(),
            );
        }
    }
    Ok(())
}

// Tests input -> blind, blinded_element
fn test_voprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
    <CS::Hash as OutputSizeUser>::OutputSize:
        IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
    for parameters in tvs {
        for i in 0..parameters.input.len() {
            let blind = CS::Group::deserialize_scalar(&parameters.blind[i])?;
            let client_blind_result =
                VoprfClient::<CS>::deterministic_blind_unchecked(&parameters.input[i], blind)?;

            assert_eq!(
                &parameters.blind[i],
                &CS::Group::serialize_scalar(client_blind_result.state.get_blind()).to_vec()
            );
            assert_eq!(
                parameters.blinded_element[i].as_slice(),
                client_blind_result.message.serialize().as_slice(),
            );
        }
    }
    Ok(())
}

// Tests input -> blind, blinded_element
fn test_poprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
    <CS::Hash as OutputSizeUser>::OutputSize:
        IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
    for parameters in tvs {
        for i in 0..parameters.input.len() {
            let blind = CS::Group::deserialize_scalar(&parameters.blind[i])?;
            let client_blind_result =
                PoprfClient::<CS>::deterministic_blind_unchecked(&parameters.input[i], blind)?;

            assert_eq!(
                &parameters.blind[i],
                &CS::Group::serialize_scalar(client_blind_result.state.get_blind()).to_vec()
            );
            assert_eq!(
                parameters.blinded_element[i].as_slice(),
                client_blind_result.message.serialize().as_slice(),
            );
        }
    }
    Ok(())
}

// Tests sksm, blinded_element -> evaluation_element
fn test_oprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
    <CS::Hash as OutputSizeUser>::OutputSize:
        IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
    for parameters in tvs {
        for i in 0..parameters.input.len() {
            let server = OprfServer::<CS>::new_with_key(&parameters.sksm)?;
            let message = server.blind_evaluate(&BlindedElement::deserialize(
                &parameters.blinded_element[i],
            )?);

            assert_eq!(
                &parameters.evaluation_element[i],
                &message.serialize().as_slice()
            );
        }
    }
    Ok(())
}

fn test_voprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
    <CS::Hash as OutputSizeUser>::OutputSize:
        IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
    <CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ScalarLen>,
    Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ScalarLen>: ArrayLength<u8>,
{
    for parameters in tvs {
        let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
        let server = VoprfServer::<CS>::new_with_key(&parameters.sksm)?;

        let mut blinded_elements = vec![];
        for blinded_element_bytes in &parameters.blinded_element {
            blinded_elements.push(BlindedElement::deserialize(blinded_element_bytes)?);
        }

        let prepared_evaluation_elements =
            server.batch_blind_evaluate_prepare(blinded_elements.iter());
        let prepared_elements: Vec<_> = prepared_evaluation_elements.collect();
        let VoprfServerBatchEvaluateFinishResult { messages, proof } = server
            .batch_blind_evaluate_finish(&mut rng, blinded_elements.iter(), &prepared_elements)?;
        let messages: Vec<_> = messages.collect();

        for (parameter, message) in parameters.evaluation_element.iter().zip(messages) {
            assert_eq!(&parameter, &message.serialize().as_slice());
        }

        assert_eq!(&parameters.proof, &proof.serialize().as_slice());
    }
    Ok(())
}

fn test_poprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
    <CS::Hash as OutputSizeUser>::OutputSize:
        IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
    <CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ScalarLen>,
    Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ScalarLen>: ArrayLength<u8>,
{
    for parameters in tvs {
        let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
        let server = PoprfServer::<CS>::new_with_key(&parameters.sksm)?;

        let mut blinded_elements = vec![];
        for blinded_element_bytes in &parameters.blinded_element {
            blinded_elements.push(BlindedElement::deserialize(blinded_element_bytes)?);
        }

        let PoprfServerBatchEvaluatePrepareResult {
            prepared_evaluation_elements,
            prepared_tweak,
        } = server.batch_blind_evaluate_prepare(blinded_elements.iter(), Some(&parameters.info))?;
        let prepared_evaluation_elements: Vec<_> = prepared_evaluation_elements.collect();
        let PoprfServerBatchEvaluateFinishResult { messages, proof } =
            PoprfServer::batch_blind_evaluate_finish::<_, _, Vec<_>>(
                &mut rng,
                blinded_elements.iter(),
                &prepared_evaluation_elements,
                &prepared_tweak,
            )
            .unwrap();

        let messages: Vec<_> = messages.collect();

        for (parameter, message) in parameters.evaluation_element.iter().zip(messages) {
            assert_eq!(&parameter, &message.serialize().as_slice());
        }

        assert_eq!(&parameters.proof, &proof.serialize().as_slice());
    }
    Ok(())
}

// Tests input, blind, evaluation_element -> output
fn test_oprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
    <CS::Hash as OutputSizeUser>::OutputSize:
        IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
    for parameters in tvs {
        for i in 0..parameters.input.len() {
            let client =
                OprfClient::<CS>::from_blind(CS::Group::deserialize_scalar(&parameters.blind[i])?);

            let client_finalize_result = client.finalize(
                &parameters.input[i],
                &EvaluationElement::deserialize(&parameters.evaluation_element[i])?,
            )?;

            assert_eq!(&parameters.output[i], &client_finalize_result.to_vec());
        }
    }
    Ok(())
}

fn test_voprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
    <CS::Hash as OutputSizeUser>::OutputSize:
        IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
    for parameters in tvs {
        let mut clients = vec![];
        for i in 0..parameters.input.len() {
            let client = VoprfClient::<CS>::from_blind_and_element(
                CS::Group::deserialize_scalar(&parameters.blind[i])?,
                CS::Group::deserialize_elem(&parameters.blinded_element[i])?,
            );
            clients.push(client.clone());
        }

        let messages: Vec<_> = parameters
            .evaluation_element
            .iter()
            .map(|x| EvaluationElement::deserialize(x).unwrap())
            .collect();

        let batch_result = VoprfClient::batch_finalize(
            &parameters.input,
            &clients,
            &messages,
            &Proof::deserialize(&parameters.proof)?,
            CS::Group::deserialize_elem(&parameters.pksm)?,
        )?;

        assert_eq!(
            parameters.output,
            batch_result
                .map(|arr| arr.map(|message| message.to_vec()))
                .collect::<Result<Vec<_>>>()?
        );
    }
    Ok(())
}

fn test_poprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
    <CS::Hash as OutputSizeUser>::OutputSize:
        IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
    for parameters in tvs {
        let mut clients = vec![];
        for i in 0..parameters.input.len() {
            let blind = CS::Group::deserialize_scalar(&parameters.blind[i])?;
            let client_blind_result =
                PoprfClient::<CS>::deterministic_blind_unchecked(&parameters.input[i], blind)?;
            let client = client_blind_result.state;
            clients.push(client.clone());
        }

        let messages: Vec<_> = parameters
            .evaluation_element
            .iter()
            .map(|x| EvaluationElement::deserialize(x).unwrap())
            .collect();

        let batch_result = PoprfClient::batch_finalize(
            parameters.input.iter().map(|input| input.as_slice()),
            &clients,
            &messages,
            &Proof::deserialize(&parameters.proof)?,
            CS::Group::deserialize_elem(&parameters.pksm)?,
            Some(&parameters.info),
        )?;

        let result: Vec<Vec<u8>> = batch_result.map(|arr| arr.unwrap().to_vec()).collect();

        assert_eq!(parameters.output, result);
    }
    Ok(())
}

// Tests input, sksm -> output
fn test_oprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
    <CS::Hash as OutputSizeUser>::OutputSize:
        IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
    for parameters in tvs {
        for i in 0..parameters.input.len() {
            let server = OprfServer::<CS>::new_with_key(&parameters.sksm)?;

            let server_evaluate_result = server.evaluate(&parameters.input[i])?;

            assert_eq!(&parameters.output[i], &server_evaluate_result.to_vec());
        }
    }
    Ok(())
}

fn test_voprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
    <CS::Hash as OutputSizeUser>::OutputSize:
        IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
    for parameters in tvs {
        for i in 0..parameters.input.len() {
            let server = VoprfServer::<CS>::new_with_key(&parameters.sksm)?;

            let server_evaluate_result = server.evaluate(&parameters.input[i])?;

            assert_eq!(&parameters.output[i], &server_evaluate_result.to_vec());
        }
    }
    Ok(())
}

fn test_poprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
    <CS::Hash as OutputSizeUser>::OutputSize:
        IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
    for parameters in tvs {
        for i in 0..parameters.input.len() {
            let server = PoprfServer::<CS>::new_with_key(&parameters.sksm)?;

            let server_evaluate_result =
                server.evaluate(&parameters.input[i], Some(&parameters.info))?;

            assert_eq!(&parameters.output[i], &server_evaluate_result.to_vec());
        }
    }
    Ok(())
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::io::ErrorKind;
use std::io::Write;
#[cfg(unix)]
use std::os::unix::fs::MetadataExt;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
#[cfg(windows)]
use std::os::windows::fs::MetadataExt;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use std::time::Instant;

use ahash::AHashMap;
use jwalk::WalkDir;
use serde::Deserialize;
use structopt::StructOpt;
use watchman_client::prelude::*;

#[derive(StructOpt, Debug)]
#[structopt(about = "Audit Watchman's in-memory database with the filesystem")]
pub(crate) struct AuditCmd {
    #[structopt(name = "path", parse(from_os_str))]
    path: PathBuf,

    #[structopt(
        long = "settle_period",
        about = "milliseconds to wait for filesystem change notifications to settle"
    )]
    settle_period_ms: Option<u64>,

    #[structopt(
        long = "settle_timeout",
        about = "fail query if settle_timeout milliseconds elapses before settle_period is reached"
    )]
    settle_timeout_ms: Option<u64>,

    #[structopt(
        long = "sync_timeout",
        about = "seconds to wait for Watchman query result",
        default_value = "120"
    )]
    sync_timeout_secs: u64,
}

query_result_type! {
    struct AuditQueryResult {
        name: NameField,
        mode: ModeAndPermissionsField,
        size: SizeField,
        mtime: MTimeField,
        oclock: ObservedClockField,
        ino: InodeNumberField,
    }
}

// Skip cookies when diffing.
fn is_cookie<T: AsRef<Path>>(name: T) -> bool {
    return name
        .as_ref()
        .file_name()
        .and_then(|s| s.to_str())
        .map_or(false, |s| s.starts_with(".watchman-cookie-"));
}

#[cfg(windows)]
/// Windows epoch starts at 1601-01-01 00:00:00 UTC. This function converts
/// Windows epoch time to Unix epoch time. 0x019DB1DED53E8000 is Windows epoch
/// fo 1970-01-01 00:00:00 UTC.
fn from_windows_epoch(epoch: i64) -> i64 {
    (epoch - 0x019DB1DED53E8000) / 10_000_000
}

#[derive(Debug)]
pub struct AuditOption {
    pub settle_period_ms: Option<u64>,
    pub settle_timeout_ms: Option<u64>,
    pub sync_timeout_secs: u64,
    pub silent: bool,
}

impl Default for AuditOption {
    fn default() -> Self {
        AuditOption {
            settle_period_ms: None,
            settle_timeout_ms: None,
            sync_timeout_secs: 120,
            silent: false,
        }
    }
}

pub async fn audit_repo(
    out: &mut (dyn Write + Send + Sync),
    client: &Client,
    repo: &Path,
    option: AuditOption,
) -> anyhow::Result<()> {
    let resolved = Arc::new(
        client
            .resolve_root(CanonicalPath::canonicalize(repo)?)
            .await?,
    );

    if resolved.watcher() == "eden" {
        return Err(anyhow::anyhow!(
            "{} is an EdenFS mount - no need to audit",
            resolved.project_root().display()
        ));
    }

    writeln!(
        out,
        "Sanity checking the filesystem at {} against watchman; this may take a couple of minutes.",
        repo.display(),
    )?;

    let config = client.get_config(&resolved).await?;
    let mut ignore_dirs = config.ignore_dirs.unwrap_or_default().clone();

    // TODO: This list is duplicated in the Watchman query below.
    ignore_dirs.push(".hg".into());
    ignore_dirs.push(".git".into());
    ignore_dirs.push(".svn".into());

    let filesystem_state_handle = {
        let resolved = resolved.clone();
        let silent = option.silent;
        tokio::spawn(async move {
            let mut filesystem_state: AHashMap<PathBuf, std::fs::Metadata> = AHashMap::new();

            let start_crawl = Instant::now();

            // Allocate outside the loop to save time.
            let resolved_path = Arc::new(resolved.path());

            let resolved_path_copy = resolved_path.clone();
            let walk_dir = WalkDir::new(&*resolved_path)
                .skip_hidden(false)
                .process_read_dir(move |_depth, path, _read_dir_state, children| {
                    let resolved_path: &Path = resolved_path_copy.as_ref();
                    let from_root = match path.strip_prefix(resolved_path) {
                        Ok(from_root) => from_root,
                        Err(_) => {
                            return;
                        }
                    };

                    if from_root == Path::new("") {
                        children.retain(|child| match child {
                            Ok(child) => {
                                ignore_dirs.iter().all(|i| i.as_os_str() != child.file_name)
                            }
                            Err(_) => true,
                        });
                    }
                });

            for entry in walk_dir {
                let entry = match entry {
                    Ok(entry) => entry,
                    Err(err) => {
                        if !silent {
                            eprintln!("error while traversing directory: {}", err);
                        }
                        continue;
                    }
                };

                let entry_path = entry.path();
                let relpath = match entry_path.strip_prefix(&*resolved_path) {
                    Ok(relpath) => relpath,
                    Err(err) => {
                        if !silent {
                            eprintln!(
                                "unable to form relative path from {} to {}: {}",
                                resolved_path.display(),
                                entry_path.display(),
                                err
                            );
                        }
                        continue;
                    }
                };

                let metadata = match entry.metadata() {
                    Ok(metadata) => metadata,
                    Err(err) => {
                        if err.io_error().map(|e| e.kind() == ErrorKind::NotFound) != Some(true) {
                            if !silent {
                                eprintln!(
                                    "error fetching metadata for {}: {}",
                                    entry_path.display(),
                                    err
                                );
                            }
                        }
                        continue;
                    }
                };
                filesystem_state.insert(relpath.to_path_buf(), metadata);
            }
            // Watchman doesn't return information about the root, so remove it here.
            filesystem_state.remove(&PathBuf::new());
            if !silent {
                eprintln!("Crawled filesystem in {:?}", start_crawl.elapsed());
            }

            filesystem_state
        })
    };

    let start_query = Instant::now();

    // Do not ignore fresh instance results: the goal is to validate the
    // correctness of query results against the filesystem state, no
    // matter what happened in the crawler.

    use Expr::*;
    let result = client
        .query::<AuditQueryResult>(
            &resolved,
            QueryRequestCommon {
                expression: Some(All(vec![
                    Exists,
                    Not(Box::new(Any(vec![
                        DirName(DirNameTerm {
                            path: ".git".into(),
                            depth: None,
                        }),
                        DirName(DirNameTerm {
                            path: ".hg".into(),
                            depth: None,
                        }),
                        DirName(DirNameTerm {
                            path: ".svn".into(),
                            depth: None,
                        }),
                        Name(NameTerm {
                            paths: vec![".git".into()],
                            wholename: true,
                        }),
                        Name(NameTerm {
                            paths: vec![".svn".into()],
                            wholename: true,
                        }),
                        Name(NameTerm {
                            paths: vec![".hg".into()],
                            wholename: true,
                        }),
                    ]))),
                ])),
                settle_period: option
                    .settle_period_ms
                    .map(Duration::from_millis)
                    .map(SettleDurationMs),
                settle_timeout: option
                    .settle_timeout_ms
                    .map(Duration::from_millis)
                    .map(SettleDurationMs),
                sync_timeout: SyncTimeout::Duration(Duration::new(option.sync_timeout_secs, 0)),

                ..Default::default()
            },
        )
        .await;

    let result = match result {
        Ok(result) => result,
        Err(err) => {
            writeln!(out, "Error during Watchman query: {}", err)?;
            // Use a different error code for Watchman query errors, including timeouts, so they can be differentiated by audit logging.
            std::process::exit(2);
        }
    };

    writeln!(
        out,
        "Queried Watchman in {:?} (is_fresh_instance = {}, clock = {:?}, version = {})",
        start_query.elapsed(),
        result.is_fresh_instance,
        result.clock,
        result.version
    )?;
    if let Some(debug) = result.debug {
        if let Some(cookie_files) = debug.cookie_files {
            writeln!(out, "    cookie files: {:?}", cookie_files)?;
        }
    }

    let filesystem_state = filesystem_state_handle.await.unwrap();

    let diff_start = Instant::now();

    let watchman_files = match result.files {
        Some(files) => files,
        None => {
            return Err(anyhow::anyhow!("No files set in result"));
        }
    };

    let mut any_differences = false;
    let mut phantoms = vec![];
    let mut missing = vec![];

    let mut watchman_state: AHashMap<&Path, &AuditQueryResult> =
        AHashMap::with_capacity(watchman_files.len());
    for watchman_file in &watchman_files {
        let filename = &*watchman_file.name;
        // Skip cookies when diffing.
        if is_cookie(filename) {
            continue;
        }
        watchman_state.insert(filename, watchman_file);

        let metadata = match filesystem_state.get(filename) {
            Some(metadata) => metadata,
            None => {
                phantoms.push(watchman_file);
                continue;
            }
        };

        let mut diffs = Vec::new();

        #[cfg(unix)]
        {
            if *watchman_file.mode != u64::from(metadata.permissions().mode()) {
                diffs.push(format!(
                    "watchman mode is {} vs. fs {}",
                    *watchman_file.mode,
                    metadata.permissions().mode()
                ));
            }

            if metadata.is_file() && *watchman_file.size != metadata.size() {
                diffs.push(format!(
                    "watchman size is {} vs. fs {}",
                    *watchman_file.size,
                    metadata.len()
                ));
            }

            if metadata.is_file() && *watchman_file.mtime != metadata.mtime() {
                diffs.push(format!(
                    "watchman mtime is {} vs. fs {}",
                    *watchman_file.mtime,
                    metadata.mtime()
                ));
            }

            if *watchman_file.ino != metadata.ino() {
                diffs.push(format!(
                    "watchman ino is {} vs. fs {}",
                    *watchman_file.ino,
                    metadata.ino()
                ));
            }
        }

        #[cfg(windows)]
        {
            // TODO: Add permission bit check for Windows.

            if metadata.is_file() && *watchman_file.size != metadata.file_size() {
                diffs.push(format!(
                    "watchman size is {} vs. fs {}",
                    *watchman_file.size,
                    metadata.len()
                ));
            }

            if metadata.is_file() {
                if let Ok(last_write_time) = metadata.last_write_time().try_into() {
                    let last_write_time = from_windows_epoch(last_write_time);
                    if *watchman_file.mtime != last_write_time {
                        diffs.push(format!(
                            "watchman mtime is {} vs. fs {}",
                            *watchman_file.mtime, last_write_time,
                        ));
                    }
                }
            }
        }

        if !diffs.is_empty() {
            writeln!(
                out,
                "Conflicting information for {}:",
                watchman_file.name.display()
            )?;
            for diff in diffs {
                writeln!(out, "  {}", diff)?;
            }
            writeln!(out, "  oclock is {:?}", *watchman_file.oclock)?;
            any_differences = true;
        }
    }

    for (path, val) in &filesystem_state {
        if is_cookie(path) {
            continue;
        }
        if !watchman_state.contains_key(&path.as_path()) {
            missing.push((path, val));
        }
    }

    phantoms.sort_by(|x, y| x.name.cmp(&y.name));
    missing.sort_by(|x, y| x.0.cmp(y.0));

    if !phantoms.is_empty() {
        writeln!(
            out,
            "There are {} items reported by watchman not on the filesystem:",
            phantoms.len()
        )?;
        for phantom in &phantoms {
            writeln!(out, "  {}", phantom.name.display())?;
        }
        any_differences = true;
    }

    if !missing.is_empty() {
        writeln!(
            out,
            "There are {} items on the filesystem not reported by watchman:",
            missing.len()
        )?;
        for (path, _) in &missing {
            writeln!(out, "  {}", path.display())?;
        }
        any_differences = true;
    }

    if any_differences {
        // This is dumb, but Rust doesn't have a standard way to return
        // nonzero exit codes yet.
        std::process::exit(1);
    }

    writeln!(out, "Diffed in {:#?}", diff_start.elapsed())?;

    Ok(())
}

impl AuditCmd {
    fn to_audit_option(&self) -> AuditOption {
        AuditOption {
            settle_period_ms: self.settle_period_ms,
            settle_timeout_ms: self.settle_timeout_ms,
            sync_timeout_secs: self.sync_timeout_secs,
            silent: false,
        }
    }

    pub async fn run(&self) -> anyhow::Result<()> {
        let client = Connector::new().connect().await?;
        let mut out = std::io::stdout();

        audit_repo(&mut out, &client, &self.path, self.to_audit_option()).await
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use structopt::clap::AppSettings;
use structopt::StructOpt;

mod audit;
mod rage;

#[derive(StructOpt, Debug)]
#[structopt(setting = AppSettings::DisableVersion,
    setting = AppSettings::VersionlessSubcommands)]
struct MainCommand {
    #[structopt(subcommand)]
    subcommand: TopLevelSubcommand,
}

#[derive(StructOpt, Debug)]
enum TopLevelSubcommand {
    Audit(audit::AuditCmd),
    Rage(rage::RageCmd),
}

impl TopLevelSubcommand {
    async fn run(&self) -> anyhow::Result<()> {
        use TopLevelSubcommand::*;
        match self {
            Audit(cmd) => cmd.run().await,
            Rage(cmd) => cmd.run().await,
        }
    }
}

#[tokio::main]
async fn main() {
    let cmd = MainCommand::from_args();
    match cmd.subcommand.run().await {
        Ok(()) => {}
        Err(e) => {
            eprintln!("error: {}", e);
            std::process::exit(1);
        }
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::fs::DirEntry;
use std::fs::File;
use std::io;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;

use anyhow::Result;
use duct::Expression;
use structopt::StructOpt;
use sysinfo::System;
use sysinfo::SystemExt;
#[cfg(target_os = "linux")]
use tabular::row;
#[cfg(target_os = "linux")]
use tabular::Table;
use watchman_client::prelude::GetConfigRequest;
use watchman_client::CanonicalPath;
use watchman_client::Client;
use watchman_client::Connector;
use watchman_client::ResolvedRoot;

#[cfg(feature = "fb")]
mod facebook;
mod stream;

use self::stream::Stream;
use crate::audit::AuditOption;

// Wrapper type around [`duct::Expression`] to provide better error messages
struct RageExpression {
    expr: Expression,
    cmd: &'static str,
}

impl RageExpression {
    fn new(expr: Expression, cmd: &'static str) -> Self {
        Self {
            expr: expr.stderr_to_stdout(),
            cmd,
        }
    }

    fn config<T: FnOnce(Expression) -> Expression>(mut self, fun: T) -> Self {
        self.expr = fun(self.expr);
        self
    }

    fn read(&self) -> String {
        self.expr.read().map_or_else(
            |e| format!("Failed to run '{}': {:?}", self.cmd, e),
            |x| x.trim().to_string(),
        )
    }
}

macro_rules! cmd {
    ($program:expr $(, $arg:expr )* $(,)? ) => {
        RageExpression::new(duct::cmd!($program, $($arg),*), stringify!($program $($arg)*))
    };
}

#[cfg(unix)]
fn getuid() -> nix::unistd::Uid {
    nix::unistd::getuid()
}

struct WatchmanRage {
    system: System,
    stream: Stream,
}

impl WatchmanRage {
    async fn new() -> Self {
        let mut system = System::new();
        system.refresh_system();
        let hostname = system.host_name();
        let stream = Stream::new(hostname);

        Self { system, stream }
    }

    fn empty_line(&mut self) -> Result<()> {
        writeln!(self.stream)?;
        Ok(())
    }

    async fn run(&mut self) -> Result<()> {
        self.print_system_info()?;
        self.empty_line()?;
        self.print_package_version()?;
        self.empty_line()?;
        self.print_cli_version()?;
        self.empty_line()?;
        self.print_watchman_env()?;
        self.empty_line()?;
        #[cfg(target_os = "linux")]
        {
            self.print_inotify()?;
            self.empty_line()?;
        }
        #[cfg(target_os = "macos")]
        {
            self.print_launchd_info()?;
            self.empty_line()?;
        }
        self.print_state_info()?;
        self.empty_line()?;
        #[cfg(any(target_os = "linux", target_os = "macos"))]
        {
            self.print_running_watchman()?;
            self.empty_line()?;
        }
        self.print_watchman_service_info().await?;
        self.empty_line()?;
        Ok(())
    }

    fn print_system_info(&mut self) -> Result<()> {
        macro_rules! write_or_unknown {
            ($out: expr, $fmt: expr, $value: expr) => {
                writeln!($out, $fmt, $value.as_deref().unwrap_or("<unknown>"))
            };
        }

        // Note: OS & Arch here are set at compile time, which can be inaccurate in some cases (Apple Silicon)
        writeln!(self.stream, "Platform: {}", std::env::consts::OS)?;
        writeln!(
            self.stream,
            "Arch (Compile Time): {}",
            std::env::consts::ARCH
        )?;
        write_or_unknown!(self.stream, "Hostname: {}", self.system.host_name())?;
        write_or_unknown!(self.stream, "Release: {}", self.system.name())?;
        write_or_unknown!(self.stream, "System Version: {}", self.system.os_version())?;
        write_or_unknown!(
            self.stream,
            "Kernel Version: {}",
            self.system.kernel_version()
        )?;
        #[cfg(unix)]
        writeln!(self.stream, "Running watchman-diag as UID: {}", getuid())?;

        Ok(())
    }

    #[cfg(unix)]
    fn print_package_version(&mut self) -> Result<()> {
        writeln!(
            self.stream,
            "RPM version (rpm -q fb-watchman): {}",
            cmd!("rpm", "-q", "fb-watchman").read()
        )?;
        Ok(())
    }

    #[cfg(windows)]
    fn print_package_version(&mut self) -> Result<()> {
        writeln!(
            self.stream,
            "Chocolatey version (clist -lr fb.watchman): {}",
            cmd!("clist", "-lr", "fb.watchman").read()
        )?;
        Ok(())
    }

    fn print_cli_version(&mut self) -> Result<()> {
        writeln!(
            self.stream,
            "CLI version (watchman -v): {}",
            cmd!("watchman", "--no-spawn", "-v").read()
        )?;
        Ok(())
    }

    fn print_watchman_env(&mut self) -> Result<()> {
        let vars = std::env::vars()
            .into_iter()
            .filter(|(k, _)| k.starts_with("WATCHMAN_"))
            .collect::<Vec<_>>();

        if !vars.is_empty() {
            writeln!(
                self.stream,
                "WARNING: The following Watchman related environment variables are set (this is unusual and may cause problems):"
            )?;

            for (k, v) in vars.iter() {
                writeln!(self.stream, "{}={}", k, v)?;
            }
        }

        Ok(())
    }

    #[cfg(target_os = "linux")]
    fn print_inotify(&mut self) -> Result<()> {
        let mut table =
            Table::new("{:<} {:<} {:<} {:<}").with_row(row!("PID", "EXE", "FD", "WATCHES"));

        macro_rules! bail {
            ($e: expr) => {
                match $e {
                    Ok(v) => v,
                    Err(_) => continue,
                }
            };
        }
        let procs = match std::fs::read_dir("/proc") {
            Ok(c) => c,
            Err(e) => {
                writeln!(self.stream, "Unable to crawl inotify information: {:?}", e)?;
                return Ok(());
            }
        };
        for proc in procs {
            let proc = bail!(proc).path();
            let content = bail!(std::fs::read_dir(proc.join("fd")));
            let exe = std::fs::read_link(proc.join("exe")).map(|x| x.display().to_string());
            let exe = exe.as_deref().unwrap_or("<unknown>");

            for entry in content {
                let fd = bail!(entry).path();
                let target = bail!(std::fs::read_link(&fd));
                if target.display().to_string() != "anon_inode:inotify" {
                    continue;
                }

                if let Some(fdnum) = fd.file_name() {
                    let fdinfo = proc.join("fdinfo").join(fdnum);
                    let fdinfo = bail!(std::fs::read_to_string(fdinfo));
                    let fdnum = fdnum.to_string_lossy();
                    let pid = proc
                        .file_name()
                        .map_or_else(|| "<unknown>".into(), |x| x.to_string_lossy());

                    if fdinfo.is_empty() {
                        table.add_row(row!(pid, exe, fdnum, "<unknown>"));
                    } else {
                        let watches = fdinfo
                            .lines()
                            .filter(|line| line.starts_with("inotify wd:"))
                            .count();
                        table.add_row(row!(pid, exe, fdnum, watches));
                    }
                }
            }
        }

        writeln!(self.stream, "Inotify watch information")?;
        writeln!(self.stream, "{}", table)?;
        Ok(())
    }

    #[cfg(target_os = "macos")]
    fn print_launchd_info(&mut self) -> Result<()> {
        writeln!(self.stream, "Launchd info:")?;
        writeln!(
            self.stream,
            "{}",
            cmd!("launchctl", "list", "com.github.facebook.watchman").read()
        )?;
        Ok(())
    }

    fn print_state_info(&mut self) -> Result<()> {
        // construct all possible state directories
        let mut roots: Vec<PathBuf> = vec![
            "/var/facebook/watchman".into(),
            "/opt/facebook/var/run/watchman".into(),
            "/opt/facebook/watchman/var/run/watchman".into(),
        ];

        if let Ok(path) = std::env::var("TEMP") {
            roots.push(path.into());
        }
        if let Ok(path) = std::env::var("TMP") {
            roots.push(path.into());
        }

        /// Are there any file ends with `-state` name in there
        fn get_state_dirs(p: &Path) -> Vec<DirEntry> {
            if let Ok(content) = std::fs::read_dir(p) {
                content
                    .filter_map(|entry| {
                        if let Ok(entry) = entry {
                            if entry.file_name().to_string_lossy().ends_with("-state") {
                                Some(entry)
                            } else {
                                None
                            }
                        } else {
                            None
                        }
                    })
                    .collect::<Vec<_>>()
            } else {
                Vec::new()
            }
        }

        fn print_state_dir(out: &mut Stream, state: &Path) -> Result<()> {
            let state_file = state.join("state");
            writeln!(out, "State information from {}\n", state.display())?;
            writeln!(out, "State file: {}", state_file.display())?;

            if let Ok(content) = std::fs::read_to_string(state_file) {
                writeln!(out, "{}", content)?;
            }

            let log_file = state.join("log");
            if let Ok(lines) = {
                File::open(&log_file).and_then(|file| {
                    // We prefer `.split` here instead of `.lines` since the log
                    // file may contain invalid utf-8 characters.
                    BufReader::new(file)
                        .split(b'\n')
                        .collect::<io::Result<Vec<_>>>()
                })
            } {
                writeln!(out, "Log samples: {}", log_file.display())?;

                for line in lines.into_iter().rev().take(300).rev() {
                    writeln!(out, "{}", String::from_utf8_lossy(&line).trim())?;
                }
            }

            Ok(())
        }

        for root in roots.iter() {
            let dirs = get_state_dirs(root);
            for state in dirs {
                print_state_dir(&mut self.stream, &state.path())?;
            }
        }

        #[cfg(windows)]
        if let Ok(path) = std::env::var("LOCALAPPDATA") {
            let windows_state = PathBuf::from(&path).join("watchman");
            print_state_dir(&mut self.stream, &windows_state)?;
        }

        Ok(())
    }

    #[cfg(any(target_os = "linux", target_os = "macos"))]
    fn print_running_watchman(&mut self) -> Result<()> {
        let lines = duct::cmd!("ps", "-ef").read()?;

        writeln!(self.stream, "Running Watchman Processes")?;
        for line in lines.lines() {
            if line.contains("watchman") {
                writeln!(self.stream, "{}", line)?;
            }
        }

        Ok(())
    }

    async fn print_watchman_service_info(&mut self) -> Result<()> {
        // Do not run if we are in sudo
        #[cfg(posix)]
        if getuid == 0 && std::env::var("SUDO_UID").is_some() {
            return;
        }

        // TODO: connect timeself.stream
        let client = Connector::new().connect().await?;
        let version = client.version().await?;

        writeln!(self.stream, "Watchman service information:")?;
        writeln!(self.stream, "{:?}", version)?;
        writeln!(self.stream, "Status:\n")?;

        writeln!(
            self.stream,
            "{}",
            cmd!("watchman", "--pretty", "debug-status").read()
        )?;

        // TODO(zeyi): it's probably better if this can return `ResolvedRoot`
        let watches = client.watch_list().await?.roots;
        writeln!(self.stream, "Watches:")?;
        for watch in watches.iter() {
            writeln!(self.stream, "- {}", watch.display())?;
        }

        for watch in watches.iter() {
            writeln!(self.stream)?;

            let option = AuditOption {
                silent: true,
                ..Default::default()
            };
            if let Err(e) = crate::audit::audit_repo(&mut self.stream, &client, watch, option).await
            {
                writeln!(
                    self.stream,
                    "Failed to sanity check {}: {:?}",
                    watch.display(),
                    e
                )?;
            }
            writeln!(self.stream)?;

            self.collect_watch_info(&client, watch).await.ok();
        }

        Ok(())
    }

    async fn check_watchman_config(&mut self, client: &Client, root: &ResolvedRoot) -> Result<()> {
        let repo_root = root.project_root();
        // We are not using `client.get_config()` method to examine the
        // configuration currently used in Watchman server because it is typed
        // and we may need to compare fields that is not yet included in the struct definition.
        let repo_config: serde_json::Value = client
            .generic_request(GetConfigRequest("get-config", repo_root.into()))
            .await?;
        let watchmanconfig_file = repo_root.join(".watchmanconfig");
        let watchmanconfig = std::fs::read_to_string(&watchmanconfig_file)?;
        let watchmanconfig: serde_json::Value = serde_json::from_str(&watchmanconfig)?;

        if let Some(repo_config) = repo_config.get("config") {
            if repo_config != &watchmanconfig {
                writeln!(
                    self.stream,
                    "Watchman root {} is using this configuration:\n {}",
                    repo_root.display(),
                    repo_config
                )?;
                writeln!(
                    self.stream,
                    "'{}' has this configuration:\n{}",
                    watchmanconfig_file.display(),
                    watchmanconfig
                )?;
                writeln!(
                    self.stream,
                    "** You should run: `watchman watch-del {}; watchman watch {}` to reload .watchmanconfig **",
                    repo_root.display(),
                    repo_root.display()
                )?;
            }
        } else {
            writeln!(
                self.stream,
                "Failed to retireve configuration from Watchman. Not checking if configuration matches on-disk setting"
            )?;
        }

        Ok(())
    }

    async fn collect_watch_info(&mut self, client: &Client, repo: &Path) -> Result<()> {
        let root = match client
            .resolve_root(CanonicalPath::with_canonicalized_path(repo.into()))
            .await
        {
            Ok(root) => root,
            Err(e) => {
                writeln!(
                    self.stream,
                    "Failed to resolve repo root for '{}': {:?}\n",
                    repo.display(),
                    e
                )?;
                return Ok(());
            }
        };

        if let Err(e) = self.check_watchman_config(client, &root).await {
            writeln!(
                self.stream,
                "Failed to check Watchman configuration for '{}': {:?}\n",
                repo.display(),
                e
            )?;
        }

        if root.watcher() != "eden" {
            writeln!(self.stream, "Sparse configuration for {}", repo.display())?;
            writeln!(
                self.stream,
                "{}\n",
                cmd!("hg", "sparse").config(|c| c.dir(repo)).read()
            )?;

            // TODO(zeyi): we could migrate this into using the watchman connection,
            // but we need to define the struct first
            writeln!(
                self.stream,
                "Content hash cache stats for {}",
                repo.display()
            )?;
            writeln!(
                self.stream,
                "{}\n",
                cmd!("watchman", "debug-contenthash", repo).read()
            )?;

            writeln!(
                self.stream,
                "Symlink target cache stats for {}",
                repo.display()
            )?;
            writeln!(
                self.stream,
                "{}\n",
                cmd!("watchman", "debug-symlink-target-cache", repo).read()
            )?;
        }

        writeln!(self.stream, "Subscriptions for {}", repo.display())?;
        writeln!(
            self.stream,
            "{}\n",
            cmd!("watchman", "debug-get-subscriptions", repo).read()
        )?;

        writeln!(self.stream, "Asserted states for {}", repo.display())?;
        writeln!(
            self.stream,
            "{}\n",
            cmd!("watchman", "debug-get-asserted-states", repo).read()
        )?;

        Ok(())
    }

    fn wait(self) {
        self.stream.wait();
    }
}

#[derive(StructOpt, Debug)]
pub(crate) struct RageCmd {}

impl RageCmd {
    pub(crate) async fn run(&self) -> Result<()> {
        let mut rage = WatchmanRage::new().await;
        rage.run().await?;
        rage.wait();

        Ok(())
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::io;
use std::io::IsTerminal;
use std::io::Stdout;
use std::io::Write;

#[cfg(feature = "fb")]
pub use super::facebook::FbReporter;

#[cfg(not(feature = "fb"))]
mod fallback {
    use std::io;
    use std::io::Write;

    use anyhow::anyhow;
    use anyhow::Result;

    pub struct FbReporter {}

    impl FbReporter {
        pub fn new(hostname: Option<String>) -> Result<Self> {
            Err(anyhow!("unimplemented"))
        }

        pub fn wait(mut self) {
            unimplemented!()
        }
    }

    impl Write for FbReporter {
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
            unimplemented!()
        }

        fn flush(&mut self) -> io::Result<()> {
            unimplemented!()
        }
    }
}

#[cfg(not(feature = "fb"))]
pub use self::fallback::*;

pub enum Stream {
    Child(FbReporter),
    Stdout(Stdout),
}

impl Stream {
    /// Creates a printer that writes to stdout
    fn new_stdout() -> Self {
        Self::Stdout(std::io::stdout())
    }

    pub fn new(hostname: Option<String>) -> Self {
        if !std::io::stdout().is_terminal() {
            Self::new_stdout()
        } else if let Ok(reporter) = FbReporter::new(hostname) {
            Self::Child(reporter)
        } else {
            Self::new_stdout()
        }
    }

    #[allow(dead_code)]
    pub fn is_redirected(&self) -> bool {
        if let Self::Child(_) = self {
            true
        } else {
            !std::io::stdout().is_terminal()
        }
    }

    fn out(&mut self) -> &mut dyn Write {
        match self {
            Self::Child(c) => c,
            Self::Stdout(out) => out,
        }
    }

    pub fn wait(self) {
        if let Self::Child(child) = self {
            // wait until reporter finishes, and leave terminal nice and clean.
            child.wait();
        }
    }
}

impl Write for Stream {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.out().write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.out().flush()
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::ffi::OsString;
use std::fmt::Write;
use std::path::PathBuf;

use serde::Deserialize;
use serde::Serialize;

/// The ByteString type represents values encoded using BSER_BYTESTRING.
/// The purpose of this encoding is to represent bytestrings with an arbitrary
/// encoding.
///
/// In practice, as used by watchman, bytestrings have the filesystem encoding
/// on posix systems (which is usually utf8 on linux, guaranteed utf8 on macos)
/// and when they appear as file names in file results are guaranteed to be utf8
/// on Windows systems.
///
/// When it comes to interoperating with Rust code, ByteString is nominally
/// equivalent to `OsString`/`PathBuf` and is convertible to and from those values.
///
/// It is worth noting that on Windows sytems the conversion from ByteString to OsString is
/// potentially lossy: while convention is that bytestring holds utf8 in the common case in
/// watchman, that isn't enforced by its serializer and we may potentially encounter an error
/// during conversion.  Converting from OsString to ByteString can potentially fail on windows
/// because Watchman and thus ByteString doesn't have a way to represent the poorly formed
/// surrogate pairs that Windows allows in its filenames.

#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(transparent)]
pub struct ByteString(#[serde(with = "serde_bytes")] Vec<u8>);

impl std::fmt::Debug for ByteString {
    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
        let escaped = self.as_escaped_string();
        write!(fmt, "\"{}\"", escaped.escape_debug())
    }
}

impl std::fmt::Display for ByteString {
    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
        let escaped = self.as_escaped_string();
        write!(fmt, "\"{}\"", escaped.escape_default())
    }
}

impl std::ops::Deref for ByteString {
    type Target = [u8];

    fn deref(&self) -> &[u8] {
        &self.0
    }
}

impl std::ops::DerefMut for ByteString {
    fn deref_mut(&mut self) -> &mut [u8] {
        &mut self.0
    }
}

impl ByteString {
    /// Returns the raw bytes as a slice.
    pub fn as_bytes(&self) -> &[u8] {
        self.0.as_slice()
    }

    /// Consumes ByteString yielding bytes.
    pub fn into_bytes(self) -> Vec<u8> {
        self.0
    }

    /// Returns a version of the bytestring encoded as a mostly-utf-8
    /// string, with invalid sequences escaped using `\xXX` hex notation.
    /// This is for diagnostic and display purposes.
    pub fn as_escaped_string(&self) -> String {
        let mut input = self.0.as_slice();
        let mut output = String::new();

        loop {
            match ::std::str::from_utf8(input) {
                Ok(valid) => {
                    output.push_str(valid);
                    break;
                }
                Err(error) => {
                    let (valid, after_valid) = input.split_at(error.valid_up_to());
                    unsafe { output.push_str(::std::str::from_utf8_unchecked(valid)) }

                    if let Some(invalid_sequence_length) = error.error_len() {
                        for b in &after_valid[..invalid_sequence_length] {
                            write!(output, "\\x{:x}", b).unwrap();
                        }
                        input = &after_valid[invalid_sequence_length..];
                    } else {
                        break;
                    }
                }
            }
        }

        output
    }
}

/// Guaranteed conversion from an owned byte vector to a ByteString
impl From<Vec<u8>> for ByteString {
    fn from(vec: Vec<u8>) -> Self {
        Self(vec)
    }
}

/// Guaranteed conversion from a UTF-8 string to a ByteString
impl From<String> for ByteString {
    fn from(s: String) -> Self {
        Self(s.into_bytes())
    }
}

impl From<&str> for ByteString {
    fn from(s: &str) -> Self {
        Self(s.as_bytes().to_vec())
    }
}

/// Attempt to convert a ByteString into a UTF-8 String
impl TryInto<String> for ByteString {
    type Error = std::string::FromUtf8Error;

    fn try_into(self) -> Result<String, Self::Error> {
        String::from_utf8(self.0)
    }
}

/// Conversion to OsString is guaranteed to succeed on unix systems
/// but can potentially fail on Windows systems.
impl TryInto<OsString> for ByteString {
    type Error = std::string::FromUtf8Error;

    #[cfg(unix)]
    fn try_into(self) -> Result<OsString, Self::Error> {
        Ok(std::os::unix::ffi::OsStringExt::from_vec(self.0))
    }

    #[cfg(windows)]
    fn try_into(self) -> Result<OsString, Self::Error> {
        let s = String::from_utf8(self.0)?;
        Ok(s.into())
    }
}

/// Conversion to PathBuf is subject to the same rules as conversion
/// to OsString
impl TryInto<PathBuf> for ByteString {
    type Error = std::string::FromUtf8Error;

    fn try_into(self) -> Result<PathBuf, Self::Error> {
        let os: OsString = self.try_into()?;
        Ok(os.into())
    }
}

/// Conversion from OsString -> ByteString is guaranteed to succeed on unix
/// systems but can potentially fail on Windows systems.
impl TryInto<ByteString> for OsString {
    type Error = &'static str;

    #[cfg(unix)]
    fn try_into(self) -> Result<ByteString, Self::Error> {
        Ok(ByteString(std::os::unix::ffi::OsStringExt::into_vec(self)))
    }

    #[cfg(windows)]
    fn try_into(self) -> Result<ByteString, Self::Error> {
        let s = self
            .into_string()
            .map_err(|_| "OsString is not representible as UTF-8")?;
        Ok(ByteString(s.into_bytes()))
    }
}

/// Conversion from PathBuf -> ByteString is subject to the same rules
/// as conversion from OsString
impl TryInto<ByteString> for PathBuf {
    type Error = &'static str;

    fn try_into(self) -> Result<ByteString, Self::Error> {
        Ok(self.into_os_string().try_into()?)
    }
}

#[cfg(test)]
mod tests {
    use super::ByteString;
    use crate::from_slice;
    use crate::ser::serialize;

    #[test]
    fn test_serde() {
        let bs = ByteString::from(vec![1, 2, 3, 4]);

        let out = serialize(Vec::<u8>::new(), &bs).unwrap();
        assert_eq!(
            out,
            b"\x00\x02\x00\x00\x00\x00\x03\x07\x02\x03\x04\x01\x02\x03\x04"
        );

        let got: ByteString = from_slice(&out).unwrap();
        assert_eq!(bs, got);
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::fmt;

use serde::de;
use serde::ser;
use thiserror::Error;

use crate::header::header_byte_desc;

pub type Result<T> = ::std::result::Result<T, Error>;

#[derive(Error, Debug)]
pub enum Error {
    #[error("while deserializing BSER: invalid start byte for {}: {}", .kind, header_byte_desc(*.byte))]
    DeInvalidStartByte { kind: String, byte: u8 },

    #[error("error while deserializing BSER: {}", msg)]
    DeCustom { msg: String },

    #[error("while deserializing BSER: recursion limit exceeded with {}", .kind)]
    DeRecursionLimitExceeded { kind: String },

    #[error("Expected {} bytes read, but only read {} bytes", .expected, .read)]
    DeEof { expected: usize, read: usize },

    #[error("Invalid magic header: {:?}", .magic)]
    DeInvalidMagic { magic: Vec<u8> },

    #[error("reader error while deserializing")]
    DeReaderError {
        #[source]
        source: anyhow::Error,
    },

    #[error("error while serializing BSER: {}", .msg)]
    SerCustom { msg: String },

    #[error("while serializing BSER: need size of {}", .kind)]
    SerNeedSize { kind: &'static str },

    #[error("while serializing BSER: integer too big: {}", .v)]
    SerU64TooBig { v: u64 },

    #[error("IO Error")]
    Io(#[from] ::std::io::Error),
}

impl Error {
    pub fn de_reader_error(source: anyhow::Error) -> Self {
        Self::DeReaderError { source }
    }
}

impl de::Error for Error {
    fn custom<T: fmt::Display>(msg: T) -> Self {
        Error::DeCustom {
            msg: format!("{}", msg),
        }
    }
}

impl ser::Error for Error {
    fn custom<T: fmt::Display>(msg: T) -> Self {
        Error::SerCustom {
            msg: format!("{}", msg),
        }
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

//! Header constants for BSER.

pub const EMPTY_HEADER: &[u8] = b"\x00\x02\x00\x00\x00\x00\x05\x00\x00\x00\x00";

pub const BSER_ARRAY: u8 = 0x00;
pub const BSER_OBJECT: u8 = 0x01;
pub const BSER_BYTESTRING: u8 = 0x02;
pub const BSER_INT8: u8 = 0x03;
pub const BSER_INT16: u8 = 0x04;
pub const BSER_INT32: u8 = 0x05;
pub const BSER_INT64: u8 = 0x06;
pub const BSER_REAL: u8 = 0x07;
pub const BSER_TRUE: u8 = 0x08;
pub const BSER_FALSE: u8 = 0x09;
pub const BSER_NULL: u8 = 0x0a;
pub const BSER_TEMPLATE: u8 = 0x0b;
pub const BSER_SKIP: u8 = 0x0c;
pub const BSER_UTF8STRING: u8 = 0x0d;

// Capabilities (we would ideally want to use EnumSet here, but
// https://github.com/contain-rs/enum-set/issues/21 stops us)
#[allow(unused)]
pub const BSER_CAP_DISABLE_UNICODE: u8 = 0x01;
#[allow(unused)]
pub const BSER_CAP_DISABLE_UNICODE_FOR_ERRORS: u8 = 0x02;

pub fn header_byte_desc(byte: u8) -> String {
    match byte {
        BSER_ARRAY => "BSER_ARRAY".into(),
        BSER_OBJECT => "BSER_OBJECT".into(),
        BSER_BYTESTRING => "BSER_BYTESTRING".into(),
        BSER_INT8 => "BSER_INT8".into(),
        BSER_INT16 => "BSER_INT16".into(),
        BSER_INT32 => "BSER_INT32".into(),
        BSER_INT64 => "BSER_INT64".into(),
        BSER_REAL => "BSER_REAL".into(),
        BSER_TRUE => "BSER_TRUE".into(),
        BSER_FALSE => "BSER_FALSE".into(),
        BSER_NULL => "BSER_NULL".into(),
        BSER_TEMPLATE => "BSER_TEMPLATE".into(),
        BSER_SKIP => "BSER_SKIP".into(),
        BSER_UTF8STRING => "BSER_UTF8STRING".into(),
        ch => format!("unknown byte '{:?}'", ch),
    }
}


#![deny(warnings, rust_2018_idioms)]
/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

pub mod bytestring;
pub mod de;
mod errors;
mod header;
pub mod ser;
pub mod value;

pub use crate::de::from_reader;
pub use crate::de::from_slice;


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::collections::HashMap;
use std::path::PathBuf;

use serde::de::Deserialize;
use serde::de::MapAccess;
use serde::de::SeqAccess;
use serde::de::Visitor;
use serde::Serialize;

use crate::bytestring::ByteString;

/// The Value type is used in cases where the schema is not known statically.
/// As used in Watchman's protocol, this allows encoding arbitrary metadata
/// that can be passed through the system by eg: the `state-enter` command,
/// or returned from a saved state storage engine.
/// The values are conceptually equivalent to json values, with the notable
/// difference that BSER can represent a binary byte string value.
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
    Array(Vec<Value>),
    Object(HashMap<String, Value>),
    ByteString(ByteString),
    Integer(i64),
    Real(f64),
    Bool(bool),
    Null,
    Utf8String(String),
}

impl From<Vec<Value>> for Value {
    fn from(v: Vec<Value>) -> Self {
        Self::Array(v)
    }
}

impl From<HashMap<String, Value>> for Value {
    fn from(v: HashMap<String, Value>) -> Self {
        Self::Object(v)
    }
}

impl From<bool> for Value {
    fn from(v: bool) -> Self {
        Self::Bool(v)
    }
}

impl From<&str> for Value {
    fn from(s: &str) -> Self {
        Self::Utf8String(s.to_string())
    }
}

impl From<String> for Value {
    fn from(s: String) -> Self {
        Self::Utf8String(s)
    }
}

impl TryInto<Value> for PathBuf {
    type Error = &'static str;

    fn try_into(self) -> Result<Value, Self::Error> {
        let s: ByteString = self.try_into()?;
        Ok(Value::ByteString(s))
    }
}

impl From<i64> for Value {
    fn from(v: i64) -> Self {
        Self::Integer(v)
    }
}

impl TryInto<Value> for usize {
    type Error = &'static str;

    fn try_into(self) -> Result<Value, Self::Error> {
        if self > i64::max_value() as usize {
            Err("value is too large to represent as i64")
        } else {
            Ok(Value::Integer(self as i64))
        }
    }
}

impl<'de> Deserialize<'de> for Value {
    #[inline]
    fn deserialize<D>(deserializer: D) -> Result<Value, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct ValueVisitor;

        impl<'de> Visitor<'de> for ValueVisitor {
            type Value = Value;

            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                formatter.write_str("any valid BSER value")
            }

            #[inline]
            fn visit_bool<E>(self, value: bool) -> Result<Value, E> {
                Ok(Value::Bool(value))
            }

            #[inline]
            fn visit_i64<E>(self, value: i64) -> Result<Value, E> {
                Ok(Value::Integer(value))
            }

            /*
            #[inline]
            fn visit_u64<E>(self, v: u64) -> Result<Value, E> {
                // maybe_put_int! doesn't work for u64 because it converts to i64
                // internally.
                if v > (i64::max_value() as u64) {
                    Err(serde::de::Error::custom(format!(
                        "value {} is too large to represent as a BSER integer",
                        v
                    )))
                } else {
                    Ok(Value::Integer(v as i64))
                }
            }
            */

            #[inline]
            fn visit_f64<E>(self, value: f64) -> Result<Value, E> {
                Ok(Value::Real(value))
            }

            #[inline]
            fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E> {
                Ok(Value::ByteString(value.to_vec().into()))
            }

            #[inline]
            fn visit_byte_buf<E>(self, value: Vec<u8>) -> Result<Self::Value, E> {
                Ok(Value::ByteString(value.into()))
            }

            #[inline]
            fn visit_str<E>(self, value: &str) -> Result<Value, E>
            where
                E: serde::de::Error,
            {
                self.visit_string(String::from(value))
            }

            #[inline]
            fn visit_string<E>(self, value: String) -> Result<Value, E> {
                Ok(Value::Utf8String(value))
            }

            #[inline]
            fn visit_some<D>(self, deserializer: D) -> Result<Value, D::Error>
            where
                D: serde::Deserializer<'de>,
            {
                Deserialize::deserialize(deserializer)
            }

            #[inline]
            fn visit_none<E>(self) -> Result<Value, E> {
                Ok(Value::Null)
            }

            #[inline]
            fn visit_unit<E>(self) -> Result<Value, E> {
                Ok(Value::Null)
            }

            #[inline]
            fn visit_seq<V>(self, mut visitor: V) -> Result<Value, V::Error>
            where
                V: SeqAccess<'de>,
            {
                let mut vec = Vec::new();

                while let Some(elem) = visitor.next_element()? {
                    vec.push(elem);
                }

                Ok(Value::Array(vec))
            }

            fn visit_map<V>(self, mut visitor: V) -> Result<Value, V::Error>
            where
                V: MapAccess<'de>,
            {
                match visitor.next_key()? {
                    Some(Value::ByteString(key)) => {
                        let mut values = HashMap::new();

                        values.insert(
                            key.try_into().map_err(serde::de::Error::custom)?,
                            visitor.next_value()?,
                        );
                        while let Some((key, value)) = visitor.next_entry()? {
                            values.insert(key, value);
                        }

                        Ok(Value::Object(values))
                    }
                    Some(Value::Utf8String(key)) => {
                        let mut values = HashMap::new();

                        values.insert(key, visitor.next_value()?);
                        while let Some((key, value)) = visitor.next_entry()? {
                            values.insert(key, value);
                        }

                        Ok(Value::Object(values))
                    }
                    Some(value) => Err(serde::de::Error::custom(format!(
                        "value {:?} is illegal as a key in a BSER map",
                        value
                    ))),
                    None => Ok(Value::Object(HashMap::new())),
                }
            }
        }

        deserializer.deserialize_any(ValueVisitor)
    }
}

impl Serialize for Value {
    #[inline]
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: ::serde::Serializer,
    {
        match *self {
            Value::Null => serializer.serialize_unit(),
            Value::Bool(b) => serializer.serialize_bool(b),
            Value::Integer(n) => serializer.serialize_i64(n),
            Value::Real(n) => serializer.serialize_f64(n),
            Value::Utf8String(ref s) => serializer.serialize_str(s),
            Value::ByteString(ref b) => serializer.serialize_bytes(b.as_bytes()),
            Value::Array(ref v) => v.serialize(serializer),
            Value::Object(ref m) => {
                use serde::ser::SerializeMap;
                let mut map = serializer.serialize_map(Some(m.len()))?;
                for (k, v) in m {
                    map.serialize_key(k)?;
                    map.serialize_value(v)?;
                }
                map.end()
            }
        }
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

//! Internal stateless code for handling BSER deserialization.

use anyhow::Context as _;
use byteorder::ByteOrder;
use byteorder::NativeEndian;

use crate::de::read::DeRead;
use crate::de::read::Reference;
use crate::errors::*;
use crate::header::*;

#[derive(Debug)]
pub struct Bunser<R> {
    read: R,
    scratch: Vec<u8>,
}

pub struct PduInfo {
    pub bser_capabilities: u32,
    pub len: i64,
    pub start: i64,
}

impl<'de, R> Bunser<R>
where
    R: DeRead<'de>,
{
    pub fn new(read: R) -> Self {
        Bunser {
            read,
            scratch: Vec::with_capacity(128),
        }
    }

    /// Read the PDU off the stream. This should be called in the beginning.
    pub fn read_pdu(&mut self) -> Result<PduInfo> {
        {
            let magic = self.read_bytes(2)?;
            if magic.get_ref() != &EMPTY_HEADER[..2] {
                return Err(Error::DeInvalidMagic {
                    magic: Vec::from(magic.get_ref()),
                });
            }
        }
        let bser_capabilities = self
            .read
            .next_u32(&mut self.scratch)
            .map_err(Error::de_reader_error)?;
        let len = self.check_next_int()?;
        let start = self.read_count();
        Ok(PduInfo {
            bser_capabilities,
            len,
            start,
        })
    }

    pub fn read_count(&self) -> i64 {
        self.read.read_count() as i64
    }

    pub fn end(&self, pdu_info: &PduInfo) -> Result<()> {
        let expected = (pdu_info.start + pdu_info.len) as usize;
        if self.read.read_count() != expected {
            return Err(Error::DeEof {
                expected,
                read: self.read.read_count(),
            });
        }
        Ok(())
    }

    #[inline]
    pub fn peek(&mut self) -> Result<u8> {
        self.read.peek().map_err(Error::de_reader_error)
    }

    #[inline]
    pub fn discard(&mut self) {
        self.read.discard();
    }

    /// Return a borrowed or copied version of the next n bytes.
    #[inline]
    pub fn read_bytes<'s>(&'s mut self, len: i64) -> Result<Reference<'de, 's, [u8]>> {
        let len = len as usize;
        self.read
            .next_bytes(len, &mut self.scratch)
            .map_err(Error::de_reader_error)
    }

    /// Return the next i8 value. This assumes the caller already knows the next
    /// value is an i8.
    pub fn next_i8(&mut self) -> Result<i8> {
        self.read.discard();
        let bytes = self
            .read_bytes(1)
            .context("error while reading i8")
            .map_err(Error::de_reader_error)?
            .get_ref();
        Ok(bytes[0] as i8)
    }

    /// Return the next i16 value. This assumes the caller already knows the
    /// next value is an i16.
    pub fn next_i16(&mut self) -> Result<i16> {
        self.read.discard();
        let bytes = self
            .read_bytes(2)
            .context("error while reading i16")
            .map_err(Error::de_reader_error)?
            .get_ref();
        Ok(NativeEndian::read_i16(bytes))
    }

    /// Return the next i32 value. This assumes the caller already knows the
    /// next value is an i32.
    pub fn next_i32(&mut self) -> Result<i32> {
        self.read.discard();
        let bytes = self
            .read_bytes(4)
            .context("error while reading i32")
            .map_err(Error::de_reader_error)?
            .get_ref();
        Ok(NativeEndian::read_i32(bytes))
    }

    /// Return the next i64 value. This assumes the caller already knows the
    /// next value is an i64.
    pub fn next_i64(&mut self) -> Result<i64> {
        self.read.discard();
        let bytes = self
            .read_bytes(8)
            .context("error while reading i64")
            .map_err(Error::de_reader_error)?
            .get_ref();
        Ok(NativeEndian::read_i64(bytes))
    }

    /// Check and return the next integer value. Errors out if the next value is
    /// not actually an int.
    pub fn check_next_int(&mut self) -> Result<i64> {
        let value = match self.peek()? {
            BSER_INT8 => self.next_i8()? as i64,
            BSER_INT16 => self.next_i16()? as i64,
            BSER_INT32 => self.next_i32()? as i64,
            BSER_INT64 => self.next_i64()? as i64,
            ch => {
                return Err(Error::DeInvalidStartByte {
                    kind: "integer".into(),
                    byte: ch,
                });
            }
        };

        Ok(value)
    }

    pub fn next_f64(&mut self) -> Result<f64> {
        self.read.discard();
        let bytes = self
            .read_bytes(8)
            .context("error while reading f64")
            .map_err(Error::de_reader_error)?
            .get_ref();
        Ok(NativeEndian::read_f64(bytes))
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use serde::de;
use serde::forward_to_deserialize_any;

use super::read::DeRead;
use super::reentrant::ReentrantGuard;
use super::Deserializer;
use crate::errors::*;
use crate::header::*;

pub struct MapAccess<'a, R> {
    de: &'a mut Deserializer<R>,
    remaining: usize,
}

impl<'a, 'de, R> MapAccess<'a, R>
where
    R: 'a + DeRead<'de>,
{
    /// Create a new `MapAccess`.
    ///
    /// `_guard` makes sure the caller is accounting for the recursion limit.
    pub fn new(de: &'a mut Deserializer<R>, nitems: usize, _guard: &ReentrantGuard) -> Self {
        MapAccess {
            de,
            remaining: nitems,
        }
    }
}

impl<'a, 'de, R> de::MapAccess<'de> for MapAccess<'a, R>
where
    R: 'a + DeRead<'de>,
{
    type Error = Error;

    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
    where
        K: de::DeserializeSeed<'de>,
    {
        if self.remaining == 0 {
            Ok(None)
        } else {
            self.remaining -= 1;
            let key = seed.deserialize(MapKey { de: &mut *self.de })?;
            Ok(Some(key))
        }
    }

    fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
    where
        V: de::DeserializeSeed<'de>,
    {
        seed.deserialize(&mut *self.de)
    }
}

/// A deserializer that is specialized to deal with map keys. Specifically, map keys are always
/// strings.
struct MapKey<'a, R> {
    de: &'a mut Deserializer<R>,
}

impl<'a, 'de, R> de::Deserializer<'de> for MapKey<'a, R>
where
    R: DeRead<'de>,
{
    type Error = Error;

    #[inline]
    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        match self.de.bunser.peek()? {
            // Both bytestrings and UTF-8 strings are treated as Unicode strings, since field
            // identifiers must be Unicode strings.
            BSER_BYTESTRING | BSER_UTF8STRING => self.de.visit_utf8string(visitor),
            other => Err(Error::DeInvalidStartByte {
                kind: "map key".into(),
                byte: other,
            }),
        }
    }

    #[inline]
    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        // Map keys cannot be null.
        visitor.visit_some(self)
    }

    #[inline]
    fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        visitor.visit_newtype_struct(self)
    }

    #[inline]
    fn deserialize_enum<V>(
        self,
        name: &'static str,
        variants: &'static [&'static str],
        visitor: V,
    ) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        self.de.deserialize_enum(name, variants, visitor)
    }

    #[inline]
    fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        self.de.deserialize_bytes(visitor)
    }

    #[inline]
    fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        self.de.deserialize_bytes(visitor)
    }

    forward_to_deserialize_any! {
        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string unit unit_struct
        seq tuple tuple_struct map struct identifier ignored_any
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

mod bunser;
mod map;
mod read;
mod reentrant;
mod seq;
mod template;
#[cfg(test)]
mod test;
mod variant;

use std::io;
use std::str;

use serde::de;
use serde::forward_to_deserialize_any;

pub use self::bunser::Bunser;
pub use self::bunser::PduInfo;
pub use self::read::DeRead;
pub use self::read::Reference;
pub use self::read::SliceRead;
use self::reentrant::ReentrantLimit;
use crate::errors::*;
use crate::header::*;

pub struct Deserializer<R> {
    bunser: Bunser<R>,
    pdu_info: PduInfo,
    remaining_depth: ReentrantLimit,
}

macro_rules! make_visit_num {
    ($fn:ident, $next:ident) => {
        #[inline]
        fn $fn<V>(&mut self, visitor: V) -> Result<V::Value>
        where
            V: de::Visitor<'de>,
        {
            visitor.$fn(self.bunser.$next()?)
        }
    };
}

fn from_trait<'de, R, T>(read: R) -> Result<T>
where
    R: DeRead<'de>,
    T: de::Deserialize<'de>,
{
    let mut d = Deserializer::new(read)?;
    let value = de::Deserialize::deserialize(&mut d)?;

    // Make sure we saw the expected length.
    d.end()?;
    Ok(value)
}

pub fn from_slice<'de, T>(slice: &'de [u8]) -> Result<T>
where
    T: de::Deserialize<'de>,
{
    from_trait(SliceRead::new(slice))
}

pub fn from_reader<R, T>(rdr: R) -> Result<T>
where
    R: io::Read,
    T: de::DeserializeOwned,
{
    from_trait(read::IoRead::new(rdr))
}

impl<'de, R> Deserializer<R>
where
    R: DeRead<'de>,
{
    pub fn new(read: R) -> Result<Self> {
        let mut bunser = Bunser::new(read);
        let pdu_info = bunser.read_pdu()?;
        Ok(Deserializer {
            bunser,
            pdu_info,
            remaining_depth: ReentrantLimit::new(128),
        })
    }

    /// This method must be called after a value has been fully deserialized.
    pub fn end(&self) -> Result<()> {
        self.bunser.end(&self.pdu_info)
    }

    #[inline]
    pub fn capabilities(&self) -> u32 {
        self.pdu_info.bser_capabilities
    }

    fn parse_value<V>(&mut self, visitor: V) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        match self.bunser.peek()? {
            BSER_ARRAY => {
                let guard = self.remaining_depth.acquire("array")?;
                self.bunser.discard();
                let nitems = self.bunser.check_next_int()?;

                visitor.visit_seq(seq::SeqAccess::new(self, nitems as usize, &guard))
            }
            BSER_OBJECT => {
                let guard = self.remaining_depth.acquire("object")?;
                self.bunser.discard();
                let nitems = self.bunser.check_next_int()?;

                visitor.visit_map(map::MapAccess::new(self, nitems as usize, &guard))
            }
            BSER_TRUE => self.visit_bool(visitor, true),
            BSER_FALSE => self.visit_bool(visitor, false),
            BSER_NULL => self.visit_unit(visitor),
            BSER_BYTESTRING => self.visit_bytestring(visitor),
            BSER_UTF8STRING => self.visit_utf8string(visitor),
            BSER_TEMPLATE => {
                let guard = self.remaining_depth.acquire("template")?;
                self.bunser.discard();

                // TODO: handle possible IO interruption better here -- will
                // probably need some intermediate states.
                let keys = self.template_keys()?;
                let nitems = self.bunser.check_next_int()?;
                let template = template::Template::new(self, keys, nitems as usize, &guard);
                visitor.visit_seq(template)
            }
            BSER_REAL => self.visit_f64(visitor),
            BSER_INT8 => self.visit_i8(visitor),
            BSER_INT16 => self.visit_i16(visitor),
            BSER_INT32 => self.visit_i32(visitor),
            BSER_INT64 => self.visit_i64(visitor),
            ch => Err(Error::DeInvalidStartByte {
                kind: "next item".into(),
                byte: ch,
            }),
        }
    }

    make_visit_num!(visit_i8, next_i8);
    make_visit_num!(visit_i16, next_i16);
    make_visit_num!(visit_i32, next_i32);
    make_visit_num!(visit_i64, next_i64);
    make_visit_num!(visit_f64, next_f64);

    fn template_keys(&mut self) -> Result<Vec<template::Key<'de>>> {
        // The list of keys is actually an array, so just use the deserializer
        // to process it.
        de::Deserialize::deserialize(self)
    }

    fn visit_bytestring<V>(&mut self, visitor: V) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        self.bunser.discard();
        let len = self.bunser.check_next_int()?;
        match self.bunser.read_bytes(len)? {
            Reference::Borrowed(s) => visitor.visit_borrowed_bytes(s),
            Reference::Copied(s) => visitor.visit_bytes(s),
        }
    }

    fn visit_utf8string<V>(&mut self, visitor: V) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        self.bunser.discard();
        let len = self.bunser.check_next_int()?;
        match self
            .bunser
            .read_bytes(len)?
            .map_result(|x| str::from_utf8(x))
            .map_err(Error::de_reader_error)?
        {
            Reference::Borrowed(s) => visitor.visit_borrowed_str(s),
            Reference::Copied(s) => visitor.visit_str(s),
        }
    }

    #[inline]
    fn visit_bool<V>(&mut self, visitor: V, value: bool) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        self.bunser.discard();
        visitor.visit_bool(value)
    }

    #[inline]
    fn visit_unit<V>(&mut self, visitor: V) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        self.bunser.discard();
        visitor.visit_unit()
    }
}

impl<'de, 'a, R> de::Deserializer<'de> for &'a mut Deserializer<R>
where
    R: DeRead<'de>,
{
    type Error = Error;

    #[inline]
    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        self.parse_value(visitor)
    }

    /// Parse a `null` as a None, and anything else as a `Some(...)`.
    #[inline]
    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        match self.bunser.peek()? {
            BSER_NULL => {
                self.bunser.discard();
                visitor.visit_none()
            }
            _ => visitor.visit_some(self),
        }
    }

    #[inline]
    fn deserialize_newtype_struct<V>(self, _name: &str, visitor: V) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        // This is e.g. E(T). Ignore the E.
        visitor.visit_newtype_struct(self)
    }

    /// Parse an enum as an object like {key: value}, or a unit variant as just
    /// a value.
    #[inline]
    fn deserialize_enum<V>(
        self,
        name: &'static str,
        _variants: &'static [&'static str],
        visitor: V,
    ) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        match self.bunser.peek()? {
            BSER_BYTESTRING | BSER_UTF8STRING => {
                visitor.visit_enum(variant::UnitVariantAccess::new(self))
            }
            BSER_OBJECT => {
                let guard = self
                    .remaining_depth
                    .acquire(format!("object-like enum '{}'", name))?;
                self.bunser.discard();
                // For enum variants the object must have exactly one entry
                // (named the variant, but serde will perform that check).
                let nitems = self.bunser.check_next_int()?;
                if nitems != 1 {
                    return Err(de::Error::invalid_value(
                        de::Unexpected::Signed(nitems),
                        &"integer `1`",
                    ));
                }
                visitor.visit_enum(variant::VariantAccess::new(self, &guard))
            }
            ch => {
                return Err(Error::DeInvalidStartByte {
                    kind: format!("enum '{}'", name),
                    byte: ch,
                });
            }
        }
    }

    forward_to_deserialize_any! {
        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes
        byte_buf unit unit_struct seq tuple tuple_struct map struct identifier
        ignored_any
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#[cfg(feature = "debug_bytes")]
use std::fmt;
use std::io;
use std::result;

use anyhow::bail;
use anyhow::Context as _;
use byteorder::ByteOrder;
use byteorder::NativeEndian;

#[cfg(feature = "debug_bytes")]
macro_rules! debug_bytes {
    ($($arg:tt)*) => {
       eprint!($($arg)*);
    }
}

#[cfg(not(feature = "debug_bytes"))]
macro_rules! debug_bytes {
    ($($arg:tt)*) => {};
}

#[cfg(feature = "debug_bytes")]
struct ByteBuf<'a>(&'a [u8]);
#[cfg(feature = "debug_bytes")]
impl<'a> fmt::LowerHex for ByteBuf<'a> {
    fn fmt(&self, fmtr: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        for byte in self.0 {
            let val = byte.clone();
            if (val >= b'-' && val <= b'9')
                || (val >= b'A' && val <= b'Z')
                || (val >= b'a' && val <= b'z')
                || val == b'_'
            {
                fmtr.write_fmt(format_args!("{}", val as char))?;
            } else {
                fmtr.write_fmt(format_args!(r"\x{:02x}", byte))?;
            }
        }
        Ok(())
    }
}

pub trait DeRead<'de> {
    /// read next byte (if peeked byte not discarded return it)
    fn next(&mut self) -> anyhow::Result<u8>;
    /// peek next byte (peeked byte should come in next and next_bytes unless discarded)
    fn peek(&mut self) -> anyhow::Result<u8>;
    /// how many bytes have been read so far.
    /// this doesn't include the peeked byte
    fn read_count(&self) -> usize;
    /// discard peeked byte
    fn discard(&mut self);
    /// read next byte (if peeked byte not discarded include it)
    fn next_bytes<'s>(
        &'s mut self,
        len: usize,
        scratch: &'s mut Vec<u8>,
    ) -> anyhow::Result<Reference<'de, 's, [u8]>>;
    /// read u32 as native endian
    fn next_u32(&mut self, scratch: &mut Vec<u8>) -> anyhow::Result<u32> {
        let bytes = self
            .next_bytes(4, scratch)
            .context("error while parsing u32")?
            .get_ref();
        Ok(NativeEndian::read_u32(bytes))
    }
}

pub struct SliceRead<'a> {
    slice: &'a [u8],
    index: usize,
}

impl<'a> SliceRead<'a> {
    pub fn new(slice: &'a [u8]) -> Self {
        SliceRead { slice, index: 0 }
    }
}

pub struct IoRead<R>
where
    R: io::Read,
{
    reader: R,
    read_count: usize,
    /// Temporary storage of peeked byte.
    peeked: Option<u8>,
}

impl<R> IoRead<R>
where
    R: io::Read,
{
    pub fn new(reader: R) -> Self {
        debug_bytes!("Read bytes:\n");
        IoRead {
            reader,
            read_count: 0,
            peeked: None,
        }
    }
}

impl<R> Drop for IoRead<R>
where
    R: io::Read,
{
    fn drop(&mut self) {
        debug_bytes!("\n");
    }
}

impl<'a> DeRead<'a> for SliceRead<'a> {
    fn next(&mut self) -> anyhow::Result<u8> {
        if self.index >= self.slice.len() {
            bail!("eof while reading next byte");
        }
        let ch = self.slice[self.index];
        self.index += 1;
        Ok(ch)
    }

    fn peek(&mut self) -> anyhow::Result<u8> {
        if self.index >= self.slice.len() {
            bail!("eof while peeking next byte");
        }
        Ok(self.slice[self.index])
    }

    #[inline]
    fn read_count(&self) -> usize {
        self.index
    }

    #[inline]
    fn discard(&mut self) {
        self.index += 1;
    }

    fn next_bytes<'s>(
        &'s mut self,
        len: usize,
        _scratch: &'s mut Vec<u8>,
    ) -> anyhow::Result<Reference<'a, 's, [u8]>> {
        // BSER has no escaping or anything similar, so just go ahead and return
        // a reference to the bytes.
        if self.index + len > self.slice.len() {
            bail!("eof while parsing bytes/string");
        }
        let borrowed = &self.slice[self.index..(self.index + len)];
        self.index += len;
        Ok(Reference::Borrowed(borrowed))
    }
}

impl<'de, R> DeRead<'de> for IoRead<R>
where
    R: io::Read,
{
    fn next(&mut self) -> anyhow::Result<u8> {
        match self.peeked.take() {
            Some(peeked) => Ok(peeked),
            None => {
                let mut buffer = [0; 1];
                self.reader.read_exact(&mut buffer)?;
                debug_bytes!("{:x}", ByteBuf(&buffer));
                self.read_count += 1;
                Ok(buffer[0])
            }
        }
    }

    fn peek(&mut self) -> anyhow::Result<u8> {
        match self.peeked {
            Some(peeked) => Ok(peeked),
            None => {
                let mut buffer = [0; 1];
                self.reader.read_exact(&mut buffer)?;
                debug_bytes!("{:x}", ByteBuf(&buffer));
                self.peeked = Some(buffer[0]);
                self.read_count += 1;
                Ok(buffer[0])
            }
        }
    }

    #[inline]
    fn read_count(&self) -> usize {
        match self.peeked {
            Some(_) => self.read_count - 1,
            None => self.read_count,
        }
    }

    #[inline]
    fn discard(&mut self) {
        self.peeked = None
    }

    fn next_bytes<'s>(
        &'s mut self,
        len: usize,
        scratch: &'s mut Vec<u8>,
    ) -> anyhow::Result<Reference<'de, 's, [u8]>> {
        scratch.resize(len, 0);
        let mut idx = 0;
        if self.peeked.is_some() {
            idx += 1;
        }
        if idx < len {
            self.reader.read_exact(&mut scratch[idx..len])?;
            debug_bytes!("{:x}", ByteBuf(&scratch[idx..len]));
            self.read_count += len - idx;
        }
        if let Some(peeked) = self.peeked.take() {
            scratch[0] = peeked;
        }
        Ok(Reference::Copied(&scratch[0..len]))
    }
}

#[derive(Debug)]
pub enum Reference<'b, 'c, T: ?Sized> {
    Borrowed(&'b T),
    Copied(&'c T),
}

impl<'b, 'c, T> Reference<'b, 'c, T>
where
    T: ?Sized,
{
    pub fn map_result<F, U, E>(self, f: F) -> anyhow::Result<Reference<'b, 'c, U>>
    where
        F: FnOnce(&T) -> result::Result<&U, E>,
        E: std::error::Error + Send + Sync + 'static,
        U: ?Sized + 'b + 'c,
    {
        match self {
            Reference::Borrowed(borrowed) => Ok(Reference::Borrowed(f(borrowed)?)),
            Reference::Copied(copied) => Ok(Reference::Copied(f(copied)?)),
        }
    }

    pub fn get_ref<'a>(&self) -> &'a T
    where
        'b: 'a,
        'c: 'a,
    {
        match *self {
            Reference::Borrowed(borrowed) => borrowed,
            Reference::Copied(copied) => copied,
        }
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

//! Module to handle reentrant/recursion limits while deserializing.

use std::cell::Cell;
use std::rc::Rc;

use crate::errors::*;

/// Sets a limit on the amount of recursion during deserialization. This does
/// not do any synchronization -- it is intended purely for single-threaded use.
pub struct ReentrantLimit(Rc<Cell<usize>>);

impl ReentrantLimit {
    /// Create a new reentrant limit.
    pub fn new(limit: usize) -> Self {
        ReentrantLimit(Rc::new(Cell::new(limit)))
    }

    /// Try to decrease the limit by 1. Return an RAII guard that when freed
    /// will increase the limit by 1.
    pub fn acquire<S: Into<String>>(&mut self, kind: S) -> Result<ReentrantGuard> {
        if self.0.get() == 0 {
            return Err(Error::DeRecursionLimitExceeded { kind: kind.into() });
        }
        self.0.set(self.0.get() - 1);
        Ok(ReentrantGuard(self.0.clone()))
    }
}

/// RAII guard for reentrant limits.
pub struct ReentrantGuard(Rc<Cell<usize>>);

impl Drop for ReentrantGuard {
    fn drop(&mut self) {
        self.0.set(self.0.get() + 1);
    }
}