/*
 * 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 super::read::DeRead;
use super::reentrant::ReentrantGuard;
use super::Deserializer;
use crate::errors::*;

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

impl<'a, 'de, R> SeqAccess<'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 {
        SeqAccess {
            de,
            remaining: nitems,
        }
    }
}

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

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


/*
 * 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::borrow::Cow;
use std::rc::Rc;

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

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

// This is ugly because #[serde(borrow)] can't be used with collections directly
// at the moment. See
// https://github.com/serde-rs/serde/issues/914#issuecomment-298801226 for more.
// Note that keys are always ASCII, so treating them as str is fine, even if they're
// serialized as BSER_BYTESTRING instances. (These keys are used as struct field
// identifiers, which are Unicode strings so can't be directly matched up with
// bytestrings.)
#[derive(Clone, Debug, Deserialize)]
pub struct Key<'a>(#[serde(borrow)] Cow<'a, str>);

/// A BSER template is logically an array of objects, all with the same or
/// similar keys.
///
/// A template is serialized as
/// `<BSER_TEMPLATE><BSER_ARRAY of keys><number of objects><object 1 values><object 2 values>...`
///
/// and gets deserialized as
///
/// ```text
/// [
///    {key1: obj1_value1, key2: obj1_value2, ...},
///    {key2: obj2_value1, key2: obj2_value2, ...},
///    ...
/// ]
/// ```
///
/// The special value BSER_SKIP is used if a particular object doesn't have a
/// key.
pub struct Template<'a, 'de, R> {
    de: &'a mut Deserializer<R>,
    keys: Rc<Vec<Key<'de>>>,
    remaining: usize,
}

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

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

    fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
    where
        T: de::DeserializeSeed<'de>,
    {
        if self.remaining == 0 {
            Ok(None)
        } else {
            self.remaining -= 1;
            let obj_de = ObjectDeserializer {
                de: &mut *self.de,
                keys: self.keys.clone(),
            };
            let value = seed.deserialize(obj_de)?;
            Ok(Some(value))
        }
    }
}

struct ObjectDeserializer<'a, 'de, R> {
    de: &'a mut Deserializer<R>,
    keys: Rc<Vec<Key<'de>>>,
}

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

    #[inline]
    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        visitor.visit_map(TemplateObject::new(&mut *self.de, self.keys))
    }

    #[inline]
    fn deserialize_newtype_struct<V>(self, _name: &'static 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)
    }

    fn deserialize_enum<V>(
        self,
        _name: &'static str,
        _variants: &'static [&'static str],
        visitor: V,
    ) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        visitor.visit_map(TemplateObject::new(&mut *self.de, self.keys))
    }

    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 option
    }
}

struct TemplateObject<'a, 'de, R> {
    de: &'a mut Deserializer<R>,
    keys: Rc<Vec<Key<'de>>>,
    cur: usize,
}

impl<'a, 'de, R> TemplateObject<'a, 'de, R>
where
    R: 'a + DeRead<'de>,
{
    fn new(de: &'a mut Deserializer<R>, keys: Rc<Vec<Key<'de>>>) -> Self {
        TemplateObject { de, keys, cur: 0 }
    }
}

impl<'a, 'de, R> de::MapAccess<'de> for TemplateObject<'a, 'de, 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.cur == self.keys.len() {
            Ok(None)
        } else {
            let cur = self.cur;
            self.cur += 1;
            let obj_de = KeyDeserializer {
                key: &self.keys[cur],
            };
            let value = seed.deserialize(obj_de)?;
            Ok(Some(value))
        }
    }

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

struct KeyDeserializer<'a, 'de> {
    key: &'a Key<'de>,
}

impl<'a, 'de: 'a> de::Deserializer<'de> for KeyDeserializer<'a, 'de> {
    type Error = Error;

    #[inline]
    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        match self.key.0 {
            Cow::Borrowed(s) => visitor.visit_borrowed_str(s),
            Cow::Owned(ref s) => visitor.visit_str(s),
        }
    }

    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 option enum newtype_struct
    }
}

struct ValueDeserializer<'a, R> {
    de: &'a mut Deserializer<R>,
}

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

    #[inline]
    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        // The only new thing here compared to the main Deserializer is that it
        // is possible to skip this value with BSER_SKIP.
        match self.de.bunser.peek()? {
            BSER_SKIP => {
                self.de.bunser.discard();
                visitor.visit_none()
            }
            _ => self.de.deserialize_any(visitor),
        }
    }

    /// Parse a BSER_SKIP or 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.de.bunser.peek()? {
            BSER_SKIP | BSER_NULL => {
                self.de.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)
    }

    // TODO: do we also need to do enum here?

    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 enum
    }
}


/*
 * 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::borrow::Cow;
use std::collections::HashMap;
use std::io::Cursor;

use maplit::hashmap;
use serde::Deserialize;

use crate::from_reader;
use crate::from_slice;

// For "from_reader" data in owned and for "from_slice" data is borrowed

#[derive(Debug, Deserialize, Eq, Hash, PartialEq)]
struct Bytestring<'a>(#[serde(borrow)] Cow<'a, [u8]>);

impl<'a> From<&'a [u8]> for Bytestring<'a> {
    fn from(value: &'a [u8]) -> Self {
        Bytestring(Cow::Borrowed(value))
    }
}

impl<'a, 'b> PartialEq<&'b [u8]> for Bytestring<'a> {
    fn eq(&self, rhs: &&'b [u8]) -> bool {
        self.0 == *rhs
    }
}

#[derive(Debug, Deserialize, Eq, PartialEq)]
struct BytestringArray<'a>(#[serde(borrow)] Vec<Bytestring<'a>>);

#[derive(Debug, Deserialize, Eq, PartialEq)]
struct BytestringObject<'a>(#[serde(borrow)] HashMap<Bytestring<'a>, Bytestring<'a>>);

#[derive(Debug, Deserialize, Eq, PartialEq)]
struct TwoBytestrings<'a>(
    #[serde(borrow)] Bytestring<'a>,
    #[serde(borrow)] Bytestring<'a>,
);

#[derive(Debug, Deserialize, Eq, PartialEq)]
enum BytestringVariant<'a> {
    TestUnit,
    TestNewtype(Bytestring<'a>),
    TestTuple(
        #[serde(borrow)] Bytestring<'a>,
        #[serde(borrow)] Bytestring<'a>,
    ),
    TestStruct {
        #[serde(borrow)]
        abc: Bytestring<'a>,
        #[serde(borrow)]
        def: Bytestring<'a>,
    },
}

#[derive(Debug, Deserialize, Eq, PartialEq)]
enum StringVariant {
    TestUnit,
    TestNewtype(String),
    TestTuple(String, String),
    TestStruct { abc: String, def: String },
}

#[test]
fn test_basic_array() {
    let bser_v2 =
        b"\x00\x02\x00\x00\x00\x00\x05\x11\x00\x00\x00\x00\x03\x02\x02\x03\x03Tom\x02\x03\x05Jerry";
    let decoded = from_slice::<BytestringArray<'_>>(bser_v2).unwrap();
    assert_eq!(decoded.0, vec![&b"Tom"[..], &b"Jerry"[..]]);

    let reader = Cursor::new(bser_v2.to_vec());
    let decoded: Vec<String> = from_reader(reader).unwrap();
    let expected = vec!["Tom", "Jerry"];
    assert_eq!(decoded, expected);
}

#[test]
fn test_basic_object() {
    let bser_v2 =
        b"\x00\x02\x00\x00\x00\x00\x05\x0f\x00\x00\x00\x01\x03\x01\x02\x03\x03abc\x02\x03\x03def";
    let decoded = from_slice::<BytestringObject<'_>>(bser_v2).unwrap();
    let expected = hashmap! {
        Bytestring::from(&b"abc"[..]) => Bytestring::from(&b"def"[..])
    };
    assert_eq!(decoded.0, expected);

    let reader = Cursor::new(bser_v2.to_vec());
    let decoded: HashMap<String, String> = from_reader(reader).unwrap();
    let expected = hashmap! {
        "abc".into() => "def".into()
    };
    assert_eq!(decoded, expected);
}

#[test]
fn test_basic_tuple() {
    let bser_v2 =
        b"\x00\x02\x00\x00\x00\x00\x05\x11\x00\x00\x00\x00\x03\x02\x02\x03\x03Tom\x02\x03\x05Jerry";
    let decoded = from_slice::<TwoBytestrings<'_>>(bser_v2).unwrap();
    assert_eq!(decoded.0, &b"Tom"[..]);
    assert_eq!(decoded.1, &b"Jerry"[..]);

    let reader = Cursor::new(bser_v2.to_vec());
    let decoded: (String, String) = from_reader(reader).unwrap();
    let expected: (String, String) = ("Tom".into(), "Jerry".into());
    assert_eq!(decoded, expected);
}

#[test]
fn test_bare_variant() {
    // "TestUnit"
    let bser_v2 = b"\x00\x02\x00\x00\x00\x00\x05\x0b\x00\x00\x00\x02\x03\x08TestUnit";
    let decoded = from_slice::<BytestringVariant<'_>>(bser_v2).unwrap();
    assert_eq!(decoded, BytestringVariant::TestUnit);

    let reader = Cursor::new(bser_v2.to_vec());
    let decoded: StringVariant = from_reader(reader).unwrap();
    assert_eq!(decoded, StringVariant::TestUnit);
}

#[test]
fn test_unit_variant() {
    // {"TestUnit": null}
    let bser_v2 = b"\x00\x02\x00\x00\x00\x00\x05\x0f\x00\x00\x00\x01\x03\x01\x02\x03\x08TestUnit\n";
    let decoded = from_slice::<BytestringVariant<'_>>(bser_v2).unwrap();
    assert_eq!(decoded, BytestringVariant::TestUnit);

    let reader = Cursor::new(bser_v2.to_vec());
    let decoded: StringVariant = from_reader(reader).unwrap();
    assert_eq!(decoded, StringVariant::TestUnit);
}

#[test]
fn test_newtype_variant() {
    // {"TestNewtype": "foobar"}
    let bser_v2 =
        b"\x00\x02\x00\x00\x00\x00\x05\x1a\x00\x00\x00\x01\x03\x01\x02\x03\x0bTestNewtype\
                    \x02\x03\x06foobar";
    let decoded = from_slice::<BytestringVariant<'_>>(bser_v2).unwrap();
    assert_eq!(
        decoded,
        BytestringVariant::TestNewtype((&b"foobar"[..]).into())
    );

    let reader = Cursor::new(bser_v2.to_vec());
    let decoded: StringVariant = from_reader(reader).unwrap();
    assert_eq!(decoded, StringVariant::TestNewtype("foobar".into()));
}

#[test]
fn test_tuple_variant() {
    // {"TestTuple": ["foo", "bar"]}
    let bser_v2 = b"\x00\x02\x00\x00\x00\x00\x05\x1e\x00\x00\x00\x01\x03\x01\x02\x03\tTestTuple\
                    \x00\x03\x02\x02\x03\x03foo\x02\x03\x03bar";
    let decoded = from_slice::<BytestringVariant<'_>>(bser_v2).unwrap();
    assert_eq!(
        decoded,
        BytestringVariant::TestTuple((&b"foo"[..]).into(), (&b"bar"[..]).into())
    );

    let reader = Cursor::new(bser_v2.to_vec());
    let decoded: StringVariant = from_reader(reader).unwrap();
    assert_eq!(
        decoded,
        StringVariant::TestTuple("foo".into(), "bar".into())
    );
}

#[test]
fn test_struct_variant() {
    // {"TestStruct": {"abc": "foo", "def": "bar"}}
    let bser_v2 = b"\x00\x02\x00\x00\x00\x00\x05+\x00\x00\x00\x01\x03\x01\x02\x03\nTestStruct\
                    \x01\x03\x02\x02\x03\x03abc\x02\x03\x03foo\x02\x03\x03def\x02\x03\x03bar";
    let decoded = from_slice::<BytestringVariant<'_>>(bser_v2).unwrap();
    assert_eq!(
        decoded,
        BytestringVariant::TestStruct {
            abc: (&b"foo"[..]).into(),
            def: (&b"bar"[..]).into(),
        }
    );

    let reader = Cursor::new(bser_v2.to_vec());
    let decoded: StringVariant = from_reader(reader).unwrap();
    assert_eq!(
        decoded,
        StringVariant::TestStruct {
            abc: "foo".into(),
            def: "bar".into(),
        }
    );
}

#[derive(Debug, Deserialize, Eq, PartialEq)]
struct BytestringTemplateObject<'a> {
    abc: i32,
    #[serde(borrow)]
    def: Option<Bytestring<'a>>,
    ghi: Option<i64>,
}

#[derive(Debug, Deserialize, Eq, PartialEq)]
struct TemplateObject {
    abc: i32,
    def: Option<String>,
    ghi: Option<i64>,
}

#[test]
fn test_template() {
    // Logical expansion of this template:
    // [
    //   {"abc": 123, "def": "bar", "ghi": null},
    //   {"abc": 456,               "ghi": 789},
    // ]
    //
    // The second "def" is skipped.
    let bser_v2 = b"\x00\x02\x00\x00\x00\x00\x05(\x00\x00\x00\x0b\x00\x03\x03\x02\x03\x03abc\x02\
                    \x03\x03def\x02\x03\x03ghi\x03\x02\x03{\x02\x03\x03bar\n\x04\xc8\x01\x0c\x04\
                    \x15\x03";
    let decoded = from_slice::<Vec<BytestringTemplateObject<'_>>>(bser_v2).unwrap();
    assert_eq!(
        decoded,
        vec![
            BytestringTemplateObject {
                abc: 123,
                def: Some((&b"bar"[..]).into()),
                ghi: None,
            },
            BytestringTemplateObject {
                abc: 456,
                def: None,
                ghi: Some(789),
            },
        ]
    );

    let reader = Cursor::new(bser_v2.to_vec());
    let decoded: Vec<TemplateObject> = from_reader(reader).unwrap();
    assert_eq!(
        decoded,
        vec![
            TemplateObject {
                abc: 123,
                def: Some("bar".into()),
                ghi: None,
            },
            TemplateObject {
                abc: 456,
                def: None,
                ghi: Some(789),
            },
        ]
    );
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(untagged)]
pub enum RequestResult<T, E> {
    Error(E),
    Ok(T),
}

#[derive(Debug, Deserialize, PartialEq)]
pub struct FileInfo {
    name: String,
    size: u32,
}

#[derive(Debug, Deserialize, PartialEq)]
pub struct Files {
    files: Vec<FileInfo>,
}

#[derive(Debug, Deserialize, PartialEq)]
pub struct RequestError {
    error: String,
}

#[derive(Debug, Deserialize, PartialEq)]
pub struct BytestringFileInfo<'a> {
    #[serde(borrow)]
    name: Bytestring<'a>,
    size: u32,
}

#[derive(Debug, Deserialize, PartialEq)]
pub struct BytestringFiles<'a> {
    #[serde(borrow)]
    files: Vec<BytestringFileInfo<'a>>,
}

#[derive(Debug, Deserialize, PartialEq)]
pub struct BytestringRequestError<'a> {
    #[serde(borrow)]
    error: Bytestring<'a>,
}

#[test]
fn test_compact_arrays() {
    // {
    //  "files": [
    //      {
    //          "name": "fbcode/scm/hg/lib/hg_watchman_client/tester/target/debug",
    //          "size": 384
    //      },
    //      {
    //          "name": "fbcode/scm/hg/lib/hg_watchman_client/tester/target/debug/deps",
    //          "size": 3200
    //      },
    //   ]
    // }

    let bser_v2 = b"\x00\x02\x00\x00\x00\x00\x04\xeb\x00\x01\x03\x04\x02\x03\x05files\x0b\x00\x03\x02\x0d\x03\x04name\x0d\x03\x04size\x03\x02\x02\x038fbcode/scm/hg/lib/hg_watchman_client/tester/target/debug\x04\x80\x01\x02\x03\x3dfbcode/scm/hg/lib/hg_watchman_client/tester/target/debug/deps\x04\x80\x0c\x02\x03\x05clock\x0d\x03\x19c\x3a1525428959\x3a45796\x3a2\x3a7717\x02\x03\x11is_fresh_instance\x09\x02\x03\x07version\x0d\x03\x054.9.1";

    let decoded = from_slice::<BytestringFiles<'_>>(bser_v2).unwrap();

    assert_eq!(
        decoded,
        BytestringFiles {
            files: vec![
                BytestringFileInfo {
                    name: (&b"fbcode/scm/hg/lib/hg_watchman_client/tester/target/debug"[..]).into(),
                    size: 384,
                },
                BytestringFileInfo {
                    name: (&b"fbcode/scm/hg/lib/hg_watchman_client/tester/target/debug/deps"[..])
                        .into(),
                    size: 3200,
                },
            ],
        }
    );

    let reader = Cursor::new(bser_v2.to_vec());
    let decoded: Files = from_reader(reader).unwrap();

    assert_eq!(
        decoded,
        Files {
            files: vec![
                FileInfo {
                    name: "fbcode/scm/hg/lib/hg_watchman_client/tester/target/debug".into(),
                    size: 384,
                },
                FileInfo {
                    name: "fbcode/scm/hg/lib/hg_watchman_client/tester/target/debug/deps".into(),
                    size: 3200,
                },
            ],
        }
    );
}

#[test]
fn test_compact_arrays_untagged_enum() {
    let bser_v2 = b"\x00\x02\x00\x00\x00\x00\x04\xeb\x00\x01\x03\x04\x02\x03\x05files\x0b\x00\x03\x02\x0d\x03\x04name\x0d\x03\x04size\x03\x02\x02\x038fbcode/scm/hg/lib/hg_watchman_client/tester/target/debug\x04\x80\x01\x02\x03\x3dfbcode/scm/hg/lib/hg_watchman_client/tester/target/debug/deps\x04\x80\x0c\x02\x03\x05clock\x0d\x03\x19c\x3a1525428959\x3a45796\x3a2\x3a7717\x02\x03\x11is_fresh_instance\x09\x02\x03\x07version\x0d\x03\x054.9.1";

    let decoded =
        from_slice::<RequestResult<BytestringFiles<'_>, BytestringRequestError<'_>>>(bser_v2)
            .unwrap();

    assert_eq!(
        decoded,
        RequestResult::Ok(BytestringFiles {
            files: vec![
                BytestringFileInfo {
                    name: (&b"fbcode/scm/hg/lib/hg_watchman_client/tester/target/debug"[..]).into(),
                    size: 384,
                },
                BytestringFileInfo {
                    name: (&b"fbcode/scm/hg/lib/hg_watchman_client/tester/target/debug/deps"[..])
                        .into(),
                    size: 3200,
                },
            ],
        })
    );

    let reader = Cursor::new(bser_v2.to_vec());

    let decoded: RequestResult<Files, RequestError> = from_reader(reader).unwrap();

    assert_eq!(
        decoded,
        RequestResult::Ok(Files {
            files: vec![
                FileInfo {
                    name: "fbcode/scm/hg/lib/hg_watchman_client/tester/target/debug".into(),
                    size: 384,
                },
                FileInfo {
                    name: "fbcode/scm/hg/lib/hg_watchman_client/tester/target/debug/deps".into(),
                    size: 3200,
                },
            ],
        })
    );
}

#[test]
// non compact arrays
fn test_arrays() {
    // {
    //    "files": [
    //      {
    //          "name": "fbcode/scm/hg/lib/hg_watchman_client/tester/target/debug",
    //          "size": 384
    //      },
    //      {
    //          "name": "fbcode/scm/hg/lib/hg_watchman_client/tester",
    //          "size": 224
    //      }
    //    ]
    // }

    let bser_v2 = b"\x00\x02\x00\x00\x00\x00\x04\xea\x00\x01\x03\x04\x02\x03\x07version\x02\x03\x054.9.1\x02\x03\x11is_fresh_instance\x09\x02\x03\x05clock\x02\x03\x19c\x3a1525428959\x3a45796\x3a2\x3a9642\x02\x03\x05files\x00\x03\x02\x01\x03\x02\x02\x03\x04size\x04\x80\x01\x02\x03\x04name\x02\x038fbcode/scm/hg/lib/hg_watchman_client/tester/target/debug\x01\x03\x02\x02\x03\x04size\x04\xe0\x00\x02\x03\x04name\x02\x03\x2bfbcode/scm/hg/lib/hg_watchman_client/tester";

    let decoded = from_slice::<BytestringFiles<'_>>(bser_v2).unwrap();

    assert_eq!(
        decoded,
        BytestringFiles {
            files: vec![
                BytestringFileInfo {
                    name: (&b"fbcode/scm/hg/lib/hg_watchman_client/tester/target/debug"[..]).into(),
                    size: 384,
                },
                BytestringFileInfo {
                    name: (&b"fbcode/scm/hg/lib/hg_watchman_client/tester"[..]).into(),
                    size: 224,
                },
            ],
        }
    );

    let reader = Cursor::new(bser_v2.to_vec());
    let decoded: Files = from_reader(reader).unwrap();

    assert_eq!(
        decoded,
        Files {
            files: vec![
                FileInfo {
                    name: "fbcode/scm/hg/lib/hg_watchman_client/tester/target/debug".into(),
                    size: 384,
                },
                FileInfo {
                    name: "fbcode/scm/hg/lib/hg_watchman_client/tester".into(),
                    size: 224,
                },
            ],
        }
    );
}

#[test]
// non compact arrays
fn test_arrays_untagged_enum() {
    let bser_v2 = b"\x00\x02\x00\x00\x00\x00\x04\xea\x00\x01\x03\x04\x02\x03\x07version\x02\x03\x054.9.1\x02\x03\x11is_fresh_instance\x09\x02\x03\x05clock\x02\x03\x19c\x3a1525428959\x3a45796\x3a2\x3a9642\x02\x03\x05files\x00\x03\x02\x01\x03\x02\x02\x03\x04size\x04\x80\x01\x02\x03\x04name\x02\x038fbcode/scm/hg/lib/hg_watchman_client/tester/target/debug\x01\x03\x02\x02\x03\x04size\x04\xe0\x00\x02\x03\x04name\x02\x03\x2bfbcode/scm/hg/lib/hg_watchman_client/tester";

    let decoded =
        from_slice::<RequestResult<BytestringFiles<'_>, BytestringRequestError<'_>>>(bser_v2)
            .unwrap();

    assert_eq!(
        decoded,
        RequestResult::Ok(BytestringFiles {
            files: vec![
                BytestringFileInfo {
                    name: (&b"fbcode/scm/hg/lib/hg_watchman_client/tester/target/debug"[..]).into(),
                    size: 384,
                },
                BytestringFileInfo {
                    name: (&b"fbcode/scm/hg/lib/hg_watchman_client/tester"[..]).into(),
                    size: 224,
                },
            ],
        })
    );

    let reader = Cursor::new(bser_v2.to_vec());
    let decoded: RequestResult<Files, RequestError> = from_reader(reader).unwrap();

    assert_eq!(
        decoded,
        RequestResult::Ok(Files {
            files: vec![
                FileInfo {
                    name: "fbcode/scm/hg/lib/hg_watchman_client/tester/target/debug".into(),
                    size: 384,
                },
                FileInfo {
                    name: "fbcode/scm/hg/lib/hg_watchman_client/tester".into(),
                    size: 224,
                },
            ],
        })
    );
}


/*
 * 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 super::read::DeRead;
use super::reentrant::ReentrantGuard;
use super::Deserializer;
use crate::errors::*;

macro_rules! impl_enum_access {
    ($type:ident) => {
        impl<'a, 'de, R> de::EnumAccess<'de> for $type<'a, R>
        where
            R: 'a + DeRead<'de>,
        {
            type Error = Error;
            type Variant = Self;

            fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self)>
            where
                V: de::DeserializeSeed<'de>,
            {
                let val = seed.deserialize(&mut *self.de)?;
                Ok((val, self))
            }
        }
    };
}

/// Deserialize access for unit, struct and tuple variants.
pub struct VariantAccess<'a, R> {
    de: &'a mut Deserializer<R>,
}

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

impl_enum_access!(VariantAccess);

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

    fn unit_variant(self) -> Result<()> {
        de::Deserialize::deserialize(self.de)
    }

    fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value>
    where
        T: de::DeserializeSeed<'de>,
    {
        seed.deserialize(self.de)
    }

    fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        de::Deserializer::deserialize_any(self.de, visitor)
    }

    fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        de::Deserializer::deserialize_any(self.de, visitor)
    }
}

/// Deserialize access for plain unit variants.
pub struct UnitVariantAccess<'a, R> {
    de: &'a mut Deserializer<R>,
}

impl<'a, 'de, R> UnitVariantAccess<'a, R>
where
    R: 'a + DeRead<'de>,
{
    pub fn new(de: &'a mut Deserializer<R>) -> Self {
        UnitVariantAccess { de }
    }
}

impl_enum_access!(UnitVariantAccess);

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

    fn unit_variant(self) -> Result<()> {
        Ok(())
    }

    fn newtype_variant_seed<T>(self, _seed: T) -> Result<T::Value>
    where
        T: de::DeserializeSeed<'de>,
    {
        Err(de::Error::invalid_type(
            de::Unexpected::UnitVariant,
            &"newtype variant",
        ))
    }

    fn tuple_variant<V>(self, _len: usize, _visitor: V) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        Err(de::Error::invalid_type(
            de::Unexpected::UnitVariant,
            &"tuple variant",
        ))
    }

    fn struct_variant<V>(self, _fields: &'static [&'static str], _visitor: V) -> Result<V::Value>
    where
        V: de::Visitor<'de>,
    {
        Err(de::Error::invalid_type(
            de::Unexpected::UnitVariant,
            &"struct variant",
        ))
    }
}


/*
 * 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::Write;

/// A writer that counts how many bytes were written.
pub struct CountWrite {
    count: usize,
}

impl CountWrite {
    pub fn new() -> Self {
        CountWrite { count: 0 }
    }

    #[inline]
    pub fn count(&self) -> usize {
        self.count
    }
}

impl Write for CountWrite {
    #[inline]
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.count += buf.len();
        Ok(buf.len())
    }

    #[inline]
    fn flush(&mut self) -> io::Result<()> {
        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.
 */

