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
#![doc = include_str!("../README.md")]

#[doc = include_str!("../README.md")]
#[cfg(doctest)]
pub struct ReadmeDoctests;

mod common;
mod msg;
mod request;
mod response;
mod utils;

pub use common::*;
pub use msg::*;
pub use request::*;
pub use response::*;
pub use semver;

/// Protocol version of major/minor/patch.
///
/// This should match the version of this crate such that any significant change to the crate
/// version will also be reflected in this constant that can be used to verify compatibility across
/// the wire.
pub const PROTOCOL_VERSION: semver::Version = semver::Version::new(
    const_str::parse!(env!("CARGO_PKG_VERSION_MAJOR"), u64),
    const_str::parse!(env!("CARGO_PKG_VERSION_MINOR"), u64),
    const_str::parse!(env!("CARGO_PKG_VERSION_PATCH"), u64),
);

/// Comparators used to indicate the [lower, upper) bounds of supported protocol versions.
const PROTOCOL_VERSION_COMPAT: (semver::Comparator, semver::Comparator) = (
    semver::Comparator {
        op: semver::Op::GreaterEq,
        major: const_str::parse!(env!("CARGO_PKG_VERSION_MAJOR"), u64),
        minor: Some(const_str::parse!(env!("CARGO_PKG_VERSION_MINOR"), u64)),
        patch: Some(const_str::parse!(env!("CARGO_PKG_VERSION_PATCH"), u64)),
        pre: semver::Prerelease::EMPTY,
    },
    semver::Comparator {
        op: semver::Op::Less,
        major: {
            let major = const_str::parse!(env!("CARGO_PKG_VERSION_MAJOR"), u64);

            // If we have a version like 0.20, then the upper bound is 0.21,
            // otherwise if we have a version like 1.2, then the upper bound is 2.0
            //
            // So only increment the major if it is greater than 0
            if major > 0 {
                major + 1
            } else {
                major
            }
        },
        minor: {
            let major = const_str::parse!(env!("CARGO_PKG_VERSION_MAJOR"), u64);
            let minor = const_str::parse!(env!("CARGO_PKG_VERSION_MINOR"), u64);

            // If we have a version like 0.20, then the upper bound is 0.21,
            // otherwise if we have a version like 1.2, then the upper bound is 2.0
            //
            // So only increment the minor if major is 0
            if major > 0 {
                None
            } else {
                Some(minor + 1)
            }
        },
        patch: None,
        pre: semver::Prerelease::EMPTY,
    },
);

/// Returns true if the provided version is compatible with the protocol version.
///
/// ```
/// use distant_protocol::{is_compatible_with, PROTOCOL_VERSION};
/// use distant_protocol::semver::Version;
///
/// // The current protocol version tied to this crate is always compatible
/// assert!(is_compatible_with(&PROTOCOL_VERSION));
///
/// // Major bumps in distant's protocol version are always considered incompatible
/// assert!(!is_compatible_with(&Version::new(
///     PROTOCOL_VERSION.major + 1,
///     PROTOCOL_VERSION.minor,
///     PROTOCOL_VERSION.patch,
/// )));
///
/// // While distant's protocol is being stabilized, minor version bumps
/// // are also considered incompatible!
/// assert!(!is_compatible_with(&Version::new(
///     PROTOCOL_VERSION.major,
///     PROTOCOL_VERSION.minor + 1,
///     PROTOCOL_VERSION.patch,
/// )));
///
/// // Patch bumps in distant's protocol are always considered compatible
/// assert!(is_compatible_with(&Version::new(
///     PROTOCOL_VERSION.major,
///     PROTOCOL_VERSION.minor,
///     PROTOCOL_VERSION.patch + 1,
/// )));
/// ```
pub fn is_compatible_with(version: &semver::Version) -> bool {
    let (lower, upper) = PROTOCOL_VERSION_COMPAT;

    lower.matches(version) && upper.matches(version)
}