Skip to content

Commit bdaa2f2

Browse files
authored
make api_versions generate a latest_version function (#11)
Make the `api_versions!` macro generate a `latest_version` function that produces the latest version. This makes it possible to add new versions without having to updating `ServerBuilder` callsites.
1 parent cd5cdd4 commit bdaa2f2

File tree

3 files changed

+73
-22
lines changed

3 files changed

+73
-22
lines changed

crates/dropshot-api-manager-types/src/versions.rs

Lines changed: 61 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -200,15 +200,15 @@ impl<'a> ExactSizeIterator for IterVersionsSemversInner<'a> {
200200
}
201201
}
202202

203-
/// Helper macro used to define API versions
203+
/// Helper macro used to define API versions.
204204
///
205205
/// ```
206206
/// use dropshot_api_manager_types::{
207207
/// SupportedVersion, SupportedVersions, api_versions,
208208
/// };
209209
///
210210
/// api_versions!([
211-
/// // Define the API versions here.
211+
/// // Define the API versions here. They must be in descending order.
212212
/// (2, ADD_FOOBAR_OPERATION),
213213
/// (1, INITIAL),
214214
/// ]);
@@ -225,9 +225,15 @@ impl<'a> ExactSizeIterator for IterVersionsSemversInner<'a> {
225225
/// pub const VERSION_INITIAL: semver::Version = semver::Version::new(1, 0, 0);
226226
/// ```
227227
///
228-
/// It also defines a function called `pub fn supported_versions() ->
229-
/// SupportedVersions` that, as the name suggests, returns a
230-
/// [`SupportedVersions`] that describes these two supported API versions.
228+
/// It also defines two functions:
229+
///
230+
/// * `pub fn supported_versions() -> SupportedVersions` that,
231+
/// as the name suggests, returns a [`SupportedVersions`] that describes these
232+
/// two supported API versions.
233+
///
234+
/// * `pub fn latest_version() -> semver::Version` that returns the latest
235+
/// supported API version. The latest supported version is the first version
236+
/// in the list (hence versions must be in descending order).
231237
// Design constraints:
232238
// - For each new API version, we need a developer-chosen semver and label that
233239
// can be used to construct an identifier.
@@ -254,23 +260,41 @@ impl<'a> ExactSizeIterator for IterVersionsSemversInner<'a> {
254260
// sure there wasn't a mismerge.
255261
#[macro_export]
256262
macro_rules! api_versions {
257-
( [ $( (
258-
$major:literal,
259-
$name:ident
260-
) ),* $(,)? ] ) => {
263+
(
264+
[
265+
(
266+
$latest_major:literal,
267+
$latest_name: ident
268+
)
269+
$(,
270+
(
271+
$major:literal,
272+
$name:ident
273+
)
274+
)*
275+
$(,)?
276+
] ) => {
261277
dropshot_api_manager_types::paste! {
278+
pub const [<VERSION_ $latest_name>]: $crate::semver::Version =
279+
$crate::semver::Version::new($latest_major, 0, 0);
280+
262281
$(
263282
pub const [<VERSION_ $name>]: $crate::semver::Version =
264283
$crate::semver::Version::new($major, 0, 0);
265284
)*
266285

267286
pub fn supported_versions() -> $crate::SupportedVersions {
268287
let mut literal_versions = vec![
288+
$crate::SupportedVersion::new([<VERSION_ $latest_name>], stringify!($latest_name)),
269289
$( $crate::SupportedVersion::new([<VERSION_ $name>], stringify!($name)) ),*
270290
];
271291
literal_versions.reverse();
272292
$crate::SupportedVersions::new(literal_versions)
273293
}
294+
295+
pub const fn latest_version() -> $crate::semver::Version {
296+
[<VERSION_ $latest_name>]
297+
}
274298
}
275299
};
276300
}
@@ -283,25 +307,41 @@ macro_rules! api_versions {
283307
/// so we can just always bump the major number.
284308
#[macro_export]
285309
macro_rules! api_versions_picky {
286-
( [ $( (
287-
$major:literal,
288-
$minor:literal,
289-
$patch:literal,
290-
$name:ident
291-
) ),* $(,)? ] ) => {
310+
( [
311+
(
312+
$latest_major:literal,
313+
$latest_minor:literal,
314+
$latest_patch:literal,
315+
$latest_name: ident
316+
)
317+
$(,
318+
(
319+
$major:literal,
320+
$minor:literal,
321+
$patch:literal,
322+
$name:ident
323+
)
324+
)* $(,)? ] ) => {
292325
dropshot_api_manager_types::paste! {
326+
pub const [<VERSION_ $latest_name>]: $crate::semver::Version =
327+
$crate::semver::Version::new($latest_major, $latest_minor, $latest_patch);
328+
293329
$(
294-
pub const [<VERSION_ $name>]: semver::Version =
295-
semver::Version::new($major, $minor, $patch);
330+
pub const [<VERSION_ $name>]: $crate::semver::Version =
331+
$crate::semver::Version::new($major, $minor, $patch);
296332
)*
297333

298-
#[track_caller]
299-
pub fn supported_versions() -> SupportedVersions {
334+
pub fn supported_versions() -> $crate::SupportedVersions {
300335
let mut literal_versions = vec![
301-
$( SupportedVersion::new([<VERSION_ $name>], $desc) ),*
336+
$crate::SupportedVersion::new([<VERSION_ $latest_name>], stringify!($latest_name)),
337+
$( $crate::SupportedVersion::new([<VERSION_ $name>], stringify!($name)) ),*
302338
];
303339
literal_versions.reverse();
304-
SupportedVersions::new(literal_versions)
340+
$crate::SupportedVersions::new(literal_versions)
341+
}
342+
343+
pub const fn latest_version() -> $crate::semver::Version {
344+
[<VERSION_ $latest_name>]
305345
}
306346
}
307347
};

crates/dropshot-api-manager/README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,12 @@ api_versions!([
143143
**For both lockstep and versioned APIs:** once the API crate is defined, update the OpenAPI manager to manage the new OpenAPI document(s). Within this directory:
144144

145145
1. In your repository's integration point's `Cargo.toml`, add a dependency on the API crate.
146-
2. Add the crate to the list of APIs managed by the OpenAPI manager. For versioned APIs, the `api_versions` macro defines a `supported_versions()` function, which you'll need to use
146+
2. Add the crate to the list of APIs managed by the OpenAPI manager. For versioned APIs, the `api_versions` macro defines a `supported_versions()` function, which you'll need to use.
147+
148+
> [!NOTE]
149+
> The `api_versions!` macro also generates a `latest_version` function which returns the latest version (the first version on the list).
150+
>
151+
> When creating the server, you must configure a [`version_policy`](https://docs.rs/dropshot/latest/dropshot/struct.ServerBuilder.html#method.version_policy) to indicate that the API is versioned. If you use the [`ClientSpecifiesVersionInHeader`](https://docs.rs/dropshot/latest/dropshot/struct.ClientSpecifiesVersionInHeader.html) policy, set the `max_version` to the output of `latest_version()`.
147152
148153
To ensure everything works well, run `cargo openapi generate`. Your OpenAPI document should be generated on disk and listed in the output.
149154

crates/integration-tests/tests/integration/versioned.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ fn test_versioned_generate_basic() -> Result<()> {
1717
let env = TestEnvironment::new()?;
1818
let apis = versioned_health_apis()?;
1919

20+
// Check that latest_version exists.
21+
assert_eq!(
22+
versioned_health::latest_version(),
23+
semver::Version::new(3, 0, 0),
24+
);
25+
2026
// Initially, no documents should exist.
2127
assert!(
2228
!env.versioned_local_document_exists("versioned-health", "1.0.0")

0 commit comments

Comments
 (0)