mod count_write;
#[cfg(test)]
mod test;

use std::io;

use bytes::BufMut;
use serde::ser;
use serde::ser::Serialize;

use self::count_write::CountWrite;
use crate::errors::*;
use crate::header::*;

// How full must the buffer get before we start flushing it?
const HIGHWATER: usize = 4096;

pub fn serialize<W, T>(mut writer: W, value: T) -> Result<W>
where
    W: io::Write,
    T: ser::Serialize,
{
    // For the PDU info we need to first count how many bytes it is going to be.
    let mut count_serializer = Serializer::new(CountWrite::new());
    value.serialize(&mut count_serializer)?;
    let count_write = count_serializer.finish()?;
    let count = count_write.count();

    // Now write out the first bits of PDU info.
    // TODO: make this tokio AsyncWrite compatible
    // TODO: support capabilities
    writer.write_all(b"\x00\x02\x00\x00\x00\x00")?;
    let mut serializer = Serializer::new(writer);
    count.serialize(&mut serializer)?;

    // Finally, serialize the value
    value.serialize(&mut serializer)?;
    Ok(serializer.finish()?)
}

pub struct Serializer<W> {
    writer: W,
    scratch: Vec<u8>,
    offset: usize,
}

/// If the value fits in the size specified by `$to`, call the `$put` function.
///
/// This works for all $val types except for `u64`.
macro_rules! maybe_put_int {
    ($self:ident, $val:expr, $to:ident, $put:ident) => {
        let min = $to::min_value() as i64;
        let max = $to::max_value() as i64;
        let val = $val as i64;
        if val >= min && val <= max {
            return $self.$put($val as $to);
        }
    };
}

