Skip to content

Commit 66cf722

Browse files
committed
[generator] Unstable APIs: add doc comments, filter out deprecated APIs, update main README
1 parent 8f429f1 commit 66cf722

File tree

13 files changed

+131
-10
lines changed

13 files changed

+131
-10
lines changed

Makefile.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ description = "Generate Elasticsearch client documentation and opens in browser"
169169
clear = true
170170
category = "Elasticsearch"
171171
command = "cargo"
172-
args = ["doc", "-p", "elasticsearch", "--no-deps", "--open"]
172+
args = ["doc", "-p", "elasticsearch", "--no-deps", "--open", "--all-features"]
173173

174174
[tasks.generate-release-notes]
175175
category = "Elasticsearch"

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ The following are a list of Cargo features that can be enabled or disabled:
4646

4747
- **native-tls** *(enabled by default)*: Enables TLS functionality provided by `native-tls`.
4848
- **rustls-tls**: Enables TLS functionality provided by `rustls`.
49+
- **beta-apis**: Enables beta APIs. Beta APIs are on track to become stable and permanent features. Use them with
50+
caution because it is possible that breaking changes are made to these APIs in a minor version.
51+
- **experimental-apis**: Enables experimental APIs. Experimental APIs are just that - an experiment. An experimental
52+
API might have breaking changes in any future version, or it might even be removed entirely. This feature also
53+
enables `beta-apis`.
4954

5055
## Getting started
5156

api_generator/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ flate2 = "~1"
1616
globset = "~0.4"
1717
Inflector = "0.11.4"
1818
indicatif = "0.12.0"
19+
itertools = "0.10.0"
1920
lazy_static = "1.4.0"
21+
log = "0.4.8"
2022
path-slash = "0.1.3"
2123
quote = "~0.3"
2224
reduce = "0.1.2"
@@ -26,6 +28,7 @@ semver = "0.9.0"
2628
serde = "~1"
2729
serde_json = "~1"
2830
serde_derive = "~1"
31+
simple_logger = "1.9.0"
2932
syn = { version = "~0.11", features = ["full"] }
3033
tar = "~0.4"
3134
toml = "0.5.6"

api_generator/src/bin/run.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ use std::{
2828
};
2929

3030
fn main() -> Result<(), failure::Error> {
31+
simple_logger::SimpleLogger::new()
32+
.with_level(log::LevelFilter::Info)
33+
.init()
34+
.unwrap();
35+
3136
// This must be run from the repo root directory, with cargo make generate-api
3237
let download_dir = fs::canonicalize(PathBuf::from("./api_generator/rest_specs"))?;
3338
let generated_dir = fs::canonicalize(PathBuf::from("./elasticsearch/src"))?;

api_generator/src/generator/code_gen/mod.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ pub mod request;
2222
pub mod root;
2323
pub mod url;
2424

25-
use crate::generator::TypeKind;
25+
use crate::generator::{Stability, TypeKind};
2626
use inflector::Inflector;
2727
use quote::Tokens;
2828
use std::str;
@@ -72,6 +72,22 @@ fn doc<I: Into<String>>(comment: I) -> syn::Attribute {
7272
}
7373
}
7474

