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
//! Compact representation of semantic versions as an i32, u32 i64 or u64
//!
//! Limitations:
//! * Storage format is not standards-conformant (none exists at the time of writing)
//! * 32-bit values can represent values in range of `major/minor/patch` = `0 - 1023` (10 bits)
//! * 64-bit values can represent values in range of `major/minor/patch` = `0 - 65535` (16 bits)
//! * Other than major/minor/patch features (e.g pre-release) are not supported
//!
//! # Examples
//!
//! Encode a semver to an integer
//! ```rust
//! use embedded_semver::Semver;
//!
//! let version = Semver::new(1, 0, 20);
//!
//! let int_semver = version.to_i32().unwrap();
//! assert_eq!(int_semver, 83886081);
//! assert_eq!(&int_semver.to_le_bytes(), &[0b0000_0001, 0b0000_0000, 0b0000_0000, 0b0000_0101]);
//! ```
//!
//! Decode an integer into a semver:
//! ```rust
//! # use embedded_semver::Semver;
//! #
//! let int_semver: i32 = 83886081;
//!
//! let version = Semver::from_i32(int_semver).unwrap();
//! assert_eq!(version, Semver::new(1, 0, 20));
//! ```
//!
//! # Binary format
//!
//! * Fields are packed with the most significant bit first (see [Msb0](https://docs.rs/bitvec/0.22.3/bitvec/order/struct.Msb0.html))
//! * Format contains a 2-bit (on 32-bit values) and 4-bit (on 64-bit values) api version, which
//!   allows for extensibility and/or api changes in future
//!
//! ## 32 bits
//! The binary format for `i32` and `u32` is represented below. Fields:
//!
//! * `version`: First two bits represent the API version (packed data format). Currenly 0 = V0
//! * `major`: Next 10 bits represent major in range of 0-1023
//! * `minor`: Next 10 bits represent minor in range of 0-1023
//! * `patch`: Next 10 bits represent patch in range of 0-1023
//!
//! ```text
//! 0         2        12        22        32
//! ├────┴────┼┴┴┴┴┴┴┴┴┴┼┴┴┴┴┴┴┴┴┴┼┴┴┴┴┴┴┴┴┴┤
//! │ API ver │ Major   │ Minor   │ Patch   │
//! │ u2      │ u10     │ u10     │ u10     │
//! └─────────┴─────────┴─────────┴─────────┘
//! ```
//!
//! ## 64 bits
//! The binary format for `i64` and `u64` is represented below. Fields:
//!
//! * `version`: First four bits represent the API version (packed data format). Currenly 0 = V0
//! * `major`: Next 16 bits represent major in range of 0-65535
//! * `minor`: Next 16 bits represent minor in range of 0-65535
//! * `patch`: Next 16 bits represent patch in range of 0-65535
//! * 12 remaining bits are unused
//!
//! ```text
//! 0         4              20                36                52          64
//! ├─┴──┴─┴──┼┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┼┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┼┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┼┴┴┴┴┴┴┴┴┴┴┴┤
//! │ API ver │ Major         │ Minor           │ Patch           | (unused)  │
//! │ u4      │ u16           │ u16             │ u16             | u12       │
//! └─────────┴───────────────┴─────────────────┴─────────────────┴───────────┘
//! ```
//!
//! # Notes about conversions
//!
//! Note that the binary representations in i32 and i64 are not the same:
//! ```rust
//! # use embedded_semver::{Semver, Error, Magic};
//! #
//! let value: i32 = 16904511;
//!
//! assert_eq!(Semver::from_i32(value).unwrap(), Semver::new(1023, 1, 5));
//! assert_eq!(
//!     Semver::from_i64(value as i64).unwrap_err(),
//!     Error::UnsupportedMagic(Magic::V3)
//! );
//! ```
//!
//! However, the underlying bits in u32 and i32 represent the same values:
//! ```rust
//! # use embedded_semver::{Semver, Error};
//! #
//! let value: i32 = 16843009;
//! let bytes = value.to_le_bytes();
//!
//! let i32_value = i32::from_le_bytes(bytes.clone());
//! let u32_value = u32::from_le_bytes(bytes);
//!
//! assert_eq!(Semver::from_i32(i32_value).unwrap(), Semver::new(1, 1, 5));
//! assert_eq!(Semver::from_u32(u32_value).unwrap(), Semver::new(1, 1, 5));
//! ```

#![cfg_attr(not(feature = "std"), no_std)]

pub mod prelude {
    pub use crate::{Error, Magic, Semver};
}

mod error;
mod helpers;
mod sizes;
mod version;

pub use error::Error;
pub use version::Semver;

/// Magic number - storage format
///
/// Currently all operations are based on [`Magic::V0`], others are left
/// for future extensibility.
#[derive(Debug, PartialEq)]
pub enum Magic {
    // can have max 4 (2 bits)
    V0 = 0,
    V1 = 1,
    V2 = 2,
    V3 = 3,
}

impl Default for Magic {
    fn default() -> Self {
        Magic::V0
    }
}