impl<W> Serializer<W>
where
    W: io::Write,
{
    // Create a new BSER serializer without leading PDU info.
    fn new(writer: W) -> Self {
        Serializer {
            writer,
            scratch: Vec::with_capacity(HIGHWATER * 2),
            offset: 0,
        }
    }

    /// Write out any internally cached data through to the writer.
    #[inline]
    pub fn flush(&mut self) -> io::Result<()> {
        while self.offset < self.scratch.len() {
            match self.writer.write(&self.scratch[self.offset..]) {
                Ok(n) => self.offset += n,
                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
                Err(e) => return Err(e),
            };
        }
        self.offset = 0;
        unsafe {
            self.scratch.set_len(0);
        }
        Ok(())
    }

    /// Finish writing any buffered data and unwrap the inner `Write`.
    pub fn finish(mut self) -> io::Result<W> {
        self.flush()?;
        Ok(self.writer)
    }

    /// Try flushing any buffered data. If successful, unwrap the inner
    /// `Write`. On failure, return `Self` and the error.
    #[inline]
    pub fn try_finish(mut self) -> ::std::result::Result<W, (Self, io::Error)> {
        match self.flush() {
            Ok(()) => Ok(self.writer),
            Err(e) => Err((self, e)),
        }
    }

    #[inline]
    fn maybe_flush(&mut self) -> Result<()> {
        if self.scratch.len() > HIGHWATER {
            Ok(self.flush()?)
        } else {
            Ok(())
        }
    }

    #[inline]
    fn put_i8(&mut self, v: i8) {
        self.scratch.push(BSER_INT8);
        self.scratch.push(v as u8);
    }

    #[inline]
    fn put_i16(&mut self, v: i16) {
        maybe_put_int!(self, v, i8, put_i8);
        self.scratch.push(BSER_INT16);
        #[cfg(target_endian = "little")]
        self.scratch.put_i16_le(v);
        #[cfg(target_endian = "big")]
        self.scratch.put_i16_be(v);
    }

    #[inline]
    fn put_i32(&mut self, v: i32) {
        maybe_put_int!(self, v, i16, put_i16);
        self.scratch.push(BSER_INT32);
        #[cfg(target_endian = "little")]
        self.scratch.put_i32_le(v);
        #[cfg(target_endian = "big")]
        self.scratch.put_i32_be(v);
    }

    #[inline]
    fn put_i64(&mut self, v: i64) {
        maybe_put_int!(self, v, i32, put_i32);
        self.scratch.push(BSER_INT64);
        #[cfg(target_endian = "little")]
        self.scratch.put_i64_le(v);
        #[cfg(target_endian = "big")]
        self.scratch.put_i64_be(v);
    }
}

