1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
use std::io::{Error as IoError, ErrorKind};
use fluvio_protocol::{Encoder, Version, Decoder};
use fluvio_protocol::bytes::{BufMut, Buf};

use crate::ErrorCode;
use crate::api::Request;
use crate::derive::{Decode, Encode};

pub const VERSIONS_API_KEY: u16 = 18;

// -----------------------------------
// ApiVersionsRequest
// -----------------------------------

#[derive(Decode, Encode, Default, Debug)]
pub struct ApiVersionsRequest {}

impl Request for ApiVersionsRequest {
    const API_KEY: u16 = VERSIONS_API_KEY;
    type Response = ApiVersionsResponse;
}

// -----------------------------------
// ApiVersionsResponse
// -----------------------------------

pub type ApiVersions = Vec<ApiVersionKey>;

#[derive(Decode, Encode, Default, Debug, PartialEq)]
pub struct ApiVersionsResponse {
    pub error_code: ErrorCode,
    pub api_keys: ApiVersions,
    pub platform_version: PlatformVersion,
}

#[derive(Decode, Encode, Default, Clone, Debug, PartialEq)]
pub struct ApiVersionKey {
    pub api_key: i16,
    pub min_version: i16,
    pub max_version: i16,
}

#[derive(Debug, PartialEq)]
pub struct PlatformVersion(String);

impl PlatformVersion {
    /// Creates a `semver::Version` object of the inner version
    pub fn to_semver(&self) -> semver::Version {
        // This is safe because of this type's invariant:
        // The only ways to construct it are From<semver::Version>,
        // via the Decoder trait, or by Default. The Decoder impl explicitly
        // checks that the inner string is Semver, and the Default impl
        // directly constructs a Semver and stringifies it. Therefore this
        // unwrapping is safe.
        semver::Version::parse(&self.0)
            .expect("Broken Invariant: PlatformVersion can only be constructed with Semver")
    }
}

impl From<semver::Version> for PlatformVersion {
    fn from(version: semver::Version) -> Self {
        Self(version.to_string())
    }
}

impl Default for PlatformVersion {
    fn default() -> Self {
        Self(semver::Version::new(0, 0, 0).to_string())
    }
}

impl Decoder for PlatformVersion {
    /// Wrap the decoder for the string inside
    fn decode<T>(&mut self, src: &mut T, version: i16) -> Result<(), IoError>
    where
        T: Buf,
    {
        // Decode the data as a String
        let mut string = String::default();
        string.decode(src, version)?;

        // Before constructing, ensure that what was decoded is valid Semver
        let _version = semver::Version::parse(&string).map_err(|_| {
            IoError::new(
                ErrorKind::InvalidData,
                "PlatformVersion is not valid semver",
            )
        })?;

        // Construct PlatformVersion with semver string
        self.0 = string;
        Ok(())
    }
}

impl Encoder for PlatformVersion {
    fn write_size(&self, version: Version) -> usize {
        self.0.write_size(version)
    }

    fn encode<T>(&self, dest: &mut T, version: Version) -> Result<(), IoError>
    where
        T: BufMut,
    {
        self.0.encode(dest, version)
    }
}

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

    #[test]
    fn test_encode_platform_version() {
        let version = semver::Version::parse("1.2.3-alpha.4+56789").unwrap();
        let version_string = version.to_string();
        let platform_version = PlatformVersion::from(version);

        // Check encoding matches
        let mut version_string_buffer: Vec<u8> = vec![];
        version_string
            .encode(&mut version_string_buffer, 0)
            .unwrap();
        let mut platform_version_buffer: Vec<u8> = vec![];
        platform_version
            .encode(&mut platform_version_buffer, 0)
            .unwrap();
        assert_eq!(version_string_buffer, platform_version_buffer);

        // Check round-trip encode/decode for PlatformVersion
        let mut decoded_platform_version = PlatformVersion::default();
        decoded_platform_version
            .decode(&mut (&*platform_version_buffer), 0)
            .unwrap();
        assert_eq!(platform_version, decoded_platform_version);
    }

    #[test]
    fn test_encode_decode_api_versions_response() {
        fn api_versions() -> ApiVersionsResponse {
            let version = semver::Version::parse("0.1.2-alpha.3+4567").unwrap();
            let platform_version = PlatformVersion::from(version);
            ApiVersionsResponse {
                error_code: ErrorCode::None,
                api_keys: vec![],
                platform_version,
            }
        }

        let api_version = api_versions();
        let mut api_versions_buffer: Vec<u8> = vec![];
        api_version.encode(&mut api_versions_buffer, 0).unwrap();

        let mut decoded_api_version = ApiVersionsResponse::default();
        decoded_api_version
            .decode(&mut (&*api_versions_buffer), 0)
            .unwrap();

        assert_eq!(api_version, decoded_api_version);
    }
}