/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::any::TypeId;
use std::collections::HashMap;
use std::collections::HashSet;

use anyhow::anyhow;
use anyhow::bail;
use anyhow::ensure;
use anyhow::Result;
use fbthrift::binary_protocol;
use fbthrift::compact_protocol;
use fbthrift::simplejson_protocol;
use fbthrift::GetUri;
use protocol::StandardProtocol;

use crate::universal_name::ensure_registered;
use crate::universal_name::get_universal_hash;
use crate::universal_name::get_universal_hash_prefix_sha_256;
use crate::universal_name::UniversalHashAlgorithm;

const UNIVERSAL_HASH_PREFIX_SHA_256_LEN: i8 = 16;

pub struct AnyRegistry {
    uri_to_typeid: HashMap<&'static str, TypeId>,
    typeid_to_uri: HashMap<TypeId, &'static str>,
    alg_to_hashes: HashMap<UniversalHashAlgorithm, HashSet<Vec<u8>>>,
}

pub trait SerializeRef = binary_protocol::SerializeRef
    + compact_protocol::SerializeRef
    + simplejson_protocol::SerializeRef;
pub trait DeserializeSlice = binary_protocol::DeserializeSlice
    + compact_protocol::DeserializeSlice
    + simplejson_protocol::DeserializeSlice;

impl AnyRegistry {
    pub fn new() -> Self {
        let mut alg_to_hashes = HashMap::new();
        alg_to_hashes.insert(UniversalHashAlgorithm::Sha2_256, HashSet::new());

        Self {
            uri_to_typeid: HashMap::new(),
            typeid_to_uri: HashMap::new(),
            alg_to_hashes,
        }
    }

    pub fn register_type<T: 'static + GetUri>(&mut self) -> Result<bool> {
        let uri = T::uri();
        let type_id = TypeId::of::<T>();
        if self.uri_to_typeid.contains_key(uri) || self.typeid_to_uri.contains_key(&type_id) {
            return Ok(false);
        }
        self.uri_to_typeid.insert(uri, type_id);
        self.typeid_to_uri.insert(type_id, uri);
        for (alg, hashes) in self.alg_to_hashes.iter_mut() {
            let hash = get_universal_hash(*alg, uri)?;
            hashes.insert(hash);
        }
        Ok(true)
    }

    pub fn has_type<T: 'static + GetUri>(&self, obj: &any::Any) -> Result<bool> {
        let type_uri = T::uri();
        let type_hash_prefix_sha2_256 =
            get_universal_hash_prefix_sha_256(type_uri, UNIVERSAL_HASH_PREFIX_SHA_256_LEN)?;
        if let Some(obj_hash_prefix_sha2_256) = &obj.typeHashPrefixSha2_256 {
            Ok(type_hash_prefix_sha2_256 == *obj_hash_prefix_sha2_256)
        } else if let Some(obj_uri) = obj.r#type.as_ref() {
            Ok(type_uri == obj_uri)
        } else {
            bail!("No type information found");
        }
    }

    pub fn store<T: 'static + SerializeRef>(
        &mut self,
        obj: &T,
        protocol: StandardProtocol,
    ) -> Result<any::Any> {
        let type_id = TypeId::of::<T>();
        let uri = self
            .typeid_to_uri
            .get(&type_id)
            .ok_or_else(|| anyhow!("Type {:?} not registered", type_id))?;
        let hash_prefix =
            get_universal_hash_prefix_sha_256(uri, UNIVERSAL_HASH_PREFIX_SHA_256_LEN)?;
        Ok(any::Any {
            r#type: Some(uri.to_string()),
            typeHashPrefixSha2_256: Some(hash_prefix),
            protocol: Some(protocol),
            data: serialize(obj, protocol)?,
            ..Default::default()
        })
    }

    pub fn load<T: DeserializeSlice>(&self, obj: &any::Any) -> Result<T> {
        if let Some(type_hash_prefix_sha2_256) = &obj.typeHashPrefixSha2_256 {
            let hashes = self
                .alg_to_hashes
                .get(&UniversalHashAlgorithm::Sha2_256)
                .ok_or_else(|| anyhow!("Hash algorithm Sha2_256 not supported"))?;
            ensure_registered(hashes, type_hash_prefix_sha2_256)?;
        } else if let Some(uri) = obj.r#type.as_ref() {
            ensure!(
                self.uri_to_typeid.contains_key(uri.as_str()),
                "Type '{}' not registered",
                uri
            );
        } else {
            bail!("No type information found");
        }
        deserialize(&obj.data, obj.protocol.unwrap_or(StandardProtocol::Compact))
    }
}

fn serialize<T: SerializeRef>(obj: &T, protocol: StandardProtocol) -> Result<Vec<u8>> {
    match protocol {
        StandardProtocol::Binary => Ok(binary_protocol::serialize(obj).to_vec()),
        StandardProtocol::Compact => Ok(compact_protocol::serialize(obj).to_vec()),
        StandardProtocol::SimpleJson => Ok(simplejson_protocol::serialize(obj).to_vec()),
        unsupported => Err(anyhow!("Unsupported protocol: {:?}", unsupported)),
    }
}

fn deserialize<T: DeserializeSlice>(data: &[u8], protocol: StandardProtocol) -> Result<T> {
    match protocol {
        StandardProtocol::Binary => binary_protocol::deserialize(data),
        StandardProtocol::Compact => compact_protocol::deserialize(data),
        StandardProtocol::SimpleJson => simplejson_protocol::deserialize(data),
        unsupported => Err(anyhow!("Unsupported protocol: {:?}", unsupported)),
    }
}

#[cfg(test)]
mod tests {
    use anyhow::Result;
    use maplit::btreemap;
    use testset::struct_map_string_i32;

    use super::*;

    fn get_test_object() -> struct_map_string_i32 {
        struct_map_string_i32 {
            field_1: btreemap! {"Answer to the Ultimate Question of Life, the Universe, and Everything.".to_owned() => 42},
            ..Default::default()
        }
    }

    fn test_serialize_round_trip_for_protocol(protocol: StandardProtocol) -> Result<()> {
        let original = get_test_object();

        let serialized = serialize(&original, protocol)?;

        let deserialized = deserialize(&serialized, protocol)?;

        assert_eq!(original, deserialized);

        Ok(())
    }

    #[test]
    fn test_round_trip() -> Result<()> {
        let protocols = vec![
            StandardProtocol::Binary,
            StandardProtocol::Compact,
            StandardProtocol::SimpleJson,
        ];

        for protocol in protocols {
            test_serialize_round_trip_for_protocol(protocol)?;
        }

        Ok(())
    }