macro_rules! write_val {
    ($self:ident, $val:expr) => {{
        $self.maybe_flush()?;
        $self.scratch.push($val);
        Ok(())
    }};
    ($self:ident, $val:expr, $put:ident) => {{
        $self.maybe_flush()?;
        $self.$put($val);
        Ok(())
    }};
}

impl<'a, W> ser::Serializer for &'a mut Serializer<W>
where
    W: io::Write,
{
    type Ok = ();
    type Error = Error;

    type SerializeSeq = Compound<'a, W>;
    type SerializeTuple = Compound<'a, W>;
    type SerializeTupleStruct = Compound<'a, W>;
    type SerializeTupleVariant = Compound<'a, W>;
    type SerializeMap = Compound<'a, W>;
    type SerializeStruct = Compound<'a, W>;
    type SerializeStructVariant = Compound<'a, W>;

    #[inline]
    fn serialize_bool(self, value: bool) -> Result<()> {
        if value {
            write_val!(self, BSER_TRUE)
        } else {
            write_val!(self, BSER_FALSE)
        }
    }

    #[inline]
    fn serialize_i8(self, v: i8) -> Result<()> {
        write_val!(self, v, put_i8)
    }

    #[inline]
    fn serialize_i16(self, v: i16) -> Result<()> {
        write_val!(self, v, put_i16)
    }

    #[inline]
    fn serialize_i32(self, v: i32) -> Result<()> {
        write_val!(self, v, put_i32)
    }

    #[inline]
    fn serialize_i64(self, v: i64) -> Result<()> {
        write_val!(self, v, put_i64)
    }

    #[inline]
    fn serialize_u8(self, v: u8) -> Result<()> {
        maybe_put_int!(self, v, i8, serialize_i8);
        self.serialize_i16(v as i16)
    }

    #[inline]
    fn serialize_u16(self, v: u16) -> Result<()> {
        maybe_put_int!(self, v, i16, serialize_i16);
        self.serialize_i32(v as i32)
    }

    #[inline]
    fn serialize_u32(self, v: u32) -> Result<()> {
        maybe_put_int!(self, v, i32, serialize_i32);
        self.serialize_i64(v as i64)
    }

    #[inline]
    fn serialize_u64(self, v: u64) -> Result<()> {
        // maybe_put_int! doesn't work for u64 because it converts to i64
        // internally.
        if v > (i64::max_value() as u64) {
            Err(Error::SerU64TooBig { v })
        } else {
            self.serialize_i64(v as i64)
        }
    }

    #[inline]
    fn serialize_f32(self, v: f32) -> Result<()> {
        self.serialize_f64(v as f64)
    }

    #[inline]
    fn serialize_f64(self, v: f64) -> Result<()> {
        self.maybe_flush()?;
        self.scratch.push(BSER_REAL);
        #[cfg(target_endian = "little")]
        self.scratch.put_f64_le(v);
        #[cfg(target_endian = "big")]
        self.scratch.put_f64_be(v);
        Ok(())
    }

    #[inline]
    fn serialize_char(self, v: char) -> Result<()> {
        self.serialize_str(&v.to_string())
    }

    #[inline]
    fn serialize_str(self, v: &str) -> Result<()> {
        self.maybe_flush()?;
        self.scratch.push(BSER_UTF8STRING);
        self.put_i64(v.len() as i64);
        self.scratch.extend_from_slice(v.as_bytes());
        Ok(())
    }

    #[inline]
    fn serialize_bytes(self, v: &[u8]) -> Result<()> {
        self.maybe_flush()?;
        self.scratch.push(BSER_BYTESTRING);
        self.put_i64(v.len() as i64);
        self.scratch.extend_from_slice(v);
        Ok(())
    }

    #[inline]
    fn serialize_unit(self) -> Result<()> {
        write_val!(self, BSER_NULL)
    }

    #[inline]
    fn serialize_unit_struct(self, _name: &'static str) -> Result<()> {
        self.serialize_unit()
    }

    #[inline]
    fn serialize_unit_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        variant: &'static str,
    ) -> Result<()> {
        self.serialize_str(variant)
    }

    #[inline]
    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<()>
    where
        T: ?Sized + ser::Serialize,
    {
        // This is e.g. E(T). Ignore the E.
        value.serialize(self)
    }

    #[inline]
    fn serialize_newtype_variant<T>(
        self,
        _name: &'static str,
        _variant_index: u32,
        variant: &'static str,
        value: &T,
    ) -> Result<()>
    where
        T: ?Sized + ser::Serialize,
    {
        use serde::ser::SerializeStruct;
        // This is e.g. E { N(T) }, where N is the variant.
        // Serialize this as {variant: value}
        let mut ser_struct = self.serialize_struct("", 1)?;
        ser_struct.serialize_field(variant, value)?;
        ser_struct.end()
    }

    #[inline]
    fn serialize_none(self) -> Result<()> {
        self.serialize_unit()
    }

    #[inline]
    fn serialize_some<T>(self, value: &T) -> Result<()>
    where
        T: ?Sized + ser::Serialize,
    {
        value.serialize(self)
    }

    #[inline]
    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
        match len {
            None => Err(Error::SerNeedSize { kind: "sequence" }),
            Some(len) => self.serialize_tuple(len),
        }
    }

    #[inline]
    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
        // (A, B, C) etc. Serialize this as an array.
        self.maybe_flush()?;
        self.scratch.push(BSER_ARRAY);
        self.put_i64(len as i64);
        Ok(Compound { ser: self })
    }

    #[inline]
    fn serialize_tuple_struct(
        self,
        _name: &'static str,
        len: usize,
    ) -> Result<Self::SerializeTupleStruct> {
        self.serialize_tuple(len)
    }

    #[inline]
    fn serialize_tuple_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        variant: &'static str,
        len: usize,
    ) -> Result<Self::SerializeTupleVariant> {
        // This is e.g. E { N(A, B, C) }, where N is the variant. Serialize this
        // as { variant: [values] }.
        self.maybe_flush()?;
        self.scratch.push(BSER_OBJECT);
        self.put_i8(1);
        self.serialize_str(variant)?;
        self.serialize_tuple(len)
    }

    #[inline]
    fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
        match len {
            None => Err(Error::SerNeedSize { kind: "map" }),
            Some(len) => self.serialize_struct("", len),
        }
    }

    #[inline]
    fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
        self.maybe_flush()?;
        // BSER objects are serialized as <BSER_OBJECT><len>(<key><value>...).
        self.scratch.push(BSER_OBJECT);
        self.put_i64(len as i64);
        Ok(Compound { ser: self })
    }

    #[inline]
    fn serialize_struct_variant(
        self,
        name: &'static str,
        variant_index: u32,
        variant: &'static str,
        len: usize,
    ) -> Result<Self::SerializeStructVariant> {
        // This is e.g. E { N { foo: A, bar: B } }, where N is the
        // variant. Serialize this as { variant: { foo: valA, bar: valB } }.
        // That's really the same as serialize_tuple_variant.
        self.serialize_tuple_variant(name, variant_index, variant, len)
    }
}

