ipfs_api_versions/
lib.rs

1// Copyright 2022 rust-ipfs-api Developers
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! Define which IPFS (Kubo) Docker images the Rust library will be tested against.
9//!
10//! # Examples
11//!
12//! ```rust
13//! use ipfs_api_versions::test_current_image;
14//!
15//! #[test_current_image]
16//! fn test_foo(image_name: &str, image_tag: &str) {
17//!     // ...test implementation to run against only the latest image
18//! }
19//! ```
20//!
21//! ```rust
22//! use ipfs_api_versions::test_supported_images;
23//!
24//! #[test_supported_images]
25//! fn test_bar(image_name: &str, image_tag: &str) {
26//!     // ...test implementation to run against all supported images
27//! }
28//! ```
29
30use proc_macro::TokenStream as CompilerTokenStream;
31use quote::{quote, quote_spanned};
32
33/// Docker images of IPFS daemon versions supported by this library, in ascending order.
34fn supported() -> Vec<(String, String)> {
35    let source = [
36        ("ipfs/go-ipfs", "v0.7.0"),
37        ("ipfs/go-ipfs", "v0.8.0"),
38        ("ipfs/go-ipfs", "v0.9.1"),
39        ("ipfs/go-ipfs", "v0.10.0"),
40        ("ipfs/go-ipfs", "v0.11.1"),
41        ("ipfs/go-ipfs", "v0.12.2"),
42        ("ipfs/go-ipfs", "v0.13.0"),
43        ("ipfs/kubo", "v0.14.0"),
44        ("ipfs/kubo", "v0.15.0"),
45        ("ipfs/kubo", "v0.16.0"),
46        ("ipfs/kubo", "v0.17.0"),
47    ];
48
49    source
50        .into_iter()
51        .map(|(i, t)| (i.into(), t.into()))
52        .collect()
53}
54
55/// Docker image of most recent supported IPFS daemon.
56fn current() -> (String, String) {
57    supported().into_iter().last().unwrap()
58}
59
60fn image_test_case(image_name: &str, image_tag: &str) -> proc_macro2::TokenStream {
61    quote! {
62        #[test_case::test_case(#image_name, #image_tag)]
63    }
64}
65
66fn unexpected_meta(meta: CompilerTokenStream) -> Option<CompilerTokenStream> {
67    let m2: proc_macro2::TokenStream = meta.into();
68
69    if let Some(m) = m2.into_iter().next() {
70        let result = quote_spanned! { m.span() =>
71            compile_error!("Macro does not expect any arguments.");
72        };
73
74        Some(result.into())
75    } else {
76        None
77    }
78}
79
80#[proc_macro_attribute]
81pub fn test_current_image(
82    meta: CompilerTokenStream,
83    input: CompilerTokenStream,
84) -> CompilerTokenStream {
85    if let Some(err) = unexpected_meta(meta) {
86        err
87    } else {
88        let (image_name, image_tag) = current();
89
90        let tokens = vec![image_test_case(&image_name, &image_tag), input.into()];
91
92        let result = quote! {
93            #(#tokens)*
94        };
95
96        result.into()
97    }
98}
99
100#[proc_macro_attribute]
101pub fn test_supported_images(
102    meta: CompilerTokenStream,
103    input: CompilerTokenStream,
104) -> CompilerTokenStream {
105    if let Some(err) = unexpected_meta(meta) {
106        err
107    } else {
108        let mut tokens: Vec<_> = supported()
109            .iter()
110            .map(|(image_name, image_tag)| {
111                quote! {
112                    #[test_case::test_case(#image_name, #image_tag)]
113                }
114            })
115            .collect();
116
117        tokens.push(input.into());
118
119        let result = quote! {
120            #(#tokens)*
121        };
122
123        result.into()
124    }
125}