pub mod ron {
    //! Wrap T in RonCodec<T> that when serialized, always serializes to a [ron] string.

    use codee::Decoder;
    use codee::Encoder;
    use serde::Deserialize;
    use serde::Serialize;
    use serde::de::DeserializeOwned;
    use server_fn::ServerFnError;
    use server_fn::codec::Encoding;
    use server_fn::codec::FromReq;
    use server_fn::codec::FromRes;
    use server_fn::codec::IntoReq;
    use server_fn::codec::IntoRes;
    use server_fn::request::ClientReq;
    use server_fn::request::Req;
    use server_fn::response::ClientRes;
    use server_fn::response::Res;

    pub struct Ron;

    impl<T> Encoder<T> for Ron
    where
        T: Serialize,
    {
        type Encoded = String;
        type Error = ron::Error;

        fn encode(val: &T) -> Result<Self::Encoded, Self::Error> {
            ron::to_string(val)
        }
    }

    impl<T> Decoder<T> for Ron
    where
        for<'de> T: Deserialize<'de>,
    {
        type Encoded = str;
        type Error = ron::error::SpannedError;

        fn decode(val: &Self::Encoded) -> Result<T, Self::Error> {
            ron::from_str(val)
        }
    }

    impl Encoding for Ron {
        const CONTENT_TYPE: &'static str = "application/ron";
        const METHOD: http::Method = http::Method::POST;
    }

    #[derive(Debug, Clone)]
    pub struct RonEncoded<T>(pub T);

    impl<T> RonEncoded<T> {
        pub fn into_inner(self) -> T {
            self.0
        }

        pub fn new(t: T) -> Self {
            Self(t)
        }
    }

    impl<T> std::ops::Deref for RonEncoded<T> {
        type Target = T;

        fn deref(&self) -> &Self::Target {
            &self.0
        }
    }

    // IntoReq
    impl<T, Request, Err> IntoReq<Ron, Request, Err> for RonEncoded<T>
    where
        Request: ClientReq<Err>,
        T: Serialize,
    {
        fn into_req(self, path: &str, accepts: &str) -> Result<Request, ServerFnError<Err>> {
            let data = Ron::encode(&self.0).map_err(|e| ServerFnError::Serialization(e.to_string()))?;
            Request::try_new_post(path, Ron::CONTENT_TYPE, accepts, data)
        }
    }

    // FromReq
    impl<T, Request, Err> FromReq<Ron, Request, Err> for RonEncoded<T>
    where
        Request: Req<Err> + Send,
        T: DeserializeOwned,
    {
        async fn from_req(req: Request) -> Result<Self, ServerFnError<Err>> {
            let data = req.try_into_string().await?;
            Ron::decode(&data).map(RonEncoded).map_err(|e| ServerFnError::Args(e.to_string()))
        }
    }

    // IntoRes
    impl<CustErr, T, Response> IntoRes<Ron, Response, CustErr> for RonEncoded<T>
    where
        Response: Res<CustErr>,
        T: Serialize + Send,
    {
        async fn into_res(self) -> Result<Response, ServerFnError<CustErr>> {
            let data = Ron::encode(&self.0).map_err(|e| ServerFnError::Serialization(e.to_string()))?;
            Response::try_from_string(Ron::CONTENT_TYPE, data)
        }
    }

    // FromRes
    impl<T, Response, Err> FromRes<Ron, Response, Err> for RonEncoded<T>
    where
        Response: ClientRes<Err> + Send,
        T: DeserializeOwned,
    {
        async fn from_res(res: Response) -> Result<Self, ServerFnError<Err>> {
            let data = res.try_into_string().await?;
            Ron::decode(&data)
                .map(RonEncoded)
                .map_err(|e| ServerFnError::Deserialization(e.to_string()))
        }
    }

    #[cfg(test)]
    mod tests {
        use super::Ron;
        use codee::Decoder;
        use codee::Encoder;
        use serde::Deserialize;
        use serde::Serialize;

        #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
        struct TestStruct {
            name: String,
            value: i32,
        }

        #[test_try::test_try]
        fn test_ron_codec() {
            let original = TestStruct {
                name: "Test".to_string(),
                value: 42,
            };
            let enc = Ron::encode(&original)?;
            let dec: TestStruct = Ron::decode(&enc)?;
            assert_eq!(dec, original);
        }
    }
}