#[doc(hidden)]
pub struct Compound<'a, W> {
    ser: &'a mut Serializer<W>,
}

macro_rules! impl_compound {
    ($trait:ty, [$($fns:ident),*]) => {
        impl<'a, W> $trait for Compound<'a, W>
            where W: io::Write
        {
            type Ok = ();
            type Error = Error;

            $(
                #[inline]
                fn $fns<T>(&mut self, value: &T) -> Result<()>
                    where T: ?Sized + ser::Serialize
                {
                    value.serialize(&mut *self.ser)
                }
            )*

            #[inline]
            fn end(self) -> Result<()> {
                Ok(())
            }
        }
    }
}

impl_compound!(ser::SerializeSeq, [serialize_element]);
impl_compound!(ser::SerializeTuple, [serialize_element]);
impl_compound!(ser::SerializeTupleStruct, [serialize_field]);
impl_compound!(ser::SerializeTupleVariant, [serialize_field]);
impl_compound!(ser::SerializeMap, [serialize_key, serialize_value]);

macro_rules! impl_compound_struct {
    ($trait:ty) => {
        impl<'a, W> $trait for Compound<'a, W>
        where
            W: io::Write,
        {
            type Ok = ();
            type Error = Error;

            #[inline]
            fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
            where
                T: ?Sized + ser::Serialize,
            {
                // TODO: this can go wrong if writing out the key succeeds but
                // writing the value fails.
                ser::SerializeMap::serialize_key(self, key)?;
                ser::SerializeMap::serialize_value(self, value)
            }

            #[inline]
            fn end(self) -> Result<()> {
                ser::SerializeMap::end(self)
            }
        }
    };
}

impl_compound_struct!(ser::SerializeStruct);
impl_compound_struct!(ser::SerializeStructVariant);


/*
 * 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::f64::consts;

use serde::Serialize;

use super::serialize;

#[derive(Debug, Serialize)]
enum TestEnum {
    TestUnit,
    TestNewtype(#[serde(with = "serde_bytes")] &'static [u8]),
    TestTuple(i8, u64),
    TestStruct { abc: (), def: char },
}

#[derive(Debug, Serialize)]
struct SerializeStruct {
    test_list: Vec<i32>,
    test_tuple: (String, Option<f64>),
    test_enum: TestEnum,
    test_enum_list: Vec<TestEnum>,
}

const BASIC_SERIALIZED: &[u8] = b"\x00\x02\x00\x00\x00\x00\x04\xcb\x00\x01\x03\x04\r\x03\t\
                                  test_list\x00\x03\x05\x03\x03\x03\x04\x03*\x04\xdb\x03\x05\x00\
                                  \x00\x08\x00\r\x03\ntest_tuple\x00\x03\x02\r\x03\x03foo\x07\
                                  \x18-DT\xfb!\t@\r\x03\ttest_enum\r\x03\x08TestUnit\r\x03\x0e\
                                  test_enum_list\x00\x03\x03\x01\x03\x01\r\x03\x0bTestNewtype\
                                  \x02\x03\tBSER test\x01\x03\x01\r\x03\tTestTuple\x00\x03\x02\x03*\
                                  \x06\xff\xff\xff\xff\xff\xff\xff\x7f\x01\x03\x01\r\x03\n\
                                  TestStruct\x00\x03\x02\r\x03\x03abc\n\r\x03\x03\
                                  def\r\x03\x04\xf0\x9f\x92\xa9";

#[test]
fn test_basic_serialize() {
    let to_serialize = SerializeStruct {
        test_list: vec![3, 4, 42, 987, 2 << 18],
        test_tuple: ("foo".into(), Some(consts::PI)),
        test_enum: TestEnum::TestUnit,
        test_enum_list: vec![
            TestEnum::TestNewtype(&b"BSER test"[..]),
            TestEnum::TestTuple(42, i64::max_value() as u64),
            TestEnum::TestStruct {
                abc: (),
                def: '\u{1f4a9}',
            },
        ],
    };

    let out = Vec::new();
    let out = serialize(out, to_serialize).unwrap();
    assert_eq!(out, BASIC_SERIALIZED);
}


/*
 * 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::path::PathBuf;

use serde::Deserialize;
use structopt::StructOpt;
use watchman_client::prelude::*;

#[derive(Debug, StructOpt)]
#[structopt(about = "Perform a glob query for a path, using watchman")]
struct Opt {
    #[structopt(default_value = ".")]
    path: PathBuf,
}

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    if let Err(err) = run().await {
        // Print a prettier error than the default
        eprintln!("{}", err);
        std::process::exit(1);
    }
    Ok(())
}

async fn run() -> Result<(), Box<dyn std::error::Error>> {
    let opt = Opt::from_args();
    let client = Connector::new().connect().await?;
    let resolved = client
        .resolve_root(CanonicalPath::canonicalize(opt.path)?)
        .await?;

    println!("resolved watch to {:?}", resolved);

    // Basic globs -> names
    let files = client.glob(&resolved, &["**/*.rs"]).await?;
    println!("files: {:#?}", files);

    query_result_type! {
        struct NameAndHash {
            name: NameField,
            hash: ContentSha1HexField,
        }
    }

    let response: QueryResult<NameAndHash> = client
        .query(
            &resolved,
            QueryRequestCommon {
                glob: Some(vec!["**/*.rs".to_string()]),
                expression: Some(Expr::Not(Box::new(Expr::Empty))),
                ..Default::default()
            },
        )
        .await?;
    println!("response: {:#?}", response);

    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.
 */