75+
fn stability_doc(stability: Stability) -> Option<syn::Attribute> {
76+
match stability {
77+
Stability::Experimental => Some(doc(r#"&nbsp;
78+
# Optional, experimental
79+
This requires the `experimental-apis` feature. Can have breaking changes in future
80+
versions or might even be removed entirely.
81+
"#)),
82+
Stability::Beta => Some(doc(r#"&nbsp;
83+
# Optional, beta
84+
This requires the `beta-apis` feature. On track to become stable but breaking changes can
85+
happen in minor versions.
86+
"#)),
87+
Stability::Stable => None,
88+
}
89+
}
90+
7591
/// AST for an expression parsed from quoted tokens
7692
pub fn parse_expr(input: quote::Tokens) -> syn::Expr {
7793
syn::parse_expr(input.to_string().as_ref()).unwrap()

api_generator/src/generator/code_gen/namespace_clients.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ pub fn generate(api: &Api, docs_dir: &PathBuf) -> Result<Vec<(String, String)>,
3333
if let Some(attr) = namespace.stability.inner_cfg_attr() {
3434
tokens.append(attr);
3535
}
36+
if let Some(mut attr) = stability_doc(namespace.stability) {
37+
attr.style = syn::AttrStyle::Inner;
38+
tokens.append(quote! { #attr });
39+
}
40+
3641
tokens.append(use_declarations());
3742

3843
let namespace_pascal_case = namespace_name.to_pascal_case();
@@ -77,10 +82,12 @@ pub fn generate(api: &Api, docs_dir: &PathBuf) -> Result<Vec<(String, String)>,
7782
.unzip();
7883

7984
let cfg_attr = namespace.stability.outer_cfg_attr();
85+
let cfg_doc = stability_doc(namespace.stability);
8086
tokens.append(quote!(
8187
#(#builders)*
8288

8389
#namespace_doc
90+
#cfg_doc
8491
#cfg_attr
8592
pub struct #namespace_client_name<'a> {
8693
transport: &'a Transport

api_generator/src/generator/code_gen/params.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
* specific language governing permissions and limitations
1717
* under the License.
1818
*/
19+
use crate::generator::code_gen::stability_doc;
1920
use crate::generator::*;
2021
use inflector::Inflector;
2122
use quote::Tokens;
@@ -65,11 +66,13 @@ fn generate_param(tokens: &mut Tokens, e: &ApiEnum) {
6566
};
6667

6768
let cfg_attr = e.stability.outer_cfg_attr();
69+
let cfg_doc = stability_doc(e.stability);
6870

6971
let generated_enum_tokens = quote!(
72+
#doc
73+
#cfg_doc
7074
#cfg_attr
7175
#[derive(Debug, PartialEq, Deserialize, Serialize, Clone, Copy)]
72-
#doc
7376
pub enum #name {
7477
#(#[serde(rename = #renames)] #variants),*
7578
}

api_generator/src/generator/code_gen/request/request_builder.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,7 @@ impl<'a> RequestBuilder<'a> {
627627
));
628628

629629
let cfg_attr = endpoint.stability.outer_cfg_attr();
630+
let cfg_doc = stability_doc(endpoint.stability);
630631

631632
quote! {
632633
#cfg_attr
@@ -635,9 +636,10 @@ impl<'a> RequestBuilder<'a> {
635636
#cfg_attr
636637
#enum_impl
637638

638-
#[derive(Clone, Debug)]
639639
#[doc = #builder_doc]
640+
#cfg_doc
640641
#cfg_attr
642+
#[derive(Clone, Debug)]
641643
pub struct #builder_expr {
642644
transport: &'a Transport,
643645
parts: #enum_ty,
@@ -676,6 +678,7 @@ impl<'a> RequestBuilder<'a> {
676678
enum_builder: &EnumBuilder,
677679
) -> Tokens {
678680
let cfg_attr = endpoint.stability.outer_cfg_attr();
681+
let cfg_doc = stability_doc(endpoint.stability);
679682

680683
let builder_ident = ident(builder_name);
681684

@@ -734,6 +737,7 @@ impl<'a> RequestBuilder<'a> {
734737
if enum_builder.contains_single_parameterless_part() {
735738
quote!(
736739
#method_doc
740+
#cfg_doc
737741
#cfg_attr
738742
pub fn #fn_name(&'a self) -> #builder_ident_ret {
739743
#builder_ident::new(#clone_expr)
@@ -743,6 +747,7 @@ impl<'a> RequestBuilder<'a> {
743747
let (enum_ty, _, _) = enum_builder.clone().build();
744748
quote!(
745749
#method_doc
750+
#cfg_doc
746751
#cfg_attr
747752
pub fn #fn_name(&'a self, parts: #enum_ty) -> #builder_ident_ret {
748753
#builder_ident::new(#clone_expr, parts)

api_generator/src/generator/code_gen/url/enum_builder.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ mod tests {
327327
url: None,
328328
},
329329
stability: Stability::Stable,
330+
deprecated: None,
330331
url: Url {
331332
paths: vec![
332333
Path {

api_generator/src/generator/mod.rs

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ use void::Void;
4242
pub mod code_gen;
4343
pub mod output;
4444

45+
use itertools::Itertools;
4546
use output::{merge_file, write_file};
47+
use std::cmp::Ordering;
4648

4749
lazy_static! {
4850
static ref VERSION: Version = semver::Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
@@ -205,6 +207,37 @@ pub struct Deprecated {
205207
pub description: String,
206208
}
207209

210+
impl PartialOrd for Deprecated {
211+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
212+
match (
213+
Version::parse(&self.version),
214+
Version::parse(&other.version),
215+
) {
216+
(Err(_), _) => None,
217+
(_, Err(_)) => None,
218+
(Ok(self_version), Ok(other_version)) => self_version.partial_cmp(&other_version),
219+
}
220+
}
221+
}
222+
223+
impl Deprecated {
224+
/// Combine optional deprecations, keeping either lack of deprecation or the highest version
225+
pub fn combine<'a>(
226+
left: &'a Option<Deprecated>,
227+
right: &'a Option<Deprecated>,
228+
) -> &'a Option<Deprecated> {
229+
if let (Some(leftd), Some(rightd)) = (left, right) {
230+
if leftd > rightd {
231+
left
232+
} else {
233+
right
234+
}
235+
} else {
236+
&None
237+
}
238+
}
239+
}
240+
208241
/// An API url path
209242
#[derive(Debug, PartialEq, Deserialize, Clone)]
210243
pub struct Path {
@@ -392,6 +425,7 @@ pub struct ApiEndpoint {
392425
documentation: Documentation,
393426
pub stability: Stability,
394427
pub url: Url,
428+
pub deprecated: Option<Deprecated>,
395429
#[serde(default = "BTreeMap::new")]
396430
pub params: BTreeMap<String, Type>,
397431
pub body: Option<Body>,
@@ -583,6 +617,11 @@ pub fn read_api(branch: &str, download_dir: &PathBuf) -> Result<Api, failure::Er
583617
let mut file = File::open(&path)?;
584618
let (name, api_endpoint) = endpoint_from_file(display, &mut file)?;
585619

620+
if api_endpoint.stability != Stability::Stable && api_endpoint.deprecated.is_some() {
621+
// Do not generate deprecated unstable endpoints
622+
continue;
623+
}
624+
586625
let name_parts: Vec<&str> = name.splitn(2, '.').collect();
587626
let (namespace, method_name) = match name_parts.len() {
588627
len if len > 1 => (name_parts[0].to_string(), name_parts[1].to_string()),
@@ -655,21 +694,37 @@ where
655694
R: Read,
656695
{
657696
// deserialize the map from the reader
658-
let endpoint: BTreeMap<String, ApiEndpoint> =
697+
let endpoints: BTreeMap<String, ApiEndpoint> =
659698
serde_json::from_reader(reader).map_err(|e| super::error::ParseError {
660699
message: format!("Failed to parse {} because: {}", name, e),
661700
})?;
662701

663702
// get the first (and only) endpoint name and endpoint body
664-
let mut first_endpoint = endpoint.into_iter().next().unwrap();
665-
first_endpoint.1.full_name = Some(first_endpoint.0.clone());
703+
let (name, mut endpoint) = endpoints.into_iter().next().unwrap();
704+
endpoint.full_name = Some(name.clone());
666705

667706
// sort the HTTP methods so that we can easily pattern match on them later
668-
for path in first_endpoint.1.url.paths.iter_mut() {
707+
for path in endpoint.url.paths.iter_mut() {
669708
path.methods.sort();
670709
}
671710

672-
Ok(first_endpoint)
711+
// endpoint deprecation is the "least deprecated" of its paths
712+
let deprecation = endpoint
713+
.url
714+
.paths
715+
.iter()
716+
.map(|p| &p.deprecated)
717+
.fold1(|d1, d2| Deprecated::combine(d1, d2))
718+
.unwrap_or(&None);
719+
720+
if let Some(deprecated) = deprecation {
721+
endpoint.deprecated = Some(Deprecated {
722+
version: deprecated.version.clone(),
723+
description: "Deprecated via one of the child items".to_string(),
724+
})
725+
}
726+
727+
Ok((name, endpoint))
673728
}
674729

675730
/// deserializes Common from a file
@@ -699,4 +754,20 @@ mod tests {
699754
assert!(Stability::Beta > Stability::Experimental);
700755
assert!(Stability::Stable > Stability::Beta);
701756
}
757+
758+
#[test]
759+
fn combine_deprecations() {
760+
let d1 = Some(Deprecated {
761+
version: "7.5.0".to_string(),
762+
description: "foo".to_string(),
763+
});
764+
765+
let d2 = Some(Deprecated {
766+
version: "7.6.0".to_string(),
767+
description: "foo".to_string(),
768+
});
769+
770+
assert_eq!(&d2, Deprecated::combine(&d1, &d2));
771+
assert_eq!(&None, Deprecated::combine(&d1, &None));
772+
}
702773
}

0 commit comments

Comments
 (0)