diff --git a/CHANGELOG.md b/CHANGELOG.md index d66c29c..c83c05c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 when executing `distant spawn` in order to run the command within a shell rather than directly - `semver` crate to be used for version information in protocol and manager +- `is_compatible_with` function to root of `distant-protocol` crate that checks + if a provided version is compatible with the protocol ### Changed diff --git a/distant-protocol/src/lib.rs b/distant-protocol/src/lib.rs index 892cba1..cc213a9 100644 --- a/distant-protocol/src/lib.rs +++ b/distant-protocol/src/lib.rs @@ -26,3 +26,83 @@ pub const PROTOCOL_VERSION: semver::Version = semver::Version::new( 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) +}