//! This example shows how to send a query to watchman and print out the files
//! changed since the given timestamp.

use std::path::PathBuf;

use structopt::StructOpt;
use watchman_client::prelude::*;

#[derive(Debug, StructOpt)]
#[structopt(about = "Query files changed since a timestamp")]
struct Opt {
    #[structopt()]
    /// Specifies the clock. Use `watchman clock <PATH>` to retrieve the current clock of a watched
    /// directory
    clock: String,

    #[structopt(short, long)]
    /// [not recommended] Uses Unix timestamp as clock
    unix_timestamp: bool,

    #[structopt(short, long, default_value = ".")]
    /// Specifies the path to watched directory
    path: PathBuf,
}

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    if let Err(err) = run().await {
        // Print a prettier error than the default
        eprintln!("{}", err);
        std::process::exit(1);
    }
    Ok(())
}

async fn run() -> Result<(), Box<dyn std::error::Error>> {
    let opt = Opt::from_args();
    let client = Connector::new().connect().await?;
    let resolved = client
        .resolve_root(CanonicalPath::canonicalize(opt.path)?)
        .await?;

    let clock_spec = if opt.unix_timestamp {
        // it is better to use watchman's clock rather than Unix timestamp.
        // see `watchman_client::pdu::ClockSpec::unix_timestamp` for details.
        ClockSpec::UnixTimestamp(opt.clock.parse()?)
    } else {
        ClockSpec::StringClock(opt.clock)
    };

    let result = client
        .query::<NameOnly>(
            &resolved,
            QueryRequestCommon {
                since: Some(Clock::Spec(clock_spec.clone())),
                ..Default::default()
            },
        )
        .await?;

    eprintln!("Clock is now: {:?}", result.clock);

    if let Some(files) = result.files {
        for file in files.iter() {
            println!("{}", file.name.display());
        }
    } else {
        eprintln!("no file changed since {:?}", clock_spec);
    }

    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::path::PathBuf;

use structopt::StructOpt;
use watchman_client::prelude::*;

#[derive(Debug, StructOpt)]
#[structopt(about = "Exercise the state-enter and state-leave commands")]
struct Opt {
    #[structopt(default_value = ".")]
    path: PathBuf,
}

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    if let Err(err) = run().await {
        // Print a prettier error than the default
        eprintln!("{}", err);
        std::process::exit(1);
    }
    Ok(())
}

