Skip to content

Commit 6213ceb

Browse files
committed
document semver
1 parent cc64a83 commit 6213ceb

File tree

2 files changed

+59
-5
lines changed

2 files changed

+59
-5
lines changed

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
mod graph;
22
mod path;
3-
mod semver;
3+
pub mod semver;
44
mod trampoline;
55

66
pub use graph::*;

src/semver.rs

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,60 @@
1+
//! A specialized map for semantic versions with alternate version lookup support.
2+
//!
3+
//! This module provides `VersionMap<T>`, which stores values indexed by semantic versions
4+
//! and supports fallback lookups through version alternates (e.g., 1.2.3 can be found
5+
//! via 1.0.0 if it's the latest patch for major version 1).
6+
17
use derivative::Derivative;
28
use semver::Version;
39
use std::collections::{BTreeMap, BTreeSet, HashMap};
410

11+
/// A map that stores values indexed by semantic versions with support for alternate lookups.
12+
///
13+
/// The `VersionMap` maintains a primary mapping from versions to values, and a secondary
14+
/// mapping that groups versions by their "alternate" keys for fallback lookups.
15+
///
16+
/// # Alternate Lookup Logic
17+
///
18+
/// - For major versions > 0: alternate is `major.0.0`
19+
/// - For minor versions > 0 (when major is 0): alternate is `0.minor.0`
20+
/// - Otherwise: alternate is `0.0.patch`
21+
/// - Pre-release versions have no alternates
22+
///
23+
/// # Example
24+
///
25+
/// ```rust
26+
/// use semver::Version;
27+
/// # use wac_trampoline::semver::VersionMap;
28+
///
29+
/// let mut map = VersionMap::new();
30+
/// map.insert(Version::new(1, 0, 1), "v1.0.1");
31+
/// map.insert(Version::new(1, 2, 0), "v1.2.0");
32+
///
33+
/// // Exact lookups
34+
/// assert_eq!(map.get_exact(&Version::new(1, 0, 1)), Some(&"v1.0.1"));
35+
///
36+
/// // Alternate lookups (finds latest patch for major version 1)
37+
/// assert_eq!(map.get(&Version::new(1, 0, 0)), Some(&"v1.2.0"));
38+
/// ```
539
#[derive(Clone, Derivative, Debug)]
640
#[derivative(Default(bound = ""))]
741
pub struct VersionMap<T> {
42+
/// Primary storage mapping versions to values
843
versions: BTreeMap<Version, T>,
44+
/// Secondary mapping for alternate version lookups
945
alternates: HashMap<Version, BTreeSet<Version>>,
1046
}
1147

1248
impl<T> VersionMap<T> {
49+
/// Creates a new empty `VersionMap`.
1350
pub fn new() -> Self {
1451
Self {
1552
versions: BTreeMap::new(),
1653
alternates: HashMap::new(),
1754
}
1855
}
1956

57+
/// Attempts to insert a version-value pair, returning an error if the version already exists.
2058
pub fn try_insert(&mut self, version: Version, value: T) -> Result<(), (Version, T)> {
2159
if self.versions.contains_key(&version) {
2260
return Err((version, value));
@@ -34,6 +72,7 @@ impl<T> VersionMap<T> {
3472
Ok(())
3573
}
3674

75+
/// Insert a version-value pair, returning the old value if it exists.
3776
pub fn insert(&mut self, version: Version, value: T) -> Option<T> {
3877
if let Some(alternate) = version_alternate(&version) {
3978
self.alternates
@@ -45,6 +84,11 @@ impl<T> VersionMap<T> {
4584
self.versions.insert(version, value)
4685
}
4786

87+
/// Gets a value by version, using alternate lookup if exact match is not found.
88+
///
89+
/// This method first tries to find an exact match. If that fails and the version
90+
/// has no build metadata, it attempts to find an alternate version and returns
91+
/// the latest version within that alternate group.
4892
pub fn get(&self, version: &Version) -> Option<&T> {
4993
if version.build.is_empty() {
5094
let maybe_value = version_alternate(version)
@@ -61,18 +105,20 @@ impl<T> VersionMap<T> {
61105
self.get_exact(version)
62106
}
63107

108+
/// Gets a value by version or returns the latest version if no specific version is provided.
64109
pub fn get_or_latest(&self, version: Option<&Version>) -> Option<&T> {
65-
if let Some(version) = version {
66-
self.get(version)
67-
} else {
68-
self.get_latest().map(|(_, value)| value)
110+
match version {
111+
Some(v) => self.get(v),
112+
None => self.get_latest().map(|(_, value)| value),
69113
}
70114
}
71115

116+
/// Returns the latest version and its associated value.
72117
pub fn get_latest(&self) -> Option<(&Version, &T)> {
73118
self.versions.last_key_value()
74119
}
75120

121+
/// Gets a value by exact version match only, without alternate lookup.
76122
pub fn get_exact(&self, version: &Version) -> Option<&T> {
77123
self.versions.get(version)
78124
}
@@ -91,7 +137,15 @@ impl<T> VersionMap<T> {
91137
}
92138
}
93139

140+
/// Computes the alternate version key for fallback lookups.
141+
///
142+
/// This function implements the alternate lookup logic:
143+
/// - Pre-release versions return `None` (no alternates)
144+
/// - Major versions > 0: return `major.0.0`
145+
/// - Minor versions > 0 (when major is 0): return `0.minor.0`
146+
/// - Otherwise: return `0.0.patch`
94147
fn version_alternate(version: &Version) -> Option<Version> {
148+
// Pre-release versions don't have alternates
95149
if !version.pre.is_empty() {
96150
None
97151
} else if version.major > 0 {

0 commit comments

Comments
 (0)