    #[test]
    fn test_unsupported_protocol() {
        let test_object = get_test_object();

        assert!(serialize(&test_object, StandardProtocol::Custom).is_err());
        assert!(serialize(&test_object, StandardProtocol::Json).is_err());

        assert!(deserialize::<struct_map_string_i32>(b"", StandardProtocol::Custom).is_err());
        assert!(deserialize::<struct_map_string_i32>(b"", StandardProtocol::Json).is_err());
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#[cxx::bridge(namespace = fbthrift_conformance::rust)]
pub mod ffi {
    #[namespace = "apache::thrift::type"]
    #[derive(Hash)]
    #[repr(i32)]
    enum UniversalHashAlgorithm {
        Sha2_256 = 2,
    }

    unsafe extern "C++" {
        include!("thrift/lib/rust/conformance/include/UniversalName.h");

        #[namespace = "apache::thrift::type"]
        type UniversalHashAlgorithm;

        fn getUniversalHash(
            alg: UniversalHashAlgorithm,
            uri: &CxxString,
        ) -> Result<UniquePtr<CxxString>>;
        fn getUniversalHashPrefix(universalHash: &CxxString, hashBytes: i8)
        -> UniquePtr<CxxString>;
        fn matchesUniversalHash(universalHash: &CxxString, prefix: &CxxString) -> bool;
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#![feature(trait_alias)]

mod any_registry;
mod bridge;
mod universal_name;

pub use any_registry::AnyRegistry;
pub use any_registry::DeserializeSlice;
pub use any_registry::SerializeRef;


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::collections::HashSet;

use anyhow::Result;
use cxx::let_cxx_string;
pub use ffi::UniversalHashAlgorithm;

use crate::bridge::ffi;

pub fn get_universal_hash(alg: UniversalHashAlgorithm, uri: &str) -> Result<Vec<u8>> {
    let_cxx_string!(uri = uri);

    ffi::getUniversalHash(alg, &uri)
        .map(|hash| hash.as_bytes().to_vec())
        .map_err(anyhow::Error::from)
}

pub fn get_universal_hash_prefix(universal_hash: &[u8], hash_bytes: i8) -> Vec<u8> {
    let_cxx_string!(universal_hash = universal_hash);

    ffi::getUniversalHashPrefix(&universal_hash, hash_bytes)
        .as_bytes()
        .to_vec()
}

pub fn get_universal_hash_prefix_sha_256(uri: &str, hash_bytes: i8) -> Result<Vec<u8>> {
    let hash = get_universal_hash(UniversalHashAlgorithm::Sha2_256, uri)?;
    Ok(get_universal_hash_prefix(&hash, hash_bytes))
}

pub fn ensure_registered(
    universal_hash_registry: &HashSet<Vec<u8>>,
    hash_prefix: &[u8],
) -> Result<()> {
    let num_matched = universal_hash_registry
        .iter()
        .filter(|hash| matches_universal_hash(hash, hash_prefix))
        .count();
    match num_matched {
        0 => Err(anyhow::anyhow!(
            "No hash found with prefix {:?}",
            hash_prefix
        )),
        1 => Ok(()),
        _ => Err(anyhow::anyhow!(
            "Multiple hashes found with prefix {:?}",
            hash_prefix
        )),
    }
}

fn matches_universal_hash(universal_hash: &[u8], prefix: &[u8]) -> bool {
    let_cxx_string!(universal_hash = universal_hash);
    let_cxx_string!(prefix = prefix);

    ffi::matchesUniversalHash(&universal_hash, &prefix)
}

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

    use super::*;

    #[test]
    fn test_get_universal_hash() {
        assert_eq!(
            get_universal_hash(UniversalHashAlgorithm::Sha2_256, "foo.com/my/type").unwrap(),
            b"\tat$\x9c\xef\xad\xb5\xea\rE;\xcb3\xadTv\x01\xfb\xfe\xc4\xb2\xd7\x95\x92N\xebg\xd4[\xe6F",
        );
    }

    #[test]
    fn test_get_universal_hash_prefix() {
        let hash = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";

        assert_eq!(get_universal_hash_prefix(hash, 0), b"",);

        assert_eq!(get_universal_hash_prefix(hash, 8), b"aaaaaaaa",);

        assert_eq!(get_universal_hash_prefix(hash, 32), hash.to_vec(),);

        assert_eq!(get_universal_hash_prefix(hash, 33), hash.to_vec(),);
    }

    #[test]
    fn test_match_universal_hash() {
        assert!(!matches_universal_hash(
            b"0123456789ABCDEF0123456789ABCDEF",
            b""
        ));

        assert!(!matches_universal_hash(
            b"0123456789ABCDEF0123456789ABCDEF",
            b"1"
        ));

        assert!(matches_universal_hash(
            b"0123456789ABCDEF0123456789ABCDEF",
            b"0"
        ));

        assert!(matches_universal_hash(
            b"0123456789ABCDEF0123456789ABCDEF",
            b"0123456789ABCDEF"
        ));

        assert!(matches_universal_hash(
            b"0123456789ABCDEF0123456789ABCDEF",
            b"0123456789ABCDEF0123456789ABCDEF"
        ));

        assert!(!matches_universal_hash(
            b"0123456789ABCDEF0123456789ABCDEF",
            b"0123456789ABCDEF0123456789ABCDEF0",
        ));
    }

    #[test]
    fn test_ensure_registered() {
        let universal_hash_registry = hashset! {
            b"DEADBEEF".to_vec(),
            b"0123456789ABCDEF0123456789ABCDEF".to_vec(),
            b"0123456789ABCDEF".to_vec(),
        };

        // Test no matching prefix
        assert!(ensure_registered(&universal_hash_registry, b"").is_err());

        assert!(ensure_registered(&universal_hash_registry, b"12345").is_err());

        // Test multiple matches
        assert!(ensure_registered(&universal_hash_registry, b"012345").is_err());

        // Test single matches
        assert!(ensure_registered(&universal_hash_registry, b"DEAD").is_ok());
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use anyhow::Result;
use fbthrift_conformance::AnyRegistry;
use maplit::btreemap;
use protocol::StandardProtocol;
use testset::struct_map_string_i32;

fn test_for_protocol(protocol: StandardProtocol) -> Result<()> {
    let mut registry = AnyRegistry::new();

    registry.register_type::<struct_map_string_i32>()?;

    let original = struct_map_string_i32 {
        field_1: btreemap! {"Answer to the Ultimate Question of Life, the Universe, and Everything.".to_owned() => 42},
        ..Default::default()
    };

    let any_obj = registry.store(&original, protocol)?;

    assert!(any_obj.typeHashPrefixSha2_256.is_some());
    assert_eq!(protocol, any_obj.protocol.unwrap());

    let loaded = registry.load(&any_obj)?;

    assert_eq!(original, loaded);

    Ok(())
}

#[test]
fn test_round_trip() -> Result<()> {
    let protocols = vec![
        StandardProtocol::Binary,
        StandardProtocol::Compact,
        StandardProtocol::SimpleJson,
    ];

    for protocol in protocols {
        test_for_protocol(protocol)?;
    }

    Ok(())
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use anyhow::Result;
use thiserror::Error;

pub use crate::hasher::Hasher;

#[derive(Error, Debug, PartialEq)]
pub enum DeterministicAccumulatorError {
    #[error("nothing has been written to the accumulator")]
    NothingWritten,
    #[error("end_ordered called on empty context")]
    EndOrderedOnEmpty,
    #[error("end_unordered called on empty context")]
    EndUnorderedOnEmpty,
    #[error("end_ordered called on unordered context")]
    EndOrderedOnUnordered,
    #[error("end_unordered called on ordered context")]
    EndUnorderedOnOrdered,
}

struct OrderedContext<T: Hasher> {
    pub count: u64,
    pub hasher: T,
}

enum Context<T: Hasher> {
    Ordered(OrderedContext<T>),
    Unordered(Vec<T::Output>),
}

pub struct DeterministicAccumulator<T: Hasher, F: Fn() -> T> {
    generator: F,
    result: Option<T::Output>,
    error: Option<DeterministicAccumulatorError>,
    context: Vec<Context<T>>,
}

impl<T: Hasher, F: Fn() -> T> DeterministicAccumulator<T, F> {
    pub fn new(generator: F) -> DeterministicAccumulator<T, F> {
        DeterministicAccumulator {
            generator,
            result: None,
            error: None,
            context: Vec::new(),
        }
    }

    pub fn get_result(self) -> Result<T::Output> {
        if let Some(error) = self.error {
            return Result::Err(error.into());
        }
        match self.result {
            None => Err(DeterministicAccumulatorError::NothingWritten.into()),
            Some(result) => Ok(result),
        }
    }

    pub fn begin_unordered(&mut self) {
        self.context.push(Context::Unordered(Vec::new()));
    }
    pub fn begin_ordered(&mut self) {
        let mut current_context = self.context.last_mut();

        match current_context {
            None => {
                self.context.push(Context::Ordered(OrderedContext {
                    count: 1,
                    hasher: (self.generator)(),
                }));
            }
            Some(ref mut context) => match context {
                Context::Ordered(ref mut ordered_context) => {
                    ordered_context.count += 1;
                }
                Context::Unordered(_) => {
                    self.context.push(Context::Ordered(OrderedContext {
                        count: 1,
                        hasher: (self.generator)(),
                    }));
                }
            },
        }
    }

    pub fn end_unordered(&mut self) {
        match self.context.pop() {
            Some(current_context) => {
                if let Context::Unordered(mut unordered_context) = current_context {
                    unordered_context.sort();
                    let mut hasher = (self.generator)();
                    for result in unordered_context {
                        hasher.combine_hasher(&result);
                    }
                    self.exit_context(hasher.finalize())
                } else {
                    self.set_error(DeterministicAccumulatorError::EndUnorderedOnOrdered);
                }
            }
            None => self.set_error(DeterministicAccumulatorError::EndUnorderedOnEmpty),
        }
    }

    pub fn end_ordered(&mut self) {
        match self.context.pop() {
            Some(current_context) => {
                if let Context::Ordered(mut ordered_context) = current_context {
                    ordered_context.count -= 1;
                    if ordered_context.count == 0 {
                        let hasher = ordered_context.hasher;
                        self.exit_context(hasher.finalize())
                    } else {
                        self.context.push(Context::Ordered(ordered_context));
                    }
                } else {
                    self.set_error(DeterministicAccumulatorError::EndOrderedOnUnordered);
                }
            }
            None => self.set_error(DeterministicAccumulatorError::EndOrderedOnEmpty),
        }
    }

    pub fn exit_context(&mut self, result: T::Output) {
        let mut current_context = self.context.last_mut();

        match current_context {
            None => {
                self.result = Some(result);
            }
            Some(ref mut context) => match context {
                Context::Ordered(ref mut ordered_context) => {
                    ordered_context.hasher.combine_hasher(&result);
                }
                Context::Unordered(ref mut unordered_context) => {
                    unordered_context.push(result);
                }
            },
        }
    }
    pub fn combine<V: Hashable<T>>(&mut self, value: V) {
        let mut current_context = self.context.last_mut();
        match current_context {
            None => {
                let mut hasher = (self.generator)();
                value.add_to_hasher(&mut hasher);
                self.result = Some(hasher.finalize());
            }
            Some(ref mut context) => match context {
                Context::Ordered(ref mut ordered_context) => {
                    value.add_to_hasher(&mut ordered_context.hasher);
                }
                Context::Unordered(ref mut unordered_context) => {
                    let mut hasher = (self.generator)();
                    value.add_to_hasher(&mut hasher);
                    unordered_context.push(hasher.finalize());
                }
            },
        }
    }

    fn set_error(&mut self, error: DeterministicAccumulatorError) {
        match self.error {
            None => self.error = Some(error),
            Some(_) => {}
        }
    }
}

pub trait Hashable<H: Hasher> {
    fn add_to_hasher(&self, hasher: &mut H);
}

impl<H: Hasher> Hashable<H> for bool {
    fn add_to_hasher(&self, hasher: &mut H) {
        hasher.combine_bool(*self);
    }
}

impl<H: Hasher> Hashable<H> for i8 {
    fn add_to_hasher(&self, hasher: &mut H) {
        hasher.combine_i8(*self);
    }
}

impl<H: Hasher> Hashable<H> for i16 {
    fn add_to_hasher(&self, hasher: &mut H) {
        hasher.combine_i16(*self);
    }
}

impl<H: Hasher> Hashable<H> for i32 {
    fn add_to_hasher(&self, hasher: &mut H) {
        hasher.combine_i32(*self);
    }
}

impl<H: Hasher> Hashable<H> for i64 {
    fn add_to_hasher(&self, hasher: &mut H) {
        hasher.combine_i64(*self);
    }
}

impl<H: Hasher> Hashable<H> for f32 {
    fn add_to_hasher(&self, hasher: &mut H) {
        hasher.combine_f32(*self);
    }
}

impl<H: Hasher> Hashable<H> for f64 {
    fn add_to_hasher(&self, hasher: &mut H) {
        hasher.combine_f64(*self);
    }
}

impl<H: Hasher> Hashable<H> for &[u8] {
    fn add_to_hasher(&self, hasher: &mut H) {
        hasher.combine_bytes(self);
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use anyhow::Result;
use fbthrift::protocol::ProtocolWriter;
use fbthrift::thrift_protocol::MessageType;
use fbthrift::ttype::TType;

use crate::DeterministicAccumulator;
use crate::Hasher;

pub struct DeterministicProtocolSerializer<T: Hasher, F: Fn() -> T> {
    acc: DeterministicAccumulator<T, F>,
    maps_is_empty_stack: Vec<bool>,
}

impl<T: Hasher, F: Fn() -> T> DeterministicProtocolSerializer<T, F> {
    pub fn new(generator: F) -> DeterministicProtocolSerializer<T, F> {
        DeterministicProtocolSerializer {
            acc: DeterministicAccumulator::<T, F>::new(generator),
            maps_is_empty_stack: Vec::new(),
        }
    }
    fn begin_container(&mut self, size: i32) {
        self.acc.begin_ordered();
        self.acc.combine(size);
    }
    fn end_container(&mut self) {
        self.acc.end_ordered()
    }
    fn combine_buf(&mut self, buffer: &[u8]) {
        self.acc.begin_ordered();
        self.acc.combine(buffer.len() as i32);
        self.acc.combine(buffer);
        self.acc.end_ordered();
    }
}

impl<T: Hasher, F: Fn() -> T> ProtocolWriter for DeterministicProtocolSerializer<T, F> {
    type Final = Result<T::Output>;

    fn write_message_begin(&mut self, _name: &str, _type_id: MessageType, _seqid: u32) {}
    fn write_message_end(&mut self) {}
    fn write_struct_begin(&mut self, _name: &str) {
        self.acc.begin_unordered();
    }
    fn write_struct_end(&mut self) {
        self.acc.end_unordered();
    }
    fn write_field_begin(&mut self, _name: &str, _type_id: TType, id: i16) {
        self.acc.begin_ordered();
        self.write_i16(id);
    }
    fn write_field_end(&mut self) {
        self.acc.end_ordered();
    }
    fn write_field_stop(&mut self) {}
    fn write_map_begin(&mut self, _key_type: TType, _value_type: TType, size: usize) {
        self.begin_container(size.try_into().unwrap());
        self.acc.begin_unordered();
        self.maps_is_empty_stack.push(true);
    }
    fn write_map_key_begin(&mut self) {
        let map_is_empty = self
            .maps_is_empty_stack
            .last_mut()
            .expect("map stack is empty during map key begin");
        if *map_is_empty {
            *map_is_empty = false; // it's not empty any more
        } else {
            self.acc.end_ordered(); // we had a previous kv pair
        }
        self.acc.begin_ordered();
    }
    fn write_map_value_begin(&mut self) {}
    fn write_map_end(&mut self) {
        let map_is_empty = self
            .maps_is_empty_stack
            .pop()
            .expect("map stack is empty on map end");
        if !map_is_empty {
            self.acc.end_ordered();
        }
        self.acc.end_unordered();
        self.end_container();
    }
    fn write_list_begin(&mut self, _elem_type: TType, size: usize) {
        self.begin_container(size.try_into().unwrap());
        self.acc.begin_ordered();
    }
    fn write_list_value_begin(&mut self) {}
    fn write_list_end(&mut self) {
        self.acc.end_ordered();
        self.end_container();
    }
    fn write_set_begin(&mut self, _elem_type: TType, size: usize) {
        self.begin_container(size as i32);
        self.acc.begin_unordered();
    }
    fn write_set_value_begin(&mut self) {}
    fn write_set_end(&mut self) {
        self.acc.end_unordered();
        self.end_container();
    }
    fn write_bool(&mut self, value: bool) {
        self.acc.combine(value);
    }
    fn write_byte(&mut self, value: i8) {
        self.acc.combine(value);
    }
    fn write_i16(&mut self, value: i16) {
        self.acc.combine(value);
    }
    fn write_i32(&mut self, value: i32) {
        self.acc.combine(value);
    }
    fn write_i64(&mut self, value: i64) {
        self.acc.combine(value);
    }
    fn write_double(&mut self, value: f64) {
        self.acc.combine(value);
    }
    fn write_float(&mut self, value: f32) {
        self.acc.combine(value);
    }
    fn write_string(&mut self, value: &str) {
        self.combine_buf(value.as_bytes());
    }
    fn write_binary(&mut self, value: &[u8]) {
        self.combine_buf(value);
    }

    fn finish(self) -> Self::Final {
        self.acc.get_result()
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pub trait Hasher {
    type Output: Ord;
    fn finalize(self) -> Self::Output;
    fn combine_bool(&mut self, value: bool);
    fn combine_i8(&mut self, value: i8);
    fn combine_i16(&mut self, value: i16);
    fn combine_i32(&mut self, value: i32);
    fn combine_i64(&mut self, value: i64);
    fn combine_f32(&mut self, value: f32);
    fn combine_f64(&mut self, value: f64);
    fn combine_bytes(&mut self, value: &[u8]);
    // fn combine(&mut self,const folly::IOBuf&mut );
    fn combine_hasher(&mut self, value: &Self::Output);
}

use ring::digest::Context;
use ring::digest::SHA256;

pub struct Sha256Hasher {
    context: Context,
}

impl Default for Sha256Hasher {
    fn default() -> Self {
        Sha256Hasher {
            context: Context::new(&SHA256),
        }
    }
}

impl Hasher for Sha256Hasher {
    type Output = [u8; 32];
    fn finalize(self) -> Self::Output {
        self.context
            .finish()
            .as_ref()
            .try_into()
            .expect("Sha256 hash should be 32 bytes!")
    }
    fn combine_bool(&mut self, value: bool) {
        if value {
            self.context.update(&[1u8; 1])
        } else {
            self.context.update(&[0u8; 1])
        }
    }
    fn combine_i8(&mut self, value: i8) {
        self.context.update(&value.to_le_bytes())
    }
    fn combine_i16(&mut self, value: i16) {
        self.context.update(&value.to_le_bytes())
    }
    fn combine_i32(&mut self, value: i32) {
        self.context.update(&value.to_le_bytes())
    }
    fn combine_i64(&mut self, value: i64) {
        self.context.update(&value.to_le_bytes())
    }
    fn combine_f32(&mut self, value: f32) {
        self.context.update(&value.to_le_bytes())
    }
    fn combine_f64(&mut self, value: f64) {
        self.context.update(&value.to_le_bytes())
    }
    fn combine_bytes(&mut self, value: &[u8]) {
        self.context.update(value)
    }
    // fn combine(self: &mutself,Sha256Hasherconst folly::IOBuf&mut);
    fn combine_hasher(&mut self, value: &Self::Output) {
        self.context.update(value)
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pub mod deterministic_accumulator;
pub mod deterministic_protocol;
pub mod hasher;
use anyhow::Result;
use fbthrift::protocol::ProtocolWriter;
use fbthrift::Serialize;

pub use crate::deterministic_accumulator::DeterministicAccumulator;
pub use crate::deterministic_accumulator::DeterministicAccumulatorError;
use crate::deterministic_protocol::DeterministicProtocolSerializer;
pub use crate::hasher::Hasher;
pub use crate::hasher::Sha256Hasher;
pub fn deterministic_hash<
    H: Hasher + Default,
    F: Fn() -> H,
    S: Serialize<DeterministicProtocolSerializer<H, F>>,
>(
    data: &S,
    hasher_generator: F,
) -> Result<H::Output> {
    let mut protocol_writer = DeterministicProtocolSerializer::new(hasher_generator);
    data.write(&mut protocol_writer);
    protocol_writer.finish()
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use fbthrift::TType;
use fbthrift_deterministic_hash::DeterministicAccumulator;
use fbthrift_deterministic_hash::DeterministicAccumulatorError;
use fbthrift_deterministic_hash::Sha256Hasher;
// use teststructs::TestSkipV2;

#[test]
fn deterministic_accumulator_combine_one_value() {
    let mut da = DeterministicAccumulator::new(Sha256Hasher::default);
    da.combine(5i8);
    assert!(da.get_result().is_ok(), "no error expected");
}

#[test]
fn deterministic_accumulator_test_unordered() {
    let mut da1 = DeterministicAccumulator::new(Sha256Hasher::default);
    da1.begin_unordered();
    da1.combine(1i8);
    da1.combine(2i8);
    da1.combine(3i8);
    da1.combine(1i8);
    da1.end_unordered();

    let mut da2 = DeterministicAccumulator::new(Sha256Hasher::default);
    da2.begin_unordered();
    da2.combine(3i8);
    da2.combine(1i8);
    da2.combine(2i8);
    da2.combine(1i8);
    da2.end_unordered();

    let result1 = da1.get_result().expect("no error expected");

    let result2 = da2.get_result().expect("no error expected");

    assert_eq!(result1, result2);
}

#[test]
fn deterministic_accumulator_test_ordered() {
    let mut da1 = DeterministicAccumulator::new(Sha256Hasher::default);
    da1.begin_ordered();
    da1.combine(1i8);
    da1.combine(2i8);
    da1.combine(3i8);
    da1.combine(1i8);
    da1.end_ordered();

    let mut da2 = DeterministicAccumulator::new(Sha256Hasher::default);
    da2.begin_ordered();
    da2.combine(3i8);
    da2.combine(1i8);
    da2.combine(2i8);
    da2.combine(1i8);
    da2.end_ordered();

    let result1 = da1.get_result().expect("no error expected");

    let result2 = da2.get_result().expect("no error expected");

    assert_ne!(result1, result2);
}

#[test]
fn deterministic_accumulator_test_nested() {
    //Examples:
    // struct MyData {
    //  1: list<i64> f1 = [1, 2, 3];
    //  2: set<i64>  f2 = {4, 5, 6};
    // }
    let mut acc = DeterministicAccumulator::new(Sha256Hasher::default);
    acc.begin_unordered(); // struct data begin
    acc.begin_ordered(); // field  f1   begin
    acc.combine(TType::List as i8); // field  f1   type
    acc.combine(1i8); // field  f1   id
    acc.begin_ordered(); // list   f1   begin
    acc.combine(TType::I64 as i8); // list   f1   type
    acc.combine(3i8); // list   f1   size
    acc.begin_ordered(); // list   f1   data begin
    acc.combine(1i8); // f1[0]
    acc.combine(2i8); // f1[1]
    acc.combine(3i8); // f1[2]
    acc.end_ordered(); // list   f1   data end
    acc.end_ordered(); // list   f1   end
    acc.end_ordered(); // field  f1   end
    acc.begin_ordered(); // field  f2   begin
    acc.combine(TType::Set as i8); // field  f2   type
    acc.combine(2i8); // field  f2   id
    acc.begin_ordered(); // set    f2   begin
    acc.combine(TType::String as i8); // set    f2   type
    acc.combine(3i8); // set    f2   size
    acc.begin_unordered(); // set    f2   data begin
    acc.combine(4i8); // f2[0]
    acc.combine(5i8); // f2[1]
    acc.combine(6i8); // f2[2]
    acc.end_unordered(); // set    f2   data end
    acc.end_ordered(); // set    f2   end
    acc.end_ordered(); // field  f2   end
    acc.end_unordered(); // struct data end

    assert_eq!(
        acc.get_result().expect("no error expected"),
        [
            133, 15, 18, 181, 19, 1, 94, 129, 18, 56, 65, 203, 169, 152, 120, 251, 250, 215, 192,
            101, 19, 89, 174, 115, 109, 217, 229, 172, 156, 148, 0, 57
        ],
    );
}

#[test]
fn deterministic_accumulator_end_ordered_on_empty() {
    let mut da = DeterministicAccumulator::new(Sha256Hasher::default);
    da.end_ordered();
    assert_eq!(
        da.get_result()
            .expect_err("error is expected")
            .downcast::<DeterministicAccumulatorError>()
            .expect("DeterministicAccumulatorError is expected"),
        DeterministicAccumulatorError::EndOrderedOnEmpty
    );
}

#[test]
fn deterministic_accumulator_end_unordered_on_empty() {
    let mut da = DeterministicAccumulator::new(Sha256Hasher::default);
    da.end_unordered();
    assert_eq!(
        da.get_result()
            .expect_err("error is expected")
            .downcast::<DeterministicAccumulatorError>()
            .expect("DeterministicAccumulatorError is expected"),
        DeterministicAccumulatorError::EndUnorderedOnEmpty
    );
}

#[test]
fn deterministic_accumulator_end_unordered_on_ordered() {
    let mut da = DeterministicAccumulator::new(Sha256Hasher::default);
    da.begin_ordered();
    da.end_unordered();
    assert_eq!(
        da.get_result()
            .expect_err("error is expected")
            .downcast::<DeterministicAccumulatorError>()
            .expect("DeterministicAccumulatorError is expected"),
        DeterministicAccumulatorError::EndUnorderedOnOrdered
    );
}

#[test]
fn deterministic_accumulator_end_ordered_on_unordered() {
    let mut da = DeterministicAccumulator::new(Sha256Hasher::default);
    da.begin_unordered();
    da.end_ordered();
    assert_eq!(
        da.get_result()
            .expect_err("error is expected")
            .downcast::<DeterministicAccumulatorError>()
            .expect("DeterministicAccumulatorError is expected"),
        DeterministicAccumulatorError::EndOrderedOnUnordered
    );
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use fbthrift::builtin_types::BTreeMap;
use fbthrift::builtin_types::BTreeSet;
use fbthrift_deterministic_hash::deterministic_hash;
use fbthrift_deterministic_hash::Sha256Hasher;
use maplit::btreemap;
use teststructs::ComplexStruct;
use teststructs::ComplexStructSha256Hash;
use teststructs::SimpleStruct1;
use teststructs::SimpleStructSha256Hash;

#[test]
fn hash_test_complex_structure() {
    let complex_struct = ComplexStruct {
        l: vec!["a".to_string(), "ab".to_string(), "abc".to_string()],
        s: BTreeSet::from(["a".to_string(), "ab".to_string(), "abc".to_string()]),
        m: btreemap! {
            "aa".to_owned() => "bb".to_owned(),
            "cc".to_owned() => "dd".to_owned(),
            "ee".to_owned() => "ff".to_owned(),
        },
        ml: btreemap! {
            "aa".to_owned() => vec![1, 2, 3],
            "bb".to_owned() => vec![],
            "cc".to_owned() => vec![3, 2],
            "ee".to_owned() => vec![1, 2, 3],
            "ff".to_owned() => vec![],
        },
        mm: btreemap! {
            "aa".to_owned() => BTreeMap::from([(1, 2)]),
            "bb".to_owned() => BTreeMap::from([]),
            "cc".to_owned() => BTreeMap::from([(2, 3), (3, 2)]),
            "ee".to_owned() => BTreeMap::from([(3, 4)]),
            "ff".to_owned() => BTreeMap::from([]),
        },
        ..Default::default()
    };
    let result =
        deterministic_hash(&complex_struct, Sha256Hasher::default).expect("expected no error");
    assert_eq!(
        result.as_ref(),
        ComplexStructSha256Hash
            .iter()
            .map(|val| *val as u8)
            .collect::<Vec<u8>>()
    );
}

#[test]
fn hash_test_simple_structure() {
    let simple_struct = SimpleStruct1 {
        byte_: 8,
        i_16: 12,
        i_32: 1,
        i_64: 2,
        f_32: 1.0,
        f_64: 2.0,
        str_with_value: "abc".to_string(),
        str_empty: "".to_string(),
        bool_true: true,
        bool_false: false,
        binary_: vec![1u8, 2, 3],
        ..Default::default()
    };
    let result =
        deterministic_hash(&simple_struct, Sha256Hasher::default).expect("expected no error");
    assert_eq!(
        result.as_ref(),
        SimpleStructSha256Hash
            .iter()
            .map(|val| *val as u8)
            .collect::<Vec<u8>>()
    );
}


// @generated by autocargo
use std::env;
use std::fs;
use std::path::Path;

use thrift_compiler::Config;
use thrift_compiler::GenContext;

#[rustfmt::skip]
fn main() {
    // Rerun if this gets rewritten.
    println!("cargo:rerun-if-changed=thrift_build.rs");

    let out_dir = env::var_os("OUT_DIR").expect("OUT_DIR env not provided");
    let out_dir: &Path = out_dir.as_ref();
    fs::write(
        out_dir.join("cratemap"),
        "test_structs crate",
    ).expect("Failed to write cratemap");

    let conf = {
        let mut conf = Config::from_env(GenContext::Lib).expect("Failed to instantiate thrift_compiler::Config");

        let path_from_manifest_to_base: &Path = "../../../../../..".as_ref();
        let cargo_manifest_dir =
            env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not provided");
        let cargo_manifest_dir: &Path = cargo_manifest_dir.as_ref();
        let base_path = cargo_manifest_dir
            .join(path_from_manifest_to_base)
            .canonicalize()
            .expect("Failed to canonicalize base_path");
        // TODO: replace canonicalize() with std::path::absolute() when
        // https://github.com/rust-lang/rust/pull/91673 is available (~Rust 1.60)
        // and remove this block.
        #[cfg(windows)]
        let base_path = Path::new(
            base_path
                .as_path()
                .to_string_lossy()
                .trim_start_matches(r"\\?\"),
            )
            .to_path_buf();

        conf.base_path(base_path);

        let options = "types_crate=teststructs__types";
        if !options.is_empty() {
            conf.options(options);
        }

        let include_srcs = vec![

        ];
        conf.include_srcs(include_srcs);

        conf
    };

    conf
        .run(&[
            "test_structs.thrift"
        ])
        .expect("Failed while running thrift compilation");
}


// @generated by autocargo
::codegen_includer_proc_macro::include!();


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::fmt::Debug;
use std::marker::PhantomData;

use crate::metadata::ThriftAnnotations;

pub trait ThriftAdapter {
    /// Aka the "from" type.
    type StandardType;
    /// Aka the "to" type.
    type AdaptedType: Clone + Debug + PartialEq + Send + Sync;

    /// The error type thrown if `from_thrift` fails during deserialization.
    type Error: Into<anyhow::Error> + Debug;

    /// Converts an instance of `StandardType` to `AdaptedType` during deserialization.
    ///
    /// The `Err` returned by this method will be propagated as a deserialization error.
    fn from_thrift(value: Self::StandardType) -> Result<Self::AdaptedType, Self::Error>;

    // TODO(emersonford): should we support a moved param here?
    /// Converts from the given `AdaptedType` back to the given `StandardType` during
    /// serialization.
    ///
    /// This must be an infallible operation as `serialize` is infallible.
    ///
    /// WARNING: you should be particularly cautious when using `.unwrap()` or any other panic-able
    /// methods in this method. If this method panics, it will be at serialization time and not at
    /// the Thrift struct creation time, meaning it will be extremely difficult to debug what the
    /// true "source" of the panic.
    ///
    /// If your `AdaptedType` -> `StandardType` conversion is truly fallible, you probably shouldn't
    /// use an adapter to begin with.
    fn to_thrift(value: &Self::AdaptedType) -> Self::StandardType;

    /// Method called when this adapter is used on a Thrift struct's field. Provides information
    /// about the specific field ID in `field_id`. The type of the struct that owns this field is
    /// passed in as `T`.
    ///
    /// Defaults to calling `from_thrift`.
    fn from_thrift_field<T: ThriftAnnotations>(
        value: Self::StandardType,
        _field_id: i16,
    ) -> Result<Self::AdaptedType, Self::Error> {
        Self::from_thrift(value)
    }

    /// Method called when this adapter is used on a Thrift struct's field. Provides information
    /// about the specific field ID in `field_id`. The type of the struct that owns this field is
    /// passed in as `T`.
    ///
    /// Defaults to calling `to_thrift`.
    fn to_thrift_field<T: ThriftAnnotations>(
        value: &Self::AdaptedType,
        _field_id: i16,
    ) -> Self::StandardType {
        Self::to_thrift(value)
    }

    /// Method called when the adapted type is not present in a field during deserialization or is
    /// populated with `..Default::default()`. The value passed here is the default original type
    /// value for the field. This can be used to record that the field was not present inside
    /// of your adapted type.
    ///
    /// **This method must be infallible, as it will be called when `Default::default()` is used
    /// (which is infallible).**
    ///
    /// WARNING: This defaults to calling `from_thrift_field` and **assumes `from_thrift_field`
    /// will not return an `Err` for the default value**.
    fn from_thrift_default<T: ThriftAnnotations>(
        value: Self::StandardType,
        field_id: i16,
    ) -> Self::AdaptedType {
        Self::from_thrift_field::<T>(value, field_id).unwrap_or_else(|e| {
            panic!(
                "`from_thrift_field` must not return an `Err` on field ID {} for its default value, but it did: '{:?}'",
                field_id, e
            );
        })
    }
}

// NOTE: we define where bounds on the structs themselves here to improve error messaging during
// the Thrift compilation process.

//// Layers multiple [ThriftTypeAdapter] together. Used when multiple Thrift typedefs with adapters
//// are layered on each other.
pub struct LayeredThriftAdapter<Fst, Snd>
where
    Fst: ThriftAdapter,
    Snd: ThriftAdapter,
    Fst::StandardType: Into<Snd::AdaptedType>,
    Snd::AdaptedType: Into<Fst::StandardType>,
{
    _inner_first: PhantomData<Fst>,
    _inner_second: PhantomData<Snd>,
}

impl<Fst, Snd> ThriftAdapter for LayeredThriftAdapter<Fst, Snd>
where
    Fst: ThriftAdapter,
    Snd: ThriftAdapter,
    Fst::StandardType: Into<Snd::AdaptedType>,
    Snd::AdaptedType: Into<Fst::StandardType>,
{
    type StandardType = <Snd as ThriftAdapter>::StandardType;
    type AdaptedType = <Fst as ThriftAdapter>::AdaptedType;

    type Error = anyhow::Error;

    #[inline]
    fn from_thrift(value: Self::StandardType) -> Result<Self::AdaptedType, Self::Error> {
        <Fst as ThriftAdapter>::from_thrift(
            <Snd as ThriftAdapter>::from_thrift(value)
                .map_err(Into::<anyhow::Error>::into)?
                .into(),
        )
        .map_err(Into::<anyhow::Error>::into)
    }

    #[inline]
    fn to_thrift(value: &Self::AdaptedType) -> Self::StandardType {
        <Snd as ThriftAdapter>::to_thrift(&<Fst as ThriftAdapter>::to_thrift(value).into())
    }

    #[inline]
    fn from_thrift_field<T: ThriftAnnotations>(
        value: Self::StandardType,
        field_id: i16,
    ) -> Result<Self::AdaptedType, Self::Error> {
        <Fst as ThriftAdapter>::from_thrift_field::<T>(
            <Snd as ThriftAdapter>::from_thrift_field::<T>(value, field_id)
                .map_err(Into::<anyhow::Error>::into)?
                .into(),
            field_id,
        )
        .map_err(Into::<anyhow::Error>::into)
    }

    #[inline]
    fn to_thrift_field<T: ThriftAnnotations>(
        value: &Self::AdaptedType,
        field_id: i16,
    ) -> Self::StandardType {
        <Snd as ThriftAdapter>::to_thrift_field::<T>(
            &<Fst as ThriftAdapter>::to_thrift_field::<T>(value, field_id).into(),
            field_id,
        )
    }

    #[inline]
    fn from_thrift_default<T: ThriftAnnotations>(
        value: Self::StandardType,
        field_id: i16,
    ) -> Self::AdaptedType {
        <Fst as ThriftAdapter>::from_thrift_default::<T>(
            <Snd as ThriftAdapter>::from_thrift_default::<T>(value, field_id).into(),
            field_id,
        )
    }
}

/// Transforms the given adapter `A` into an adapter with the signature `Vec<StandardType>`
/// -> `Vec<AdaptedType>`. Because Rust doesn't have HKT, we cannot make this "generic" over
/// multiple collection types.
pub struct ListMapAdapter
where
    A: ThriftAdapter,
{
    _inner_adapter: PhantomData,
}

impl ThriftAdapter for ListMapAdapter
where
    A: ThriftAdapter,
{
    type StandardType = Vec;
    type AdaptedType = Vec;

    type Error = A::Error;

    #[inline]
    fn from_thrift(value: Self::StandardType) -> Result<Self::AdaptedType, Self::Error> {
        value
            .into_iter()
            .map(|elem| A::from_thrift(elem))
            .collect::<Result<Self::AdaptedType, Self::Error>>()
    }

    #[inline]
    fn to_thrift(value: &Self::AdaptedType) -> Self::StandardType {
        value.iter().map(|elem| A::to_thrift(elem)).collect()
    }

    #[inline]
    fn from_thrift_field<T: ThriftAnnotations>(
        value: Self::StandardType,
        field_id: i16,
    ) -> Result<Self::AdaptedType, Self::Error> {
        value
            .into_iter()
            .map(|elem| A::from_thrift_field::<T>(elem, field_id))
            .collect::<Result<Self::AdaptedType, Self::Error>>()
    }

    #[inline]
    fn to_thrift_field<T: ThriftAnnotations>(
        value: &Self::AdaptedType,
        field_id: i16,
    ) -> Self::StandardType {
        value
            .iter()
            .map(|elem| A::to_thrift_field::<T>(elem, field_id))
            .collect()
    }

    #[inline]
    fn from_thrift_default<T: ThriftAnnotations>(
        value: Self::StandardType,
        field_id: i16,
    ) -> Self::AdaptedType {
        value
            .into_iter()
            .map(|elem| A::from_thrift_default::<T>(elem, field_id))
            .collect()
    }
}

/// Transforms the given adapter `A` into an adapter with the signature `BTreeSet<StandardType>`
/// -> `BTreeSet<AdaptedType>`. Because Rust doesn't have HKT, we cannot make this "generic" over
/// multiple collection types.
pub struct SetMapAdapter
where
    A: ThriftAdapter,
    A::StandardType: Ord + PartialEq,
    A::AdaptedType: Ord + PartialEq,
{
    _inner_adapter: PhantomData,
}

impl ThriftAdapter for SetMapAdapter
where
    A: ThriftAdapter,
    A::StandardType: Ord + PartialEq,
    A::AdaptedType: Ord + PartialEq,
{
    type StandardType = BTreeSet;
    type AdaptedType = BTreeSet;

    type Error = A::Error;

    #[inline]
    fn from_thrift(value: Self::StandardType) -> Result<Self::AdaptedType, Self::Error> {
        value
            .into_iter()
            .map(|elem| A::from_thrift(elem))
            .collect::<Result<Self::AdaptedType, Self::Error>>()
    }

    #[inline]
    fn to_thrift(value: &Self::AdaptedType) -> Self::StandardType {
        value.iter().map(|elem| A::to_thrift(elem)).collect()
    }

    #[inline]
    fn from_thrift_field<T: ThriftAnnotations>(
        value: Self::StandardType,
        field_id: i16,
    ) -> Result<Self::AdaptedType, Self::Error> {
        value
            .into_iter()
            .map(|elem| A::from_thrift_field::<T>(elem, field_id))
            .collect::<Result<Self::AdaptedType, Self::Error>>()
    }

    #[inline]
    fn to_thrift_field<T: ThriftAnnotations>(
        value: &Self::AdaptedType,
        field_id: i16,
    ) -> Self::StandardType {
        value
            .iter()
            .map(|elem| A::to_thrift_field::<T>(elem, field_id))
            .collect()
    }

    #[inline]
    fn from_thrift_default<T: ThriftAnnotations>(
        value: Self::StandardType,
        field_id: i16,
    ) -> Self::AdaptedType {
        value
            .into_iter()
            .map(|elem| A::from_thrift_default::<T>(elem, field_id))
            .collect()
    }
}

/// Transforms the given adapter `KA` and `KV` into an adapter with the signature
/// `BTreeMap<KA::StandardType, KV::StandardType>` -> `BTreeMap<KA::AdaptedType, KV::AdaptedType>`.
/// Because Rust doesn't have HKT, we cannot make this "generic" over multiple collection types.
pub struct MapMapAdapter<KA, KV>
where
    KA: ThriftAdapter,
    KV: ThriftAdapter,
    KA::StandardType: Ord + PartialEq,
    KA::AdaptedType: Ord + PartialEq,
{
    _inner_key_adapter: PhantomData<KA>,
    _inner_val_adapter: PhantomData<KV>,
}

impl<KA, KV> ThriftAdapter for MapMapAdapter<KA, KV>
where
    KA: ThriftAdapter,
    KV: ThriftAdapter,
    KA::StandardType: Ord + PartialEq,
    KA::AdaptedType: Ord + PartialEq,
{
    type StandardType = BTreeMap<KA::StandardType, KV::StandardType>;
    type AdaptedType = BTreeMap<KA::AdaptedType, KV::AdaptedType>;

    type Error = anyhow::Error;

    #[inline]
    fn from_thrift(value: Self::StandardType) -> Result<Self::AdaptedType, Self::Error> {
        value
            .into_iter()
            .map(|(key, val)| {
                Ok((
                    KA::from_thrift(key).map_err(Into::<anyhow::Error>::into)?,
                    KV::from_thrift(val).map_err(Into::<anyhow::Error>::into)?,
                ))
            })
            .collect::<Result<Self::AdaptedType, Self::Error>>()
    }

    #[inline]
    fn to_thrift(value: &Self::AdaptedType) -> Self::StandardType {
        value
            .iter()
            .map(|(key, val)| (KA::to_thrift(key), KV::to_thrift(val)))
            .collect::<Self::StandardType>()
    }

    #[inline]
    fn from_thrift_field<T: ThriftAnnotations>(
        value: Self::StandardType,
        field_id: i16,
    ) -> Result<Self::AdaptedType, Self::Error> {
        value
            .into_iter()
            .map(|(key, val)| {
                Ok((
                    KA::from_thrift_field::<T>(key, field_id)
                        .map_err(Into::<anyhow::Error>::into)?,
                    KV::from_thrift_field::<T>(val, field_id)
                        .map_err(Into::<anyhow::Error>::into)?,
                ))
            })
            .collect::<Result<Self::AdaptedType, Self::Error>>()
    }

    #[inline]
    fn to_thrift_field<T: ThriftAnnotations>(
        value: &Self::AdaptedType,
        field_id: i16,
    ) -> Self::StandardType {
        value
            .iter()
            .map(|(key, val)| {
                (
                    KA::to_thrift_field::<T>(key, field_id),
                    KV::to_thrift_field::<T>(val, field_id),
                )
            })
            .collect::<Self::StandardType>()
    }

    #[inline]
    fn from_thrift_default<T: ThriftAnnotations>(
        value: Self::StandardType,
        field_id: i16,
    ) -> Self::AdaptedType {
        value
            .into_iter()
            .map(|(key, val)| {
                (
                    KA::from_thrift_default::<T>(key, field_id),
                    KV::from_thrift_default::<T>(val, field_id),
                )
            })
            .collect::<Self::AdaptedType>()
    }
}

/// No-op adapter. Used if the key of a map is an adapted type, but the value isn't, or vice versa.
pub struct IdentityAdapter<T>
where
    T: Clone + Debug + Send + Sync + PartialEq,
{
    _inner: PhantomData<T>,
}

impl<T> ThriftAdapter for IdentityAdapter<T>
where
    T: Clone + Debug + Send + Sync + PartialEq,
{
    type StandardType = T;
    type AdaptedType = T;

    type Error = std::convert::Infallible;

    fn from_thrift(value: Self::StandardType) -> Result<Self::AdaptedType, Self::Error> {
        Ok(value)
    }

    fn to_thrift(value: &Self::AdaptedType) -> Self::StandardType {
        value.clone()
    }
}

/// Convenience trait for newtype adapters.
///
/// This trait simplifies construction of adapters that newtype the standard
/// type. On top of the standard bounds, the newtype (adapted type) needs to
/// also have a [`From`] and [`Into`] implementation for the standard thrift
/// type.
///
/// # Examples
///
/// ```
/// use fbthrift::adapter::NewTypeAdapter;
///
/// #[derive(Clone, Debug)]
/// struct MyInt(i64);
///
/// impl From<MyInt> for i64 {
///     fn from(v: MyInt) -> Self { v.0 }
/// }
///
/// impl From<i64> for MyInt {
///     fn from(v: i64) -> Self { Self(v) }
/// }
///
/// struct MyIntAdapter;
///
/// impl NewTypeAdapter for MyIntAdapter {
///     type StandardType = i64;
///     type AdaptedType = MyInt;
/// }
/// ```
pub trait NewTypeAdapter {
    /// The thrift type that the adapter interprets.
    type StandardType;
    /// The Rust type to adapt to.
    type AdaptedType;
}

impl<Adapter, StandardType, AdaptedType> ThriftAdapter for Adapter
where
    Adapter: NewTypeAdapter<StandardType = StandardType, AdaptedType = AdaptedType>,
    AdaptedType: From<StandardType> + Into<StandardType> + Clone + Debug + Send + Sync + PartialEq,
{
    type StandardType = <Self as NewTypeAdapter>::StandardType;
    type AdaptedType = <Self as NewTypeAdapter>::AdaptedType;

    type Error = <Self::AdaptedType as TryFrom<Self::StandardType>>::Error;

    fn to_thrift(value: &Self::AdaptedType) -> Self::StandardType {
        value.clone().into()
    }

    fn from_thrift(value: Self::StandardType) -> Result<Self::AdaptedType, Self::Error> {
        Self::AdaptedType::try_from(value)
    }
}

#[cfg(test)]
mod tests {
    use std::any::TypeId;

    use super::*;

    // Represents the following flow
    // String (Final Adapted Type) <- BoolToStringAdapter -> Bool <- I64ToBoolAdapter -> i64
    // <- I8ToI64Adapter -> i8 (Original Thrift type)
    struct DummyParentStruct {}

    impl ThriftAnnotations for DummyParentStruct {}

    struct BoolToStringAdapter {}

    impl ThriftAdapter for BoolToStringAdapter {
        type StandardType = bool;
        type AdaptedType = String;

        type Error = anyhow::Error;

        fn from_thrift(value: Self::StandardType) -> Result<Self::AdaptedType, Self::Error> {
            Ok(value.to_string())
        }

        fn to_thrift(value: &Self::AdaptedType) -> Self::StandardType {
            value == "true"
        }

        fn from_thrift_field<T: ThriftAnnotations>(
            value: Self::StandardType,
            field_id: i16,
        ) -> Result<Self::AdaptedType, Self::Error> {
            assert_eq!(field_id, 42);
            assert_eq!(TypeId::of::<DummyParentStruct>(), TypeId::of::<T>());

            Self::from_thrift(value)
        }

        fn to_thrift_field<T: ThriftAnnotations>(
            value: &Self::AdaptedType,
            field_id: i16,
        ) -> Self::StandardType {
            assert_eq!(field_id, 42);
            assert_eq!(TypeId::of::<DummyParentStruct>(), TypeId::of::<T>());

            Self::to_thrift(value)
        }

        fn from_thrift_default<T: ThriftAnnotations>(
            value: Self::StandardType,
            field_id: i16,
        ) -> Self::AdaptedType {
            assert_eq!(field_id, 42);
            assert_eq!(TypeId::of::<DummyParentStruct>(), TypeId::of::<T>());

            Self::from_thrift_field::<T>(value, field_id).unwrap()
        }
    }

    struct I64ToBoolAdapter {}

    impl ThriftAdapter for I64ToBoolAdapter {
        type StandardType = i64;
        type AdaptedType = bool;

        type Error = anyhow::Error;

        fn from_thrift(value: Self::StandardType) -> Result<Self::AdaptedType, Self::Error> {
            match value {
                0 => Ok(false),
                1 => Ok(true),
                other => anyhow::bail!("invalid num {}", other),
            }
        }

        fn to_thrift(value: &Self::AdaptedType) -> Self::StandardType {
            if *value { 1 } else { 0 }
        }

        fn from_thrift_field<T: ThriftAnnotations>(
            value: Self::StandardType,
            field_id: i16,
        ) -> Result<Self::AdaptedType, Self::Error> {
            assert_eq!(field_id, 42);
            assert_eq!(TypeId::of::<DummyParentStruct>(), TypeId::of::<T>());

            Self::from_thrift(value)
        }

        fn to_thrift_field<T: ThriftAnnotations>(
            value: &Self::AdaptedType,
            field_id: i16,
        ) -> Self::StandardType {
            assert_eq!(field_id, 42);
            assert_eq!(TypeId::of::<DummyParentStruct>(), TypeId::of::<T>());

            Self::to_thrift(value)
        }

        fn from_thrift_default<T: ThriftAnnotations>(
            value: Self::StandardType,
            field_id: i16,
        ) -> Self::AdaptedType {
            assert_eq!(field_id, 42);
            assert_eq!(TypeId::of::<DummyParentStruct>(), TypeId::of::<T>());

            Self::from_thrift_field::<T>(value, field_id).unwrap()
        }
    }

    struct I8ToI64Adapter {}

    impl ThriftAdapter for I8ToI64Adapter {
        type StandardType = i8;
        type AdaptedType = i64;

        type Error = anyhow::Error;

        fn from_thrift(value: Self::StandardType) -> Result<Self::AdaptedType, Self::Error> {
            Ok(value.into())
        }

        fn to_thrift(value: &Self::AdaptedType) -> Self::StandardType {
            (*value).try_into().unwrap()
        }

        fn from_thrift_field<T: ThriftAnnotations>(
            value: Self::StandardType,
            field_id: i16,
        ) -> Result<Self::AdaptedType, Self::Error> {
            assert_eq!(field_id, 42);
            assert_eq!(TypeId::of::<DummyParentStruct>(), TypeId::of::<T>());

            Self::from_thrift(value)
        }

        fn to_thrift_field<T: ThriftAnnotations>(
            value: &Self::AdaptedType,
            field_id: i16,
        ) -> Self::StandardType {
            assert_eq!(field_id, 42);
            assert_eq!(TypeId::of::<DummyParentStruct>(), TypeId::of::<T>());

            Self::to_thrift(value)
        }

        fn from_thrift_default<T: ThriftAnnotations>(
            value: Self::StandardType,
            field_id: i16,
        ) -> Self::AdaptedType {
            assert_eq!(field_id, 42);
            assert_eq!(TypeId::of::<DummyParentStruct>(), TypeId::of::<T>());

            Self::from_thrift_field::<T>(value, field_id).unwrap()
        }
    }

    type TestLayeredAdapter = LayeredThriftAdapter<
        BoolToStringAdapter,
        LayeredThriftAdapter<I64ToBoolAdapter, I8ToI64Adapter>,
    >;

    #[test]
    fn test_from_thrift() {
        assert_eq!(TestLayeredAdapter::from_thrift(0_i8).unwrap(), "false");
        assert_eq!(TestLayeredAdapter::from_thrift(1_i8).unwrap(), "true");
    }

    #[test]
    fn test_from_thrift_error() {
        let res = TestLayeredAdapter::from_thrift(3_i8);

        assert!(res.is_err());
        assert_eq!(res.unwrap_err().to_string(), "invalid num 3");
    }

    #[test]
    fn test_to_thrift() {
        assert_eq!(TestLayeredAdapter::to_thrift(&"false".to_string()), 0_i8);
        assert_eq!(TestLayeredAdapter::to_thrift(&"true".to_string()), 1_i8);
    }

    #[test]
    fn test_thrift_field() {
        assert_eq!(
            TestLayeredAdapter::from_thrift_field::<DummyParentStruct>(0_i8, 42).unwrap(),
            "false"
        );
        assert_eq!(
            TestLayeredAdapter::from_thrift_default::<DummyParentStruct>(0_i8, 42),
            "false"
        );
        assert_eq!(
            TestLayeredAdapter::to_thrift_field::<DummyParentStruct>(&"false".to_string(), 42),
            0_i8
        );
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::any::Any;

use crate::deserialize::Deserialize;
use crate::exceptions::ExceptionInfo;
use crate::exceptions::ResultInfo;
use crate::exceptions::ResultType;
use crate::protocol::Field;
use crate::protocol::ProtocolReader;
use crate::protocol::ProtocolWriter;
use crate::serialize::Serialize;
use crate::thrift_protocol::ProtocolID;
use crate::ttype::TType;
use crate::Result;

// Reference is thrift/lib/cpp/TApplicationException.h
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
#[repr(i32)]
pub enum ApplicationExceptionErrorCode {
    #[default]
    Unknown = 0,
    UnknownMethod = 1,
    InvalidMessageType = 2,
    WrongMethodName = 3,
    BadSequenceID = 4,
    MissingResult = 5,
    InternalError = 6,
    ProtocolError = 7,
    InvalidTransform = 8,
    InvalidProtocol = 9,
    UnsupportedClientType = 10,
    Loadshedding = 11,
    Timeout = 12,
    InjectedFailure = 13,
    ChecksumMismatch = 14,
    Interruption = 15,
}

const TAPPLICATION_EXCEPTION_ERROR_CODE: &str = "ApplicationExceptionErrorCode";

#[derive(
    Debug,
    Clone,
    Eq,
    PartialEq,
    Ord,
    PartialOrd,
    Hash,
    Default,
    thiserror::Error
)]
#[error("{type_:?}: {message}")]
pub struct ApplicationException {
    pub message: String,
    pub type_: ApplicationExceptionErrorCode,
}

impl ApplicationException {
    #[cold]
    pub fn new(type_: ApplicationExceptionErrorCode, message: String) -> Self {
        ApplicationException { message, type_ }
    }

    // Used for methods which should exist but don't
    #[cold]
    pub fn unimplemented_method(handler: &str, method: &str) -> Self {
        ApplicationException {
            type_: ApplicationExceptionErrorCode::UnknownMethod,
            message: format!("Method {} not implemented for {}", method, handler),
        }
    }

    /// Indicator that this service doesn't have the method, but another might
    #[inline]
    pub fn unknown_method() -> Self {
        ApplicationException {
            type_: ApplicationExceptionErrorCode::UnknownMethod,
            message: String::new(), // no allocation
        }
    }

    #[cold]
    pub fn missing_arg(method: &str, arg: &str) -> Self {
        ApplicationException {
            type_: ApplicationExceptionErrorCode::ProtocolError,
            message: format!("{} missing arg {}", method, arg),
        }
    }

    #[cold]
    pub fn missing_field(method: &str, field: &str) -> Self {
        ApplicationException {
            type_: ApplicationExceptionErrorCode::ProtocolError,
            message: format!("Struct {} missing field {}", method, field),
        }
    }

    #[cold]
    pub fn invalid_protocol(badproto: ProtocolID) -> Self {
        ApplicationException {
            type_: ApplicationExceptionErrorCode::InvalidProtocol,
            message: format!("Invalid protocol {:?}", badproto),
        }
    }

    /// An undeclared "exception"/panic etc
    #[cold]
    pub fn handler_panic(method: &str, exn: Box<dyn Any + Send + 'static>) -> Self {
        ApplicationException {
            type_: ApplicationExceptionErrorCode::Unknown,
            message: format!(
                "Handler for `{}` panicked with: {}",
                method,
                panic_message::get_panic_message(&exn).unwrap_or("<panic>")
            ),
        }
    }
}

impl ExceptionInfo for ApplicationException {
    #[rustfmt::skip]
    fn exn_name(&self) -> &'static str {
        match self.type_ {
            ApplicationExceptionErrorCode::Unknown => "ApplicationExceptionErrorCode::Unknown",
            ApplicationExceptionErrorCode::UnknownMethod => "ApplicationExceptionErrorCode::UnknownMethod",
            ApplicationExceptionErrorCode::InvalidMessageType => "ApplicationExceptionErrorCode::InvalidMessageType",
            ApplicationExceptionErrorCode::WrongMethodName => "ApplicationExceptionErrorCode::WrongMethodName",
            ApplicationExceptionErrorCode::BadSequenceID => "ApplicationExceptionErrorCode::BadSequenceID",
            ApplicationExceptionErrorCode::MissingResult => "ApplicationExceptionErrorCode::MissingResult",
            ApplicationExceptionErrorCode::InternalError => "ApplicationExceptionErrorCode::InternalError",
            ApplicationExceptionErrorCode::ProtocolError => "ApplicationExceptionErrorCode::ProtocolError",
            ApplicationExceptionErrorCode::InvalidTransform => "ApplicationExceptionErrorCode::InvalidTransform",
            ApplicationExceptionErrorCode::InvalidProtocol => "ApplicationExceptionErrorCode::InvalidProtocol",
            ApplicationExceptionErrorCode::UnsupportedClientType => "ApplicationExceptionErrorCode::UnsupportedClientType",
            ApplicationExceptionErrorCode::Loadshedding => "ApplicationExceptionErrorCode::Loadshedding",
            ApplicationExceptionErrorCode::Timeout => "ApplicationExceptionErrorCode::Timeout",
            ApplicationExceptionErrorCode::InjectedFailure => "ApplicationExceptionErrorCode::InjectedFailure",
            ApplicationExceptionErrorCode::ChecksumMismatch => "ApplicationExceptionErrorCode::ChecksumMismatch",
            ApplicationExceptionErrorCode::Interruption => "ApplicationExceptionErrorCode::Interruption",
        }
    }

    fn exn_value(&self) -> String {
        self.message.clone()
    }

    #[inline]
    fn exn_is_declared(&self) -> bool {
        false
    }
}

impl ResultInfo for ApplicationException {
    fn result_type(&self) -> ResultType {
        ResultType::Exception
    }
}

impl<P> Deserialize<P> for ApplicationException
where
    P: ProtocolReader,
{
    /// Decodes a ApplicationException message from a Protocol stream
    fn read(iprot: &mut P) -> Result<Self> {
        iprot.read_struct_begin(|_| ())?;

        let mut message = String::from("");
        let mut type_ = ApplicationExceptionErrorCode::Unknown;

        loop {
            // Start reading the next field
            static FIELDS: &[Field] = &[
                Field::new("message", TType::String, 1),
                Field::new("type", TType::I32, 2),
            ];
            let (_, ttype, id) = iprot.read_field_begin(|_| (), FIELDS)?;

            match (ttype, id) {
                (TType::Stop, _) => break,
                (TType::String, 1) => message = iprot.read_string()?,
                (TType::I32, 2) => {
                    type_ = match iprot.read_i32()? {
                        1 => ApplicationExceptionErrorCode::UnknownMethod,
                        2 => ApplicationExceptionErrorCode::InvalidMessageType,
                        3 => ApplicationExceptionErrorCode::WrongMethodName,
                        4 => ApplicationExceptionErrorCode::BadSequenceID,
                        5 => ApplicationExceptionErrorCode::MissingResult,
                        6 => ApplicationExceptionErrorCode::InternalError,
                        7 => ApplicationExceptionErrorCode::ProtocolError,
                        8 => ApplicationExceptionErrorCode::InvalidTransform,
                        9 => ApplicationExceptionErrorCode::InvalidProtocol,
                        10 => ApplicationExceptionErrorCode::UnsupportedClientType,
                        11 => ApplicationExceptionErrorCode::Loadshedding,
                        12 => ApplicationExceptionErrorCode::Timeout,
                        13 => ApplicationExceptionErrorCode::InjectedFailure,
                        14 => ApplicationExceptionErrorCode::ChecksumMismatch,
                        15 => ApplicationExceptionErrorCode::Interruption,

                        _ => ApplicationExceptionErrorCode::Unknown,
                    }
                }
                (ttype, _) => iprot.skip(ttype)?,
            };

            // Finished reading the end of the field
            iprot.read_field_end()?;
        }
        iprot.read_struct_end()?;
        Ok(ApplicationException::new(type_, message))
    }
}

impl<P> Serialize<P> for ApplicationException
where
    P: ProtocolWriter,
{
    /// Writes an application exception to the Protocol stream
    fn write(&self, oprot: &mut P) {
        oprot.write_struct_begin(TAPPLICATION_EXCEPTION_ERROR_CODE);

        if !self.message.is_empty() {
            oprot.write_field_begin("message", TType::String, 1);
            oprot.write_string(&self.message);
            oprot.write_field_end();
        }
        oprot.write_field_begin("type", TType::I32, 2);
        oprot.write_i32(self.type_ as i32);
        oprot.write_field_end();
        oprot.write_field_stop();
        oprot.write_struct_end();
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::io::Cursor;

use anyhow::anyhow;
use anyhow::Result;
use bufsize::SizeCounter;
use bytes::Bytes;
use bytes::BytesMut;
use ghost::phantom;

use crate::binary_type::CopyFromBuf;
use crate::bufext::BufExt;
use crate::bufext::BufMutExt;
use crate::bufext::DeserializeSource;
use crate::deserialize::Deserialize;
use crate::errors::ProtocolError;
use crate::framing::Framing;
use crate::protocol::Field;
use crate::protocol::Protocol;
use crate::protocol::ProtocolReader;
use crate::protocol::ProtocolWriter;
use crate::serialize::Serialize;
use crate::thrift_protocol::MessageType;
use crate::thrift_protocol::ProtocolID;
use crate::ttype::TType;

pub const BINARY_VERSION_MASK: u32 = 0xffff_0000;
pub const BINARY_VERSION_1: u32 = 0x8001_0000;

/// A straight-forward binary format that encodes numeric values in fixed width.
///
/// ```ignore
/// let protocol = BinaryProtocol;
/// let transport = HttpClient::new(ENDPOINT)?;
/// let client = <dyn BuckGraphService>::new(protocol, transport);
/// ```
///
/// The type parameter is the Framing expected by the transport on which this
/// protocol is operating. Usually by convention the transport itself serves as
/// the Framing impl, so for example in the case of HttpClient above, the
/// compiler has inferred `F = HttpClient`.
///
/// Where the compiler reports that a Framing can't be inferred, one can be
/// specified explicitly:
///
/// ```ignore
/// let protocol = BinaryProtocol::<SRHeaderTransport>;
/// ```
#[phantom]
#[derive(Copy, Clone)]
pub struct BinaryProtocol<F = Bytes>;

pub struct BinaryProtocolSerializer<B> {
    buffer: B,
}

pub struct BinaryProtocolDeserializer<B> {
    buffer: B,
}

impl<F> Protocol for BinaryProtocol<F>
where
    F: Framing + 'static,
{
    type Frame = F;
    type Sizer = BinaryProtocolSerializer<SizeCounter>;
    type Serializer = BinaryProtocolSerializer<F::EncBuf>;
    type Deserializer = BinaryProtocolDeserializer<F::DecBuf>;

    const PROTOCOL_ID: ProtocolID = ProtocolID::BinaryProtocol;

    fn serializer<SZ, SER>(size: SZ, ser: SER) -> <Self::Serializer as ProtocolWriter>::Final
    where
        SZ: FnOnce(&mut Self::Sizer),
        SER: FnOnce(&mut Self::Serializer),
    {
        let mut sizer = BinaryProtocolSerializer {
            buffer: SizeCounter::new(),
        };
        size(&mut sizer);
        let sz = sizer.finish();
        let mut buf = BinaryProtocolSerializer {
            buffer: F::enc_with_capacity(sz),
        };
        ser(&mut buf);
        buf.finish()
    }

    fn deserializer(buf: F::DecBuf) -> Self::Deserializer {
        BinaryProtocolDeserializer::new(buf)
    }

    fn into_buffer(deser: Self::Deserializer) -> F::DecBuf {
        deser.into_inner()
    }
}

impl<B> BinaryProtocolSerializer<B> {
    pub fn with_buffer(buffer: B) -> Self {
        Self { buffer }
    }
}

impl<B: BufMutExt> BinaryProtocolSerializer<B> {
    fn write_u32(&mut self, value: u32) {
        self.buffer.put_u32(value)
    }
}

impl<B: BufExt> BinaryProtocolDeserializer<B> {
    pub fn new(buffer: B) -> Self {
        BinaryProtocolDeserializer { buffer }
    }

    pub fn into_inner(self) -> B {
        self.buffer
    }

    fn peek_bytes(&self, len: usize) -> Option<&[u8]> {
        if self.buffer.chunk().len() >= len {
            Some(&self.buffer.chunk()[..len])
        } else {
            None
        }
    }

    fn read_u32(&mut self) -> Result<u32> {
        ensure_err!(self.buffer.remaining() >= 4, ProtocolError::EOF);

        Ok(self.buffer.get_u32())
    }
}

impl<B: BufMutExt> ProtocolWriter for BinaryProtocolSerializer<B> {
    type Final = B::Final;

    fn write_message_begin(&mut self, name: &str, type_id: MessageType, seqid: u32) {
        let version = BINARY_VERSION_1 | (type_id as u32);
        self.write_i32(version as i32);
        self.write_string(name);
        self.write_u32(seqid);
    }

    #[inline]
    fn write_message_end(&mut self) {}

    #[inline]
    fn write_struct_begin(&mut self, _name: &str) {}

    #[inline]
    fn write_struct_end(&mut self) {}

    fn write_field_begin(&mut self, _name: &str, type_id: TType, id: i16) {
        self.write_byte(type_id as i8);
        self.write_i16(id);
    }

    #[inline]
    fn write_field_end(&mut self) {}

    #[inline]
    fn write_field_stop(&mut self) {
        self.write_byte(TType::Stop as i8)
    }

    fn write_map_begin(&mut self, key_type: TType, value_type: TType, size: usize) {
        self.write_byte(key_type as i8);
        self.write_byte(value_type as i8);
        self.write_i32(i32::try_from(size as u64).expect("map size overflow"));
    }

    #[inline]
    fn write_map_key_begin(&mut self) {}

    #[inline]
    fn write_map_value_begin(&mut self) {}

    #[inline]
    fn write_map_end(&mut self) {}

    fn write_list_begin(&mut self, elem_type: TType, size: usize) {
        self.write_byte(elem_type as i8);
        self.write_i32(i32::try_from(size as u64).expect("list size overflow"));
    }

    #[inline]
    fn write_list_value_begin(&mut self) {}

    #[inline]
    fn write_list_end(&mut self) {}

    fn write_set_begin(&mut self, elem_type: TType, size: usize) {
        self.write_byte(elem_type as i8);
        self.write_i32(i32::try_from(size as u64).expect("set size overflow"));
    }

    #[inline]
    fn write_set_value_begin(&mut self) {}

    fn write_set_end(&mut self) {}

    fn write_bool(&mut self, value: bool) {
        if value {
            self.write_byte(1)
        } else {
            self.write_byte(0)
        }
    }

    fn write_byte(&mut self, value: i8) {
        self.buffer.put_i8(value)
    }

    fn write_i16(&mut self, value: i16) {
        self.buffer.put_i16(value)
    }

    fn write_i32(&mut self, value: i32) {
        self.buffer.put_i32(value)
    }

    fn write_i64(&mut self, value: i64) {
        self.buffer.put_i64(value)
    }

    fn write_double(&mut self, value: f64) {
        self.buffer.put_f64(value)
    }

    fn write_float(&mut self, value: f32) {
        self.buffer.put_f32(value)
    }

    fn write_string(&mut self, value: &str) {
        self.write_i32(value.len() as i32);
        self.buffer.put_slice(value.as_bytes())
    }

    fn write_binary(&mut self, value: &[u8]) {
        self.write_i32(value.len() as i32);
        self.buffer.put_slice(value)
    }

    fn finish(self) -> B::Final {
        self.buffer.finalize()
    }
}

impl<B: BufExt> ProtocolReader for BinaryProtocolDeserializer<B> {
    fn read_message_begin<F, T>(&mut self, msgfn: F) -> Result<(T, MessageType, u32)>
    where
        F: FnOnce(&[u8]) -> T,
    {
        let versionty = self.read_i32()? as u32;

        let msgtype = MessageType::try_from(versionty & !BINARY_VERSION_MASK)?; // !u32 -> ~u32
        let version = versionty & BINARY_VERSION_MASK;
        ensure_err!(version == BINARY_VERSION_1, ProtocolError::BadVersion);

        let name = {
            let len = self.read_i32()? as usize;
            let (len, name) = {
                if self.peek_bytes(len).is_some() {
                    let namebuf = self.peek_bytes(len).unwrap();
                    (namebuf.len(), msgfn(namebuf))
                } else {
                    ensure_err!(
                        self.buffer.remaining() >= len,
                        ProtocolError::InvalidDataLength
                    );
                    let namebuf: Vec<u8> = Vec::copy_from_buf(&mut self.buffer, len);
                    (0, msgfn(namebuf.as_slice()))
                }
            };
            self.buffer.advance(len);
            name
        };
        let seq_id = self.read_u32()?;

        Ok((name, msgtype, seq_id))
    }

    fn read_message_end(&mut self) -> Result<()> {
        Ok(())
    }

    fn read_struct_begin<F, T>(&mut self, namefn: F) -> Result<T>
    where
        F: FnOnce(&[u8]) -> T,
    {
        Ok(namefn(&[]))
    }

    fn read_struct_end(&mut self) -> Result<()> {
        Ok(())
    }

    fn read_field_begin<F, T>(&mut self, fieldfn: F, _fields: &[Field]) -> Result<(T, TType, i16)>
    where
        F: FnOnce(&[u8]) -> T,
    {
        let type_id = TType::try_from(self.read_byte()?)?;
        let seq_id = match type_id {
            TType::Stop => 0,
            _ => self.read_i16()?,
        };
        Ok((fieldfn(&[]), type_id, seq_id))
    }

    fn read_field_end(&mut self) -> Result<()> {
        Ok(())
    }

    fn read_map_begin(&mut self) -> Result<(TType, TType, Option<usize>)> {
        let k_type = TType::try_from(self.read_byte()?)?;
        let v_type = TType::try_from(self.read_byte()?)?;

        let size = self.read_i32()?;
        ensure_err!(size >= 0, ProtocolError::InvalidDataLength);
        Ok((k_type, v_type, Some(size as usize)))
    }

    #[inline]
    fn read_map_key_begin(&mut self) -> Result<bool> {
        Ok(true)
    }

    #[inline]
    fn read_map_value_begin(&mut self) -> Result<()> {
        Ok(())
    }

    #[inline]
    fn read_map_value_end(&mut self) -> Result<()> {
        Ok(())
    }

    fn read_map_end(&mut self) -> Result<()> {
        Ok(())
    }

    fn read_list_begin(&mut self) -> Result<(TType, Option<usize>)> {
        let elem_type = TType::try_from(self.read_byte()?)?;
        let size = self.read_i32()?;
        ensure_err!(size >= 0, ProtocolError::InvalidDataLength);
        Ok((elem_type, Some(size as usize)))
    }

    #[inline]
    fn read_list_value_begin(&mut self) -> Result<bool> {
        Ok(true)
    }

    #[inline]
    fn read_list_value_end(&mut self) -> Result<()> {
        Ok(())
    }

    fn read_list_end(&mut self) -> Result<()> {
        Ok(())
    }

    fn read_set_begin(&mut self) -> Result<(TType, Option<usize>)> {
        let elem_type = TType::try_from(self.read_byte()?)?;
        let size = self.read_i32()?;
        ensure_err!(size >= 0, ProtocolError::InvalidDataLength);
        Ok((elem_type, Some(size as usize)))
    }

    #[inline]
    fn read_set_value_begin(&mut self) -> Result<bool> {
        Ok(true)
    }

    #[inline]
    fn read_set_value_end(&mut self) -> Result<()> {
        Ok(())
    }

    fn read_set_end(&mut self) -> Result<()> {
        Ok(())
    }

    fn read_bool(&mut self) -> Result<bool> {
        match self.read_byte()? {
            0 => Ok(false),
            _ => Ok(true),
        }
    }

    fn read_byte(&mut self) -> Result<i8> {
        ensure_err!(self.buffer.remaining() >= 1, ProtocolError::EOF);

        Ok(self.buffer.get_i8())
    }

    fn read_i16(&mut self) -> Result<i16> {
        ensure_err!(self.buffer.remaining() >= 2, ProtocolError::EOF);

        Ok(self.buffer.get_i16())
    }

    fn read_i32(&mut self) -> Result<i32> {
        ensure_err!(self.buffer.remaining() >= 4, ProtocolError::EOF);

        Ok(self.buffer.get_i32())
    }

    fn read_i64(&mut self) -> Result<i64> {
        ensure_err!(self.buffer.remaining() >= 8, ProtocolError::EOF);

        Ok(self.buffer.get_i64())
    }

    fn read_double(&mut self) -> Result<f64> {
        ensure_err!(self.buffer.remaining() >= 8, ProtocolError::EOF);

        Ok(self.buffer.get_f64())
    }

    fn read_float(&mut self) -> Result<f32> {
        ensure_err!(self.buffer.remaining() >= 4, ProtocolError::EOF);

        Ok(self.buffer.get_f32())
    }

    fn read_string(&mut self) -> Result<String> {
        let vec = self.read_binary::<Vec<u8>>()?;

        String::from_utf8(vec)
            .map_err(|utf8_error| anyhow!("deserializing `string` from Thrift binary protocol got invalid utf-8, you need to use `binary` instead: {utf8_error}"))
    }

    fn read_binary<V: CopyFromBuf>(&mut self) -> Result<V> {
        let received_len = self.read_i32()?;
        ensure_err!(received_len >= 0, ProtocolError::InvalidDataLength);

        let received_len = received_len as usize;

        ensure_err!(self.buffer.remaining() >= received_len, ProtocolError::EOF);
        Ok(V::copy_from_buf(&mut self.buffer, received_len))
    }
}

/// How large an item will be when `serialize()` is called
pub fn serialize_size<T>(v: &T) -> usize
where
    T: Serialize<BinaryProtocolSerializer<SizeCounter>>,
{
    let mut sizer = BinaryProtocolSerializer::with_buffer(SizeCounter::new());
    v.write(&mut sizer);
    sizer.finish()
}

/// Serialize a Thrift value using the binary protocol to a pre-allocated buffer.
/// This will panic if the buffer is not large enough. A buffer at least as
/// large as the return value of `serialize_size` will not panic.
pub fn serialize_to_buffer<T>(v: T, buffer: BytesMut) -> BinaryProtocolSerializer<BytesMut>
where
    T: Serialize<BinaryProtocolSerializer<BytesMut>>,
{
    // Now that we have the size, allocate an output buffer and serialize into it
    let mut buf = BinaryProtocolSerializer::with_buffer(buffer);
    v.write(&mut buf);
    buf
}

pub trait SerializeRef:
    Serialize<BinaryProtocolSerializer<SizeCounter>> + Serialize<BinaryProtocolSerializer<BytesMut>>
where
    for<'a> &'a Self: Serialize<BinaryProtocolSerializer<SizeCounter>>,
    for<'a> &'a Self: Serialize<BinaryProtocolSerializer<BytesMut>>,
{
}

impl<T> SerializeRef for T
where
    T: Serialize<BinaryProtocolSerializer<BytesMut>>,
    T: Serialize<BinaryProtocolSerializer<SizeCounter>>,
    for<'a> &'a T: Serialize<BinaryProtocolSerializer<BytesMut>>,
    for<'a> &'a T: Serialize<BinaryProtocolSerializer<SizeCounter>>,
{
}

/// Serialize a Thrift value using the binary protocol.
pub fn serialize<T>(v: T) -> Bytes
where
    T: Serialize<BinaryProtocolSerializer<SizeCounter>>
        + Serialize<BinaryProtocolSerializer<BytesMut>>,
{
    let sz = serialize_size(&v);
    let buf = serialize_to_buffer(v, BytesMut::with_capacity(sz));
    // Done
    buf.finish()
}

pub trait DeserializeSlice:
    for<'a> Deserialize<BinaryProtocolDeserializer<Cursor<&'a [u8]>>>
{
}

impl<T> DeserializeSlice for T where
    T: for<'a> Deserialize<BinaryProtocolDeserializer<Cursor<&'a [u8]>>>
{
}

/// Deserialize a Thrift blob using the binary protocol.
pub fn deserialize<T, B, C>(b: B) -> Result<T>
where
    B: Into<DeserializeSource<C>>,
    C: BufExt,
    T: Deserialize<BinaryProtocolDeserializer<C>>,
{
    let source: DeserializeSource<C> = b.into();
    let mut deser = BinaryProtocolDeserializer::new(source.0);
    T::read(&mut deser)
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::io::Cursor;

use bytes::Bytes;

use crate::bufext::BufExt;

/// Trait implemented on types that can be used as `binary` types in
/// thrift.  These types copy data from the Thrift buffer.
pub trait BinaryType: Sized {
    fn with_capacity(capacity: usize) -> Self;
    fn extend_from_slice(&mut self, other: &[u8]);
    fn from_vec(vec: Vec<u8>) -> Self {
        let mut binary = Self::with_capacity(vec.len());
        binary.extend_from_slice(&vec);
        binary
    }
}

/// Trait for copying from the Thrift buffer.  Special implementations
/// may do this without actually copying.
pub trait CopyFromBuf: Sized {
    fn copy_from_buf<B: BufExt>(buffer: &mut B, len: usize) -> Self;
    fn from_vec(vec: Vec<u8>) -> Self {
        Self::copy_from_buf(&mut Cursor::new(&vec), vec.len())
    }
}

impl BinaryType for Vec<u8> {
    fn with_capacity(capacity: usize) -> Self {
        Vec::with_capacity(capacity)
    }
    fn extend_from_slice(&mut self, other: &[u8]) {
        Vec::extend_from_slice(self, other);
    }
    fn from_vec(vec: Vec<u8>) -> Self {
        vec
    }
}

impl<T: BinaryType> CopyFromBuf for T {
    fn copy_from_buf<B: BufExt>(buffer: &mut B, len: usize) -> Self {
        assert!(buffer.remaining() >= len);
        let mut result = T::with_capacity(len);
        let mut remaining = len;

        while remaining > 0 {
            let part = buffer.chunk();
            let part_len = part.len().min(remaining);
            result.extend_from_slice(&part[..part_len]);
            remaining -= part_len;
            buffer.advance(part_len);
        }

        result
    }
    fn from_vec(vec: Vec<u8>) -> Self {
        BinaryType::from_vec(vec)
    }
}

pub(crate) struct Discard;

impl CopyFromBuf for Discard {
    fn copy_from_buf<B: BufExt>(buffer: &mut B, len: usize) -> Self {
        buffer.advance(len);
        Discard
    }
}

impl CopyFromBuf for Bytes {
    fn copy_from_buf<B: BufExt>(buffer: &mut B, len: usize) -> Self {
        buffer.copy_or_reuse_bytes(len)
    }
    fn from_vec(vec: Vec<u8>) -> Self {
        vec.into()
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::io::Cursor;

use bufsize::SizeCounter;
use bytes::buf::Chain;
use bytes::Buf;
use bytes::BufMut;
use bytes::Bytes;
use bytes::BytesMut;

use crate::varint;

pub trait BufExt: Buf {
    /// Copy `len` Bytes from this (and advance the position), or reuse them from the underlying
    /// buffer if possible.
    fn copy_or_reuse_bytes(&mut self, len: usize) -> Bytes {
        // Default is to just copy.
        self.copy_to_bytes(len)
    }
}

impl BufExt for Bytes {}

impl BufExt for Cursor<Bytes> {
    // We can get a reference to the underlying Bytes here, and reuse that.
    fn copy_or_reuse_bytes(&mut self, len: usize) -> Bytes {
        let pos = self.position() as usize;
        let end = pos + len;
        // Panics if len is too large (same as Bytes)
        let bytes = self.get_ref().slice(pos..end);
        self.set_position(end as u64);
        bytes
    }
}

impl<T: AsRef<[u8]> + ?Sized> BufExt for Cursor<&T> {}

impl<T: BufExt, U: BufExt> BufExt for Chain<T, U> {}

pub trait BufMutExt: BufMut {
    type Final: Send + 'static;

    fn put_varint_u64(&mut self, v: u64)
    where
        Self: Sized,
    {
        varint::write_u64(self, v)
    }

    fn put_varint_i64(&mut self, v: i64)
    where
        Self: Sized,
    {
        varint::write_u64(self, varint::zigzag(v))
    }

    fn finalize(self) -> Self::Final;
}

impl BufMutExt for BytesMut {
    type Final = Bytes;

    fn finalize(self) -> Self::Final {
        self.freeze()
    }
}

impl BufMutExt for SizeCounter {
    type Final = usize;

    #[inline]
    fn put_varint_u64(&mut self, v: u64) {
        self.put_uint(v, varint::u64_len(v));
    }

    #[inline]
    fn put_varint_i64(&mut self, v: i64) {
        self.put_int(v, varint::u64_len(varint::zigzag(v)));
    }

    #[inline]
    fn finalize(self) -> Self::Final {
        self.size()
    }
}
// new type so we can impl From for Bytes vs other things that are AsRef<[u8]>
// Not implemented for AsRef<[u8]> as Bytes implements that
pub struct DeserializeSource<B: BufExt>(pub(crate) B);

impl<B: BufExt> DeserializeSource<B> {
    pub fn new(b: B) -> Self {
        DeserializeSource(b)
    }
}

// These types will use a copying cursor
macro_rules! impl_deser_as_ref_u8 {
    ( $($t:ty),* ) => {
        $(
            impl<'a> From<&'a $t> for DeserializeSource<Cursor<&'a [u8]>> {
                fn from(from: &'a $t) -> Self {
                    let data: &[u8] = from.as_ref();
                    Self(Cursor::new(data))
                }
            }
        )*
    }
}

impl_deser_as_ref_u8!([u8], Vec<u8>, String, str);

// These types take ownership without copying
// Have to explicitly do the Into types as well due to no upstream From<&Bytes> for Bytes.
macro_rules! impl_deser_into_bytes {
    ( $($t:ty),* ) => {
        $(
            impl From<$t> for DeserializeSource<Cursor<Bytes>> {
                fn from(from: $t) -> Self {
                    Self(Cursor::new(from.into()))
                }
            }
        )*
    }
}

impl_deser_into_bytes!(Bytes, Vec<u8>, String);

// Special case for &Bytes that is not covered in upstream crates From defs
impl From<&Bytes> for DeserializeSource<Cursor<Bytes>> {
    fn from(from: &Bytes) -> Self {
        // ok to clone Bytes, it just increments ref count
        Self(Cursor::new(from.clone()))
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Types that are available by default in `rust.type` annotations.

pub use std::collections::*;

pub use bytes::Bytes;
pub use ordered_float::OrderedFloat;


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::ffi::CStr;
use std::sync::Arc;

use futures::future;
use futures::future::BoxFuture;
use futures::stream::BoxStream;
use futures::FutureExt;

use crate::help::Spawner;
use crate::Framing;
use crate::FramingDecoded;
use crate::FramingEncodedFinal;
use crate::Protocol;

#[derive(Debug)]
pub enum ClientStreamElement<Payload> {
    Reply(Payload),
    ApplicationEx(Payload),
}

impl<Payload> ClientStreamElement<Payload> {
    pub fn payload(&self) -> &Payload {
        match self {
            ClientStreamElement::Reply(payload) | ClientStreamElement::ApplicationEx(payload) => {
                payload
            }
        }
    }

    pub fn into_payload(self) -> Payload {
        match self {
            ClientStreamElement::Reply(payload) | ClientStreamElement::ApplicationEx(payload) => {
                payload
            }
        }
    }
}

pub trait ClientFactory {
    type Api: ?Sized;

    fn new<P, T>(protocol: P, transport: T) -> Arc<Self::Api>
    where
        P: Protocol<Frame = T> + 'static,
        T: Transport,
        P::Deserializer: Send,
    {
        let spawner = crate::NoopSpawner;
        Self::with_spawner(protocol, transport, spawner)
    }

    fn with_spawner<P, T, S>(protocol: P, transport: T, spawner: S) -> Arc<Self::Api>
    where
        P: Protocol<Frame = T> + 'static,
        T: Transport,
        P::Deserializer: Send,
        S: Spawner;
}

pub trait Transport: Framing + Send + Sync + Sized + 'static {
    type RpcOptions: Default;

    fn call(
        &self,
        service_name: &'static CStr,
        fn_name: &'static CStr,
        req: FramingEncodedFinal<Self>,
        rpc_options: Self::RpcOptions,
    ) -> BoxFuture<'static, anyhow::Result<FramingDecoded<Self>>>;

    fn call_stream(
        &self,
        _service_name: &'static CStr,
        _fn_name: &'static CStr,
        _req: FramingEncodedFinal<Self>,
        _rpc_options: Self::RpcOptions,
    ) -> BoxFuture<
        'static,
        anyhow::Result<(
            FramingDecoded<Self>,
            BoxStream<'static, anyhow::Result<ClientStreamElement<FramingDecoded<Self>>>>,
        )>,
    > {
        future::err(anyhow::Error::msg(
            "Streaming is not supported by this transport",
        ))
        .boxed()
    }

    fn create_interaction(&self, _method_name: &'static CStr) -> Result<Self, anyhow::Error> {
        anyhow::bail!("Interactions are not supported by this transport");
    }
}