async fn run() -> Result<(), Box<dyn std::error::Error>> {
    let opt = Opt::from_args();
    let client = Connector::new().connect().await?;
    let resolved = client
        .resolve_root(CanonicalPath::canonicalize(opt.path)?)
        .await?;

    println!("resolved watch to {:?}", resolved);

    client
        .state_enter(&resolved, "woot", SyncTimeout::Default, None)
        .await?;
    println!("asserted woot state");
    tokio::time::sleep(std::time::Duration::from_secs(10)).await;
    client
        .state_leave(&resolved, "woot", SyncTimeout::Default, None)
        .await?;
    println!("vacated woot state");

    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.
 */

//! This example shows how to initiate a subscription and print out
//! file changes as they are reported
use std::path::PathBuf;

use structopt::StructOpt;
use watchman_client::prelude::*;

#[derive(Debug, StructOpt)]
#[structopt(about = "Subscribe to watchman and stream file changes for a path")]
struct Opt {
    #[structopt(default_value = ".")]
    path: PathBuf,
}

#[tokio::main(flavor = "current_thread")]
async fn main() {
    if let Err(err) = run().await {
        // Print a prettier error than the default
        eprintln!("{}", err);
        std::process::exit(1);
    }
}

async fn run() -> Result<(), Box<dyn std::error::Error>> {
    let opt = Opt::from_args();
    let client = Connector::new().connect().await?;
    let resolved = client
        .resolve_root(CanonicalPath::canonicalize(opt.path)?)
        .await?;

    let (mut sub, initial) = client
        .subscribe::<NameOnly>(&resolved, SubscribeRequest::default())
        .await?;

    println!("{} {:#?}", sub.name(), initial);
    loop {
        let item = sub.next().await?;
        println!("{:#?}", item);
    }
}


/*
 * 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.
 */

//! Working with the watchman expression term syntax
use std::path::PathBuf;

use maplit::hashmap;
use serde::Serialize;
use serde_bser::value::Value;

use crate::pdu::*;

/// An expression term used to filter candidate files from query results.
#[derive(Serialize, Debug, Clone)]
#[serde(into = "Value")]
pub enum Expr {
    /// Always evaluates to true
    True,

    /// Always evaluates to false
    False,

    /// Inverts the match state of the child term
    Not(Box<Expr>),

    /// Evaluates to true IFF all child terms evaluate true
    All(Vec<Expr>),

    /// Evaluates to true if any child terms evaluate true
    Any(Vec<Expr>),

    /// Match on the parent directory structure
    /// <https://facebook.github.io/watchman/docs/expr/dirname.html>
    DirName(DirNameTerm),

    /// Evaluates as true if the file exists, has size 0 and is a regular
    /// file or directory.
    /// <https://facebook.github.io/watchman/docs/expr/empty.html>
    Empty,

    /// Evaluates as true if the file exists; this is useful for filtering
    /// out notifications for files that have been deleted.
    /// Note that this term doesn't add value for `path` and `glob` generators
    /// which implicitly add this constraint.
    /// <https://facebook.github.io/watchman/docs/expr/exists.html>
    Exists,

    /// Performs a glob-style match against the file name
    /// <https://facebook.github.io/watchman/docs/expr/match.html>
    Match(MatchTerm),

    /// Performs an exact match against the file name.
    /// <https://facebook.github.io/watchman/docs/expr/name.html>
    Name(NameTerm),

    /// Use PCRE to match the filename.
    /// Note that this is an optional server feature and using this term
    /// on a server that doesn't support this feature will generate an
    /// error in response to the query.
    /// <https://facebook.github.io/watchman/docs/expr/pcre.html>
    Pcre(PcreTerm),

    /// Evaluates as true if the specified time property of the file is
    /// greater than the since value.
    /// <https://facebook.github.io/watchman/docs/expr/since.html>
    Since(SinceTerm),

    /// Evaluate as true if the size of a file matches the specified constraint.
    /// Files that do not presently exist will evaluate as false.
    /// <https://facebook.github.io/watchman/docs/expr/size.html>
    Size(RelOp),

    /// Evaluate as true if the filename suffix (also known as extension)
    /// matches the provided set of suffixes.
    /// Suffix matches are always case insensitive.
    /// `php` matches `foo.php` and `foo.PHP` but not `foophp`.
    /// <https://facebook.github.io/watchman/docs/expr/suffix.html>
    Suffix(Vec<PathBuf>),

    /// Evaluate as true if the file type exactly matches the specified type.
    FileType(FileType),
}

impl Into<Value> for Expr {
    fn into(self) -> Value {
        match self {
            Self::True => "true".into(),
            Self::False => "false".into(),
            Self::Not(expr) => Value::Array(vec!["not".into(), (*expr).into()]),
            Self::All(expr) => {
                let mut expr: Vec<Value> = expr.into_iter().map(Into::into).collect();
                expr.insert(0, "allof".into());
                Value::Array(expr)
            }
            Self::Any(expr) => {
                let mut expr: Vec<Value> = expr.into_iter().map(Into::into).collect();
                expr.insert(0, "anyof".into());
                Value::Array(expr)
            }
            Self::DirName(term) => {
                let mut expr: Vec<Value> = vec!["dirname".into(), term.path.try_into().unwrap()];
                if let Some(depth) = term.depth {
                    expr.push(depth.into_term("depth"));
                }
                expr.into()
            }
            Self::Empty => "empty".into(),
            Self::Exists => "exists".into(),
            Self::Match(term) => vec![
                "match".into(),
                term.glob.into(),
                if term.wholename {
                    "wholename"
                } else {
                    "basename"
                }
                .into(),
                Value::Object(hashmap! {
                    "includedotfiles".to_string() => term.include_dot_files.into(),
                    "noescape".to_string() => term.no_escape.into()
                }),
            ]
            .into(),
            Self::Name(term) => vec![
                "name".into(),
                Value::Array(
                    term.paths
                        .into_iter()
                        .map(|p| p.try_into().unwrap())
                        .collect(),
                ),
                if term.wholename {
                    "wholename"
                } else {
                    "basename"
                }
                .into(),
            ]
            .into(),
            Self::Pcre(term) => vec![
                "pcre".into(),
                term.pattern.into(),
                if term.wholename {
                    "wholename"
                } else {
                    "basename"
                }
                .into(),
            ]
            .into(),
            Self::Since(term) => match term {
                SinceTerm::ObservedClock(c) => {
                    vec!["since".into(), c.into(), "oclock".into()].into()
                }
                SinceTerm::CreatedClock(c) => {
                    vec!["since".into(), c.into(), "cclock".into()].into()
                }
                SinceTerm::MTime(c) => {
                    vec!["since".into(), c.to_string().into(), "mtime".into()].into()
                }
                SinceTerm::CTime(c) => {
                    vec!["since".into(), c.to_string().into(), "ctime".into()].into()
                }
            },
            Self::Size(term) => term.into_term("size"),
            Self::Suffix(term) => vec![
                "suffix".into(),
                Value::Array(term.into_iter().map(|p| p.try_into().unwrap()).collect()),
            ]
            .into(),
            Self::FileType(term) => vec!["type".into(), term.to_string().into()].into(),
        }
    }
}

/// Performs an exact match against the file name.
/// <https://facebook.github.io/watchman/docs/expr/name.html>
#[derive(Clone, Debug)]
pub struct NameTerm {
    pub paths: Vec<PathBuf>,
    /// By default, the name is evaluated against the basename portion
    /// of the filename.  Set wholename=true to have it match against
    /// the path relative to the root of the project.
    pub wholename: bool,
}

/// Match on the parent directory structure
/// <https://facebook.github.io/watchman/docs/expr/dirname.html>
#[derive(Clone, Debug)]
pub struct DirNameTerm {
    /// The path to a directory
    pub path: PathBuf,
    /// Specifies the matching depth.  A file has depth == 0
    /// if it is contained directory within `path`, depth == 1 if
    /// it is in a direct child directory of `path`, depth == 2 if
    /// in a grand-child directory and so on.
    /// If None, the default is considered to GreaterOrEqual depth 0.
    pub depth: Option<RelOp>,
}

/// Use PCRE to match the filename.
/// Note that this is an optional server feature and using this term
/// on a server that doesn't support this feature will generate an
/// error in response to the query.
/// <https://facebook.github.io/watchman/docs/expr/pcre.html>
#[derive(Clone, Debug, Default)]
pub struct PcreTerm {
    /// The perl compatible regular expression
    pub pattern: String,

    /// By default, the name is evaluated against the basename portion
    /// of the filename.  Set wholename=true to have it match against
    /// the path relative to the root of the project.
    pub wholename: bool,
}

/// Encodes the match expression term
/// <https://facebook.github.io/watchman/docs/expr/match.html>
#[derive(Clone, Debug, Default)]
pub struct MatchTerm {
    /// The glob expression to evaluate
    pub glob: String,
    /// By default, the glob is evaluated against the basename portion
    /// of the filename.  Set wholename=true to have it match against
    /// the path relative to the root of the project.
    pub wholename: bool,
    /// By default, paths whose names start with a `.` are not matched.
    /// Set include_dot_files=true to include them
    pub include_dot_files: bool,
    /// By default, backslashes in the pattern escape the next character.
    /// To have `\` treated literally, set no_escape=true.
    pub no_escape: bool,
}

/// Specifies a relational comparison with an integer value
#[derive(Clone, Debug)]
pub enum RelOp {
    Equal(usize),
    NotEqual(usize),
    Greater(usize),
    GreaterOrEqual(usize),
    Less(usize),
    LessOrEqual(usize),
}

impl RelOp {
    fn into_term(self, field: &str) -> Value {
        let (op, value) = match self {
            Self::Equal(value) => ("eq", value),
            Self::NotEqual(value) => ("ne", value),
            Self::Greater(value) => ("gt", value),
            Self::GreaterOrEqual(value) => ("ge", value),
            Self::Less(value) => ("lt", value),
            Self::LessOrEqual(value) => ("le", value),
        };
        Value::Array(vec![field.into(), op.into(), value.try_into().unwrap()])
    }
}

/// Evaluates as true if the specified time property of the file is greater
/// than the since value.
/// <https://facebook.github.io/watchman/docs/expr/since.html>
#[derive(Clone, Debug)]
pub enum SinceTerm {
    /// Yield true if the file was observed to be modified more recently than
    /// the specified clockspec
    ObservedClock(ClockSpec),

    /// Yield true if the file changed from !exists -> exists more recently
    /// than the specified clockspec
    CreatedClock(ClockSpec),

    /// Yield true if the mtime stat field is >= the provided timestamp.
    /// Note that this is >= because it has 1-second granularity.
    MTime(i64),

    /// Yield true if the ctime stat field is >= the provided timestamp.
    /// Note that this is >= because it has 1-second granularity.
    CTime(i64),
}

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

    fn val(expr: Expr) -> Value {
        expr.into()
    }

    #[test]
    fn exprs() {
        assert_eq!(val(Expr::True), "true".into());
        assert_eq!(val(Expr::False), "false".into());
        assert_eq!(val(Expr::Empty), "empty".into());
        assert_eq!(val(Expr::Exists), "exists".into());
        assert_eq!(
            val(Expr::Not(Box::new(Expr::False))),
            vec!["not".into(), "false".into()].into()
        );
        assert_eq!(
            val(Expr::All(vec![Expr::True, Expr::False])),
            vec!["allof".into(), "true".into(), "false".into()].into()
        );
        assert_eq!(
            val(Expr::Any(vec![Expr::True, Expr::False])),
            vec!["anyof".into(), "true".into(), "false".into()].into()
        );

        assert_eq!(
            val(Expr::DirName(DirNameTerm {
                path: "foo".into(),
                depth: None,
            })),
            vec!["dirname".into(), Value::ByteString("foo".into())].into()
        );
        assert_eq!(
            val(Expr::DirName(DirNameTerm {
                path: "foo".into(),
                depth: Some(RelOp::GreaterOrEqual(1)),
            })),
            vec![
                "dirname".into(),
                Value::ByteString("foo".into()),
                vec!["depth".into(), "ge".into(), 1.into()].into()
            ]
            .into()
        );

        assert_eq!(
            val(Expr::Match(MatchTerm {
                glob: "*.txt".into(),
                ..Default::default()
            })),
            vec![
                "match".into(),
                "*.txt".into(),
                "basename".into(),
                hashmap! {
                    "includedotfiles".to_string() => Value::Bool(false),
                    "noescape".to_string() => Value::Bool(false),
                }
                .into()
            ]
            .into()
        );

        assert_eq!(
            val(Expr::Match(MatchTerm {
                glob: "*.txt".into(),
                wholename: true,
                include_dot_files: true,
                ..Default::default()
            })),
            vec![
                "match".into(),
                "*.txt".into(),
                "wholename".into(),
                hashmap! {
                    "includedotfiles".to_string() => Value::Bool(true),
                    "noescape".to_string() => Value::Bool(false),
                }
                .into()
            ]
            .into()
        );

        assert_eq!(
            val(Expr::Name(NameTerm {
                paths: vec!["foo".into()],
                wholename: true,
            })),
            vec![
                "name".into(),
                vec![Value::ByteString("foo".into())].into(),
                "wholename".into()
            ]
            .into()
        );

        assert_eq!(
            val(Expr::Pcre(PcreTerm {
                pattern: "foo$".into(),
                wholename: true,
            })),
            vec!["pcre".into(), "foo$".into(), "wholename".into()].into()
        );

        assert_eq!(
            val(Expr::FileType(FileType::Regular)),
            vec!["type".into(), "f".into()].into()
        );

        assert_eq!(
            val(Expr::Suffix(vec!["php".into(), "js".into()])),
            vec![
                "suffix".into(),
                vec![
                    Value::ByteString("php".into()),
                    Value::ByteString("js".into())
                ]
                .into()
            ]
            .into()
        );

        assert_eq!(
            val(Expr::Since(SinceTerm::ObservedClock(ClockSpec::null()))),
            vec!["since".into(), "c:0:0".into(), "oclock".into()].into()
        );
    }
}


/*
 * 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.
 */

//! Defines the fields used in the query result structs

// don't show deprecation warnings about NewField when we build this file
#![allow(deprecated)]

use std::path::PathBuf;

use serde::Deserialize;
use serde_bser::bytestring::ByteString;

use crate::prelude::*;

