From 10e59ac5d002c0b91dece34ed2a082ba5f022edd Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Wed, 25 Jul 2018 23:40:28 -0700 Subject: API schema: identifier checking, expand query param --- rust/fatcat-api/README.md | 2 +- rust/fatcat-api/api.yaml | 105 ++++++--- rust/fatcat-api/api/swagger.yaml | 299 +++++++++++++++++++++++++- rust/fatcat-api/examples/client.rs | 10 +- rust/fatcat-api/examples/server_lib/server.rs | 20 +- rust/fatcat-api/src/client.rs | 60 +++++- rust/fatcat-api/src/lib.rs | 40 ++-- rust/fatcat-api/src/models.rs | 40 ++++ rust/fatcat-api/src/server.rs | 30 ++- 9 files changed, 520 insertions(+), 86 deletions(-) (limited to 'rust/fatcat-api') diff --git a/rust/fatcat-api/README.md b/rust/fatcat-api/README.md index 8695e15f..ba1f13f9 100644 --- a/rust/fatcat-api/README.md +++ b/rust/fatcat-api/README.md @@ -13,7 +13,7 @@ To see how to make this your own, look here: [README](https://github.com/swagger-api/swagger-codegen/blob/master/README.md) - API version: 0.1.0 -- Build date: 2018-07-25T21:32:18.916Z +- Build date: 2018-07-26T06:37:35.353Z This autogenerated project defines an API crate `fatcat` which contains: * An `Api` trait defining the API in Rust. diff --git a/rust/fatcat-api/api.yaml b/rust/fatcat-api/api.yaml index 350e25c8..1a20c024 100644 --- a/rust/fatcat-api/api.yaml +++ b/rust/fatcat-api/api.yaml @@ -13,6 +13,33 @@ consumes: produces: - application/json +# don't want these to be rust types (at least for now) +x-fatcat-ident: &FATCATIDENT + type: string + example: "q3nouwy3nnbsvo3h5klxsx4a7y" + pattern: "[a-zA-Z2-7]{26}" + minLength: 26 + maxLength: 26 + description: "base32-encoded unique identifier" +x-fatcat-uuid: &FATCATUUID + type: string + example: "86daea5b-1b6b-432a-bb67-ea97795f80fe" + pattern: "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" + minLength: 36 + maxLength: 36 + description: "UUID (lower-case, dash-separated, hex-encoded 128-bit)" +x-issn: &FATCATISSN + type: string + example: "1234-5678" + pattern: "\\d{4}-\\d{3}[0-9X]" + minLength: 9 + maxLength: 9 +x-orcid: &FATCATORCID + type: string + example: "0000-0002-1825-0097" + pattern: "\\d{4}-\\d{4}-\\d{4}-\\d{4}" + minLength: 19 + maxLength: 19 # Common properties across entities x-entity-props: &ENTITYPROPS @@ -20,21 +47,13 @@ x-entity-props: &ENTITYPROPS type: string enum: ["wip", "active", "redirect", "deleted"] ident: - type: string - #format: custom - example: "q3nouwy3nnbsvo3h5klxsx4a7y" + <<: *FATCATIDENT revision: - type: string - #format: uuid - example: "86daea5b-1b6b-432a-bb67-ea97795f80fe" + <<: *FATCATUUID redirect: - type: string - #format: custom - example: "q3nouwy3nnbsvo3h5klxsx4a7y" + <<: *FATCATIDENT editgroup_id: - type: string - #format: custom - example: "q3nouwy3nnbsvo3h5klxsx4a7y" + <<: *FATCATIDENT extra: type: object additionalProperties: {} @@ -69,9 +88,7 @@ definitions: type: string example: "Society of Curious Students" issnl: - type: string - #format: custom - example: "1234-5678" + <<: *FATCATISSN wikidata_qid: type: string abbrev: @@ -92,9 +109,7 @@ definitions: surname: type: string orcid: - type: string - #format: custom - example: "0000-0002-1825-0097" + <<: *FATCATORCID wikidata_qid: type: string file_entity: @@ -151,6 +166,14 @@ definitions: work_id: type: string example: "q3nouwy3nnbsvo3h5klxsx4a7y" + container: + $ref: "#/definitions/container_entity" + description: "Optional; GET-only" + files: + description: "Optional; GET-only" + type: array + items: + $ref: "#/definitions/file_entity" container_id: type: string example: "q3nouwy3nnbsvo3h5klxsx4a7y" @@ -279,13 +302,9 @@ definitions: - editor_id properties: id: - type: string - #format: custom - example: "q3nouwy3nnbsvo3h5klxsx4a7y" + <<: *FATCATIDENT editor_id: - type: string - #format: custon - example: "q3nouwy3nnbsvo3h5klxsx4a7y" + <<: *FATCATIDENT description: type: string extra: @@ -365,6 +384,9 @@ definitions: creator_id: type: string #format: ident + creator: + $ref: "#/definitions/creator_entity" + description: "Optional; GET-only" raw_name: type: string extra: @@ -434,6 +456,11 @@ paths: in: path type: string required: true + - name: expend + in: query + type: string + required: false + description: "List of sub-entities to expand in response. For now, only 'all' accepted." get: operationId: "get_container" responses: @@ -469,8 +496,8 @@ paths: parameters: - name: issnl in: query - type: string required: true + <<: *FATCATISSN responses: 200: description: Found Entity @@ -517,6 +544,11 @@ paths: in: path type: string required: true + - name: expend + in: query + type: string + required: false + description: "List of sub-entities to expand in response. For now, only 'all' accepted." get: operationId: "get_creator" responses: @@ -568,8 +600,8 @@ paths: parameters: - name: orcid in: query - type: string required: true + <<: *FATCATORCID responses: 200: description: Found Entity @@ -616,6 +648,11 @@ paths: in: path type: string required: true + - name: expend + in: query + type: string + required: false + description: "List of sub-entities to expand in response. For now, only 'all' accepted." get: operationId: "get_file" responses: @@ -699,6 +736,11 @@ paths: in: path type: string required: true + - name: expend + in: query + type: string + required: false + description: "List of sub-entities to expand in response. For now, only 'all' accepted." get: operationId: "get_release" responses: @@ -798,6 +840,11 @@ paths: in: path type: string required: true + - name: expend + in: query + type: string + required: false + description: "List of sub-entities to expand in response. For now, only 'all' accepted." get: operationId: "get_work" responses: @@ -913,9 +960,8 @@ paths: parameters: - name: id in: path - type: string - #format: custom required: true + <<: *FATCATIDENT get: operationId: "get_editgroup" responses: @@ -939,9 +985,8 @@ paths: parameters: - name: id in: path - type: string - #format: custom required: true + <<: *FATCATIDENT post: operationId: "accept_editgroup" responses: diff --git a/rust/fatcat-api/api/swagger.yaml b/rust/fatcat-api/api/swagger.yaml index be23cdd6..a3e43170 100644 --- a/rust/fatcat-api/api/swagger.yaml +++ b/rust/fatcat-api/api/swagger.yaml @@ -142,6 +142,14 @@ paths: type: "string" formatString: "\\\"{}\\\"" example: "\"id_example\".to_string()" + - name: "expend" + in: "query" + description: "List of sub-entities to expand in response. For now, only 'all'\ + \ accepted." + required: false + type: "string" + formatString: "{:?}" + example: "Some(\"expend_example\".to_string())" responses: 200: description: "Found Entity" @@ -253,6 +261,9 @@ paths: in: "query" required: true type: "string" + maxLength: 9 + minLength: 9 + pattern: "\\d{4}-\\d{3}[0-9X]" formatString: "\\\"{}\\\"" example: "\"issnl_example\".to_string()" responses: @@ -426,6 +437,14 @@ paths: type: "string" formatString: "\\\"{}\\\"" example: "\"id_example\".to_string()" + - name: "expend" + in: "query" + description: "List of sub-entities to expand in response. For now, only 'all'\ + \ accepted." + required: false + type: "string" + formatString: "{:?}" + example: "Some(\"expend_example\".to_string())" responses: 200: description: "Found Entity" @@ -591,6 +610,9 @@ paths: in: "query" required: true type: "string" + maxLength: 19 + minLength: 19 + pattern: "\\d{4}-\\d{4}-\\d{4}-\\d{4}" formatString: "\\\"{}\\\"" example: "\"orcid_example\".to_string()" responses: @@ -764,6 +786,14 @@ paths: type: "string" formatString: "\\\"{}\\\"" example: "\"id_example\".to_string()" + - name: "expend" + in: "query" + description: "List of sub-entities to expand in response. For now, only 'all'\ + \ accepted." + required: false + type: "string" + formatString: "{:?}" + example: "Some(\"expend_example\".to_string())" responses: 200: description: "Found Entity" @@ -1048,6 +1078,14 @@ paths: type: "string" formatString: "\\\"{}\\\"" example: "\"id_example\".to_string()" + - name: "expend" + in: "query" + description: "List of sub-entities to expand in response. For now, only 'all'\ + \ accepted." + required: false + type: "string" + formatString: "{:?}" + example: "Some(\"expend_example\".to_string())" responses: 200: description: "Found Entity" @@ -1386,6 +1424,14 @@ paths: type: "string" formatString: "\\\"{}\\\"" example: "\"id_example\".to_string()" + - name: "expend" + in: "query" + description: "List of sub-entities to expand in response. For now, only 'all'\ + \ accepted." + required: false + type: "string" + formatString: "{:?}" + example: "Some(\"expend_example\".to_string())" responses: 200: description: "Found Entity" @@ -1687,8 +1733,12 @@ paths: parameters: - name: "id" in: "path" + description: "base32-encoded unique identifier" required: true type: "string" + maxLength: 26 + minLength: 26 + pattern: "[a-zA-Z2-7]{26}" formatString: "\\\"{}\\\"" example: "\"id_example\".to_string()" responses: @@ -1739,8 +1789,12 @@ paths: parameters: - name: "id" in: "path" + description: "base32-encoded unique identifier" required: true type: "string" + maxLength: 26 + minLength: 26 + pattern: "[a-zA-Z2-7]{26}" formatString: "\\\"{}\\\"" example: "\"id_example\".to_string()" responses: @@ -1935,6 +1989,9 @@ definitions: issnl: type: "string" example: "1234-5678" + minLength: 9 + maxLength: 9 + pattern: "\\d{4}-\\d{3}[0-9X]" publisher: type: "string" example: "Society of Curious Students" @@ -1946,15 +2003,31 @@ definitions: editgroup_id: type: "string" example: "q3nouwy3nnbsvo3h5klxsx4a7y" + description: "base32-encoded unique identifier" + minLength: 26 + maxLength: 26 + pattern: "[a-zA-Z2-7]{26}" redirect: type: "string" example: "q3nouwy3nnbsvo3h5klxsx4a7y" + description: "base32-encoded unique identifier" + minLength: 26 + maxLength: 26 + pattern: "[a-zA-Z2-7]{26}" revision: type: "string" example: "86daea5b-1b6b-432a-bb67-ea97795f80fe" + description: "UUID (lower-case, dash-separated, hex-encoded 128-bit)" + minLength: 36 + maxLength: 36 + pattern: "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" ident: type: "string" example: "q3nouwy3nnbsvo3h5klxsx4a7y" + description: "base32-encoded unique identifier" + minLength: 26 + maxLength: 26 + pattern: "[a-zA-Z2-7]{26}" state: type: "string" enum: @@ -1986,6 +2059,9 @@ definitions: orcid: type: "string" example: "0000-0002-1825-0097" + minLength: 19 + maxLength: 19 + pattern: "\\d{4}-\\d{4}-\\d{4}-\\d{4}" surname: type: "string" given_name: @@ -2003,15 +2079,31 @@ definitions: ident: type: "string" example: "q3nouwy3nnbsvo3h5klxsx4a7y" + description: "base32-encoded unique identifier" + minLength: 26 + maxLength: 26 + pattern: "[a-zA-Z2-7]{26}" revision: type: "string" example: "86daea5b-1b6b-432a-bb67-ea97795f80fe" + description: "UUID (lower-case, dash-separated, hex-encoded 128-bit)" + minLength: 36 + maxLength: 36 + pattern: "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" redirect: type: "string" example: "q3nouwy3nnbsvo3h5klxsx4a7y" + description: "base32-encoded unique identifier" + minLength: 26 + maxLength: 26 + pattern: "[a-zA-Z2-7]{26}" editgroup_id: type: "string" example: "q3nouwy3nnbsvo3h5klxsx4a7y" + description: "base32-encoded unique identifier" + minLength: 26 + maxLength: 26 + pattern: "[a-zA-Z2-7]{26}" extra: type: "object" example: @@ -2059,15 +2151,31 @@ definitions: editgroup_id: type: "string" example: "q3nouwy3nnbsvo3h5klxsx4a7y" + description: "base32-encoded unique identifier" + minLength: 26 + maxLength: 26 + pattern: "[a-zA-Z2-7]{26}" redirect: type: "string" example: "q3nouwy3nnbsvo3h5klxsx4a7y" + description: "base32-encoded unique identifier" + minLength: 26 + maxLength: 26 + pattern: "[a-zA-Z2-7]{26}" revision: type: "string" example: "86daea5b-1b6b-432a-bb67-ea97795f80fe" + description: "UUID (lower-case, dash-separated, hex-encoded 128-bit)" + minLength: 36 + maxLength: 36 + pattern: "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" ident: type: "string" example: "q3nouwy3nnbsvo3h5klxsx4a7y" + description: "base32-encoded unique identifier" + minLength: 26 + maxLength: 26 + pattern: "[a-zA-Z2-7]{26}" state: type: "string" enum: @@ -2148,6 +2256,14 @@ definitions: container_id: type: "string" example: "q3nouwy3nnbsvo3h5klxsx4a7y" + files: + type: "array" + description: "Optional; GET-only" + items: + $ref: "#/definitions/file_entity" + container: + description: "Optional; GET-only" + $ref: "#/definitions/container_entity" work_id: type: "string" example: "q3nouwy3nnbsvo3h5klxsx4a7y" @@ -2163,28 +2279,81 @@ definitions: ident: type: "string" example: "q3nouwy3nnbsvo3h5klxsx4a7y" + description: "base32-encoded unique identifier" + minLength: 26 + maxLength: 26 + pattern: "[a-zA-Z2-7]{26}" revision: type: "string" example: "86daea5b-1b6b-432a-bb67-ea97795f80fe" + description: "UUID (lower-case, dash-separated, hex-encoded 128-bit)" + minLength: 36 + maxLength: 36 + pattern: "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" redirect: type: "string" example: "q3nouwy3nnbsvo3h5klxsx4a7y" + description: "base32-encoded unique identifier" + minLength: 26 + maxLength: 26 + pattern: "[a-zA-Z2-7]{26}" editgroup_id: type: "string" example: "q3nouwy3nnbsvo3h5klxsx4a7y" + description: "base32-encoded unique identifier" + minLength: 26 + maxLength: 26 + pattern: "[a-zA-Z2-7]{26}" extra: type: "object" example: + container: + redirect: "q3nouwy3nnbsvo3h5klxsx4a7y" + coden: "coden" + ident: "q3nouwy3nnbsvo3h5klxsx4a7y" + extra: "{}" + name: "Journal of Important Results" + publisher: "Society of Curious Students" + editgroup_id: "q3nouwy3nnbsvo3h5klxsx4a7y" + issnl: "1234-5678" + abbrev: "abbrev" + wikidata_qid: "wikidata_qid" + state: "wip" + revision: "86daea5b-1b6b-432a-bb67-ea97795f80fe" ident: "q3nouwy3nnbsvo3h5klxsx4a7y" language: "language" title: "title" contribs: - - raw_name: "raw_name" + - creator: + redirect: "q3nouwy3nnbsvo3h5klxsx4a7y" + surname: "surname" + ident: "q3nouwy3nnbsvo3h5klxsx4a7y" + extra: "{}" + editgroup_id: "q3nouwy3nnbsvo3h5klxsx4a7y" + orcid: "0000-0002-1825-0097" + wikidata_qid: "wikidata_qid" + state: "wip" + given_name: "given_name" + display_name: "Grace Hopper" + revision: "86daea5b-1b6b-432a-bb67-ea97795f80fe" + raw_name: "raw_name" role: "role" extra: "{}" creator_id: "creator_id" index: 1 - - raw_name: "raw_name" + - creator: + redirect: "q3nouwy3nnbsvo3h5klxsx4a7y" + surname: "surname" + ident: "q3nouwy3nnbsvo3h5klxsx4a7y" + extra: "{}" + editgroup_id: "q3nouwy3nnbsvo3h5klxsx4a7y" + orcid: "0000-0002-1825-0097" + wikidata_qid: "wikidata_qid" + state: "wip" + given_name: "given_name" + display_name: "Grace Hopper" + revision: "86daea5b-1b6b-432a-bb67-ea97795f80fe" + raw_name: "raw_name" role: "role" extra: "{}" creator_id: "creator_id" @@ -2231,6 +2400,45 @@ definitions: release_date: "2000-01-23" isbn13: "isbn13" publisher: "publisher" + files: + - redirect: "q3nouwy3nnbsvo3h5klxsx4a7y" + sha256: "a77e4c11a57f1d757fca5754a8f83b5d4ece49a2d28596889127c1a2f3f28832" + ident: "q3nouwy3nnbsvo3h5klxsx4a7y" + releases: + - "releases" + - "releases" + revision: "86daea5b-1b6b-432a-bb67-ea97795f80fe" + sha1: "f013d66c7f6817d08b7eb2a93e6d0440c1f3e7f8" + urls: + - rel: "webarchive" + url: "https://example.edu/~frau/prcding.pdf" + - rel: "webarchive" + url: "https://example.edu/~frau/prcding.pdf" + size: 1048576 + extra: "{}" + editgroup_id: "q3nouwy3nnbsvo3h5klxsx4a7y" + mimetype: "application/pdf" + state: "wip" + md5: "d41efcc592d1e40ac13905377399eb9b" + - redirect: "q3nouwy3nnbsvo3h5klxsx4a7y" + sha256: "a77e4c11a57f1d757fca5754a8f83b5d4ece49a2d28596889127c1a2f3f28832" + ident: "q3nouwy3nnbsvo3h5klxsx4a7y" + releases: + - "releases" + - "releases" + revision: "86daea5b-1b6b-432a-bb67-ea97795f80fe" + sha1: "f013d66c7f6817d08b7eb2a93e6d0440c1f3e7f8" + urls: + - rel: "webarchive" + url: "https://example.edu/~frau/prcding.pdf" + - rel: "webarchive" + url: "https://example.edu/~frau/prcding.pdf" + size: 1048576 + extra: "{}" + editgroup_id: "q3nouwy3nnbsvo3h5klxsx4a7y" + mimetype: "application/pdf" + state: "wip" + md5: "d41efcc592d1e40ac13905377399eb9b" pmcid: "pmcid" container_id: "q3nouwy3nnbsvo3h5klxsx4a7y" doi: "10.1234/abcde.789" @@ -2243,15 +2451,31 @@ definitions: editgroup_id: type: "string" example: "q3nouwy3nnbsvo3h5klxsx4a7y" + description: "base32-encoded unique identifier" + minLength: 26 + maxLength: 26 + pattern: "[a-zA-Z2-7]{26}" redirect: type: "string" example: "q3nouwy3nnbsvo3h5klxsx4a7y" + description: "base32-encoded unique identifier" + minLength: 26 + maxLength: 26 + pattern: "[a-zA-Z2-7]{26}" revision: type: "string" example: "86daea5b-1b6b-432a-bb67-ea97795f80fe" + description: "UUID (lower-case, dash-separated, hex-encoded 128-bit)" + minLength: 36 + maxLength: 36 + pattern: "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" ident: type: "string" example: "q3nouwy3nnbsvo3h5klxsx4a7y" + description: "base32-encoded unique identifier" + minLength: 26 + maxLength: 26 + pattern: "[a-zA-Z2-7]{26}" state: type: "string" enum: @@ -2516,9 +2740,17 @@ definitions: id: type: "string" example: "q3nouwy3nnbsvo3h5klxsx4a7y" + description: "base32-encoded unique identifier" + minLength: 26 + maxLength: 26 + pattern: "[a-zA-Z2-7]{26}" editor_id: type: "string" example: "q3nouwy3nnbsvo3h5klxsx4a7y" + description: "base32-encoded unique identifier" + minLength: 26 + maxLength: 26 + pattern: "[a-zA-Z2-7]{26}" description: type: "string" extra: @@ -2751,6 +2983,9 @@ definitions: format: "int64" creator_id: type: "string" + creator: + description: "Optional; GET-only" + $ref: "#/definitions/creator_entity" raw_name: type: "string" extra: @@ -2758,6 +2993,18 @@ definitions: role: type: "string" example: + creator: + redirect: "q3nouwy3nnbsvo3h5klxsx4a7y" + surname: "surname" + ident: "q3nouwy3nnbsvo3h5klxsx4a7y" + extra: "{}" + editgroup_id: "q3nouwy3nnbsvo3h5klxsx4a7y" + orcid: "0000-0002-1825-0097" + wikidata_qid: "wikidata_qid" + state: "wip" + given_name: "given_name" + display_name: "Grace Hopper" + revision: "86daea5b-1b6b-432a-bb67-ea97795f80fe" raw_name: "raw_name" role: "role" extra: "{}" @@ -2907,6 +3154,32 @@ definitions: prev_revision: "86daea5b-1b6b-432a-bb67-ea97795f80fe" revision: "86daea5b-1b6b-432a-bb67-ea97795f80fe" upperCaseName: "EDITGROUP_EDITS" +x-fatcat-ident: + type: "string" + example: "q3nouwy3nnbsvo3h5klxsx4a7y" + pattern: "[a-zA-Z2-7]{26}" + minLength: 26 + maxLength: 26 + description: "base32-encoded unique identifier" +x-fatcat-uuid: + type: "string" + example: "86daea5b-1b6b-432a-bb67-ea97795f80fe" + pattern: "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" + minLength: 36 + maxLength: 36 + description: "UUID (lower-case, dash-separated, hex-encoded 128-bit)" +x-issn: + type: "string" + example: "1234-5678" + pattern: "\\d{4}-\\d{3}[0-9X]" + minLength: 9 + maxLength: 9 +x-orcid: + type: "string" + example: "0000-0002-1825-0097" + pattern: "\\d{4}-\\d{4}-\\d{4}-\\d{4}" + minLength: 19 + maxLength: 19 x-entity-props: state: type: "string" @@ -2916,17 +3189,33 @@ x-entity-props: - "redirect" - "deleted" ident: - type: "string" + description: "base32-encoded unique identifier" + maxLength: 26 + minLength: 26 + pattern: "[a-zA-Z2-7]{26}" example: "q3nouwy3nnbsvo3h5klxsx4a7y" - revision: type: "string" + revision: + description: "UUID (lower-case, dash-separated, hex-encoded 128-bit)" + maxLength: 36 + minLength: 36 + pattern: "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" example: "86daea5b-1b6b-432a-bb67-ea97795f80fe" + type: "string" redirect: type: "string" example: "q3nouwy3nnbsvo3h5klxsx4a7y" + pattern: "[a-zA-Z2-7]{26}" + minLength: 26 + maxLength: 26 + description: "base32-encoded unique identifier" editgroup_id: - type: "string" + description: "base32-encoded unique identifier" + maxLength: 26 + minLength: 26 + pattern: "[a-zA-Z2-7]{26}" example: "q3nouwy3nnbsvo3h5klxsx4a7y" + type: "string" extra: type: "object" additionalProperties: {} diff --git a/rust/fatcat-api/examples/client.rs b/rust/fatcat-api/examples/client.rs index 46ff0abd..3cfcd965 100644 --- a/rust/fatcat-api/examples/client.rs +++ b/rust/fatcat-api/examples/client.rs @@ -156,7 +156,7 @@ fn main() { } Some("GetContainer") => { - let result = client.get_container("id_example".to_string()).wait(); + let result = client.get_container("id_example".to_string(), Some("expend_example".to_string())).wait(); println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from(""))); } @@ -166,7 +166,7 @@ fn main() { } Some("GetCreator") => { - let result = client.get_creator("id_example".to_string()).wait(); + let result = client.get_creator("id_example".to_string(), Some("expend_example".to_string())).wait(); println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from(""))); } @@ -196,7 +196,7 @@ fn main() { } Some("GetFile") => { - let result = client.get_file("id_example".to_string()).wait(); + let result = client.get_file("id_example".to_string(), Some("expend_example".to_string())).wait(); println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from(""))); } @@ -206,7 +206,7 @@ fn main() { } Some("GetRelease") => { - let result = client.get_release("id_example".to_string()).wait(); + let result = client.get_release("id_example".to_string(), Some("expend_example".to_string())).wait(); println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from(""))); } @@ -226,7 +226,7 @@ fn main() { } Some("GetWork") => { - let result = client.get_work("id_example".to_string()).wait(); + let result = client.get_work("id_example".to_string(), Some("expend_example".to_string())).wait(); println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from(""))); } diff --git a/rust/fatcat-api/examples/server_lib/server.rs b/rust/fatcat-api/examples/server_lib/server.rs index a25e175f..bd18d8fb 100644 --- a/rust/fatcat-api/examples/server_lib/server.rs +++ b/rust/fatcat-api/examples/server_lib/server.rs @@ -110,9 +110,9 @@ impl Api for Server { Box::new(futures::failed("Generic failure".into())) } - fn get_container(&self, id: String, context: &Context) -> Box + Send> { + fn get_container(&self, id: String, expend: Option, context: &Context) -> Box + Send> { let context = context.clone(); - println!("get_container(\"{}\") - X-Span-ID: {:?}", id, context.x_span_id.unwrap_or(String::from("")).clone()); + println!("get_container(\"{}\", {:?}) - X-Span-ID: {:?}", id, expend, context.x_span_id.unwrap_or(String::from("")).clone()); Box::new(futures::failed("Generic failure".into())) } @@ -127,9 +127,9 @@ impl Api for Server { Box::new(futures::failed("Generic failure".into())) } - fn get_creator(&self, id: String, context: &Context) -> Box + Send> { + fn get_creator(&self, id: String, expend: Option, context: &Context) -> Box + Send> { let context = context.clone(); - println!("get_creator(\"{}\") - X-Span-ID: {:?}", id, context.x_span_id.unwrap_or(String::from("")).clone()); + println!("get_creator(\"{}\", {:?}) - X-Span-ID: {:?}", id, expend, context.x_span_id.unwrap_or(String::from("")).clone()); Box::new(futures::failed("Generic failure".into())) } @@ -168,9 +168,9 @@ impl Api for Server { Box::new(futures::failed("Generic failure".into())) } - fn get_file(&self, id: String, context: &Context) -> Box + Send> { + fn get_file(&self, id: String, expend: Option, context: &Context) -> Box + Send> { let context = context.clone(); - println!("get_file(\"{}\") - X-Span-ID: {:?}", id, context.x_span_id.unwrap_or(String::from("")).clone()); + println!("get_file(\"{}\", {:?}) - X-Span-ID: {:?}", id, expend, context.x_span_id.unwrap_or(String::from("")).clone()); Box::new(futures::failed("Generic failure".into())) } @@ -185,9 +185,9 @@ impl Api for Server { Box::new(futures::failed("Generic failure".into())) } - fn get_release(&self, id: String, context: &Context) -> Box + Send> { + fn get_release(&self, id: String, expend: Option, context: &Context) -> Box + Send> { let context = context.clone(); - println!("get_release(\"{}\") - X-Span-ID: {:?}", id, context.x_span_id.unwrap_or(String::from("")).clone()); + println!("get_release(\"{}\", {:?}) - X-Span-ID: {:?}", id, expend, context.x_span_id.unwrap_or(String::from("")).clone()); Box::new(futures::failed("Generic failure".into())) } @@ -214,9 +214,9 @@ impl Api for Server { Box::new(futures::failed("Generic failure".into())) } - fn get_work(&self, id: String, context: &Context) -> Box + Send> { + fn get_work(&self, id: String, expend: Option, context: &Context) -> Box + Send> { let context = context.clone(); - println!("get_work(\"{}\") - X-Span-ID: {:?}", id, context.x_span_id.unwrap_or(String::from("")).clone()); + println!("get_work(\"{}\", {:?}) - X-Span-ID: {:?}", id, expend, context.x_span_id.unwrap_or(String::from("")).clone()); Box::new(futures::failed("Generic failure".into())) } diff --git a/rust/fatcat-api/src/client.rs b/rust/fatcat-api/src/client.rs index 2b6fd2a6..10d4287d 100644 --- a/rust/fatcat-api/src/client.rs +++ b/rust/fatcat-api/src/client.rs @@ -1035,8 +1035,16 @@ impl Api for Client { Box::new(futures::done(result)) } - fn get_container(&self, param_id: String, context: &Context) -> Box + Send> { - let url = format!("{}/v0/container/{id}", self.base_path, id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET)); + fn get_container(&self, param_id: String, param_expend: Option, context: &Context) -> Box + Send> { + // Query parameters + let query_expend = param_expend.map_or_else(String::new, |query| format!("expend={expend}&", expend = query.to_string())); + + let url = format!( + "{}/v0/container/{id}?{expend}", + self.base_path, + id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET), + expend = utf8_percent_encode(&query_expend, QUERY_ENCODE_SET) + ); let hyper_client = (self.hyper_client)(); let request = hyper_client.request(hyper::method::Method::Get, &url); @@ -1163,8 +1171,16 @@ impl Api for Client { Box::new(futures::done(result)) } - fn get_creator(&self, param_id: String, context: &Context) -> Box + Send> { - let url = format!("{}/v0/creator/{id}", self.base_path, id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET)); + fn get_creator(&self, param_id: String, param_expend: Option, context: &Context) -> Box + Send> { + // Query parameters + let query_expend = param_expend.map_or_else(String::new, |query| format!("expend={expend}&", expend = query.to_string())); + + let url = format!( + "{}/v0/creator/{id}?{expend}", + self.base_path, + id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET), + expend = utf8_percent_encode(&query_expend, QUERY_ENCODE_SET) + ); let hyper_client = (self.hyper_client)(); let request = hyper_client.request(hyper::method::Method::Get, &url); @@ -1517,8 +1533,16 @@ impl Api for Client { Box::new(futures::done(result)) } - fn get_file(&self, param_id: String, context: &Context) -> Box + Send> { - let url = format!("{}/v0/file/{id}", self.base_path, id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET)); + fn get_file(&self, param_id: String, param_expend: Option, context: &Context) -> Box + Send> { + // Query parameters + let query_expend = param_expend.map_or_else(String::new, |query| format!("expend={expend}&", expend = query.to_string())); + + let url = format!( + "{}/v0/file/{id}?{expend}", + self.base_path, + id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET), + expend = utf8_percent_encode(&query_expend, QUERY_ENCODE_SET) + ); let hyper_client = (self.hyper_client)(); let request = hyper_client.request(hyper::method::Method::Get, &url); @@ -1645,8 +1669,16 @@ impl Api for Client { Box::new(futures::done(result)) } - fn get_release(&self, param_id: String, context: &Context) -> Box + Send> { - let url = format!("{}/v0/release/{id}", self.base_path, id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET)); + fn get_release(&self, param_id: String, param_expend: Option, context: &Context) -> Box + Send> { + // Query parameters + let query_expend = param_expend.map_or_else(String::new, |query| format!("expend={expend}&", expend = query.to_string())); + + let url = format!( + "{}/v0/release/{id}?{expend}", + self.base_path, + id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET), + expend = utf8_percent_encode(&query_expend, QUERY_ENCODE_SET) + ); let hyper_client = (self.hyper_client)(); let request = hyper_client.request(hyper::method::Method::Get, &url); @@ -1882,8 +1914,16 @@ impl Api for Client { Box::new(futures::done(result)) } - fn get_work(&self, param_id: String, context: &Context) -> Box + Send> { - let url = format!("{}/v0/work/{id}", self.base_path, id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET)); + fn get_work(&self, param_id: String, param_expend: Option, context: &Context) -> Box + Send> { + // Query parameters + let query_expend = param_expend.map_or_else(String::new, |query| format!("expend={expend}&", expend = query.to_string())); + + let url = format!( + "{}/v0/work/{id}?{expend}", + self.base_path, + id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET), + expend = utf8_percent_encode(&query_expend, QUERY_ENCODE_SET) + ); let hyper_client = (self.hyper_client)(); let request = hyper_client.request(hyper::method::Method::Get, &url); diff --git a/rust/fatcat-api/src/lib.rs b/rust/fatcat-api/src/lib.rs index 47d23d04..c55d6eea 100644 --- a/rust/fatcat-api/src/lib.rs +++ b/rust/fatcat-api/src/lib.rs @@ -466,11 +466,11 @@ pub trait Api { fn get_changelog_entry(&self, id: i64, context: &Context) -> Box + Send>; - fn get_container(&self, id: String, context: &Context) -> Box + Send>; + fn get_container(&self, id: String, expend: Option, context: &Context) -> Box + Send>; fn get_container_history(&self, id: String, limit: Option, context: &Context) -> Box + Send>; - fn get_creator(&self, id: String, context: &Context) -> Box + Send>; + fn get_creator(&self, id: String, expend: Option, context: &Context) -> Box + Send>; fn get_creator_history(&self, id: String, limit: Option, context: &Context) -> Box + Send>; @@ -482,11 +482,11 @@ pub trait Api { fn get_editor_changelog(&self, id: String, context: &Context) -> Box + Send>; - fn get_file(&self, id: String, context: &Context) -> Box + Send>; + fn get_file(&self, id: String, expend: Option, context: &Context) -> Box + Send>; fn get_file_history(&self, id: String, limit: Option, context: &Context) -> Box + Send>; - fn get_release(&self, id: String, context: &Context) -> Box + Send>; + fn get_release(&self, id: String, expend: Option, context: &Context) -> Box + Send>; fn get_release_files(&self, id: String, context: &Context) -> Box + Send>; @@ -494,7 +494,7 @@ pub trait Api { fn get_stats(&self, more: Option, context: &Context) -> Box + Send>; - fn get_work(&self, id: String, context: &Context) -> Box + Send>; + fn get_work(&self, id: String, expend: Option, context: &Context) -> Box + Send>; fn get_work_history(&self, id: String, limit: Option, context: &Context) -> Box + Send>; @@ -539,11 +539,11 @@ pub trait ApiNoContext { fn get_changelog_entry(&self, id: i64) -> Box + Send>; - fn get_container(&self, id: String) -> Box + Send>; + fn get_container(&self, id: String, expend: Option) -> Box + Send>; fn get_container_history(&self, id: String, limit: Option) -> Box + Send>; - fn get_creator(&self, id: String) -> Box + Send>; + fn get_creator(&self, id: String, expend: Option) -> Box + Send>; fn get_creator_history(&self, id: String, limit: Option) -> Box + Send>; @@ -555,11 +555,11 @@ pub trait ApiNoContext { fn get_editor_changelog(&self, id: String) -> Box + Send>; - fn get_file(&self, id: String) -> Box + Send>; + fn get_file(&self, id: String, expend: Option) -> Box + Send>; fn get_file_history(&self, id: String, limit: Option) -> Box + Send>; - fn get_release(&self, id: String) -> Box + Send>; + fn get_release(&self, id: String, expend: Option) -> Box + Send>; fn get_release_files(&self, id: String) -> Box + Send>; @@ -567,7 +567,7 @@ pub trait ApiNoContext { fn get_stats(&self, more: Option) -> Box + Send>; - fn get_work(&self, id: String) -> Box + Send>; + fn get_work(&self, id: String, expend: Option) -> Box + Send>; fn get_work_history(&self, id: String, limit: Option) -> Box + Send>; @@ -654,16 +654,16 @@ impl<'a, T: Api> ApiNoContext for ContextWrapper<'a, T> { self.api().get_changelog_entry(id, &self.context()) } - fn get_container(&self, id: String) -> Box + Send> { - self.api().get_container(id, &self.context()) + fn get_container(&self, id: String, expend: Option) -> Box + Send> { + self.api().get_container(id, expend, &self.context()) } fn get_container_history(&self, id: String, limit: Option) -> Box + Send> { self.api().get_container_history(id, limit, &self.context()) } - fn get_creator(&self, id: String) -> Box + Send> { - self.api().get_creator(id, &self.context()) + fn get_creator(&self, id: String, expend: Option) -> Box + Send> { + self.api().get_creator(id, expend, &self.context()) } fn get_creator_history(&self, id: String, limit: Option) -> Box + Send> { @@ -686,16 +686,16 @@ impl<'a, T: Api> ApiNoContext for ContextWrapper<'a, T> { self.api().get_editor_changelog(id, &self.context()) } - fn get_file(&self, id: String) -> Box + Send> { - self.api().get_file(id, &self.context()) + fn get_file(&self, id: String, expend: Option) -> Box + Send> { + self.api().get_file(id, expend, &self.context()) } fn get_file_history(&self, id: String, limit: Option) -> Box + Send> { self.api().get_file_history(id, limit, &self.context()) } - fn get_release(&self, id: String) -> Box + Send> { - self.api().get_release(id, &self.context()) + fn get_release(&self, id: String, expend: Option) -> Box + Send> { + self.api().get_release(id, expend, &self.context()) } fn get_release_files(&self, id: String) -> Box + Send> { @@ -710,8 +710,8 @@ impl<'a, T: Api> ApiNoContext for ContextWrapper<'a, T> { self.api().get_stats(more, &self.context()) } - fn get_work(&self, id: String) -> Box + Send> { - self.api().get_work(id, &self.context()) + fn get_work(&self, id: String, expend: Option) -> Box + Send> { + self.api().get_work(id, expend, &self.context()) } fn get_work_history(&self, id: String, limit: Option) -> Box + Send> { diff --git a/rust/fatcat-api/src/models.rs b/rust/fatcat-api/src/models.rs index d85a1bbd..61812dc2 100644 --- a/rust/fatcat-api/src/models.rs +++ b/rust/fatcat-api/src/models.rs @@ -65,18 +65,22 @@ pub struct ContainerEntity { #[serde(skip_serializing_if = "Option::is_none")] pub extra: Option, + /// base32-encoded unique identifier #[serde(rename = "editgroup_id")] #[serde(skip_serializing_if = "Option::is_none")] pub editgroup_id: Option, + /// base32-encoded unique identifier #[serde(rename = "redirect")] #[serde(skip_serializing_if = "Option::is_none")] pub redirect: Option, + /// UUID (lower-case, dash-separated, hex-encoded 128-bit) #[serde(rename = "revision")] #[serde(skip_serializing_if = "Option::is_none")] pub revision: Option, + /// base32-encoded unique identifier #[serde(rename = "ident")] #[serde(skip_serializing_if = "Option::is_none")] pub ident: Option, @@ -132,18 +136,22 @@ pub struct CreatorEntity { #[serde(skip_serializing_if = "Option::is_none")] pub state: Option, + /// base32-encoded unique identifier #[serde(rename = "ident")] #[serde(skip_serializing_if = "Option::is_none")] pub ident: Option, + /// UUID (lower-case, dash-separated, hex-encoded 128-bit) #[serde(rename = "revision")] #[serde(skip_serializing_if = "Option::is_none")] pub revision: Option, + /// base32-encoded unique identifier #[serde(rename = "redirect")] #[serde(skip_serializing_if = "Option::is_none")] pub redirect: Option, + /// base32-encoded unique identifier #[serde(rename = "editgroup_id")] #[serde(skip_serializing_if = "Option::is_none")] pub editgroup_id: Option, @@ -173,10 +181,12 @@ impl CreatorEntity { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Editgroup { + /// base32-encoded unique identifier #[serde(rename = "id")] #[serde(skip_serializing_if = "Option::is_none")] pub id: Option, + /// base32-encoded unique identifier #[serde(rename = "editor_id")] pub editor_id: String, @@ -366,18 +376,22 @@ pub struct FileEntity { #[serde(skip_serializing_if = "Option::is_none")] pub extra: Option, + /// base32-encoded unique identifier #[serde(rename = "editgroup_id")] #[serde(skip_serializing_if = "Option::is_none")] pub editgroup_id: Option, + /// base32-encoded unique identifier #[serde(rename = "redirect")] #[serde(skip_serializing_if = "Option::is_none")] pub redirect: Option, + /// UUID (lower-case, dash-separated, hex-encoded 128-bit) #[serde(rename = "revision")] #[serde(skip_serializing_if = "Option::is_none")] pub revision: Option, + /// base32-encoded unique identifier #[serde(rename = "ident")] #[serde(skip_serializing_if = "Option::is_none")] pub ident: Option, @@ -433,6 +447,11 @@ pub struct ReleaseContrib { #[serde(skip_serializing_if = "Option::is_none")] pub creator_id: Option, + /// Optional; GET-only + #[serde(rename = "creator")] + #[serde(skip_serializing_if = "Option::is_none")] + pub creator: Option, + #[serde(rename = "raw_name")] #[serde(skip_serializing_if = "Option::is_none")] pub raw_name: Option, @@ -451,6 +470,7 @@ impl ReleaseContrib { ReleaseContrib { index: None, creator_id: None, + creator: None, raw_name: None, extra: None, role: None, @@ -529,6 +549,16 @@ pub struct ReleaseEntity { #[serde(skip_serializing_if = "Option::is_none")] pub container_id: Option, + /// Optional; GET-only + #[serde(rename = "files")] + #[serde(skip_serializing_if = "Option::is_none")] + pub files: Option>, + + /// Optional; GET-only + #[serde(rename = "container")] + #[serde(skip_serializing_if = "Option::is_none")] + pub container: Option, + #[serde(rename = "work_id")] #[serde(skip_serializing_if = "Option::is_none")] pub work_id: Option, @@ -541,18 +571,22 @@ pub struct ReleaseEntity { #[serde(skip_serializing_if = "Option::is_none")] pub state: Option, + /// base32-encoded unique identifier #[serde(rename = "ident")] #[serde(skip_serializing_if = "Option::is_none")] pub ident: Option, + /// UUID (lower-case, dash-separated, hex-encoded 128-bit) #[serde(rename = "revision")] #[serde(skip_serializing_if = "Option::is_none")] pub revision: Option, + /// base32-encoded unique identifier #[serde(rename = "redirect")] #[serde(skip_serializing_if = "Option::is_none")] pub redirect: Option, + /// base32-encoded unique identifier #[serde(rename = "editgroup_id")] #[serde(skip_serializing_if = "Option::is_none")] pub editgroup_id: Option, @@ -582,6 +616,8 @@ impl ReleaseEntity { release_status: None, release_type: None, container_id: None, + files: None, + container: None, work_id: None, title: title, state: None, @@ -705,18 +741,22 @@ pub struct WorkEntity { #[serde(skip_serializing_if = "Option::is_none")] pub extra: Option, + /// base32-encoded unique identifier #[serde(rename = "editgroup_id")] #[serde(skip_serializing_if = "Option::is_none")] pub editgroup_id: Option, + /// base32-encoded unique identifier #[serde(rename = "redirect")] #[serde(skip_serializing_if = "Option::is_none")] pub redirect: Option, + /// UUID (lower-case, dash-separated, hex-encoded 128-bit) #[serde(rename = "revision")] #[serde(skip_serializing_if = "Option::is_none")] pub revision: Option, + /// base32-encoded unique identifier #[serde(rename = "ident")] #[serde(skip_serializing_if = "Option::is_none")] pub ident: Option, diff --git a/rust/fatcat-api/src/server.rs b/rust/fatcat-api/src/server.rs index e94fd5ef..0b170f03 100644 --- a/rust/fatcat-api/src/server.rs +++ b/rust/fatcat-api/src/server.rs @@ -1495,7 +1495,11 @@ where .map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse path parameter id: {}", e))))? }; - match api.get_container(param_id, context).wait() { + // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response) + let query_params = req.get::().unwrap_or_default(); + let param_expend = query_params.get("expend").and_then(|list| list.first()).and_then(|x| x.parse::().ok()); + + match api.get_container(param_id, param_expend, context).wait() { Ok(rsp) => match rsp { GetContainerResponse::FoundEntity(body) => { let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); @@ -1675,7 +1679,11 @@ where .map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse path parameter id: {}", e))))? }; - match api.get_creator(param_id, context).wait() { + // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response) + let query_params = req.get::().unwrap_or_default(); + let param_expend = query_params.get("expend").and_then(|list| list.first()).and_then(|x| x.parse::().ok()); + + match api.get_creator(param_id, param_expend, context).wait() { Ok(rsp) => match rsp { GetCreatorResponse::FoundEntity(body) => { let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); @@ -2187,7 +2195,11 @@ where .map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse path parameter id: {}", e))))? }; - match api.get_file(param_id, context).wait() { + // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response) + let query_params = req.get::().unwrap_or_default(); + let param_expend = query_params.get("expend").and_then(|list| list.first()).and_then(|x| x.parse::().ok()); + + match api.get_file(param_id, param_expend, context).wait() { Ok(rsp) => match rsp { GetFileResponse::FoundEntity(body) => { let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); @@ -2367,7 +2379,11 @@ where .map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse path parameter id: {}", e))))? }; - match api.get_release(param_id, context).wait() { + // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response) + let query_params = req.get::().unwrap_or_default(); + let param_expend = query_params.get("expend").and_then(|list| list.first()).and_then(|x| x.parse::().ok()); + + match api.get_release(param_id, param_expend, context).wait() { Ok(rsp) => match rsp { GetReleaseResponse::FoundEntity(body) => { let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); @@ -2693,7 +2709,11 @@ where .map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse path parameter id: {}", e))))? }; - match api.get_work(param_id, context).wait() { + // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response) + let query_params = req.get::().unwrap_or_default(); + let param_expend = query_params.get("expend").and_then(|list| list.first()).and_then(|x| x.parse::().ok()); + + match api.get_work(param_id, param_expend, context).wait() { Ok(rsp) => match rsp { GetWorkResponse::FoundEntity(body) => { let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); -- cgit v1.2.3