From 55f9b88ba1d63db75ef9cde3cf94e5c98526ad2f Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Tue, 25 Aug 2020 18:09:18 -0700 Subject: significant increase in parse and test coverage --- src/parse.rs | 494 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 481 insertions(+), 13 deletions(-) (limited to 'src/parse.rs') diff --git a/src/parse.rs b/src/parse.rs index 0bd1eeb..2a4c0a5 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -12,46 +12,85 @@ pub struct ApiRequest { #[derive(Serialize, Deserialize, Debug, Default)] #[serde(deny_unknown_fields)] pub struct UrlQueryParams { + #[serde(skip_serializing_if="Option::is_none")] pub allow_no_indices: Option, + #[serde(skip_serializing_if="Option::is_none")] pub allow_partial_search_results: Option, + #[serde(skip_serializing_if="Option::is_none")] pub batched_reduce_size: Option, + #[serde(skip_serializing_if="Option::is_none")] pub ccs_minimize_roundtrips: Option, + #[serde(skip_serializing_if="Option::is_none")] pub docvalue_fields: Option, // array of strings, comma-separated + #[serde(skip_serializing_if="Option::is_none")] pub expand_wildcards: Option, + #[serde(skip_serializing_if="Option::is_none")] pub explain: Option, + #[serde(skip_serializing_if="Option::is_none")] pub from: Option, + #[serde(skip_serializing_if="Option::is_none")] pub ignore_throttled: Option, + #[serde(skip_serializing_if="Option::is_none")] pub ignore_unavailable: Option, + #[serde(skip_serializing_if="Option::is_none")] pub max_concurrent_shard_requests: Option, + #[serde(skip_serializing_if="Option::is_none")] pub pre_filter_shard_size: Option, + #[serde(skip_serializing_if="Option::is_none")] pub preference: Option, + #[serde(skip_serializing_if="Option::is_none")] pub q: Option, + #[serde(skip_serializing_if="Option::is_none")] pub request_cache: Option, + #[serde(skip_serializing_if="Option::is_none")] pub rest_total_hits_as_int: Option, + #[serde(skip_serializing_if="Option::is_none")] pub routing: Option, + #[serde(skip_serializing_if="Option::is_none")] pub scroll: Option, // string is "time value" + #[serde(skip_serializing_if="Option::is_none")] pub search_type: Option, + #[serde(skip_serializing_if="Option::is_none")] pub seq_no_primary_term: Option, + #[serde(skip_serializing_if="Option::is_none")] pub size: Option, + #[serde(skip_serializing_if="Option::is_none")] pub sort: Option, // array of strings, comma-separated + #[serde(skip_serializing_if="Option::is_none")] pub _source: Option, // TODO: bool or string + #[serde(skip_serializing_if="Option::is_none")] pub _source_excludes: Option, // array of strings, comma-separated + #[serde(skip_serializing_if="Option::is_none")] pub _source_includes: Option, // array of strings, comma-separated + #[serde(skip_serializing_if="Option::is_none")] pub stats: Option, + #[serde(skip_serializing_if="Option::is_none")] pub stored_fields: Option, // array of strings, comma-separated + #[serde(skip_serializing_if="Option::is_none")] pub suggest_field: Option, + #[serde(skip_serializing_if="Option::is_none")] pub suggest_text: Option, + #[serde(skip_serializing_if="Option::is_none")] pub terminate_after: Option, + #[serde(skip_serializing_if="Option::is_none")] pub timeout: Option, // string is "time units" + #[serde(skip_serializing_if="Option::is_none")] pub track_scores: Option, + #[serde(skip_serializing_if="Option::is_none")] pub track_total_hits: Option, // XXX: bool or integer + #[serde(skip_serializing_if="Option::is_none")] pub typed_keys: Option, + #[serde(skip_serializing_if="Option::is_none")] pub version: Option, // additional generic params + #[serde(skip_serializing_if="Option::is_none")] pub human: Option, + #[serde(skip_serializing_if="Option::is_none")] pub pretty: Option, + #[serde(skip_serializing_if="Option::is_none")] pub filter_path: Option, + #[serde(skip_serializing_if="Option::is_none")] pub error_trace: Option, } @@ -59,74 +98,142 @@ pub struct UrlQueryParams { #[derive(Serialize, Deserialize, Debug)] #[serde(deny_unknown_fields)] pub struct SearchBody { + #[serde(skip_serializing_if="Option::is_none")] pub query: Option, + #[serde(skip_serializing_if="Option::is_none")] pub highlight: Option, + #[serde(skip_serializing_if="Option::is_none")] pub collapse: Option, + #[serde(skip_serializing_if="Option::is_none")] pub post_filter: Option, // TODO: leaf query only? + #[serde(skip_serializing_if="Option::is_none")] pub rescore: Option, // TODO: single or an array of rescore objects // script_fields disabled + #[serde(skip_serializing_if="Option::is_none")] + pub aggs: Option>, + #[serde(skip_serializing_if="Option::is_none")] + pub aggregations: Option>, // https://www.elastic.co/guide/en/elasticsearch/reference/current/sort-search-results.html + #[serde(skip_serializing_if="Option::is_none")] pub sort: Option>, + #[serde(skip_serializing_if="Option::is_none")] pub slice: Option, + #[serde(skip_serializing_if="Option::is_none")] pub stored_fields: Option, // array of strings, or "_none_" // overlap with URL query parameters + #[serde(skip_serializing_if="Option::is_none")] pub docvalue_fields: Option>, + #[serde(skip_serializing_if="Option::is_none")] pub explain: Option, + #[serde(skip_serializing_if="Option::is_none")] pub from: Option, - pub min_score: Option, + #[serde(skip_serializing_if="Option::is_none")] + pub min_score: Option, + #[serde(skip_serializing_if="Option::is_none")] pub seq_no_primary_term: Option, + #[serde(skip_serializing_if="Option::is_none")] pub size: Option, + #[serde(skip_serializing_if="Option::is_none")] pub _source: Option, // XXX: bool, string, or object + #[serde(skip_serializing_if="Option::is_none")] pub terminate_after: Option, + #[serde(skip_serializing_if="Option::is_none")] pub timeout: Option, // string is "time units" } #[derive(Serialize, Deserialize, Debug)] #[serde(deny_unknown_fields)] pub struct ScrollBody { - pub scroll_id: String, + pub scroll_id: StringOrArray, + #[serde(skip_serializing_if="Option::is_none")] pub scroll: Option, } #[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] pub struct ApiSlice { id: u32, max: u32, + #[serde(skip_serializing_if="Option::is_none")] field: Option, } #[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] pub struct ApiRescore{ + #[serde(skip_serializing_if="Option::is_none")] pub query: Option, + #[serde(skip_serializing_if="Option::is_none")] pub window_size: Option, } // TODO: could revert to having query types as an enum, with flattening #[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] pub struct ApiQuery { // compound queries #[serde(rename = "bool")] + #[serde(skip_serializing_if="Option::is_none")] bool_query: Option, + #[serde(skip_serializing_if="Option::is_none")] boosting: Option, + #[serde(skip_serializing_if="Option::is_none")] constant_score: Option, // fulltext (leaf) queries - - // term-level (leaf) queries #[serde(rename = "match")] - match_query: Option>, + #[serde(skip_serializing_if="Option::is_none")] + match_query: Option>, + #[serde(skip_serializing_if="Option::is_none")] match_phrase: Option>, - query_string: Option, + #[serde(skip_serializing_if="Option::is_none")] + multi_match: Option, + #[serde(skip_serializing_if="Option::is_none")] + query_string: Option, + #[serde(skip_serializing_if="Option::is_none")] + simple_query_string: Option, + + // term-level (leaf) queries + #[serde(skip_serializing_if="Option::is_none")] + exists: Option, + #[serde(skip_serializing_if="Option::is_none")] + match_all: Option, + #[serde(skip_serializing_if="Option::is_none")] + match_none: Option, + #[serde(skip_serializing_if="Option::is_none")] + ids: Option, + #[serde(skip_serializing_if="Option::is_none")] + wildcard: Option>, // also works for wildcard + #[serde(skip_serializing_if="Option::is_none")] + prefix: Option>, // also works for prefix query + #[serde(skip_serializing_if="Option::is_none")] + range: Option>, + #[serde(skip_serializing_if="Option::is_none")] + term: Option>, + #[serde(skip_serializing_if="Option::is_none")] + // TODO: boost in terms query + terms: Option>>, // other + #[serde(skip_serializing_if="Option::is_none")] nested: Option, + #[serde(skip_serializing_if="Option::is_none")] rescore_query: Option>, + + // fields as part of a rescore query + #[serde(skip_serializing_if="Option::is_none")] + score_mode: Option, + #[serde(skip_serializing_if="Option::is_none")] + query_weight: Option, + #[serde(skip_serializing_if="Option::is_none")] + rescore_query_weight: Option, } #[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] pub struct ApiHighlight{ // TODO: fields could also be an array of strings? fields: HashMap, @@ -136,13 +243,19 @@ pub struct ApiHighlight{ } #[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] #[serde(untagged)] pub enum SortMapValue { String(String), - Object { order: String, mode: Option }, + Object { + order: String, + #[serde(skip_serializing_if="Option::is_none")] + mode: Option, + }, } #[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] #[serde(untagged)] pub enum SortElement{ String(String), @@ -150,13 +263,208 @@ pub enum SortElement{ } #[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] #[serde(untagged)] pub enum DocValOrString { String(String), - Object {field: String, format: Option}, + Object { + field: String, + #[serde(skip_serializing_if="Option::is_none")] + format: Option, + }, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +#[serde(untagged)] +pub enum MatchQueryOrString { + Object(MatchQuery), + String(String), +} + +// https://www.elastic.co/guide/en/elasticsearch/reference/7.9/query-dsl-match-query.html +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct MatchQuery { + query: String, + + #[serde(flatten)] + options: MatchOptions, +} + +// https://www.elastic.co/guide/en/elasticsearch/reference/7.9/query-dsl-multi-match-query.html +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct MultiMatchQuery { + query: String, + fields: Vec, + #[serde(skip_serializing_if="Option::is_none")] + #[serde(rename="type")] + query_type: Option, + #[serde(skip_serializing_if="Option::is_none")] + tie_breaker: Option, + #[serde(flatten)] + options: MatchOptions, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct MatchOptions { + #[serde(skip_serializing_if="Option::is_none")] + analyzer: Option, + #[serde(skip_serializing_if="Option::is_none")] + auto_generate_synonyms_phrase_query: Option, + #[serde(skip_serializing_if="Option::is_none")] + fuzziness: Option, + #[serde(skip_serializing_if="Option::is_none")] + max_expansions: Option, + #[serde(skip_serializing_if="Option::is_none")] + prefix_length: Option, + #[serde(skip_serializing_if="Option::is_none")] + fuzzy_transpositions: Option, + #[serde(skip_serializing_if="Option::is_none")] + fuzzy_rewrite: Option, + #[serde(skip_serializing_if="Option::is_none")] + lenient: Option, + #[serde(skip_serializing_if="Option::is_none")] + operator: Option, + #[serde(skip_serializing_if="Option::is_none")] + minimum_should_match: Option, + #[serde(skip_serializing_if="Option::is_none")] + zero_terms_query: Option, +} + +// https://www.elastic.co/guide/en/elasticsearch/reference/7.9/query-dsl-query-string-query.html +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct QueryStringQuery { + query: String, + #[serde(skip_serializing_if="Option::is_none")] + #[serde(rename="type")] + query_type: Option, + #[serde(skip_serializing_if="Option::is_none")] + default_field: Option, + #[serde(skip_serializing_if="Option::is_none")] + allow_leading_wildcard: Option, + #[serde(skip_serializing_if="Option::is_none")] + analyze_wildcard: Option, + #[serde(skip_serializing_if="Option::is_none")] + analyzer: Option, + #[serde(skip_serializing_if="Option::is_none")] + auto_generate_synonyms_phrase_query: Option, + #[serde(skip_serializing_if="Option::is_none")] + boost: Option, + #[serde(skip_serializing_if="Option::is_none")] + default_operator: Option, + #[serde(skip_serializing_if="Option::is_none")] + enable_position_increments: Option, + #[serde(skip_serializing_if="Option::is_none")] + fields: Option>, + #[serde(skip_serializing_if="Option::is_none")] + fuzziness: Option, + #[serde(skip_serializing_if="Option::is_none")] + fuzzy_max_expansions: Option, + #[serde(skip_serializing_if="Option::is_none")] + fuzzy_prefix_length: Option, + #[serde(skip_serializing_if="Option::is_none")] + fuzzy_transpositions: Option, + #[serde(skip_serializing_if="Option::is_none")] + lenient: Option, + #[serde(skip_serializing_if="Option::is_none")] + max_determinized_states: Option, + #[serde(skip_serializing_if="Option::is_none")] + minimum_should_match: Option, + #[serde(skip_serializing_if="Option::is_none")] + quote_analyzer: Option, + #[serde(skip_serializing_if="Option::is_none")] + phrase_slop: Option, + #[serde(skip_serializing_if="Option::is_none")] + quote_field_suffix: Option, + #[serde(skip_serializing_if="Option::is_none")] + rewrite: Option, + #[serde(skip_serializing_if="Option::is_none")] + time_zone: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct SimpleBoost { + #[serde(skip_serializing_if="Option::is_none")] + boost: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct IdsQuery { + values: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +#[serde(untagged)] +pub enum TermQueryOrString { + String(String), + Object(TermQuery), +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct TermQuery { + value: String, + #[serde(skip_serializing_if="Option::is_none")] + rewrite: Option, + #[serde(skip_serializing_if="Option::is_none")] + boost: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +#[serde(untagged)] +pub enum StringOrNum { + String(String), + Int(u64), + Float(f64), +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +#[serde(untagged)] +pub enum Num { + Int(u64), + Float(f64), +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +#[serde(untagged)] +pub enum StringOrArray { + String(String), + Array(Vec), +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct RangeQuery { + #[serde(skip_serializing_if="Option::is_none")] + gt: Option, + #[serde(skip_serializing_if="Option::is_none")] + gte: Option, + #[serde(skip_serializing_if="Option::is_none")] + lt: Option, + #[serde(skip_serializing_if="Option::is_none")] + lte: Option, + #[serde(skip_serializing_if="Option::is_none")] + format: Option, + #[serde(skip_serializing_if="Option::is_none")] + relation: Option, + #[serde(skip_serializing_if="Option::is_none")] + timezone: Option, + #[serde(skip_serializing_if="Option::is_none")] + boost: Option, } #[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] #[serde(untagged)] pub enum QueryFieldOrString { Object(QueryField), @@ -164,77 +472,128 @@ pub enum QueryFieldOrString { } #[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] pub struct QueryField{ query: String, + #[serde(skip_serializing_if="Option::is_none")] fuzziness: Option, + #[serde(skip_serializing_if="Option::is_none")] slop: Option, - boost: Option, + #[serde(skip_serializing_if="Option::is_none")] + boost: Option, } #[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] pub struct BoolQuery { + #[serde(skip_serializing_if="Option::is_none")] must: Option>, + #[serde(skip_serializing_if="Option::is_none")] filter: Option>, + #[serde(skip_serializing_if="Option::is_none")] should: Option>, + #[serde(skip_serializing_if="Option::is_none")] must_not: Option>, - minimum_should_match: Option, - boost: Option, + #[serde(skip_serializing_if="Option::is_none")] + minimum_should_match: Option, + #[serde(skip_serializing_if="Option::is_none")] + boost: Option, } #[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] pub struct NestedQuery { path: String, query: Box, + #[serde(skip_serializing_if="Option::is_none")] score_mode: Option, + #[serde(skip_serializing_if="Option::is_none")] ignore_unmapped: Option, + #[serde(skip_serializing_if="Option::is_none")] + inner_hits: Option, } #[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] pub struct BoostingQuery { positive: Box, negative: Box, - negative_boost: f64, + negative_boost: Num, } #[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] pub struct ConstantScoreQuery { filter: Box, - boost: Option, + #[serde(skip_serializing_if="Option::is_none")] + boost: Option, } // https://www.elastic.co/guide/en/elasticsearch/reference/current/highlighting.html #[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] pub struct HighlightField{ + #[serde(skip_serializing_if="Option::is_none")] boundary_chars: Option, + #[serde(skip_serializing_if="Option::is_none")] boundary_max_scan: Option, + #[serde(skip_serializing_if="Option::is_none")] boundary_scanner: Option, + #[serde(skip_serializing_if="Option::is_none")] boundary_scanner_locale: Option, + #[serde(skip_serializing_if="Option::is_none")] encoder: Option, + #[serde(skip_serializing_if="Option::is_none")] force_source: Option, + #[serde(skip_serializing_if="Option::is_none")] fragmenter: Option, + #[serde(skip_serializing_if="Option::is_none")] fragment_offset: Option, + #[serde(skip_serializing_if="Option::is_none")] fragment_size: Option, + #[serde(skip_serializing_if="Option::is_none")] highlight_query: Option, + #[serde(skip_serializing_if="Option::is_none")] matched_fields: Option>, + #[serde(skip_serializing_if="Option::is_none")] no_match_size: Option, + #[serde(skip_serializing_if="Option::is_none")] number_of_fragments: Option, + #[serde(skip_serializing_if="Option::is_none")] order: Option, + #[serde(skip_serializing_if="Option::is_none")] phrase_limit: Option, + #[serde(skip_serializing_if="Option::is_none")] pre_tags: Option>, + #[serde(skip_serializing_if="Option::is_none")] post_tags: Option>, + #[serde(skip_serializing_if="Option::is_none")] require_field_match: Option, + #[serde(skip_serializing_if="Option::is_none")] tags_schema: Option, #[serde(rename = "type")] + #[serde(skip_serializing_if="Option::is_none")] highlight_type: Option, } #[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +#[serde(untagged)] +pub enum SimpleFieldOrString { + String(String), + Object { field: String }, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] pub struct ApiCollapse{ field: String, + #[serde(skip_serializing_if="Option::is_none")] inner_hits: Option, } #[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] #[serde(untagged)] pub enum InnerHitsOneOrMore { Single(InnerHits), @@ -242,9 +601,118 @@ pub enum InnerHitsOneOrMore { } #[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] pub struct InnerHits { + #[serde(skip_serializing_if="Option::is_none")] from: Option, + #[serde(skip_serializing_if="Option::is_none")] size: Option, + #[serde(skip_serializing_if="Option::is_none")] sort: Option>, + #[serde(skip_serializing_if="Option::is_none")] name: Option, + #[serde(skip_serializing_if="Option::is_none")] + collapse: Option>, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct ApiAggregation { + // bucket type aggregations + #[serde(skip_serializing_if="Option::is_none")] + nested: Option, + #[serde(skip_serializing_if="Option::is_none")] + filter: Option, + #[serde(skip_serializing_if="Option::is_none")] + histogram: Option, + #[serde(skip_serializing_if="Option::is_none")] + terms: Option, + #[serde(skip_serializing_if="Option::is_none")] + significant_terms: Option, + + // metrics type aggregations + #[serde(skip_serializing_if="Option::is_none")] + avg: Option, + #[serde(skip_serializing_if="Option::is_none")] + min: Option, + #[serde(skip_serializing_if="Option::is_none")] + max: Option, + #[serde(skip_serializing_if="Option::is_none")] + sum: Option, + #[serde(skip_serializing_if="Option::is_none")] + value_count: Option, + #[serde(skip_serializing_if="Option::is_none")] + stats: Option, + #[serde(skip_serializing_if="Option::is_none")] + percentiles: Option, + + // nested aggregations + #[serde(skip_serializing_if="Option::is_none")] + aggs: Option>>, + #[serde(skip_serializing_if="Option::is_none")] + aggregations: Option>>, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct NestedAggregation{ + path: String, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct SimpleAggregation{ + field: String, + #[serde(skip_serializing_if="Option::is_none")] + interval: Option, + #[serde(skip_serializing_if="Option::is_none")] + missing: Option, + #[serde(skip_serializing_if="Option::is_none")] + keyed: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct DateHistogramAggregation{ + field: String, + #[serde(skip_serializing_if="Option::is_none")] + fixed_interval: Option, + #[serde(skip_serializing_if="Option::is_none")] + calendar_interval: Option, + #[serde(skip_serializing_if="Option::is_none")] + format: Option, + #[serde(skip_serializing_if="Option::is_none")] + time_zone: Option, + #[serde(skip_serializing_if="Option::is_none")] + offset: Option, + #[serde(skip_serializing_if="Option::is_none")] + order: Option>, + #[serde(skip_serializing_if="Option::is_none")] + keyed: Option, + #[serde(skip_serializing_if="Option::is_none")] + missing: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct TermsAggregation { + field: String, + #[serde(skip_serializing_if="Option::is_none")] + size: Option, + #[serde(skip_serializing_if="Option::is_none")] + shard_size: Option, + #[serde(skip_serializing_if="Option::is_none")] + min_doc_count: Option, + #[serde(skip_serializing_if="Option::is_none")] + show_term_doc_count_error: Option, + #[serde(skip_serializing_if="Option::is_none")] + order: Option>, + #[serde(skip_serializing_if="Option::is_none")] + include: Option, + #[serde(skip_serializing_if="Option::is_none")] + exclude: Option, + #[serde(skip_serializing_if="Option::is_none")] + execution_hint: Option, + #[serde(skip_serializing_if="Option::is_none")] + missing: Option, } -- cgit v1.2.3