/// This trait is used to furnish the caller with the watchman
/// field name for an entry in the file results
#[doc(hidden)]
pub trait QueryFieldName {
    fn field_name() -> &'static str;
}

/// This trait is used to produce the complete list of file
/// result field names for a query
#[doc(hidden)]
pub trait QueryFieldList {
    fn field_list() -> Vec<&'static str>;
}

/// This macro defines a field struct that can be composed using
/// the `query_result_type!` macro into a struct that can be used
/// with the `Client::query` method.
macro_rules! define_field {(
    $(#[$meta:meta])*
    $tyname:ident, $ty:ty, $field_name:literal) => {
        #[derive(Deserialize, Clone, Debug)]
        $(#[$meta])*
        pub struct $tyname {
            #[serde(rename = $field_name)]
            val: $ty,
        }

        impl QueryFieldName for $tyname {
            fn field_name() -> &'static str {
                $field_name
            }
        }

        impl $tyname {
            /// Fields should only be consumed when constructed via a QueryResult.
            /// This constructor is provided for users to easily mock out
            /// QueryResult objects in tests.
            pub fn new(val: $ty) -> Self {
                $tyname {
                    val,
                }
            }

            /// Consumes the field and returns the underlying
            /// value storage
            pub fn into_inner(self) -> $ty {
                self.val
            }
        }

        impl std::ops::Deref for $tyname {
            type Target = $ty;
            fn deref(&self) -> &Self::Target {
                &self.val
            }
        }

        impl std::ops::DerefMut for $tyname {
            fn deref_mut(&mut self) -> &mut Self::Target {
                &mut self.val
            }
        }
    };
}

define_field!(
    /// The field corresponding to the `name` of the file.
    NameField,
    PathBuf,
    "name"
);

define_field!(
    /// Like NameField but does not assume UTF-8.
    BytesNameField,
    ByteString,
    "name"
);

define_field!(
    /// The field corresponding to the `exists` status of the file
    ExistsField,
    bool,
    "exists"
);

define_field!(
    /// The field corresponding to the `cclock` field.
    /// the cclock is the created clock; the clock value when we first observed the file,
    /// or the clock value when it last switched from !exists to exists.
    CreatedClockField,
    ClockSpec,
    "cclock"
);

define_field!(
    /// The field corresponding to the `oclock` field.
    /// the oclock is the observed clock; the clock value where we last observed some
    /// change in this file or its metadata.
    ObservedClockField,
    ClockSpec,
    "oclock"
);

define_field!(
    /// The field corresponding to the `content.sha1hex` field.
    /// For regular files this evaluates to the sha1 hash of the
    /// file contents.
    ContentSha1HexField,
    ContentSha1Hex,
    "content.sha1hex"
);

define_field!(
    /// The field corresponding to the `ctime` field.
    /// ctime is the last inode change time measured in integer seconds since the
    /// unix epoch.
    CTimeField,
    i64,
    "ctime"
);

define_field!(
    /// The field corresponding to the `ctime_f` field.
    /// ctime is the last inode change time measured in floating point seconds
    /// (including the fractional portion) since the unix epoch.
    CTimeAsFloatField,
    f32,
    "ctime_f"
);

define_field!(
    /// The field corresponding to the `mtime` field.
    /// mtime is the last modified time measured in integer seconds
    /// since the unix epoch.
    MTimeField,
    i64,
    "mtime"
);

define_field!(
    /// The field corresponding to the `mtime_f` field.
    /// mtime is the last modified time measured in floating point seconds
    /// (including the fractional portion) since the unix epoch.
    MTimeAsFloatField,
    f32,
    "mtime_f"
);

define_field!(
    /// The field corresponding to the `size` field.
    /// This represents the size of the file in bytes.
    SizeField,
    u64,
    "size"
);

define_field!(
    /// The field corresponding to the `mode` field.
    /// This encodes the full file type and permission bits.
    /// Note that most programs and users are more comfortable with
    /// this value when printed in octal.
    /// It is recommended to use `FileTypeField` if all you need is the
    /// file type and not the permission bits, as it is cheaper to
    /// determine just the type in a virtualized filesystem.
    ModeAndPermissionsField,
    u64,
    "mode"
);

define_field!(
    /// The field corresponding to the `uid` field.
    /// The uid field is the owning uid expressed as an integer.
    /// This field is not meaningful on Windows.
    OwnerUidField,
    u32,
    "uid"
);

define_field!(
    /// The field corresponding to the `gid` field.
    /// The gid field is the owning gid expressed as an integer.
    /// This field is not meaningful on Windows.
    OwnerGidField,
    u32,
    "gid"
);

define_field!(
    /// The field corresponding to the `ino` field.
    /// The ino field is the inode number expressed as an integer.
    /// This field is not meaningful on Windows.
    InodeNumberField,
    u64,
    "ino"
);

define_field!(
    /// The field corresponding to the `dev` field.
    /// The dev field is the device number expressed as an integer.
    /// This field is not meaningful on Windows.
    DeviceNumberField,
    u64,
    "dev"
);

define_field!(
    /// The field corresponding to the `nlink` field.
    /// The nlink field is the number of hard links to the file
    /// expressed as an integer.
    NumberOfLinksField,
    u64,
    "nlink"
);

define_field!(
    /// The field corresponding to the `type` field.
    /// The type field encodes the type of the file.
    FileTypeField,
    FileType,
    "type"
);

define_field!(
    /// The field corresponding to the `symlink_target` field.
    /// For files of type symlink this evaluates to the result
    /// of readlink(2) on the file.
    SymlinkTargetField,
    Option<String>,
    "symlink_target"
);

define_field!(
    /// The field corresponding to the `new` field.
    /// The new field evaluates to true if a file is newer than
    /// the since generator criteria.
    ///
    /// Use of this field is discouraged as there are a number of
    /// situations in which the newness has a counter-intuitive
    /// value.  In addition, computing newness in a virtualized
    /// filesystem is relatively expensive.
    ///
    /// If your application needs to reason about the transition
    /// from `!exists -> exists` then you should track the
    /// `ExistsField` in your application.
    #[deprecated(note = "NewField can have counter-intuitive \
                         values in a number of situations so it \
                         is recommended that you track \
                         ExistsField instead")]
    NewField,
    bool,
    "new"
);

/// A macro to help define a type to hold file information from
/// a query.
/// This macro enables a type-safe way to define the set of fields
/// to be returned and de-serialize only those fields.
///
/// This defines a struct that will receive the name and content
/// hash fields from the results.  When used together with
/// `Client::query`, the query will automatically use the appropriate
/// list of field names:
///
/// ```
/// use watchman_client::prelude::*;
/// use serde::Deserialize;
///
/// query_result_type! {
///     struct NameAndHash {
///         name: NameField,
///         hash: ContentSha1HexField,
///     }
/// }
/// ```
///
/// The struct must consist of 2 or more fields; the macro subsystem
/// won't allow for generating an appropriate type definition for a single
/// field result.
///
/// If you need only a single field, look at [NameOnly](struct.NameOnly.html).
///
/// The field types must implement an undocumented trait that enables
/// the automatic field naming and correct deserialization regardless
/// of the field name in the struct.  As such, you should consider
/// the set of fields to be limited to those provided by this crate.
#[macro_export]
macro_rules! query_result_type {(
    $struct_vis:vis struct $tyname:ident {
        $($field_vis:vis $field_name:ident : $field_ty:ty),+ $(,)?
    }
    ) => (

#[derive(Deserialize, Debug, Clone)]
$struct_vis struct $tyname {
    $(
        #[serde(flatten)]
        $field_vis $field_name: $field_ty,
    )*
}

impl QueryFieldList for $tyname {
    fn field_list() -> Vec <&'static str> {
         vec![
        $(
            <$field_ty>::field_name(),
        )*
        ]
    }
}
    )
}

/// Use the `NameOnly` struct when your desired field list in your
/// query results consist only of the name field.
/// It is not possible to use the `query_result_type!` macro to define
/// an appropriate type due to limitations in the Rust macro system.
#[derive(Deserialize, Debug, Clone)]
#[serde(from = "PathBuf")]
pub struct NameOnly {
    pub name: NameField,
}

impl QueryFieldList for NameOnly {
    fn field_list() -> Vec<&'static str> {
        vec!["name"]
    }
}

impl From<PathBuf> for NameOnly {
    fn from(path: PathBuf) -> Self {
        Self {
            name: NameField { val: path },
        }
    }
}

#[derive(Deserialize, Debug, Clone)]
pub struct NoField {}

impl QueryFieldList for NoField {
    fn field_list() -> Vec<&'static str> {
        vec![]
    }
}