aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--rust/Cargo.lock882
-rw-r--r--rust/Cargo.toml13
-rw-r--r--rust/fatcat-api/.gitignore2
-rw-r--r--rust/fatcat-api/Cargo.toml48
-rw-r--r--rust/fatcat-api/README.md123
-rw-r--r--rust/fatcat-api/api.yaml464
-rw-r--r--rust/fatcat-api/api/swagger.yaml1196
-rw-r--r--rust/fatcat-api/examples/ca.pem17
-rw-r--r--rust/fatcat-api/examples/client.rs350
-rw-r--r--rust/fatcat-api/examples/server-chain.pem66
-rw-r--r--rust/fatcat-api/examples/server-key.pem28
-rw-r--r--rust/fatcat-api/examples/server.rs78
-rw-r--r--rust/fatcat-api/examples/server_lib/mod.rs26
-rw-r--r--rust/fatcat-api/examples/server_lib/server.rs287
-rw-r--r--rust/fatcat-api/src/client/mod.rs2573
-rw-r--r--rust/fatcat-api/src/lib.rs599
-rw-r--r--rust/fatcat-api/src/mimetypes.rs313
-rw-r--r--rust/fatcat-api/src/models.rs392
-rw-r--r--rust/fatcat-api/src/server/auth.rs93
-rw-r--r--rust/fatcat-api/src/server/mod.rs2485
-rw-r--r--rust/src/api_lib.rs34
-rw-r--r--rust/src/api_server.rs288
-rw-r--r--rust/src/bin/fatcat-tokio.rs67
-rw-r--r--rust/src/bin/show_creators.rs24
-rw-r--r--rust/src/lib.rs40
25 files changed, 10449 insertions, 39 deletions
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 083434d1..42759107 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -15,6 +15,19 @@ dependencies = [
]
[[package]]
+name = "arrayvec"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "ascii"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "atty"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -25,6 +38,27 @@ dependencies = [
]
[[package]]
+name = "backtrace"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "backtrace-sys"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "base64"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -42,22 +76,68 @@ dependencies = [
]
[[package]]
+name = "base64"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "bitflags"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "bitflags"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "buf_redux"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "byteorder"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "bytes"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "cc"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "cfg-if"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "chrono"
+version = "0.2.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "chrono"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
@@ -77,6 +157,11 @@ dependencies = [
]
[[package]]
+name = "chunked_transfer"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "clap"
version = "2.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -99,6 +184,53 @@ dependencies = [
]
[[package]]
+name = "core-foundation"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-epoch 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "diesel"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -133,6 +265,63 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "encoding"
+version = "0.2.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "encoding-index-japanese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "encoding-index-korean 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "encoding-index-simpchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "encoding-index-singlebyte 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "encoding-index-tradchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "encoding-index-japanese"
+version = "1.20141219.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "encoding-index-korean"
+version = "1.20141219.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "encoding-index-simpchinese"
+version = "1.20141219.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "encoding-index-singlebyte"
+version = "1.20141219.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "encoding-index-tradchinese"
+version = "1.20141219.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "encoding_index_tests"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "error"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -142,6 +331,14 @@ dependencies = [
]
[[package]]
+name = "error-chain"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "backtrace 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "fatcat"
version = "0.1.0"
dependencies = [
@@ -149,15 +346,63 @@ dependencies = [
"clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)",
"diesel 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"dotenv 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fatcat-api 0.1.0",
"futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
- "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "hyper 0.11.26 (registry+https://github.com/rust-lang/crates.io-index)",
"iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"iron-slog 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"iron-test 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "swagger 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "swagger 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
+name = "fatcat-api"
+version = "0.1.0"
+dependencies = [
+ "chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "hyper 0.11.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "hyper-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mime 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "multipart 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_ignored 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_urlencoded 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "swagger 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -177,6 +422,15 @@ version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "futures-cpupool"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "httparse"
version = "1.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -200,6 +454,45 @@ dependencies = [
]
[[package]]
+name = "hyper"
+version = "0.11.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "base64 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mime 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "hyper-tls"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "hyper 0.11.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "idna"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -210,6 +503,15 @@ dependencies = [
]
[[package]]
+name = "iovec"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "iron"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -265,10 +567,24 @@ dependencies = [
[[package]]
name = "itoa"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "itoa"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "kernel32-sys"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "language-tags"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -284,6 +600,11 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "lazycell"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "libc"
version = "0.2.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -311,6 +632,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memchr"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "memchr"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
@@ -318,6 +647,11 @@ dependencies = [
]
[[package]]
+name = "memoffset"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "mime"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -326,6 +660,14 @@ dependencies = [
]
[[package]]
+name = "mime"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "mime_guess"
version = "1.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -337,11 +679,88 @@ dependencies = [
]
[[package]]
+name = "mio"
+version = "0.6.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "miow"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "modifier"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "multipart"
+version = "0.13.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "buf_redux 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "iron 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mime_guess 1.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
+ "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tiny_http 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "twoway 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "native-tls"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "schannel 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "net2"
+version = "0.2.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "nodrop"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "num"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -382,6 +801,29 @@ dependencies = [
]
[[package]]
+name = "openssl"
+version = "0.9.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl-sys 0.9.30 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "vcpkg 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "percent-encoding"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -422,6 +864,11 @@ dependencies = [
]
[[package]]
+name = "pkg-config"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "plugin"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -523,6 +970,27 @@ dependencies = [
]
[[package]]
+name = "relay"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "remove_dir_all"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "rustc-serialize"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -533,6 +1001,45 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "schannel"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "scoped-tls"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "scopeguard"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "security-framework"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "serde"
version = "1.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -548,6 +1055,14 @@ dependencies = [
]
[[package]]
+name = "serde_ignored"
+version = "0.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "serde_json"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -558,31 +1073,62 @@ dependencies = [
]
[[package]]
+name = "serde_urlencoded"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "siphasher"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "slab"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "slab"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "slog"
version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "smallvec"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "strsim"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "swagger"
-version = "0.7.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)",
- "iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "hyper 0.11.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "hyper-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "slog 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -606,6 +1152,20 @@ dependencies = [
]
[[package]]
+name = "take"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "tempdir"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "termion"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -643,11 +1203,193 @@ dependencies = [
]
[[package]]
+name = "tiny_http"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "ascii 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "chunked_transfer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-fs 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-threadpool 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-timer 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-udp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-core"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-timer 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-executor"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-fs"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-threadpool 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-io"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-proto"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
+ "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-reactor"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-service"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-tcp"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-threadpool"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-timer"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-tls"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-udp"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "traitobject"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "twoway"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "typeable"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -674,6 +1416,14 @@ dependencies = [
]
[[package]]
+name = "unicase"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "unicode-bidi"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -714,6 +1464,16 @@ dependencies = [
[[package]]
name = "url"
+version = "0.2.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "url"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
@@ -729,10 +1489,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "uuid"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "uuid"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -757,6 +1527,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
@@ -765,6 +1540,11 @@ dependencies = [
]
[[package]]
+name = "winapi-build"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -774,56 +1554,110 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
+[[package]]
+name = "ws2_32-sys"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[metadata]
"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
+"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"
+"checksum ascii 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae7d751998c189c1d4468cf0a39bb2eae052a9c58d50ebb3b9591ee3813ad50"
"checksum atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2fc4a1aa4c24c0718a250f0681885c1af91419d242f29eb8f2ab28502d80dbd1"
+"checksum backtrace 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea58cd16fd6c9d120b5bcb01d63883ae4cc7ba2aed35c1841b862a3c7ef6639"
+"checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661"
"checksum base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30e93c03064e7590d0466209155251b90c22e37fab1daf2771582598b5827557"
"checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9"
+"checksum base64 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9263aa6a38da271eec5c91a83ce1e800f093c8535788d403d626d8d5c3f8f007"
+"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
"checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789"
+"checksum buf_redux 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b9279646319ff816b05fb5897883ece50d7d854d12b59992683d4f8a71b0f949"
"checksum byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "73b5bdfe7ee3ad0b99c9801d58807a9dbc9e09196365b0203853b99889ab3c87"
+"checksum bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "2f1d50c876fb7545f5f289cd8b2aee3f359d073ae819eed5d6373638e2c61e59"
+"checksum cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "0ebb87d1116151416c0cf66a0e3fb6430cccd120fd6300794b4dfaa050ac40ba"
"checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18"
+"checksum chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9213f7cd7c27e95c2b57c49f0e69b1ea65b27138da84a170133fd21b07659c00"
"checksum chrono 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "158b0bd7d75cbb6bf9c25967a48a2e9f77da95876b858eadfabaa99cd069de6e"
"checksum chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1cce36c92cb605414e9b824f866f5babe0a0368e39ea07393b9b63cf3844c0e6"
+"checksum chunked_transfer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "498d20a7aaf62625b9bf26e637cf7736417cde1d0c99f1d04d1170229a85cf87"
"checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536"
"checksum conduit-mime-types 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "95ca30253581af809925ef68c2641cc140d6183f43e12e0af4992d53768bd7b8"
+"checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67"
+"checksum core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "065a5d7ffdcbc8fa145d6f0746f3555025b9097a9e9cda59f7467abae670c78d"
+"checksum crossbeam-deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fe8153ef04a7594ded05b427ffad46ddeaf22e63fd48d42b3e1e3bb4db07cae7"
+"checksum crossbeam-epoch 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9b4e2817eb773f770dcb294127c011e22771899c21d18fce7dd739c0b9832e81"
+"checksum crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d636a8b3bcc1b409d7ffd3facef8f21dcb4009626adbd0c5e6c4305c07253c7b"
"checksum diesel 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24815a0c2094f2c8dafe74ab3b9e975892f44acbb94b4d4b4898025a7615efa4"
"checksum diesel_derives 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6471a2b637b414d3ee1504cf230409a550381c79204282f8fe06c527e4ae56be"
"checksum dotenv 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "400b347fe65ccfbd8f545c9d9a75d04b0caf23fec49aaa838a9a05398f94c019"
"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
+"checksum encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec"
+"checksum encoding-index-japanese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91"
+"checksum encoding-index-korean 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81"
+"checksum encoding-index-simpchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7"
+"checksum encoding-index-singlebyte 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a"
+"checksum encoding-index-tradchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18"
+"checksum encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569"
"checksum error 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "a6e606f14042bb87cc02ef6a14db6c90ab92ed6f62d87e69377bc759fd7987cc"
+"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
+"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "1a70b146671de62ec8c8ed572219ca5d594d9b06c0b364d5e67b722fc559b48c"
+"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
"checksum httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f407128745b78abc95c0ffbe4e5d37427fdc0d45470710cfef8c44522a2e37"
"checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2"
+"checksum hyper 0.11.26 (registry+https://github.com/rust-lang/crates.io-index)" = "66b16eb6213713f3c72d0ed14ce56423ae84dced8df73d2a2c8675f0495ae7ea"
+"checksum hyper-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a5aa51f6ae9842239b0fac14af5f22123b8432b4cc774a44ff059fcba0f675ca"
"checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d"
+"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
"checksum iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2440ae846e7a8c7f9b401db8f6e31b4ea5e7d3688b91761337da7e054520c75b"
"checksum iron 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d8e17268922834707e1c29e8badbf9c712c9c43378e1b6a3388946baff10be2"
"checksum iron-slog 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb0cc34306c77baf5089b3af209a0547c0ce36cdeaee2424a9fa0e45c042f4a0"
"checksum iron-test 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1944bcf30f8b3f51ebf01e715517dd9755e9480934778d6de70179a41d283c1"
+"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c"
"checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682"
+"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
+"checksum lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef"
"checksum libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "6fd41f331ac7c5b8ac259b8bf82c75c0fb2e469bbf37d2becbba9a6a2221965b"
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2"
"checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376"
+"checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a"
"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
+"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3"
"checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0"
+"checksum mime 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0b28683d0b09bbc20be1c9b3f6f24854efb1356ffcffee08ea3f6e65596e85fa"
"checksum mime_guess 1.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b7e2b09d08313f84e0fb82d13a4d859109a17543fe9af3b6d941dc1431f7de79"
+"checksum mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "6d771e3ef92d58a8da8df7d6976bfca9371ed1de6619d9d5a5ce5b1f29b85bfe"
+"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
"checksum modifier 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41f5c9112cb662acd3b204077e0de5bc66305fa8df65c8019d5adb10e9ab6e58"
+"checksum multipart 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92f54eb45230c3aa20864ccf0c277eeaeadcf5e437e91731db498dbf7fbe0ec6"
+"checksum native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f74dbadc8b43df7864539cedb7bc91345e532fdd913cfdc23ad94f4d2d40fbc0"
+"checksum net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "9044faf1413a1057267be51b5afba8eb1090bd2231c693664aa1db716fe1eae0"
+"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
"checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e"
"checksum num-integer 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "6ac0ea58d64a89d9d6b7688031b3be9358d6c919badcf7fbb0527ccfd891ee45"
"checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124"
"checksum num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775393e285254d2f5004596d69bb8bc1149754570dcc08cf30cabeba67955e28"
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
+"checksum openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "a3605c298474a3aa69de92d21139fb5e2a81688d308262359d85cdd0d12a7985"
+"checksum openssl-sys 0.9.30 (registry+https://github.com/rust-lang/crates.io-index)" = "73ae718c3562989cd3a0a5c26610feca02f8116822f6f195e6cf4887481e57f5"
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
"checksum phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "7d37a244c75a9748e049225155f56dbcb98fe71b192fd25fd23cb914b5ad62f2"
"checksum phf_codegen 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "4e4048fe7dd7a06b8127ecd6d3803149126e9b33c7558879846da3a63f734f2b"
"checksum phf_generator 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "05a079dd052e7b674d21cb31cbb6c05efd56a2cd2827db7692e2f1a507ebd998"
"checksum phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "c2261d544c2bb6aa3b10022b0be371b9c7c64f762ef28c6f5d4f1ef6d97b5930"
+"checksum pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "110d5ee3593dbb73f56294327fe5668bcc997897097cbc76b51e7aed3f52452f"
"checksum plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1a6a0dc3910bc8db877ffed8e457763b317cf880df4ae19109b9f77d277cf6e0"
"checksum pq-sys 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9323a6ce484fc41174d40f80ba87af6247f86a7ba57856af68d3aa0c8642d2f0"
"checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0"
@@ -836,39 +1670,75 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384"
"checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7"
+"checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a"
+"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
+"checksum rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "76d7ba1feafada44f2d38eed812bd2489a03c0f5abb975799251518b68848649"
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f"
+"checksum schannel 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "85fd9df495640643ad2d00443b3d78aae69802ad488debab4f1dd52fc1806ade"
+"checksum scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28"
+"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
+"checksum security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "dfa44ee9c54ce5eecc9de7d5acbad112ee58755239381f687e564004ba4a2332"
+"checksum security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "5421621e836278a0b139268f36eee0dc7e389b784dc3f79d8f11aabadf41bead"
"checksum serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)" = "97f6a6c3caba0cf8f883b53331791036404ce3c1bd895961cf8bb2f8cecfd84b"
"checksum serde_derive 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)" = "f51b0ef935cf8a41a77bce553da1f8751a739b7ad82dd73669475a22e6ecedb0"
+"checksum serde_ignored 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "190e9765dcedb56be63b6e0993a006c7e3b071a016a304736e4a315dc01fb142"
"checksum serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "f3ad6d546e765177cf3dded3c2e424a8040f870083a0e64064746b958ece9cb1"
+"checksum serde_urlencoded 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce0fd303af908732989354c6f02e05e2e6d597152870f2c6990efb0577137480"
"checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537"
+"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
+"checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d"
"checksum slog 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2f7bfce6405155042d42ec0e645efe43eddedd7be280063ce0623b120014e7f9"
+"checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013"
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
-"checksum swagger 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "190ef0c6327759d0beb76d969b236fa3cc42469f9e107f626bbcc152727b4d12"
+"checksum swagger 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97dbc25f067d3341f3c8fc6d77471891878b1585a614018c7f542d6b5cf1dc61"
"checksum syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c97c05b8ebc34ddd6b967994d5c6e9852fa92f8b82b3858c39451f97346dcce5"
"checksum syn 0.13.10 (registry+https://github.com/rust-lang/crates.io-index)" = "77961dcdac942fa8bc033c16f3a790b311c8a27d00811b878ebd8cf9b7ba39d5"
+"checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5"
+"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693"
"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b"
+"checksum tiny_http 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "2f4d55c9a213880d1f0c89ded183f209c6e45b912ca6c7df6f93c163773572e1"
+"checksum tokio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d00555353b013e170ed8bc4e13f648a317d1fd12157dbcae13f7013f6cf29f5"
+"checksum tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "aeeffbbb94209023feaef3c196a41cbcdafa06b4a6f893f68779bb5e53796f71"
+"checksum tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8cac2a7883ff3567e9d66bb09100d09b33d90311feca0206c7ca034bc0c55113"
+"checksum tokio-fs 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "76766830bbf9a2d5bfb50c95350d56a2e79e2c80f675967fff448bc615899708"
+"checksum tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6af9eb326f64b2d6b68438e1953341e00ab3cf54de7e35d92bfc73af8555313a"
+"checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389"
+"checksum tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3cedc8e5af5131dc3423ffa4f877cce78ad25259a9a62de0613735a13ebc64b"
+"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162"
+"checksum tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec9b094851aadd2caf83ba3ad8e8c4ce65a42104f7b94d9e6550023f0407853f"
+"checksum tokio-threadpool 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5783254b10c7c84a56f62c74766ef7e5b83d1f13053218c7cab8d3f2c826fa0e"
+"checksum tokio-timer 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535fed0ccee189f3d48447587697ba3fd234b3dbbb091f0ec4613ddfec0a7c4c"
+"checksum tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "772f4b04e560117fe3b0a53e490c16ddc8ba6ec437015d91fa385564996ed913"
+"checksum tokio-udp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "137bda266504893ac4774e0ec4c2108f7ccdbcb7ac8dced6305fe9e4e0b5041a"
"checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079"
+"checksum twoway 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1"
"checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887"
"checksum typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6"
"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d"
"checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33"
+"checksum unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284b6d3db520d67fbe88fd778c21510d1b0ba4a551e5d0fbb023d33405f6de8a"
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25"
"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
"checksum unsafe-any 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f"
+"checksum url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)" = "cbaa8377a162d88e7d15db0cf110c8523453edcbc5bc66d2b6fffccffa34a068"
"checksum url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f808aadd8cfec6ef90e4a14eb46f24511824d1ac596b9682703c87056c8678b7"
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
+"checksum uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "78c590b5bd79ed10aad8fb75f078a59d8db445af6c743e55c4a53227fc01c13f"
"checksum uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc7e3b898aa6f6c08e5295b6c89258d1331e9ac578cc992fb818759951bdc22"
"checksum vcpkg 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7ed0f6789c8a85ca41bbc1c9d175422116a9869bd1cf31bb08e1493ecce60380"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
+"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
+"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 94a9a091..f35490e5 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -3,20 +3,27 @@ name = "fatcat"
version = "0.1.0"
authors = ["Bryan Newbold <bnewbold@archive.org>"]
+[workspace]
+members = ["fatcat-api"]
+
[dependencies]
+fatcat-api = {version = "*", path = "fatcat-api"}
diesel = { version = "1.0.0", features = ["postgres"] }
dotenv = "0.9.0"
clap = "*"
+error-chain = "0.11"
# API server
chrono = { version = "0.4", features = ["serde"] }
futures = "0.1"
-hyper = "0.10"
-#hyper-openssl = "0.2"
+hyper = "0.11"
+#hyper-tls = {version = "0.1.2", optional = true}
iron = "0.5"
iron-slog = "*"
iron-test = "*"
-swagger = "0.7"
+swagger = "0.10"
+tokio-core = "0.1.6"
+tokio-proto = "0.1.1"
# TODO
#sentry = "*"
diff --git a/rust/fatcat-api/.gitignore b/rust/fatcat-api/.gitignore
new file mode 100644
index 00000000..a9d37c56
--- /dev/null
+++ b/rust/fatcat-api/.gitignore
@@ -0,0 +1,2 @@
+target
+Cargo.lock
diff --git a/rust/fatcat-api/Cargo.toml b/rust/fatcat-api/Cargo.toml
new file mode 100644
index 00000000..8eb4279d
--- /dev/null
+++ b/rust/fatcat-api/Cargo.toml
@@ -0,0 +1,48 @@
+[package]
+name = "fatcat-api"
+version = "0.1.0"
+authors = []
+description = "A scalable, versioned, API-oriented catalog of bibliographic entities and file metadata"
+license = "Unlicense"
+
+[features]
+default = ["client", "server"]
+client = ["serde_json", "serde_ignored", "hyper", "hyper-tls", "native-tls", "openssl", "tokio-core", "url", "uuid"]
+server = ["serde_json", "serde_ignored", "hyper", "hyper-tls", "native-tls", "openssl", "tokio-core", "tokio-proto", "tokio-tls", "regex", "percent-encoding", "url", "uuid"]
+
+[dependencies]
+# Required by example server.
+#
+chrono = { version = "0.4", features = ["serde"] }
+futures = "0.1"
+hyper = {version = "0.11", optional = true}
+hyper-tls = {version = "0.1.2", optional = true}
+swagger = "0.10.0"
+
+# Not required by example server.
+#
+lazy_static = "0.2"
+log = "0.3.0"
+mime = "0.3.3"
+multipart = {version = "0.13.3", optional = true}
+native-tls = {version = "0.1.4", optional = true}
+openssl = {version = "0.9.14", optional = true}
+percent-encoding = {version = "1.0.0", optional = true}
+regex = {version = "0.2", optional = true}
+serde = "1.0"
+serde_derive = "1.0"
+serde_ignored = {version = "0.0.4", optional = true}
+serde_json = {version = "1.0", optional = true}
+serde_urlencoded = {version = "0.5.1", optional = true}
+tokio-core = {version = "0.1.6", optional = true}
+tokio-proto = {version = "0.1.1", optional = true}
+tokio-tls = {version = "0.1.3", optional = true, features = ["tokio-proto"]}
+url = {version = "1.5", optional = true}
+uuid = {version = "0.5", optional = true, features = ["serde", "v4"]}
+# ToDo: this should be updated to point at the official crate once
+# https://github.com/RReverser/serde-xml-rs/pull/45 is accepted upstream
+
+
+[dev-dependencies]
+clap = "2.25"
+error-chain = "0.11"
diff --git a/rust/fatcat-api/README.md b/rust/fatcat-api/README.md
new file mode 100644
index 00000000..5a24942b
--- /dev/null
+++ b/rust/fatcat-api/README.md
@@ -0,0 +1,123 @@
+# Rust API for fatcat
+
+A scalable, versioned, API-oriented catalog of bibliographic entities and file metadata
+
+## Overview
+This client/server was generated by the [swagger-codegen]
+(https://github.com/swagger-api/swagger-codegen) project.
+By using the [OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote server, you can easily generate a server stub.
+-
+
+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-05-15T05:47:34.699Z
+
+This autogenerated project defines an API crate `fatcat` which contains:
+* An `Api` trait defining the API in Rust.
+* Data types representing the underlying data model.
+* A `Client` type which implements `Api` and issues HTTP requests for each operation.
+* A router which accepts HTTP requests and invokes the appropriate `Api` method for each operation.
+
+It also contains an example server and client which make use of `fatcat`:
+* The example server starts up a web server using the `fatcat` router,
+ and supplies a trivial implementation of `Api` which returns failure for every operation.
+* The example client provides a CLI which lets you invoke any single operation on the
+ `fatcat` client by passing appropriate arguments on the command line.
+
+You can use the example server and client as a basis for your own code.
+See below for [more detail on implementing a server](#writing-a-server).
+
+
+## Examples
+
+Run examples with:
+
+```
+cargo run --example <example-name>
+```
+
+To pass in arguments to the examples, put them after `--`, for example:
+
+```
+cargo run --example client -- --help
+```
+
+### Running the server
+To run the server, follow these simple steps:
+
+```
+cargo run --example server
+```
+
+### Running a client
+To run a client, follow one of the following simple steps:
+
+```
+cargo run --example client ContainerIdGet
+cargo run --example client ContainerLookupGet
+cargo run --example client ContainerPost
+cargo run --example client CreatorIdGet
+cargo run --example client CreatorLookupGet
+cargo run --example client CreatorPost
+cargo run --example client EditgroupIdAcceptPost
+cargo run --example client EditgroupIdGet
+cargo run --example client EditgroupPost
+cargo run --example client EditorUsernameChangelogGet
+cargo run --example client EditorUsernameGet
+cargo run --example client FileIdGet
+cargo run --example client FileLookupGet
+cargo run --example client FilePost
+cargo run --example client ReleaseIdGet
+cargo run --example client ReleaseLookupGet
+cargo run --example client ReleasePost
+cargo run --example client WorkIdGet
+cargo run --example client WorkPost
+```
+
+### HTTPS
+The examples can be run in HTTPS mode by passing in the flag `--https`, for example:
+
+```
+cargo run --example server -- --https
+```
+
+This will use the keys/certificates from the examples directory. Note that the server chain is signed with
+`CN=localhost`.
+
+
+## Writing a server
+
+The server example is designed to form the basis for implementing your own server. Simply follow these steps.
+
+* Set up a new Rust project, e.g., with `cargo init --bin`.
+* Insert `fatcat` into the `members` array under [workspace] in the root `Cargo.toml`, e.g., `members = [ "fatcat" ]`.
+* Add `fatcat = {version = "0.1.0", path = "fatcat"}` under `[dependencies]` in the root `Cargo.toml`.
+* Copy the `[dependencies]` and `[dev-dependencies]` from `fatcat/Cargo.toml` into the root `Cargo.toml`'s `[dependencies]` section.
+ * Copy all of the `[dev-dependencies]`, but only the `[dependencies]` that are required by the example server. These should be clearly indicated by comments.
+ * Remove `"optional = true"` from each of these lines if present.
+
+Each autogenerated API will contain an implementation stub and main entry point, which should be copied into your project the first time:
+```
+cp fatcat/examples/server.rs src/main.rs
+cp fatcat/examples/server_lib/mod.rs src/lib.rs
+cp fatcat/examples/server_lib/server.rs src/server.rs
+```
+
+Now
+
+* From `src/main.rs`, remove the `mod server_lib;` line, and uncomment and fill in the `extern crate` line with the name of this server crate.
+* Move the block of imports "required by the service library" from `src/main.rs` to `src/lib.rs` and uncomment.
+* Change the `let server = server::Server {};` line to `let server = SERVICE_NAME::server().unwrap();` where `SERVICE_NAME` is the name of the server crate.
+* Run `cargo build` to check it builds.
+* Run `cargo fmt` to reformat the code.
+* Commit the result before making any further changes (lest format changes get confused with your own updates).
+
+Now replace the implementations in `src/server.rs` with your own code as required.
+
+## Updating your server to track API changes
+
+Later, if the API changes, you can copy new sections from the autogenerated API stub into your implementation.
+Alternatively, implement the now-missing methods based on the compiler's error messages.
diff --git a/rust/fatcat-api/api.yaml b/rust/fatcat-api/api.yaml
new file mode 100644
index 00000000..badb17ab
--- /dev/null
+++ b/rust/fatcat-api/api.yaml
@@ -0,0 +1,464 @@
+---
+swagger: "2.0"
+info:
+ title: fatcat
+ description: A scalable, versioned, API-oriented catalog of bibliographic entities
+ and file metadata
+ version: 0.1.0
+schemes: [http]
+basePath: /v0
+host: api.fatcat.wiki
+consumes:
+ - application/json
+produces:
+ - application/json
+
+
+# Common properties across entities
+x-entity-props: &ENTITYPROPS
+ state:
+ type: string
+ enum: ["wip", "active", "redirect", "deleted"]
+ ident:
+ type: string
+ #format: uuid
+ revision:
+ type: integer
+ redirect:
+ type: string
+ #format: uuid
+ editgroup:
+ type: integer
+x-entity-edit-props: &ENTITYEDITPROPS
+ id:
+ type: integer
+ ident:
+ type: string
+ revision:
+ type: integer
+ editgroup_id:
+ type: integer
+
+
+definitions:
+ error:
+ type: object
+ required:
+ - message
+ properties:
+ message:
+ type: string
+ success:
+ type: object
+ required:
+ - message
+ properties:
+ message:
+ type: string
+ creator_entity:
+ type: object
+ properties:
+ <<: *ENTITYPROPS
+ name:
+ type: string
+ orcid:
+ type: string
+ #format: custom
+ container_entity:
+ type: object
+ properties:
+ <<: *ENTITYPROPS
+ name:
+ type: string
+ parent:
+ type: string
+ publisher:
+ type: string
+ issn:
+ type: string
+ #format: custom
+ file_entity:
+ type: object
+ properties:
+ <<: *ENTITYPROPS
+ size:
+ type: integer
+ sha1:
+ type: string
+ #format: custom
+ url:
+ type: string
+ format: url
+ release_entity:
+ type: object
+ properties:
+ <<: *ENTITYPROPS
+ work:
+ type: string
+ container:
+ type: string
+ license:
+ type: string
+ release_type:
+ type: string
+ date:
+ type: date
+ doi:
+ type: string
+ #format: custom
+ volume:
+ type: string
+ pages:
+ type: string
+ issue:
+ type: string
+ work_entity:
+ type: object
+ properties:
+ <<: *ENTITYPROPS
+ title:
+ type: string
+ work_type:
+ type: string
+ entity_edit:
+ type: object
+ properties:
+ <<: *ENTITYEDITPROPS
+ editor:
+ type: object
+ required:
+ - username
+ properties:
+ username:
+ type: string
+ editgroup:
+ type: object
+ required:
+ - id
+ - editor_id
+ properties:
+ id:
+ type: integer
+ editor_id:
+ type: integer
+ changelogentry:
+ type: object
+ required:
+ - index
+ properties:
+ index:
+ type: integer
+ editgroup_id:
+ type: integer
+ timestamp:
+ type: string
+ format: date-time
+
+x-entity-responses: &ENTITYRESPONSES
+ 400:
+ description: bad request
+ schema:
+ $ref: "#/definitions/error"
+ default:
+ description: generic error response
+ schema:
+ $ref: "#/definitions/error"
+
+paths:
+ /creator:
+ post:
+ parameters:
+ - name: body
+ in: body
+ schema:
+ $ref: "#/definitions/creator_entity"
+ responses:
+ 201:
+ description: created
+ schema:
+ $ref: "#/definitions/entity_edit"
+ <<: *ENTITYRESPONSES
+ /creator/{id}:
+ parameters:
+ - name: id
+ in: path
+ type: string
+ required: true
+ get:
+ responses:
+ 200:
+ description: fetch a single creator by id
+ schema:
+ $ref: "#/definitions/creator_entity"
+ <<: *ENTITYRESPONSES
+ /creator/lookup:
+ get:
+ parameters:
+ - name: orcid
+ in: query
+ type: string
+ required: true
+ responses:
+ 200:
+ description: find a single creator by external identifer
+ schema:
+ $ref: "#/definitions/creator_entity"
+ 404:
+ description: no such creator
+ schema:
+ $ref: "#/definitions/error"
+ <<: *ENTITYRESPONSES
+ /container:
+ post:
+ parameters:
+ - name: body
+ in: body
+ schema:
+ $ref: "#/definitions/container_entity"
+ responses:
+ 201:
+ description: created
+ schema:
+ $ref: "#/definitions/entity_edit"
+ <<: *ENTITYRESPONSES
+ /container/{id}:
+ parameters:
+ - name: id
+ in: path
+ type: string
+ required: true
+ get:
+ responses:
+ 200:
+ description: fetch a single container by id
+ schema:
+ $ref: "#/definitions/container_entity"
+ <<: *ENTITYRESPONSES
+ /container/lookup:
+ get:
+ parameters:
+ - name: issn
+ in: query
+ type: string
+ required: true
+ responses:
+ 200:
+ description: find a single container by external identifer
+ schema:
+ $ref: "#/definitions/container_entity"
+ 404:
+ description: no such container
+ schema:
+ $ref: "#/definitions/error"
+ <<: *ENTITYRESPONSES
+ /file:
+ post:
+ parameters:
+ - name: body
+ in: body
+ schema:
+ $ref: "#/definitions/file_entity"
+ responses:
+ 201:
+ description: created
+ schema:
+ $ref: "#/definitions/entity_edit"
+ <<: *ENTITYRESPONSES
+ /file/{id}:
+ parameters:
+ - name: id
+ in: path
+ type: string
+ required: true
+ get:
+ responses:
+ 200:
+ description: fetch a single file by id
+ schema:
+ $ref: "#/definitions/file_entity"
+ <<: *ENTITYRESPONSES
+ /file/lookup:
+ get:
+ parameters:
+ - name: sha1
+ in: query
+ type: string
+ required: true
+ responses:
+ 200:
+ description: find a single file by external identifer
+ schema:
+ $ref: "#/definitions/file_entity"
+ 404:
+ description: no such file
+ schema:
+ $ref: "#/definitions/error"
+ <<: *ENTITYRESPONSES
+ /release:
+ post:
+ parameters:
+ - name: body
+ in: body
+ schema:
+ $ref: "#/definitions/release_entity"
+ responses:
+ 201:
+ description: created
+ schema:
+ $ref: "#/definitions/entity_edit"
+ <<: *ENTITYRESPONSES
+ /release/{id}:
+ parameters:
+ - name: id
+ in: path
+ type: string
+ required: true
+ get:
+ responses:
+ 200:
+ description: fetch a single release by id
+ schema:
+ $ref: "#/definitions/release_entity"
+ <<: *ENTITYRESPONSES
+ /release/lookup:
+ get:
+ parameters:
+ - name: doi
+ in: query
+ type: string
+ required: true
+ responses:
+ 200:
+ description: find a single release by external identifer
+ schema:
+ $ref: "#/definitions/release_entity"
+ 404:
+ description: no such release
+ schema:
+ $ref: "#/definitions/error"
+ <<: *ENTITYRESPONSES
+ /work:
+ post:
+ parameters:
+ - name: body
+ in: body
+ schema:
+ $ref: "#/definitions/work_entity"
+ responses:
+ 201:
+ description: created
+ schema:
+ $ref: "#/definitions/entity_edit"
+ <<: *ENTITYRESPONSES
+ /work/{id}:
+ parameters:
+ - name: id
+ in: path
+ type: string
+ required: true
+ get:
+ responses:
+ 200:
+ description: fetch a single work by id
+ schema:
+ $ref: "#/definitions/work_entity"
+ <<: *ENTITYRESPONSES
+ /editor/{username}:
+ parameters:
+ - name: username
+ in: path
+ type: string
+ required: true
+ get:
+ responses:
+ 200:
+ description: fetch generic information about an editor
+ schema:
+ $ref: "#/definitions/editor"
+ 404:
+ description: username not found
+ schema:
+ $ref: "#/definitions/error"
+ default:
+ description: generic error response
+ schema:
+ $ref: "#/definitions/error"
+ /editor/{username}/changelog:
+ parameters:
+ - name: username
+ in: path
+ type: string
+ required: true
+ get:
+ responses:
+ 200:
+ description: find changes (editgroups) by this editor which have been merged
+ schema:
+ $ref: "#/definitions/changelogentry"
+ 404:
+ description: username not found
+ schema:
+ $ref: "#/definitions/error"
+ default:
+ description: generic error response
+ schema:
+ $ref: "#/definitions/error"
+ /editgroup:
+ post:
+ responses:
+ 201:
+ description: successfully created
+ schema:
+ $ref: "#/definitions/editgroup"
+ 400:
+ description: invalid request parameters
+ schema:
+ $ref: "#/definitions/error"
+ default:
+ description: generic error response
+ schema:
+ $ref: "#/definitions/error"
+ /editgroup/{id}:
+ parameters:
+ - name: id
+ in: path
+ type: integer
+ required: true
+ get:
+ responses:
+ 200:
+ description: fetch editgroup by identifier
+ schema:
+ $ref: "#/definitions/editgroup"
+ 404:
+ description: no such editgroup
+ schema:
+ $ref: "#/definitions/error"
+ default:
+ description: generic error response
+ schema:
+ $ref: "#/definitions/error"
+ /editgroup/{id}/accept:
+ parameters:
+ - name: id
+ in: path
+ type: integer
+ required: true
+ post:
+ responses:
+ 200:
+ description: merged editgroup successfully ("live")
+ schema:
+ $ref: "#/definitions/success"
+ 400:
+ description: editgroup is in an unmergable state
+ schema:
+ $ref: "#/definitions/error"
+ 404:
+ description: no such editgroup
+ schema:
+ $ref: "#/definitions/error"
+ default:
+ description: generic error response
+ schema:
+ $ref: "#/definitions/error"
diff --git a/rust/fatcat-api/api/swagger.yaml b/rust/fatcat-api/api/swagger.yaml
new file mode 100644
index 00000000..66f77d5b
--- /dev/null
+++ b/rust/fatcat-api/api/swagger.yaml
@@ -0,0 +1,1196 @@
+---
+swagger: "2.0"
+info:
+ description: "A scalable, versioned, API-oriented catalog of bibliographic entities\
+ \ and file metadata"
+ version: "0.1.0"
+ title: "fatcat"
+host: "api.fatcat.wiki"
+basePath: "/v0"
+schemes:
+- "http"
+consumes:
+- "application/json"
+produces:
+- "application/json"
+paths:
+ /creator:
+ post:
+ parameters:
+ - in: "body"
+ name: "body"
+ required: false
+ schema:
+ $ref: "#/definitions/creator_entity"
+ uppercase_data_type: "CREATORENTITY"
+ refName: "creator_entity"
+ formatString: "{:?}"
+ example: "None"
+ model_key: "changelogentry"
+ uppercase_operation_id: "CREATOR_POST"
+ consumesJson: true
+ responses:
+ 201:
+ description: "created"
+ schema:
+ $ref: "#/definitions/entity_edit"
+ x-responseId: "Created"
+ x-uppercaseResponseId: "CREATED"
+ uppercase_operation_id: "CREATOR_POST"
+ uppercase_data_type: "ENTITYEDIT"
+ producesJson: true
+ 400:
+ description: "bad request"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "BadRequest"
+ x-uppercaseResponseId: "BAD_REQUEST"
+ uppercase_operation_id: "CREATOR_POST"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ default:
+ description: "generic error response"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "GenericErrorResponse"
+ x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE"
+ uppercase_operation_id: "CREATOR_POST"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ operation_id: "creator_post"
+ uppercase_operation_id: "CREATOR_POST"
+ path: "/creator"
+ PATH_ID: "CREATOR"
+ hasPathParams: false
+ HttpMethod: "Post"
+ /creator/{id}:
+ get:
+ parameters:
+ - name: "id"
+ in: "path"
+ required: true
+ type: "string"
+ formatString: "\\\"{}\\\""
+ example: "\"id_example\".to_string()"
+ responses:
+ 200:
+ description: "fetch a single creator by id"
+ schema:
+ $ref: "#/definitions/creator_entity"
+ x-responseId: "FetchASingleCreatorById"
+ x-uppercaseResponseId: "FETCH_A_SINGLE_CREATOR_BY_ID"
+ uppercase_operation_id: "CREATOR_ID_GET"
+ uppercase_data_type: "CREATORENTITY"
+ producesJson: true
+ 400:
+ description: "bad request"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "BadRequest"
+ x-uppercaseResponseId: "BAD_REQUEST"
+ uppercase_operation_id: "CREATOR_ID_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ default:
+ description: "generic error response"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "GenericErrorResponse"
+ x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE"
+ uppercase_operation_id: "CREATOR_ID_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ operation_id: "creator_id_get"
+ uppercase_operation_id: "CREATOR_ID_GET"
+ path: "/creator/:id"
+ PATH_ID: "CREATOR_ID"
+ hasPathParams: true
+ HttpMethod: "Get"
+ /creator/lookup:
+ get:
+ parameters:
+ - name: "orcid"
+ in: "query"
+ required: true
+ type: "string"
+ formatString: "\\\"{}\\\""
+ example: "\"orcid_example\".to_string()"
+ responses:
+ 200:
+ description: "find a single creator by external identifer"
+ schema:
+ $ref: "#/definitions/creator_entity"
+ x-responseId: "FindASingleCreatorByExternalIdentifer"
+ x-uppercaseResponseId: "FIND_A_SINGLE_CREATOR_BY_EXTERNAL_IDENTIFER"
+ uppercase_operation_id: "CREATOR_LOOKUP_GET"
+ uppercase_data_type: "CREATORENTITY"
+ producesJson: true
+ 400:
+ description: "bad request"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "BadRequest"
+ x-uppercaseResponseId: "BAD_REQUEST"
+ uppercase_operation_id: "CREATOR_LOOKUP_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ 404:
+ description: "no such creator"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "NoSuchCreator"
+ x-uppercaseResponseId: "NO_SUCH_CREATOR"
+ uppercase_operation_id: "CREATOR_LOOKUP_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ default:
+ description: "generic error response"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "GenericErrorResponse"
+ x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE"
+ uppercase_operation_id: "CREATOR_LOOKUP_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ operation_id: "creator_lookup_get"
+ uppercase_operation_id: "CREATOR_LOOKUP_GET"
+ path: "/creator/lookup"
+ PATH_ID: "CREATOR_LOOKUP"
+ hasPathParams: false
+ HttpMethod: "Get"
+ /container:
+ post:
+ parameters:
+ - in: "body"
+ name: "body"
+ required: false
+ schema:
+ $ref: "#/definitions/container_entity"
+ uppercase_data_type: "CONTAINERENTITY"
+ refName: "container_entity"
+ formatString: "{:?}"
+ example: "None"
+ model_key: "changelogentry"
+ uppercase_operation_id: "CONTAINER_POST"
+ consumesJson: true
+ responses:
+ 201:
+ description: "created"
+ schema:
+ $ref: "#/definitions/entity_edit"
+ x-responseId: "Created"
+ x-uppercaseResponseId: "CREATED"
+ uppercase_operation_id: "CONTAINER_POST"
+ uppercase_data_type: "ENTITYEDIT"
+ producesJson: true
+ 400:
+ description: "bad request"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "BadRequest"
+ x-uppercaseResponseId: "BAD_REQUEST"
+ uppercase_operation_id: "CONTAINER_POST"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ default:
+ description: "generic error response"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "GenericErrorResponse"
+ x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE"
+ uppercase_operation_id: "CONTAINER_POST"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ operation_id: "container_post"
+ uppercase_operation_id: "CONTAINER_POST"
+ path: "/container"
+ PATH_ID: "CONTAINER"
+ hasPathParams: false
+ HttpMethod: "Post"
+ /container/{id}:
+ get:
+ parameters:
+ - name: "id"
+ in: "path"
+ required: true
+ type: "string"
+ formatString: "\\\"{}\\\""
+ example: "\"id_example\".to_string()"
+ responses:
+ 200:
+ description: "fetch a single container by id"
+ schema:
+ $ref: "#/definitions/container_entity"
+ x-responseId: "FetchASingleContainerById"
+ x-uppercaseResponseId: "FETCH_A_SINGLE_CONTAINER_BY_ID"
+ uppercase_operation_id: "CONTAINER_ID_GET"
+ uppercase_data_type: "CONTAINERENTITY"
+ producesJson: true
+ 400:
+ description: "bad request"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "BadRequest"
+ x-uppercaseResponseId: "BAD_REQUEST"
+ uppercase_operation_id: "CONTAINER_ID_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ default:
+ description: "generic error response"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "GenericErrorResponse"
+ x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE"
+ uppercase_operation_id: "CONTAINER_ID_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ operation_id: "container_id_get"
+ uppercase_operation_id: "CONTAINER_ID_GET"
+ path: "/container/:id"
+ PATH_ID: "CONTAINER_ID"
+ hasPathParams: true
+ HttpMethod: "Get"
+ /container/lookup:
+ get:
+ parameters:
+ - name: "issn"
+ in: "query"
+ required: true
+ type: "string"
+ formatString: "\\\"{}\\\""
+ example: "\"issn_example\".to_string()"
+ responses:
+ 200:
+ description: "find a single container by external identifer"
+ schema:
+ $ref: "#/definitions/container_entity"
+ x-responseId: "FindASingleContainerByExternalIdentifer"
+ x-uppercaseResponseId: "FIND_A_SINGLE_CONTAINER_BY_EXTERNAL_IDENTIFER"
+ uppercase_operation_id: "CONTAINER_LOOKUP_GET"
+ uppercase_data_type: "CONTAINERENTITY"
+ producesJson: true
+ 400:
+ description: "bad request"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "BadRequest"
+ x-uppercaseResponseId: "BAD_REQUEST"
+ uppercase_operation_id: "CONTAINER_LOOKUP_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ 404:
+ description: "no such container"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "NoSuchContainer"
+ x-uppercaseResponseId: "NO_SUCH_CONTAINER"
+ uppercase_operation_id: "CONTAINER_LOOKUP_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ default:
+ description: "generic error response"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "GenericErrorResponse"
+ x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE"
+ uppercase_operation_id: "CONTAINER_LOOKUP_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ operation_id: "container_lookup_get"
+ uppercase_operation_id: "CONTAINER_LOOKUP_GET"
+ path: "/container/lookup"
+ PATH_ID: "CONTAINER_LOOKUP"
+ hasPathParams: false
+ HttpMethod: "Get"
+ /file:
+ post:
+ parameters:
+ - in: "body"
+ name: "body"
+ required: false
+ schema:
+ $ref: "#/definitions/file_entity"
+ uppercase_data_type: "FILEENTITY"
+ refName: "file_entity"
+ formatString: "{:?}"
+ example: "None"
+ model_key: "changelogentry"
+ uppercase_operation_id: "FILE_POST"
+ consumesJson: true
+ responses:
+ 201:
+ description: "created"
+ schema:
+ $ref: "#/definitions/entity_edit"
+ x-responseId: "Created"
+ x-uppercaseResponseId: "CREATED"
+ uppercase_operation_id: "FILE_POST"
+ uppercase_data_type: "ENTITYEDIT"
+ producesJson: true
+ 400:
+ description: "bad request"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "BadRequest"
+ x-uppercaseResponseId: "BAD_REQUEST"
+ uppercase_operation_id: "FILE_POST"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ default:
+ description: "generic error response"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "GenericErrorResponse"
+ x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE"
+ uppercase_operation_id: "FILE_POST"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ operation_id: "file_post"
+ uppercase_operation_id: "FILE_POST"
+ path: "/file"
+ PATH_ID: "FILE"
+ hasPathParams: false
+ HttpMethod: "Post"
+ /file/{id}:
+ get:
+ parameters:
+ - name: "id"
+ in: "path"
+ required: true
+ type: "string"
+ formatString: "\\\"{}\\\""
+ example: "\"id_example\".to_string()"
+ responses:
+ 200:
+ description: "fetch a single file by id"
+ schema:
+ $ref: "#/definitions/file_entity"
+ x-responseId: "FetchASingleFileById"
+ x-uppercaseResponseId: "FETCH_A_SINGLE_FILE_BY_ID"
+ uppercase_operation_id: "FILE_ID_GET"
+ uppercase_data_type: "FILEENTITY"
+ producesJson: true
+ 400:
+ description: "bad request"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "BadRequest"
+ x-uppercaseResponseId: "BAD_REQUEST"
+ uppercase_operation_id: "FILE_ID_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ default:
+ description: "generic error response"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "GenericErrorResponse"
+ x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE"
+ uppercase_operation_id: "FILE_ID_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ operation_id: "file_id_get"
+ uppercase_operation_id: "FILE_ID_GET"
+ path: "/file/:id"
+ PATH_ID: "FILE_ID"
+ hasPathParams: true
+ HttpMethod: "Get"
+ /file/lookup:
+ get:
+ parameters:
+ - name: "sha1"
+ in: "query"
+ required: true
+ type: "string"
+ formatString: "\\\"{}\\\""
+ example: "\"sha1_example\".to_string()"
+ responses:
+ 200:
+ description: "find a single file by external identifer"
+ schema:
+ $ref: "#/definitions/file_entity"
+ x-responseId: "FindASingleFileByExternalIdentifer"
+ x-uppercaseResponseId: "FIND_A_SINGLE_FILE_BY_EXTERNAL_IDENTIFER"
+ uppercase_operation_id: "FILE_LOOKUP_GET"
+ uppercase_data_type: "FILEENTITY"
+ producesJson: true
+ 400:
+ description: "bad request"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "BadRequest"
+ x-uppercaseResponseId: "BAD_REQUEST"
+ uppercase_operation_id: "FILE_LOOKUP_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ 404:
+ description: "no such file"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "NoSuchFile"
+ x-uppercaseResponseId: "NO_SUCH_FILE"
+ uppercase_operation_id: "FILE_LOOKUP_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ default:
+ description: "generic error response"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "GenericErrorResponse"
+ x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE"
+ uppercase_operation_id: "FILE_LOOKUP_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ operation_id: "file_lookup_get"
+ uppercase_operation_id: "FILE_LOOKUP_GET"
+ path: "/file/lookup"
+ PATH_ID: "FILE_LOOKUP"
+ hasPathParams: false
+ HttpMethod: "Get"
+ /release:
+ post:
+ parameters:
+ - in: "body"
+ name: "body"
+ required: false
+ schema:
+ $ref: "#/definitions/release_entity"
+ uppercase_data_type: "RELEASEENTITY"
+ refName: "release_entity"
+ formatString: "{:?}"
+ example: "None"
+ model_key: "changelogentry"
+ uppercase_operation_id: "RELEASE_POST"
+ consumesJson: true
+ responses:
+ 201:
+ description: "created"
+ schema:
+ $ref: "#/definitions/entity_edit"
+ x-responseId: "Created"
+ x-uppercaseResponseId: "CREATED"
+ uppercase_operation_id: "RELEASE_POST"
+ uppercase_data_type: "ENTITYEDIT"
+ producesJson: true
+ 400:
+ description: "bad request"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "BadRequest"
+ x-uppercaseResponseId: "BAD_REQUEST"
+ uppercase_operation_id: "RELEASE_POST"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ default:
+ description: "generic error response"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "GenericErrorResponse"
+ x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE"
+ uppercase_operation_id: "RELEASE_POST"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ operation_id: "release_post"
+ uppercase_operation_id: "RELEASE_POST"
+ path: "/release"
+ PATH_ID: "RELEASE"
+ hasPathParams: false
+ HttpMethod: "Post"
+ /release/{id}:
+ get:
+ parameters:
+ - name: "id"
+ in: "path"
+ required: true
+ type: "string"
+ formatString: "\\\"{}\\\""
+ example: "\"id_example\".to_string()"
+ responses:
+ 200:
+ description: "fetch a single release by id"
+ schema:
+ $ref: "#/definitions/release_entity"
+ x-responseId: "FetchASingleReleaseById"
+ x-uppercaseResponseId: "FETCH_A_SINGLE_RELEASE_BY_ID"
+ uppercase_operation_id: "RELEASE_ID_GET"
+ uppercase_data_type: "RELEASEENTITY"
+ producesJson: true
+ 400:
+ description: "bad request"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "BadRequest"
+ x-uppercaseResponseId: "BAD_REQUEST"
+ uppercase_operation_id: "RELEASE_ID_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ default:
+ description: "generic error response"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "GenericErrorResponse"
+ x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE"
+ uppercase_operation_id: "RELEASE_ID_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ operation_id: "release_id_get"
+ uppercase_operation_id: "RELEASE_ID_GET"
+ path: "/release/:id"
+ PATH_ID: "RELEASE_ID"
+ hasPathParams: true
+ HttpMethod: "Get"
+ /release/lookup:
+ get:
+ parameters:
+ - name: "doi"
+ in: "query"
+ required: true
+ type: "string"
+ formatString: "\\\"{}\\\""
+ example: "\"doi_example\".to_string()"
+ responses:
+ 200:
+ description: "find a single release by external identifer"
+ schema:
+ $ref: "#/definitions/release_entity"
+ x-responseId: "FindASingleReleaseByExternalIdentifer"
+ x-uppercaseResponseId: "FIND_A_SINGLE_RELEASE_BY_EXTERNAL_IDENTIFER"
+ uppercase_operation_id: "RELEASE_LOOKUP_GET"
+ uppercase_data_type: "RELEASEENTITY"
+ producesJson: true
+ 400:
+ description: "bad request"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "BadRequest"
+ x-uppercaseResponseId: "BAD_REQUEST"
+ uppercase_operation_id: "RELEASE_LOOKUP_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ 404:
+ description: "no such release"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "NoSuchRelease"
+ x-uppercaseResponseId: "NO_SUCH_RELEASE"
+ uppercase_operation_id: "RELEASE_LOOKUP_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ default:
+ description: "generic error response"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "GenericErrorResponse"
+ x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE"
+ uppercase_operation_id: "RELEASE_LOOKUP_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ operation_id: "release_lookup_get"
+ uppercase_operation_id: "RELEASE_LOOKUP_GET"
+ path: "/release/lookup"
+ PATH_ID: "RELEASE_LOOKUP"
+ hasPathParams: false
+ HttpMethod: "Get"
+ /work:
+ post:
+ parameters:
+ - in: "body"
+ name: "body"
+ required: false
+ schema:
+ $ref: "#/definitions/work_entity"
+ uppercase_data_type: "WORKENTITY"
+ refName: "work_entity"
+ formatString: "{:?}"
+ example: "None"
+ model_key: "changelogentry"
+ uppercase_operation_id: "WORK_POST"
+ consumesJson: true
+ responses:
+ 201:
+ description: "created"
+ schema:
+ $ref: "#/definitions/entity_edit"
+ x-responseId: "Created"
+ x-uppercaseResponseId: "CREATED"
+ uppercase_operation_id: "WORK_POST"
+ uppercase_data_type: "ENTITYEDIT"
+ producesJson: true
+ 400:
+ description: "bad request"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "BadRequest"
+ x-uppercaseResponseId: "BAD_REQUEST"
+ uppercase_operation_id: "WORK_POST"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ default:
+ description: "generic error response"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "GenericErrorResponse"
+ x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE"
+ uppercase_operation_id: "WORK_POST"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ operation_id: "work_post"
+ uppercase_operation_id: "WORK_POST"
+ path: "/work"
+ PATH_ID: "WORK"
+ hasPathParams: false
+ HttpMethod: "Post"
+ /work/{id}:
+ get:
+ parameters:
+ - name: "id"
+ in: "path"
+ required: true
+ type: "string"
+ formatString: "\\\"{}\\\""
+ example: "\"id_example\".to_string()"
+ responses:
+ 200:
+ description: "fetch a single work by id"
+ schema:
+ $ref: "#/definitions/work_entity"
+ x-responseId: "FetchASingleWorkById"
+ x-uppercaseResponseId: "FETCH_A_SINGLE_WORK_BY_ID"
+ uppercase_operation_id: "WORK_ID_GET"
+ uppercase_data_type: "WORKENTITY"
+ producesJson: true
+ 400:
+ description: "bad request"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "BadRequest"
+ x-uppercaseResponseId: "BAD_REQUEST"
+ uppercase_operation_id: "WORK_ID_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ default:
+ description: "generic error response"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "GenericErrorResponse"
+ x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE"
+ uppercase_operation_id: "WORK_ID_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ operation_id: "work_id_get"
+ uppercase_operation_id: "WORK_ID_GET"
+ path: "/work/:id"
+ PATH_ID: "WORK_ID"
+ hasPathParams: true
+ HttpMethod: "Get"
+ /editor/{username}:
+ get:
+ parameters:
+ - name: "username"
+ in: "path"
+ required: true
+ type: "string"
+ formatString: "\\\"{}\\\""
+ example: "\"username_example\".to_string()"
+ responses:
+ 200:
+ description: "fetch generic information about an editor"
+ schema:
+ $ref: "#/definitions/editor"
+ x-responseId: "FetchGenericInformationAboutAnEditor"
+ x-uppercaseResponseId: "FETCH_GENERIC_INFORMATION_ABOUT_AN_EDITOR"
+ uppercase_operation_id: "EDITOR_USERNAME_GET"
+ uppercase_data_type: "EDITOR"
+ producesJson: true
+ 404:
+ description: "username not found"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "UsernameNotFound"
+ x-uppercaseResponseId: "USERNAME_NOT_FOUND"
+ uppercase_operation_id: "EDITOR_USERNAME_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ default:
+ description: "generic error response"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "GenericErrorResponse"
+ x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE"
+ uppercase_operation_id: "EDITOR_USERNAME_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ operation_id: "editor_username_get"
+ uppercase_operation_id: "EDITOR_USERNAME_GET"
+ path: "/editor/:username"
+ PATH_ID: "EDITOR_USERNAME"
+ hasPathParams: true
+ HttpMethod: "Get"
+ /editor/{username}/changelog:
+ get:
+ parameters:
+ - name: "username"
+ in: "path"
+ required: true
+ type: "string"
+ formatString: "\\\"{}\\\""
+ example: "\"username_example\".to_string()"
+ responses:
+ 200:
+ description: "find changes (editgroups) by this editor which have been merged"
+ schema:
+ $ref: "#/definitions/changelogentry"
+ x-responseId: "FindChanges_"
+ x-uppercaseResponseId: "FIND_CHANGES_"
+ uppercase_operation_id: "EDITOR_USERNAME_CHANGELOG_GET"
+ uppercase_data_type: "CHANGELOGENTRY"
+ producesJson: true
+ 404:
+ description: "username not found"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "UsernameNotFound"
+ x-uppercaseResponseId: "USERNAME_NOT_FOUND"
+ uppercase_operation_id: "EDITOR_USERNAME_CHANGELOG_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ default:
+ description: "generic error response"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "GenericErrorResponse"
+ x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE"
+ uppercase_operation_id: "EDITOR_USERNAME_CHANGELOG_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ operation_id: "editor_username_changelog_get"
+ uppercase_operation_id: "EDITOR_USERNAME_CHANGELOG_GET"
+ path: "/editor/:username/changelog"
+ PATH_ID: "EDITOR_USERNAME_CHANGELOG"
+ hasPathParams: true
+ HttpMethod: "Get"
+ /editgroup:
+ post:
+ parameters: []
+ responses:
+ 201:
+ description: "successfully created"
+ schema:
+ $ref: "#/definitions/editgroup"
+ x-responseId: "SuccessfullyCreated"
+ x-uppercaseResponseId: "SUCCESSFULLY_CREATED"
+ uppercase_operation_id: "EDITGROUP_POST"
+ uppercase_data_type: "EDITGROUP"
+ producesJson: true
+ 400:
+ description: "invalid request parameters"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "InvalidRequestParameters"
+ x-uppercaseResponseId: "INVALID_REQUEST_PARAMETERS"
+ uppercase_operation_id: "EDITGROUP_POST"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ default:
+ description: "generic error response"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "GenericErrorResponse"
+ x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE"
+ uppercase_operation_id: "EDITGROUP_POST"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ operation_id: "editgroup_post"
+ uppercase_operation_id: "EDITGROUP_POST"
+ path: "/editgroup"
+ PATH_ID: "EDITGROUP"
+ hasPathParams: false
+ HttpMethod: "Post"
+ /editgroup/{id}:
+ get:
+ parameters:
+ - name: "id"
+ in: "path"
+ required: true
+ type: "integer"
+ formatString: "{}"
+ example: "56"
+ responses:
+ 200:
+ description: "fetch editgroup by identifier"
+ schema:
+ $ref: "#/definitions/editgroup"
+ x-responseId: "FetchEditgroupByIdentifier"
+ x-uppercaseResponseId: "FETCH_EDITGROUP_BY_IDENTIFIER"
+ uppercase_operation_id: "EDITGROUP_ID_GET"
+ uppercase_data_type: "EDITGROUP"
+ producesJson: true
+ 404:
+ description: "no such editgroup"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "NoSuchEditgroup"
+ x-uppercaseResponseId: "NO_SUCH_EDITGROUP"
+ uppercase_operation_id: "EDITGROUP_ID_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ default:
+ description: "generic error response"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "GenericErrorResponse"
+ x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE"
+ uppercase_operation_id: "EDITGROUP_ID_GET"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ operation_id: "editgroup_id_get"
+ uppercase_operation_id: "EDITGROUP_ID_GET"
+ path: "/editgroup/:id"
+ PATH_ID: "EDITGROUP_ID"
+ hasPathParams: true
+ HttpMethod: "Get"
+ /editgroup/{id}/accept:
+ post:
+ parameters:
+ - name: "id"
+ in: "path"
+ required: true
+ type: "integer"
+ formatString: "{}"
+ example: "56"
+ responses:
+ 200:
+ description: "merged editgroup successfully (\"live\")"
+ schema:
+ $ref: "#/definitions/success"
+ x-responseId: "MergedEditgroupSuccessfully_"
+ x-uppercaseResponseId: "MERGED_EDITGROUP_SUCCESSFULLY_"
+ uppercase_operation_id: "EDITGROUP_ID_ACCEPT_POST"
+ uppercase_data_type: "SUCCESS"
+ producesJson: true
+ 400:
+ description: "editgroup is in an unmergable state"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "EditgroupIsInAnUnmergableState"
+ x-uppercaseResponseId: "EDITGROUP_IS_IN_AN_UNMERGABLE_STATE"
+ uppercase_operation_id: "EDITGROUP_ID_ACCEPT_POST"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ 404:
+ description: "no such editgroup"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "NoSuchEditgroup"
+ x-uppercaseResponseId: "NO_SUCH_EDITGROUP"
+ uppercase_operation_id: "EDITGROUP_ID_ACCEPT_POST"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ default:
+ description: "generic error response"
+ schema:
+ $ref: "#/definitions/error"
+ x-responseId: "GenericErrorResponse"
+ x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE"
+ uppercase_operation_id: "EDITGROUP_ID_ACCEPT_POST"
+ uppercase_data_type: "ERROR"
+ producesJson: true
+ operation_id: "editgroup_id_accept_post"
+ uppercase_operation_id: "EDITGROUP_ID_ACCEPT_POST"
+ path: "/editgroup/:id/accept"
+ PATH_ID: "EDITGROUP_ID_ACCEPT"
+ hasPathParams: true
+ HttpMethod: "Post"
+definitions:
+ error:
+ type: "object"
+ required:
+ - "message"
+ properties:
+ message:
+ type: "string"
+ upperCaseName: "ERROR"
+ success:
+ type: "object"
+ required:
+ - "message"
+ properties:
+ message:
+ type: "string"
+ example:
+ message: "message"
+ upperCaseName: "SUCCESS"
+ creator_entity:
+ type: "object"
+ properties:
+ orcid:
+ type: "string"
+ name:
+ type: "string"
+ editgroup:
+ type: "integer"
+ redirect:
+ type: "string"
+ revision:
+ type: "integer"
+ ident:
+ type: "string"
+ state:
+ type: "string"
+ enum:
+ - "wip"
+ - "active"
+ - "redirect"
+ - "deleted"
+ example:
+ redirect: "redirect"
+ editgroup: 0
+ ident: "ident"
+ name: "name"
+ orcid: "orcid"
+ state: "wip"
+ revision: 6
+ upperCaseName: "CREATOR_ENTITY"
+ container_entity:
+ type: "object"
+ properties:
+ issn:
+ type: "string"
+ publisher:
+ type: "string"
+ parent:
+ type: "string"
+ name:
+ type: "string"
+ state:
+ type: "string"
+ enum:
+ - "wip"
+ - "active"
+ - "redirect"
+ - "deleted"
+ ident:
+ type: "string"
+ revision:
+ type: "integer"
+ redirect:
+ type: "string"
+ editgroup:
+ type: "integer"
+ example:
+ redirect: "redirect"
+ parent: "parent"
+ editgroup: 6
+ issn: "issn"
+ ident: "ident"
+ name: "name"
+ publisher: "publisher"
+ state: "wip"
+ revision: 0
+ upperCaseName: "CONTAINER_ENTITY"
+ file_entity:
+ type: "object"
+ properties:
+ url:
+ type: "string"
+ format: "url"
+ sha1:
+ type: "string"
+ size:
+ type: "integer"
+ editgroup:
+ type: "integer"
+ redirect:
+ type: "string"
+ revision:
+ type: "integer"
+ ident:
+ type: "string"
+ state:
+ type: "string"
+ enum:
+ - "wip"
+ - "active"
+ - "redirect"
+ - "deleted"
+ example:
+ sha1: "sha1"
+ redirect: "redirect"
+ editgroup: 6
+ size: 0
+ ident: "ident"
+ state: "wip"
+ url: "http://example.com/aeiou"
+ revision: 1
+ upperCaseName: "FILE_ENTITY"
+ release_entity:
+ type: "object"
+ properties:
+ issue:
+ type: "string"
+ pages:
+ type: "string"
+ volume:
+ type: "string"
+ doi:
+ type: "string"
+ release_type:
+ type: "string"
+ license:
+ type: "string"
+ container:
+ type: "string"
+ work:
+ type: "string"
+ state:
+ type: "string"
+ enum:
+ - "wip"
+ - "active"
+ - "redirect"
+ - "deleted"
+ ident:
+ type: "string"
+ revision:
+ type: "integer"
+ redirect:
+ type: "string"
+ editgroup:
+ type: "integer"
+ example:
+ container: "container"
+ redirect: "redirect"
+ editgroup: 6
+ issue: "issue"
+ work: "work"
+ ident: "ident"
+ release_type: "release_type"
+ revision: 0
+ volume: "volume"
+ license: "license"
+ pages: "pages"
+ state: "wip"
+ doi: "doi"
+ upperCaseName: "RELEASE_ENTITY"
+ work_entity:
+ type: "object"
+ properties:
+ work_type:
+ type: "string"
+ title:
+ type: "string"
+ editgroup:
+ type: "integer"
+ redirect:
+ type: "string"
+ revision:
+ type: "integer"
+ ident:
+ type: "string"
+ state:
+ type: "string"
+ enum:
+ - "wip"
+ - "active"
+ - "redirect"
+ - "deleted"
+ example:
+ redirect: "redirect"
+ editgroup: 0
+ ident: "ident"
+ work_type: "work_type"
+ state: "wip"
+ title: "title"
+ revision: 6
+ upperCaseName: "WORK_ENTITY"
+ entity_edit:
+ type: "object"
+ properties:
+ editgroup_id:
+ type: "integer"
+ revision:
+ type: "integer"
+ ident:
+ type: "string"
+ id:
+ type: "integer"
+ example:
+ ident: "ident"
+ editgroup_id: 0
+ id: 1
+ revision: 6
+ upperCaseName: "ENTITY_EDIT"
+ editor:
+ type: "object"
+ required:
+ - "username"
+ properties:
+ username:
+ type: "string"
+ example:
+ username: "username"
+ upperCaseName: "EDITOR"
+ editgroup:
+ type: "object"
+ required:
+ - "editor_id"
+ - "id"
+ properties:
+ id:
+ type: "integer"
+ editor_id:
+ type: "integer"
+ example:
+ editor_id: 6
+ id: 0
+ upperCaseName: "EDITGROUP"
+ changelogentry:
+ type: "object"
+ required:
+ - "index"
+ properties:
+ index:
+ type: "integer"
+ editgroup_id:
+ type: "integer"
+ timestamp:
+ type: "string"
+ format: "date-time"
+ example:
+ index: 0
+ editgroup_id: 6
+ timestamp: "2000-01-23T04:56:07.000+00:00"
+ upperCaseName: "CHANGELOGENTRY"
+x-entity-props:
+ state:
+ type: "string"
+ enum:
+ - "wip"
+ - "active"
+ - "redirect"
+ - "deleted"
+ ident:
+ type: "string"
+ revision:
+ type: "integer"
+ redirect:
+ type: "string"
+ editgroup:
+ type: "integer"
+x-entity-edit-props:
+ id:
+ type: "integer"
+ ident:
+ type: "string"
+ revision:
+ type: "integer"
+ editgroup_id:
+ type: "integer"
+x-entity-responses:
+ 400:
+ description: "bad request"
+ schema:
+ $ref: "#/definitions/error"
+ default:
+ description: "generic error response"
+ schema:
+ $ref: "#/definitions/error"
diff --git a/rust/fatcat-api/examples/ca.pem b/rust/fatcat-api/examples/ca.pem
new file mode 100644
index 00000000..d2317fb5
--- /dev/null
+++ b/rust/fatcat-api/examples/ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICtjCCAZ4CCQDpKecRERZ0xDANBgkqhkiG9w0BAQsFADAdMQswCQYDVQQGEwJV
+UzEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwNTIzMTYwMDIzWhcNMTcwNjIyMTYwMDIz
+WjAdMQswCQYDVQQGEwJVUzEOMAwGA1UEAxMFTXkgQ0EwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCt66py3x7sCSASRF2D05L5wkNDxAUjQKYx23W8Gbwv
+GMGykk89BIdU5LX1JB1cKiUOkoIxfwAYuWc2V/wzTvVV7+11besnk3uX1c9KiqUF
+LIX7kn/z5hzS4aelhKvH+MJlSZCSlp1ytpZbwo5GB5Pi2SGH56jDBiBoDRNBVdWL
+z4wH7TdrQjqWwNxIZumD5OGMtcfJyuX08iPiEOaslOeoMqzObhvjc9aUgjVjhqyA
+FkJGTXsi0oaD7oml+NE+mTNfEeZvEJQpLSjBY0OvQHzuHkyGBShBnfu/9x7/NRwd
+WaqsLiF7/re9KDGYdJwP7Cu6uxYfKAyWarp6h2mG/GIdAgMBAAEwDQYJKoZIhvcN
+AQELBQADggEBAGIl/VVIafeq/AJOQ9r7TzzB2ABJYr7NZa6bTu5O1jSp1Fonac15
+SZ8gvRxODgH22ZYSqghPG4xzq4J3hkytlQqm57ZEt2I2M3OqIp17Ndcc1xDYzpLl
+tA0FrVn6crQTM8vQkTDtGesaCWX+7Fir5dK7HnYWzfpSmsOpST07PfbNisEXKOxG
+Dj4lBL1OnhTjsJeymVS1pFvkKkrcEJO+IxFiHL3CDsWjcXB0Z+E1zBtPoYyYsNsO
+rBrjUxcZewF4xqWZhpW90Mt61fY2nRgU0uUwHcvDQUqvmzKcsqYa4mPKzfBI5mxo
+01Ta96cDD6pS5Y1hOflZ0g84f2g/7xBLLDA=
+-----END CERTIFICATE-----
diff --git a/rust/fatcat-api/examples/client.rs b/rust/fatcat-api/examples/client.rs
new file mode 100644
index 00000000..6f7aa151
--- /dev/null
+++ b/rust/fatcat-api/examples/client.rs
@@ -0,0 +1,350 @@
+#![allow(missing_docs, unused_variables, trivial_casts)]
+
+extern crate clap;
+extern crate fatcat;
+#[allow(unused_extern_crates)]
+extern crate futures;
+#[allow(unused_extern_crates)]
+extern crate swagger;
+extern crate tokio_core;
+#[allow(unused_extern_crates)]
+extern crate uuid;
+
+use clap::{App, Arg};
+#[allow(unused_imports)]
+use fatcat::{ApiError, ApiNoContext, ContainerIdGetResponse, ContainerLookupGetResponse,
+ ContainerPostResponse, ContextWrapperExt, CreatorIdGetResponse,
+ CreatorLookupGetResponse, CreatorPostResponse, EditgroupIdAcceptPostResponse,
+ EditgroupIdGetResponse, EditgroupPostResponse, EditorUsernameChangelogGetResponse,
+ EditorUsernameGetResponse, FileIdGetResponse, FileLookupGetResponse,
+ FilePostResponse, ReleaseIdGetResponse, ReleaseLookupGetResponse,
+ ReleasePostResponse, WorkIdGetResponse, WorkPostResponse};
+#[allow(unused_imports)]
+use futures::{future, stream, Future, Stream};
+use tokio_core::reactor;
+
+fn main() {
+ let matches = App::new("client")
+ .arg(
+ Arg::with_name("operation")
+ .help("Sets the operation to run")
+ .possible_values(&[
+ "ContainerIdGet",
+ "ContainerLookupGet",
+ "ContainerPost",
+ "CreatorIdGet",
+ "CreatorLookupGet",
+ "CreatorPost",
+ "EditgroupIdAcceptPost",
+ "EditgroupIdGet",
+ "EditgroupPost",
+ "EditorUsernameChangelogGet",
+ "EditorUsernameGet",
+ "FileIdGet",
+ "FileLookupGet",
+ "FilePost",
+ "ReleaseIdGet",
+ "ReleaseLookupGet",
+ "ReleasePost",
+ "WorkIdGet",
+ "WorkPost",
+ ])
+ .required(true)
+ .index(1),
+ )
+ .arg(
+ Arg::with_name("https")
+ .long("https")
+ .help("Whether to use HTTPS or not"),
+ )
+ .arg(
+ Arg::with_name("host")
+ .long("host")
+ .takes_value(true)
+ .default_value("api.fatcat.wiki")
+ .help("Hostname to contact"),
+ )
+ .arg(
+ Arg::with_name("port")
+ .long("port")
+ .takes_value(true)
+ .default_value("8080")
+ .help("Port to contact"),
+ )
+ .get_matches();
+
+ let mut core = reactor::Core::new().unwrap();
+ let is_https = matches.is_present("https");
+ let base_url = format!(
+ "{}://{}:{}",
+ if is_https { "https" } else { "http" },
+ matches.value_of("host").unwrap(),
+ matches.value_of("port").unwrap()
+ );
+ let client = if matches.is_present("https") {
+ // Using Simple HTTPS
+ fatcat::Client::try_new_https(core.handle(), &base_url, "examples/ca.pem")
+ .expect("Failed to create HTTPS client")
+ } else {
+ // Using HTTP
+ fatcat::Client::try_new_http(core.handle(), &base_url)
+ .expect("Failed to create HTTP client")
+ };
+
+ // Using a non-default `Context` is not required; this is just an example!
+ let client = client.with_context(fatcat::Context::new_with_span_id(
+ self::uuid::Uuid::new_v4().to_string(),
+ ));
+
+ match matches.value_of("operation") {
+ Some("ContainerIdGet") => {
+ let result = core.run(client.container_id_get("id_example".to_string()));
+ println!(
+ "{:?} (X-Span-ID: {:?})",
+ result,
+ client
+ .context()
+ .x_span_id
+ .clone()
+ .unwrap_or(String::from("<none>"))
+ );
+ }
+
+ Some("ContainerLookupGet") => {
+ let result = core.run(client.container_lookup_get("issn_example".to_string()));
+ println!(
+ "{:?} (X-Span-ID: {:?})",
+ result,
+ client
+ .context()
+ .x_span_id
+ .clone()
+ .unwrap_or(String::from("<none>"))
+ );
+ }
+
+ Some("ContainerPost") => {
+ let result = core.run(client.container_post(None));
+ println!(
+ "{:?} (X-Span-ID: {:?})",
+ result,
+ client
+ .context()
+ .x_span_id
+ .clone()
+ .unwrap_or(String::from("<none>"))
+ );
+ }
+
+ Some("CreatorIdGet") => {
+ let result = core.run(client.creator_id_get("id_example".to_string()));
+ println!(
+ "{:?} (X-Span-ID: {:?})",
+ result,
+ client
+ .context()
+ .x_span_id
+ .clone()
+ .unwrap_or(String::from("<none>"))
+ );
+ }
+
+ Some("CreatorLookupGet") => {
+ let result = core.run(client.creator_lookup_get("orcid_example".to_string()));
+ println!(
+ "{:?} (X-Span-ID: {:?})",
+ result,
+ client
+ .context()
+ .x_span_id
+ .clone()
+ .unwrap_or(String::from("<none>"))
+ );
+ }
+
+ Some("CreatorPost") => {
+ let result = core.run(client.creator_post(None));
+ println!(
+ "{:?} (X-Span-ID: {:?})",
+ result,
+ client
+ .context()
+ .x_span_id
+ .clone()
+ .unwrap_or(String::from("<none>"))
+ );
+ }
+
+ Some("EditgroupIdAcceptPost") => {
+ let result = core.run(client.editgroup_id_accept_post(56));
+ println!(
+ "{:?} (X-Span-ID: {:?})",
+ result,
+ client
+ .context()
+ .x_span_id
+ .clone()
+ .unwrap_or(String::from("<none>"))
+ );
+ }
+
+ Some("EditgroupIdGet") => {
+ let result = core.run(client.editgroup_id_get(56));
+ println!(
+ "{:?} (X-Span-ID: {:?})",
+ result,
+ client
+ .context()
+ .x_span_id
+ .clone()
+ .unwrap_or(String::from("<none>"))
+ );
+ }
+
+ Some("EditgroupPost") => {
+ let result = core.run(client.editgroup_post());
+ println!(
+ "{:?} (X-Span-ID: {:?})",
+ result,
+ client
+ .context()
+ .x_span_id
+ .clone()
+ .unwrap_or(String::from("<none>"))
+ );
+ }
+
+ Some("EditorUsernameChangelogGet") => {
+ let result =
+ core.run(client.editor_username_changelog_get("username_example".to_string()));
+ println!(
+ "{:?} (X-Span-ID: {:?})",
+ result,
+ client
+ .context()
+ .x_span_id
+ .clone()
+ .unwrap_or(String::from("<none>"))
+ );
+ }
+
+ Some("EditorUsernameGet") => {
+ let result = core.run(client.editor_username_get("username_example".to_string()));
+ println!(
+ "{:?} (X-Span-ID: {:?})",
+ result,
+ client
+ .context()
+ .x_span_id
+ .clone()
+ .unwrap_or(String::from("<none>"))
+ );
+ }
+
+ Some("FileIdGet") => {
+ let result = core.run(client.file_id_get("id_example".to_string()));
+ println!(
+ "{:?} (X-Span-ID: {:?})",
+ result,
+ client
+ .context()
+ .x_span_id
+ .clone()
+ .unwrap_or(String::from("<none>"))
+ );
+ }
+
+ Some("FileLookupGet") => {
+ let result = core.run(client.file_lookup_get("sha1_example".to_string()));
+ println!(
+ "{:?} (X-Span-ID: {:?})",
+ result,
+ client
+ .context()
+ .x_span_id
+ .clone()
+ .unwrap_or(String::from("<none>"))
+ );
+ }
+
+ Some("FilePost") => {
+ let result = core.run(client.file_post(None));
+ println!(
+ "{:?} (X-Span-ID: {:?})",
+ result,
+ client
+ .context()
+ .x_span_id
+ .clone()
+ .unwrap_or(String::from("<none>"))
+ );
+ }
+
+ Some("ReleaseIdGet") => {
+ let result = core.run(client.release_id_get("id_example".to_string()));
+ println!(
+ "{:?} (X-Span-ID: {:?})",
+ result,
+ client
+ .context()
+ .x_span_id
+ .clone()
+ .unwrap_or(String::from("<none>"))
+ );
+ }
+
+ Some("ReleaseLookupGet") => {
+ let result = core.run(client.release_lookup_get("doi_example".to_string()));
+ println!(
+ "{:?} (X-Span-ID: {:?})",
+ result,
+ client
+ .context()
+ .x_span_id
+ .clone()
+ .unwrap_or(String::from("<none>"))
+ );
+ }
+
+ Some("ReleasePost") => {
+ let result = core.run(client.release_post(None));
+ println!(
+ "{:?} (X-Span-ID: {:?})",
+ result,
+ client
+ .context()
+ .x_span_id
+ .clone()
+ .unwrap_or(String::from("<none>"))
+ );
+ }
+
+ Some("WorkIdGet") => {
+ let result = core.run(client.work_id_get("id_example".to_string()));
+ println!(
+ "{:?} (X-Span-ID: {:?})",
+ result,
+ client
+ .context()
+ .x_span_id
+ .clone()
+ .unwrap_or(String::from("<none>"))
+ );
+ }
+
+ Some("WorkPost") => {
+ let result = core.run(client.work_post(None));
+ println!(
+ "{:?} (X-Span-ID: {:?})",
+ result,
+ client
+ .context()
+ .x_span_id
+ .clone()
+ .unwrap_or(String::from("<none>"))
+ );
+ }
+
+ _ => panic!("Invalid operation provided"),
+ }
+}
diff --git a/rust/fatcat-api/examples/server-chain.pem b/rust/fatcat-api/examples/server-chain.pem
new file mode 100644
index 00000000..47d7e201
--- /dev/null
+++ b/rust/fatcat-api/examples/server-chain.pem
@@ -0,0 +1,66 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 4096 (0x1000)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, CN=My CA
+ Validity
+ Not Before: May 23 16:00:23 2017 GMT
+ Not After : Apr 29 16:00:23 2117 GMT
+ Subject: CN=localhost, C=US
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c9:d4:43:60:50:fc:d6:0f:38:4d:5d:5e:aa:7c:
+ c0:5e:a9:ec:d9:93:78:d3:93:72:28:41:f5:08:a5:
+ ea:ac:67:07:d7:1f:f7:7d:74:69:7e:46:89:20:4b:
+ 7a:2d:9b:02:08:e7:6f:0f:1d:0c:0f:c7:60:69:19:
+ 4b:df:7e:ca:75:94:0b:49:71:e3:6d:f2:e8:79:fd:
+ ed:0a:94:67:55:f3:ca:6b:61:ba:58:b7:2e:dd:7b:
+ ca:b9:02:9f:24:36:ac:26:8f:04:8f:81:c8:35:10:
+ f4:aa:33:b2:24:16:f8:f7:1e:ea:f7:16:fe:fa:34:
+ c3:dd:bb:2c:ba:7a:df:4d:e2:da:1e:e5:d2:28:44:
+ 6e:c8:96:e0:fd:09:0c:14:0c:31:dc:e0:ca:c1:a7:
+ 9b:bf:16:8c:f7:36:3f:1b:2e:dd:90:eb:45:78:51:
+ bf:59:22:1e:c6:8c:0a:69:88:e5:03:5e:73:b7:fc:
+ 93:7f:1b:46:1b:97:68:c5:c0:8b:35:1f:bb:1e:67:
+ 7f:55:b7:3b:55:3f:ea:f2:ca:db:cc:52:cd:16:89:
+ db:15:47:bd:f2:cd:6c:7a:d7:b4:1a:ac:c8:15:6c:
+ 6a:fb:77:c4:e9:f2:30:e0:14:24:66:65:6f:2a:e5:
+ 2d:cc:f6:81:ae:57:c8:d1:9b:38:90:dc:60:93:02:
+ 5e:cb
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: sha256WithRSAEncryption
+ 1c:7c:39:e8:3d:49:b2:09:1e:68:5a:2f:74:18:f4:63:b5:8c:
+ f6:e6:a1:e3:4d:95:90:99:ef:32:5c:34:40:e8:55:13:0e:e0:
+ 1c:be:cd:ab:3f:64:38:99:5e:2b:c1:81:53:a0:18:a8:f6:ee:
+ 6a:33:73:6c:9a:73:9d:86:08:5d:c7:11:38:46:4c:cd:a0:47:
+ 37:8f:fe:a6:50:a9:02:21:99:42:86:5e:47:fe:65:56:60:1d:
+ 16:53:86:bd:e4:63:c5:69:cf:fa:30:51:ab:a1:c3:50:53:cc:
+ 66:1c:4c:ff:3f:2a:39:4d:a2:8f:9d:d1:a7:8b:22:e4:78:69:
+ 24:06:83:4d:cc:0a:c0:87:69:9b:bc:80:a9:d2:b7:a5:23:84:
+ 7e:a2:32:26:7c:78:0e:bd:db:cd:3b:69:18:33:b8:44:ef:96:
+ b4:99:86:ee:06:bd:51:1c:c7:a1:a4:0c:c4:4c:51:a0:df:ac:
+ 14:07:88:8e:d7:39:45:fe:52:e0:a3:4c:db:5d:7a:ab:4d:e4:
+ ca:06:e8:bd:74:6f:46:e7:93:4a:4f:1b:67:e7:a5:9f:ef:9c:
+ 02:49:d1:f2:d5:e9:53:ee:09:21:ac:08:c8:15:f7:af:35:b9:
+ 4f:11:0f:43:ae:46:8e:fd:5b:8d:a3:4e:a7:2c:b7:25:ed:e4:
+ e5:94:1d:e3
+-----BEGIN CERTIFICATE-----
+MIICtTCCAZ0CAhAAMA0GCSqGSIb3DQEBCwUAMB0xCzAJBgNVBAYTAlVTMQ4wDAYD
+VQQDEwVNeSBDQTAgFw0xNzA1MjMxNjAwMjNaGA8yMTE3MDQyOTE2MDAyM1owITES
+MBAGA1UEAxMJbG9jYWxob3N0MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAMnUQ2BQ/NYPOE1dXqp8wF6p7NmTeNOTcihB9Qil6qxn
+B9cf9310aX5GiSBLei2bAgjnbw8dDA/HYGkZS99+ynWUC0lx423y6Hn97QqUZ1Xz
+ymthuli3Lt17yrkCnyQ2rCaPBI+ByDUQ9KozsiQW+Pce6vcW/vo0w927LLp6303i
+2h7l0ihEbsiW4P0JDBQMMdzgysGnm78WjPc2Pxsu3ZDrRXhRv1kiHsaMCmmI5QNe
+c7f8k38bRhuXaMXAizUfux5nf1W3O1U/6vLK28xSzRaJ2xVHvfLNbHrXtBqsyBVs
+avt3xOnyMOAUJGZlbyrlLcz2ga5XyNGbOJDcYJMCXssCAwEAATANBgkqhkiG9w0B
+AQsFAAOCAQEAHHw56D1JsgkeaFovdBj0Y7WM9uah402VkJnvMlw0QOhVEw7gHL7N
+qz9kOJleK8GBU6AYqPbuajNzbJpznYYIXccROEZMzaBHN4/+plCpAiGZQoZeR/5l
+VmAdFlOGveRjxWnP+jBRq6HDUFPMZhxM/z8qOU2ij53Rp4si5HhpJAaDTcwKwIdp
+m7yAqdK3pSOEfqIyJnx4Dr3bzTtpGDO4RO+WtJmG7ga9URzHoaQMxExRoN+sFAeI
+jtc5Rf5S4KNM2116q03kygbovXRvRueTSk8bZ+eln++cAknR8tXpU+4JIawIyBX3
+rzW5TxEPQ65Gjv1bjaNOpyy3Je3k5ZQd4w==
+-----END CERTIFICATE-----
diff --git a/rust/fatcat-api/examples/server-key.pem b/rust/fatcat-api/examples/server-key.pem
new file mode 100644
index 00000000..29c00682
--- /dev/null
+++ b/rust/fatcat-api/examples/server-key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDJ1ENgUPzWDzhN
+XV6qfMBeqezZk3jTk3IoQfUIpeqsZwfXH/d9dGl+RokgS3otmwII528PHQwPx2Bp
+GUvffsp1lAtJceNt8uh5/e0KlGdV88prYbpYty7de8q5Ap8kNqwmjwSPgcg1EPSq
+M7IkFvj3Hur3Fv76NMPduyy6et9N4toe5dIoRG7IluD9CQwUDDHc4MrBp5u/Foz3
+Nj8bLt2Q60V4Ub9ZIh7GjAppiOUDXnO3/JN/G0Ybl2jFwIs1H7seZ39VtztVP+ry
+ytvMUs0WidsVR73yzWx617QarMgVbGr7d8Tp8jDgFCRmZW8q5S3M9oGuV8jRmziQ
+3GCTAl7LAgMBAAECggEBAKEd1q9j14KWYc64s6KLthGbutyxsinMMbxbct11fdIk
+6YhdF3fJ35ETg9IJDr6rWEN9ZRX+jStncNpVfFEs6ThVd3Eo/nI+EEGaaIkikR93
+X2a7fEPn7/yVHu70XdBN6L1bPDvHUeiy4W2hmRrgT90OjGm1rNRWHOm7yugOwIZu
+HclzbR9Ca7EInFnotUiDQm9sw9VKHbJHqWx6OORdZrxR2ytYs0Qkq0XpGMvti2HW
+7WAmKTg5QM8myXW7+/4iqb/u68wVBR2BBalShKmIf7lim9O3W2a1RjDdsvm/wNe9
+I+D+Iq825vpqkKXcrxYlpVg7hYiaQaW/MNsEb7lQRjECgYEA/RJYby0POW+/k0Jn
+jO8UmJVEMiuGa8WIUu/JJWMOmzRCukjSRNQOkt7niQrZPJYE8W6clM6RJTolWf9L
+IL6mIb+mRaoudUk8SHGDq7ho1iMg9GK8lhYxvKh1Q6uv8EyVSkgLknAEY0NANKC1
+zNdU5Dhven9aRX2gq9vP4XwMz2MCgYEAzCogQ7IFk+gkp3k491dOZnrGRoRCfuzo
+4CJtyKFgOSd7BjmpcKkj0IPfVBjw6GjMIxfQRMTQmxAjjWevH45vG8l0Iiwz/gSp
+81b5nsDEX5uv2Olcmcz5zxRFy36jOZ9ihMWinxcIlT2oDbyCdbruDKZq9ieJ9S8g
+4qGx0OkwE3kCgYEA7CmAiU89U9YqqttfEq/RQoqY91CSwmO10d+ej9seuEtOsdRf
+FIfnibulycdr7hP5TOxyBpO1802NqayJiWcgVYIpQf2MGTtcnCYCP+95NcvWZvj1
+EAJqK6nwtFO1fcOZ1ZXh5qfOEGujsPkAbsXLnKXlsiTCMvMHSxl3pu5Cbg0CgYBf
+JjbZNctRrjv+7Qj2hPLd4dQsIxGWc7ToWENP4J2mpVa5hQAJqFovoHXhjKohtk2F
+AWEn243Y5oGbMjo0e74edhmwn2cvuF64MM2vBem/ISCn98IXT6cQskMA3qkVfsl8
+VVs/x41ReGWs2TD3y0GMFbb9t1mdMfSiincDhNnKCQKBgGfeT4jKyYeCoCw4OLI1
+G75Gd0METt/IkppwODPpNwj3Rp9I5jctWZFA/3wCX/zk0HgBeou5AFNS4nQZ/X/L
+L9axbSdR7UJTGkT1r4gu3rLkPV4Tk+8XM03/JT2cofMlzQBuhvl1Pn4SgKowz7hl
+lS76ECw4Av3T0S34VW9Z5oye
+-----END PRIVATE KEY-----
diff --git a/rust/fatcat-api/examples/server.rs b/rust/fatcat-api/examples/server.rs
new file mode 100644
index 00000000..f9b8617c
--- /dev/null
+++ b/rust/fatcat-api/examples/server.rs
@@ -0,0 +1,78 @@
+//! Main binary entry point for fatcat implementation.
+
+#![allow(missing_docs)]
+
+// Imports required by this file.
+// extern crate <name of this crate>;
+extern crate clap;
+extern crate fatcat;
+extern crate hyper;
+extern crate native_tls;
+extern crate openssl;
+extern crate swagger;
+extern crate tokio_proto;
+extern crate tokio_tls;
+
+// Imports required by server library.
+// extern crate fatcat;
+// extern crate swagger;
+extern crate chrono;
+extern crate futures;
+#[macro_use]
+extern crate error_chain;
+
+use clap::{App, Arg};
+use hyper::server::Http;
+use openssl::error::ErrorStack;
+use openssl::ssl::{SslAcceptorBuilder, SslMethod};
+use openssl::x509::X509_FILETYPE_PEM;
+use swagger::auth::AllowAllAuthenticator;
+use tokio_proto::TcpServer;
+
+mod server_lib;
+
+// Builds an SSL implementation for Simple HTTPS from some hard-coded file names
+fn ssl() -> Result<SslAcceptorBuilder, ErrorStack> {
+ let mut ssl = SslAcceptorBuilder::mozilla_intermediate_raw(SslMethod::tls())?;
+
+ // Server authentication
+ ssl.set_private_key_file("examples/server-key.pem", X509_FILETYPE_PEM)?;
+ ssl.set_certificate_chain_file("examples/server-chain.pem")?;
+ ssl.check_private_key()?;
+
+ Ok(ssl)
+}
+
+/// Create custom server, wire it to the autogenerated router,
+/// and pass it to the web server.
+fn main() {
+ let matches = App::new("server")
+ .arg(
+ Arg::with_name("https")
+ .long("https")
+ .help("Whether to use HTTPS or not"),
+ )
+ .get_matches();
+
+ let service_fn = fatcat::server::auth::NewService::new(AllowAllAuthenticator::new(
+ server_lib::NewService,
+ "cosmo",
+ ));
+
+ let addr = "127.0.0.1:8080"
+ .parse()
+ .expect("Failed to parse bind address");
+ if matches.is_present("https") {
+ let ssl = ssl().expect("Failed to load SSL keys");
+ let builder: native_tls::TlsAcceptorBuilder =
+ native_tls::backend::openssl::TlsAcceptorBuilderExt::from_openssl(ssl);
+ let tls_acceptor = builder.build().expect("Failed to build TLS acceptor");
+ TcpServer::new(
+ tokio_tls::proto::Server::new(Http::new(), tls_acceptor),
+ addr,
+ ).serve(service_fn);
+ } else {
+ // Using HTTP
+ TcpServer::new(Http::new(), addr).serve(service_fn);
+ }
+}
diff --git a/rust/fatcat-api/examples/server_lib/mod.rs b/rust/fatcat-api/examples/server_lib/mod.rs
new file mode 100644
index 00000000..a45fbe50
--- /dev/null
+++ b/rust/fatcat-api/examples/server_lib/mod.rs
@@ -0,0 +1,26 @@
+//! Main library entry point for fatcat implementation.
+
+mod server;
+
+mod errors {
+ error_chain!{}
+}
+
+pub use self::errors::*;
+use fatcat;
+use hyper;
+use std::io;
+
+pub struct NewService;
+
+impl hyper::server::NewService for NewService {
+ type Request = (hyper::Request, fatcat::Context);
+ type Response = hyper::Response;
+ type Error = hyper::Error;
+ type Instance = fatcat::server::Service<server::Server>;
+
+ /// Instantiate a new server.
+ fn new_service(&self) -> io::Result<Self::Instance> {
+ Ok(fatcat::server::Service::new(server::Server))
+ }
+}
diff --git a/rust/fatcat-api/examples/server_lib/server.rs b/rust/fatcat-api/examples/server_lib/server.rs
new file mode 100644
index 00000000..0dec7ed3
--- /dev/null
+++ b/rust/fatcat-api/examples/server_lib/server.rs
@@ -0,0 +1,287 @@
+//! Server implementation of fatcat.
+
+#![allow(unused_imports)]
+
+use chrono;
+use futures::{self, Future};
+
+use std::collections::HashMap;
+
+use swagger;
+
+use fatcat::models;
+use fatcat::{Api, ApiError, ContainerIdGetResponse, ContainerLookupGetResponse,
+ ContainerPostResponse, Context, CreatorIdGetResponse, CreatorLookupGetResponse,
+ CreatorPostResponse, EditgroupIdAcceptPostResponse, EditgroupIdGetResponse,
+ EditgroupPostResponse, EditorUsernameChangelogGetResponse, EditorUsernameGetResponse,
+ FileIdGetResponse, FileLookupGetResponse, FilePostResponse, ReleaseIdGetResponse,
+ ReleaseLookupGetResponse, ReleasePostResponse, WorkIdGetResponse, WorkPostResponse};
+
+#[derive(Copy, Clone)]
+pub struct Server;
+
+impl Api for Server {
+ fn container_id_get(
+ &self,
+ id: String,
+ context: &Context,
+ ) -> Box<Future<Item = ContainerIdGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "container_id_get(\"{}\") - X-Span-ID: {:?}",
+ id,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn container_lookup_get(
+ &self,
+ issn: String,
+ context: &Context,
+ ) -> Box<Future<Item = ContainerLookupGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "container_lookup_get(\"{}\") - X-Span-ID: {:?}",
+ issn,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn container_post(
+ &self,
+ body: Option<models::ContainerEntity>,
+ context: &Context,
+ ) -> Box<Future<Item = ContainerPostResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "container_post({:?}) - X-Span-ID: {:?}",
+ body,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn creator_id_get(
+ &self,
+ id: String,
+ context: &Context,
+ ) -> Box<Future<Item = CreatorIdGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "creator_id_get(\"{}\") - X-Span-ID: {:?}",
+ id,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn creator_lookup_get(
+ &self,
+ orcid: String,
+ context: &Context,
+ ) -> Box<Future<Item = CreatorLookupGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "creator_lookup_get(\"{}\") - X-Span-ID: {:?}",
+ orcid,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn creator_post(
+ &self,
+ body: Option<models::CreatorEntity>,
+ context: &Context,
+ ) -> Box<Future<Item = CreatorPostResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "creator_post({:?}) - X-Span-ID: {:?}",
+ body,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn editgroup_id_accept_post(
+ &self,
+ id: i32,
+ context: &Context,
+ ) -> Box<Future<Item = EditgroupIdAcceptPostResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "editgroup_id_accept_post({}) - X-Span-ID: {:?}",
+ id,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn editgroup_id_get(
+ &self,
+ id: i32,
+ context: &Context,
+ ) -> Box<Future<Item = EditgroupIdGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "editgroup_id_get({}) - X-Span-ID: {:?}",
+ id,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn editgroup_post(
+ &self,
+ context: &Context,
+ ) -> Box<Future<Item = EditgroupPostResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "editgroup_post() - X-Span-ID: {:?}",
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn editor_username_changelog_get(
+ &self,
+ username: String,
+ context: &Context,
+ ) -> Box<Future<Item = EditorUsernameChangelogGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "editor_username_changelog_get(\"{}\") - X-Span-ID: {:?}",
+ username,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn editor_username_get(
+ &self,
+ username: String,
+ context: &Context,
+ ) -> Box<Future<Item = EditorUsernameGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "editor_username_get(\"{}\") - X-Span-ID: {:?}",
+ username,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn file_id_get(
+ &self,
+ id: String,
+ context: &Context,
+ ) -> Box<Future<Item = FileIdGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "file_id_get(\"{}\") - X-Span-ID: {:?}",
+ id,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn file_lookup_get(
+ &self,
+ sha1: String,
+ context: &Context,
+ ) -> Box<Future<Item = FileLookupGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "file_lookup_get(\"{}\") - X-Span-ID: {:?}",
+ sha1,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn file_post(
+ &self,
+ body: Option<models::FileEntity>,
+ context: &Context,
+ ) -> Box<Future<Item = FilePostResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "file_post({:?}) - X-Span-ID: {:?}",
+ body,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn release_id_get(
+ &self,
+ id: String,
+ context: &Context,
+ ) -> Box<Future<Item = ReleaseIdGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "release_id_get(\"{}\") - X-Span-ID: {:?}",
+ id,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn release_lookup_get(
+ &self,
+ doi: String,
+ context: &Context,
+ ) -> Box<Future<Item = ReleaseLookupGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "release_lookup_get(\"{}\") - X-Span-ID: {:?}",
+ doi,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn release_post(
+ &self,
+ body: Option<models::ReleaseEntity>,
+ context: &Context,
+ ) -> Box<Future<Item = ReleasePostResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "release_post({:?}) - X-Span-ID: {:?}",
+ body,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn work_id_get(
+ &self,
+ id: String,
+ context: &Context,
+ ) -> Box<Future<Item = WorkIdGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "work_id_get(\"{}\") - X-Span-ID: {:?}",
+ id,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn work_post(
+ &self,
+ body: Option<models::WorkEntity>,
+ context: &Context,
+ ) -> Box<Future<Item = WorkPostResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "work_post({:?}) - X-Span-ID: {:?}",
+ body,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+}
diff --git a/rust/fatcat-api/src/client/mod.rs b/rust/fatcat-api/src/client/mod.rs
new file mode 100644
index 00000000..361fd390
--- /dev/null
+++ b/rust/fatcat-api/src/client/mod.rs
@@ -0,0 +1,2573 @@
+#![allow(unused_extern_crates)]
+extern crate chrono;
+extern crate hyper_tls;
+extern crate mime;
+extern crate native_tls;
+extern crate openssl;
+extern crate tokio_core;
+extern crate url;
+
+use self::tokio_core::reactor::Handle;
+use self::url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};
+use futures;
+use futures::{Future, Stream};
+use futures::{future, stream};
+use hyper;
+use hyper::Uri;
+use hyper::header::{ContentType, Headers};
+use std::borrow::Cow;
+use std::error;
+use std::fmt;
+use std::io::{Error, ErrorKind, Read};
+use std::path::Path;
+use std::str;
+use std::str::FromStr;
+use std::sync::Arc;
+
+use mimetypes;
+
+use serde_json;
+
+#[allow(unused_imports)]
+use std::collections::{BTreeMap, HashMap};
+#[allow(unused_imports)]
+use swagger;
+
+use swagger::{ApiError, Context, XSpanId};
+
+use models;
+use {Api, ContainerIdGetResponse, ContainerLookupGetResponse, ContainerPostResponse,
+ CreatorIdGetResponse, CreatorLookupGetResponse, CreatorPostResponse,
+ EditgroupIdAcceptPostResponse, EditgroupIdGetResponse, EditgroupPostResponse,
+ EditorUsernameChangelogGetResponse, EditorUsernameGetResponse, FileIdGetResponse,
+ FileLookupGetResponse, FilePostResponse, ReleaseIdGetResponse, ReleaseLookupGetResponse,
+ ReleasePostResponse, WorkIdGetResponse, WorkPostResponse};
+
+/// Convert input into a base path, e.g. "http://example:123". Also checks the scheme as it goes.
+fn into_base_path(
+ input: &str,
+ correct_scheme: Option<&'static str>,
+) -> Result<String, ClientInitError> {
+ // First convert to Uri, since a base path is a subset of Uri.
+ let uri = Uri::from_str(input)?;
+
+ let scheme = uri.scheme().ok_or(ClientInitError::InvalidScheme)?;
+
+ // Check the scheme if necessary
+ if let Some(correct_scheme) = correct_scheme {
+ if scheme != correct_scheme {
+ return Err(ClientInitError::InvalidScheme);
+ }
+ }
+
+ let host = uri.host().ok_or_else(|| ClientInitError::MissingHost)?;
+ let port = uri.port().map(|x| format!(":{}", x)).unwrap_or_default();
+ Ok(format!("{}://{}{}", scheme, host, port))
+}
+
+/// A client that implements the API by making HTTP calls out to a server.
+#[derive(Clone)]
+pub struct Client {
+ hyper_client: Arc<
+ Fn(
+ &Handle
+ ) -> Box<
+ hyper::client::Service<
+ Request = hyper::Request<hyper::Body>,
+ Response = hyper::Response,
+ Error = hyper::Error,
+ Future = hyper::client::FutureResponse,
+ >,
+ >
+ + Sync
+ + Send,
+ >,
+ handle: Arc<Handle>,
+ base_path: String,
+}
+
+impl fmt::Debug for Client {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "Client {{ base_path: {} }}", self.base_path)
+ }
+}
+
+impl Client {
+ /// Create an HTTP client.
+ ///
+ /// # Arguments
+ /// * `handle` - tokio reactor handle to use for execution
+ /// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
+ pub fn try_new_http(handle: Handle, base_path: &str) -> Result<Client, ClientInitError> {
+ let http_connector = swagger::http_connector();
+ Self::try_new_with_connector::<hyper::client::HttpConnector>(
+ handle,
+ base_path,
+ Some("http"),
+ http_connector,
+ )
+ }
+
+ /// Create a client with a TLS connection to the server.
+ ///
+ /// # Arguments
+ /// * `handle` - tokio reactor handle to use for execution
+ /// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
+ /// * `ca_certificate` - Path to CA certificate used to authenticate the server
+ pub fn try_new_https<CA>(
+ handle: Handle,
+ base_path: &str,
+ ca_certificate: CA,
+ ) -> Result<Client, ClientInitError>
+ where
+ CA: AsRef<Path>,
+ {
+ let https_connector = swagger::https_connector(ca_certificate);
+ Self::try_new_with_connector::<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>(
+ handle,
+ base_path,
+ Some("https"),
+ https_connector,
+ )
+ }
+
+ /// Create a client with a mutually authenticated TLS connection to the server.
+ ///
+ /// # Arguments
+ /// * `handle` - tokio reactor handle to use for execution
+ /// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
+ /// * `ca_certificate` - Path to CA certificate used to authenticate the server
+ /// * `client_key` - Path to the client private key
+ /// * `client_certificate` - Path to the client's public certificate associated with the private key
+ pub fn try_new_https_mutual<CA, K, C, T>(
+ handle: Handle,
+ base_path: &str,
+ ca_certificate: CA,
+ client_key: K,
+ client_certificate: C,
+ ) -> Result<Client, ClientInitError>
+ where
+ CA: AsRef<Path>,
+ K: AsRef<Path>,
+ C: AsRef<Path>,
+ {
+ let https_connector =
+ swagger::https_mutual_connector(ca_certificate, client_key, client_certificate);
+ Self::try_new_with_connector::<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>(
+ handle,
+ base_path,
+ Some("https"),
+ https_connector,
+ )
+ }
+
+ /// Create a client with a custom implementation of hyper::client::Connect.
+ ///
+ /// Intended for use with custom implementations of connect for e.g. protocol logging
+ /// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection,
+ /// this function should be used in conjunction with
+ /// `swagger::{http_connector, https_connector, https_mutual_connector}`.
+ ///
+ /// For ordinary tcp connections, prefer the use of `try_new_http`, `try_new_https`
+ /// and `try_new_https_mutual`, to avoid introducing a dependency on the underlying transport layer.
+ ///
+ /// # Arguments
+ ///
+ /// * `handle` - tokio reactor handle to use for execution
+ /// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
+ /// * `protocol` - Which protocol to use when constructing the request url, e.g. `Some("http")`
+ /// * `connector_fn` - Function which returns an implementation of `hyper::client::Connect`
+ pub fn try_new_with_connector<C>(
+ handle: Handle,
+ base_path: &str,
+ protocol: Option<&'static str>,
+ connector_fn: Box<Fn(&Handle) -> C + Send + Sync>,
+ ) -> Result<Client, ClientInitError>
+ where
+ C: hyper::client::Connect + hyper::client::Service,
+ {
+ let hyper_client = {
+ move |handle: &Handle| -> Box<
+ hyper::client::Service<
+ Request = hyper::Request<hyper::Body>,
+ Response = hyper::Response,
+ Error = hyper::Error,
+ Future = hyper::client::FutureResponse,
+ >,
+ > {
+ let connector = connector_fn(handle);
+ Box::new(
+ hyper::Client::configure()
+ .connector(connector)
+ .build(handle),
+ )
+ }
+ };
+
+ Ok(Client {
+ hyper_client: Arc::new(hyper_client),
+ handle: Arc::new(handle),
+ base_path: into_base_path(base_path, protocol)?,
+ })
+ }
+
+ /// Constructor for creating a `Client` by passing in a pre-made `hyper` client.
+ ///
+ /// One should avoid relying on this function if possible, since it adds a dependency on the underlying transport
+ /// implementation, which it would be better to abstract away. Therefore, using this function may lead to a loss of
+ /// code generality, which may make it harder to move the application to a serverless environment, for example.
+ ///
+ /// The reason for this function's existence is to support legacy test code, which did mocking at the hyper layer.
+ /// This is not a recommended way to write new tests. If other reasons are found for using this function, they
+ /// should be mentioned here.
+ pub fn try_new_with_hyper_client(
+ hyper_client: Arc<
+ Fn(
+ &Handle
+ ) -> Box<
+ hyper::client::Service<
+ Request = hyper::Request<hyper::Body>,
+ Response = hyper::Response,
+ Error = hyper::Error,
+ Future = hyper::client::FutureResponse,
+ >,
+ >
+ + Sync
+ + Send,
+ >,
+ handle: Handle,
+ base_path: &str,
+ ) -> Result<Client, ClientInitError> {
+ Ok(Client {
+ hyper_client: hyper_client,
+ handle: Arc::new(handle),
+ base_path: into_base_path(base_path, None)?,
+ })
+ }
+}
+
+impl Api for Client {
+ fn container_id_get(
+ &self,
+ param_id: String,
+ context: &Context,
+ ) -> Box<Future<Item = ContainerIdGetResponse, Error = ApiError>> {
+ let uri = format!(
+ "{}/v0/container/{id}",
+ self.base_path,
+ id = utf8_percent_encode(&param_id.to_string(), PATH_SEGMENT_ENCODE_SET)
+ );
+
+ let uri = match Uri::from_str(&uri) {
+ Ok(uri) => uri,
+ Err(err) => {
+ return Box::new(futures::done(Err(ApiError(format!(
+ "Unable to build URI: {}",
+ err
+ )))))
+ }
+ };
+
+ let mut request = hyper::Request::new(hyper::Method::Get, uri);
+
+ context
+ .x_span_id
+ .as_ref()
+ .map(|header| request.headers_mut().set(XSpanId(header.clone())));
+
+ let hyper_client = (self.hyper_client)(&*self.handle);
+ Box::new(
+ hyper_client
+ .call(request)
+ .map_err(|e| ApiError(format!("No response received: {}", e)))
+ .and_then(|mut response| match response.status().as_u16() {
+ 200 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::ContainerEntity>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ ContainerIdGetResponse::FetchASingleContainerById(body)
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 400 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| ContainerIdGetResponse::BadRequest(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 0 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ ContainerIdGetResponse::GenericErrorResponse(body)
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ code => {
+ let headers = response.headers().clone();
+ Box::new(response.body().take(100).concat2().then(move |body| {
+ future::err(ApiError(format!(
+ "Unexpected response code {}:\n{:?}\n\n{}",
+ code,
+ headers,
+ match body {
+ Ok(ref body) => match str::from_utf8(body) {
+ Ok(body) => Cow::from(body),
+ Err(e) => {
+ Cow::from(format!("<Body was not UTF8: {:?}>", e))
+ }
+ },
+ Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
+ }
+ )))
+ })) as Box<Future<Item = _, Error = _>>
+ }
+ }),
+ )
+ }
+
+ fn container_lookup_get(
+ &self,
+ param_issn: String,
+ context: &Context,
+ ) -> Box<Future<Item = ContainerLookupGetResponse, Error = ApiError>> {
+ // Query parameters
+ let query_issn = format!("issn={issn}&", issn = param_issn.to_string());
+
+ let uri = format!(
+ "{}/v0/container/lookup?{issn}",
+ self.base_path,
+ issn = utf8_percent_encode(&query_issn, QUERY_ENCODE_SET)
+ );
+
+ let uri = match Uri::from_str(&uri) {
+ Ok(uri) => uri,
+ Err(err) => {
+ return Box::new(futures::done(Err(ApiError(format!(
+ "Unable to build URI: {}",
+ err
+ )))))
+ }
+ };
+
+ let mut request = hyper::Request::new(hyper::Method::Get, uri);
+
+ context
+ .x_span_id
+ .as_ref()
+ .map(|header| request.headers_mut().set(XSpanId(header.clone())));
+
+ let hyper_client = (self.hyper_client)(&*self.handle);
+ Box::new(
+ hyper_client
+ .call(request)
+ .map_err(|e| ApiError(format!("No response received: {}", e)))
+ .and_then(|mut response| match response.status().as_u16() {
+ 200 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::ContainerEntity>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ ContainerLookupGetResponse::FindASingleContainerByExternalIdentifer(body)
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 400 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| ContainerLookupGetResponse::BadRequest(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 404 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| ContainerLookupGetResponse::NoSuchContainer(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 0 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ ContainerLookupGetResponse::GenericErrorResponse(body)
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ code => {
+ let headers = response.headers().clone();
+ Box::new(response.body().take(100).concat2().then(move |body| {
+ future::err(ApiError(format!(
+ "Unexpected response code {}:\n{:?}\n\n{}",
+ code,
+ headers,
+ match body {
+ Ok(ref body) => match str::from_utf8(body) {
+ Ok(body) => Cow::from(body),
+ Err(e) => {
+ Cow::from(format!("<Body was not UTF8: {:?}>", e))
+ }
+ },
+ Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
+ }
+ )))
+ })) as Box<Future<Item = _, Error = _>>
+ }
+ }),
+ )
+ }
+
+ fn container_post(
+ &self,
+ param_body: Option<models::ContainerEntity>,
+ context: &Context,
+ ) -> Box<Future<Item = ContainerPostResponse, Error = ApiError>> {
+ let uri = format!("{}/v0/container", self.base_path);
+
+ let uri = match Uri::from_str(&uri) {
+ Ok(uri) => uri,
+ Err(err) => {
+ return Box::new(futures::done(Err(ApiError(format!(
+ "Unable to build URI: {}",
+ err
+ )))))
+ }
+ };
+
+ let mut request = hyper::Request::new(hyper::Method::Post, uri);
+
+ let body = param_body
+ .map(|ref body| serde_json::to_string(body).expect("impossible to fail to serialize"));
+
+ if let Some(body) = body {
+ request.set_body(body.into_bytes());
+ }
+
+ request
+ .headers_mut()
+ .set(ContentType(mimetypes::requests::CONTAINER_POST.clone()));
+ context
+ .x_span_id
+ .as_ref()
+ .map(|header| request.headers_mut().set(XSpanId(header.clone())));
+
+ let hyper_client = (self.hyper_client)(&*self.handle);
+ Box::new(
+ hyper_client
+ .call(request)
+ .map_err(|e| ApiError(format!("No response received: {}", e)))
+ .and_then(|mut response| match response.status().as_u16() {
+ 201 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::EntityEdit>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| ContainerPostResponse::Created(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 400 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| ContainerPostResponse::BadRequest(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 0 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| ContainerPostResponse::GenericErrorResponse(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ code => {
+ let headers = response.headers().clone();
+ Box::new(response.body().take(100).concat2().then(move |body| {
+ future::err(ApiError(format!(
+ "Unexpected response code {}:\n{:?}\n\n{}",
+ code,
+ headers,
+ match body {
+ Ok(ref body) => match str::from_utf8(body) {
+ Ok(body) => Cow::from(body),
+ Err(e) => {
+ Cow::from(format!("<Body was not UTF8: {:?}>", e))
+ }
+ },
+ Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
+ }
+ )))
+ })) as Box<Future<Item = _, Error = _>>
+ }
+ }),
+ )
+ }
+
+ fn creator_id_get(
+ &self,
+ param_id: String,
+ context: &Context,
+ ) -> Box<Future<Item = CreatorIdGetResponse, Error = ApiError>> {
+ let uri = format!(
+ "{}/v0/creator/{id}",
+ self.base_path,
+ id = utf8_percent_encode(&param_id.to_string(), PATH_SEGMENT_ENCODE_SET)
+ );
+
+ let uri = match Uri::from_str(&uri) {
+ Ok(uri) => uri,
+ Err(err) => {
+ return Box::new(futures::done(Err(ApiError(format!(
+ "Unable to build URI: {}",
+ err
+ )))))
+ }
+ };
+
+ let mut request = hyper::Request::new(hyper::Method::Get, uri);
+
+ context
+ .x_span_id
+ .as_ref()
+ .map(|header| request.headers_mut().set(XSpanId(header.clone())));
+
+ let hyper_client = (self.hyper_client)(&*self.handle);
+ Box::new(
+ hyper_client
+ .call(request)
+ .map_err(|e| ApiError(format!("No response received: {}", e)))
+ .and_then(|mut response| match response.status().as_u16() {
+ 200 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::CreatorEntity>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ CreatorIdGetResponse::FetchASingleCreatorById(body)
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 400 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| CreatorIdGetResponse::BadRequest(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 0 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| CreatorIdGetResponse::GenericErrorResponse(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ code => {
+ let headers = response.headers().clone();
+ Box::new(response.body().take(100).concat2().then(move |body| {
+ future::err(ApiError(format!(
+ "Unexpected response code {}:\n{:?}\n\n{}",
+ code,
+ headers,
+ match body {
+ Ok(ref body) => match str::from_utf8(body) {
+ Ok(body) => Cow::from(body),
+ Err(e) => {
+ Cow::from(format!("<Body was not UTF8: {:?}>", e))
+ }
+ },
+ Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
+ }
+ )))
+ })) as Box<Future<Item = _, Error = _>>
+ }
+ }),
+ )
+ }
+
+ fn creator_lookup_get(
+ &self,
+ param_orcid: String,
+ context: &Context,
+ ) -> Box<Future<Item = CreatorLookupGetResponse, Error = ApiError>> {
+ // Query parameters
+ let query_orcid = format!("orcid={orcid}&", orcid = param_orcid.to_string());
+
+ let uri = format!(
+ "{}/v0/creator/lookup?{orcid}",
+ self.base_path,
+ orcid = utf8_percent_encode(&query_orcid, QUERY_ENCODE_SET)
+ );
+
+ let uri = match Uri::from_str(&uri) {
+ Ok(uri) => uri,
+ Err(err) => {
+ return Box::new(futures::done(Err(ApiError(format!(
+ "Unable to build URI: {}",
+ err
+ )))))
+ }
+ };
+
+ let mut request = hyper::Request::new(hyper::Method::Get, uri);
+
+ context
+ .x_span_id
+ .as_ref()
+ .map(|header| request.headers_mut().set(XSpanId(header.clone())));
+
+ let hyper_client = (self.hyper_client)(&*self.handle);
+ Box::new(
+ hyper_client
+ .call(request)
+ .map_err(|e| ApiError(format!("No response received: {}", e)))
+ .and_then(|mut response| match response.status().as_u16() {
+ 200 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::CreatorEntity>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ CreatorLookupGetResponse::FindASingleCreatorByExternalIdentifer(
+ body,
+ )
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 400 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| CreatorLookupGetResponse::BadRequest(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 404 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| CreatorLookupGetResponse::NoSuchCreator(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 0 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ CreatorLookupGetResponse::GenericErrorResponse(body)
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ code => {
+ let headers = response.headers().clone();
+ Box::new(response.body().take(100).concat2().then(move |body| {
+ future::err(ApiError(format!(
+ "Unexpected response code {}:\n{:?}\n\n{}",
+ code,
+ headers,
+ match body {
+ Ok(ref body) => match str::from_utf8(body) {
+ Ok(body) => Cow::from(body),
+ Err(e) => {
+ Cow::from(format!("<Body was not UTF8: {:?}>", e))
+ }
+ },
+ Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
+ }
+ )))
+ })) as Box<Future<Item = _, Error = _>>
+ }
+ }),
+ )
+ }
+
+ fn creator_post(
+ &self,
+ param_body: Option<models::CreatorEntity>,
+ context: &Context,
+ ) -> Box<Future<Item = CreatorPostResponse, Error = ApiError>> {
+ let uri = format!("{}/v0/creator", self.base_path);
+
+ let uri = match Uri::from_str(&uri) {
+ Ok(uri) => uri,
+ Err(err) => {
+ return Box::new(futures::done(Err(ApiError(format!(
+ "Unable to build URI: {}",
+ err
+ )))))
+ }
+ };
+
+ let mut request = hyper::Request::new(hyper::Method::Post, uri);
+
+ let body = param_body
+ .map(|ref body| serde_json::to_string(body).expect("impossible to fail to serialize"));
+
+ if let Some(body) = body {
+ request.set_body(body.into_bytes());
+ }
+
+ request
+ .headers_mut()
+ .set(ContentType(mimetypes::requests::CREATOR_POST.clone()));
+ context
+ .x_span_id
+ .as_ref()
+ .map(|header| request.headers_mut().set(XSpanId(header.clone())));
+
+ let hyper_client = (self.hyper_client)(&*self.handle);
+ Box::new(
+ hyper_client
+ .call(request)
+ .map_err(|e| ApiError(format!("No response received: {}", e)))
+ .and_then(|mut response| match response.status().as_u16() {
+ 201 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::EntityEdit>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| CreatorPostResponse::Created(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 400 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| CreatorPostResponse::BadRequest(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 0 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| CreatorPostResponse::GenericErrorResponse(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ code => {
+ let headers = response.headers().clone();
+ Box::new(response.body().take(100).concat2().then(move |body| {
+ future::err(ApiError(format!(
+ "Unexpected response code {}:\n{:?}\n\n{}",
+ code,
+ headers,
+ match body {
+ Ok(ref body) => match str::from_utf8(body) {
+ Ok(body) => Cow::from(body),
+ Err(e) => {
+ Cow::from(format!("<Body was not UTF8: {:?}>", e))
+ }
+ },
+ Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
+ }
+ )))
+ })) as Box<Future<Item = _, Error = _>>
+ }
+ }),
+ )
+ }
+
+ fn editgroup_id_accept_post(
+ &self,
+ param_id: i32,
+ context: &Context,
+ ) -> Box<Future<Item = EditgroupIdAcceptPostResponse, Error = ApiError>> {
+ let uri = format!(
+ "{}/v0/editgroup/{id}/accept",
+ self.base_path,
+ id = utf8_percent_encode(&param_id.to_string(), PATH_SEGMENT_ENCODE_SET)
+ );
+
+ let uri = match Uri::from_str(&uri) {
+ Ok(uri) => uri,
+ Err(err) => {
+ return Box::new(futures::done(Err(ApiError(format!(
+ "Unable to build URI: {}",
+ err
+ )))))
+ }
+ };
+
+ let mut request = hyper::Request::new(hyper::Method::Post, uri);
+
+ context
+ .x_span_id
+ .as_ref()
+ .map(|header| request.headers_mut().set(XSpanId(header.clone())));
+
+ let hyper_client = (self.hyper_client)(&*self.handle);
+ Box::new(
+ hyper_client
+ .call(request)
+ .map_err(|e| ApiError(format!("No response received: {}", e)))
+ .and_then(|mut response| match response.status().as_u16() {
+ 200 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Success>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ EditgroupIdAcceptPostResponse::MergedEditgroupSuccessfully_(
+ body,
+ )
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 400 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ EditgroupIdAcceptPostResponse::EditgroupIsInAnUnmergableState(
+ body,
+ )
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 404 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ EditgroupIdAcceptPostResponse::NoSuchEditgroup(body)
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 0 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ EditgroupIdAcceptPostResponse::GenericErrorResponse(body)
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ code => {
+ let headers = response.headers().clone();
+ Box::new(response.body().take(100).concat2().then(move |body| {
+ future::err(ApiError(format!(
+ "Unexpected response code {}:\n{:?}\n\n{}",
+ code,
+ headers,
+ match body {
+ Ok(ref body) => match str::from_utf8(body) {
+ Ok(body) => Cow::from(body),
+ Err(e) => {
+ Cow::from(format!("<Body was not UTF8: {:?}>", e))
+ }
+ },
+ Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
+ }
+ )))
+ })) as Box<Future<Item = _, Error = _>>
+ }
+ }),
+ )
+ }
+
+ fn editgroup_id_get(
+ &self,
+ param_id: i32,
+ context: &Context,
+ ) -> Box<Future<Item = EditgroupIdGetResponse, Error = ApiError>> {
+ let uri = format!(
+ "{}/v0/editgroup/{id}",
+ self.base_path,
+ id = utf8_percent_encode(&param_id.to_string(), PATH_SEGMENT_ENCODE_SET)
+ );
+
+ let uri = match Uri::from_str(&uri) {
+ Ok(uri) => uri,
+ Err(err) => {
+ return Box::new(futures::done(Err(ApiError(format!(
+ "Unable to build URI: {}",
+ err
+ )))))
+ }
+ };
+
+ let mut request = hyper::Request::new(hyper::Method::Get, uri);
+
+ context
+ .x_span_id
+ .as_ref()
+ .map(|header| request.headers_mut().set(XSpanId(header.clone())));
+
+ let hyper_client = (self.hyper_client)(&*self.handle);
+ Box::new(
+ hyper_client
+ .call(request)
+ .map_err(|e| ApiError(format!("No response received: {}", e)))
+ .and_then(|mut response| match response.status().as_u16() {
+ 200 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Editgroup>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ EditgroupIdGetResponse::FetchEditgroupByIdentifier(body)
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 404 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| EditgroupIdGetResponse::NoSuchEditgroup(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 0 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ EditgroupIdGetResponse::GenericErrorResponse(body)
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ code => {
+ let headers = response.headers().clone();
+ Box::new(response.body().take(100).concat2().then(move |body| {
+ future::err(ApiError(format!(
+ "Unexpected response code {}:\n{:?}\n\n{}",
+ code,
+ headers,
+ match body {
+ Ok(ref body) => match str::from_utf8(body) {
+ Ok(body) => Cow::from(body),
+ Err(e) => {
+ Cow::from(format!("<Body was not UTF8: {:?}>", e))
+ }
+ },
+ Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
+ }
+ )))
+ })) as Box<Future<Item = _, Error = _>>
+ }
+ }),
+ )
+ }
+
+ fn editgroup_post(
+ &self,
+ context: &Context,
+ ) -> Box<Future<Item = EditgroupPostResponse, Error = ApiError>> {
+ let uri = format!("{}/v0/editgroup", self.base_path);
+
+ let uri = match Uri::from_str(&uri) {
+ Ok(uri) => uri,
+ Err(err) => {
+ return Box::new(futures::done(Err(ApiError(format!(
+ "Unable to build URI: {}",
+ err
+ )))))
+ }
+ };
+
+ let mut request = hyper::Request::new(hyper::Method::Post, uri);
+
+ context
+ .x_span_id
+ .as_ref()
+ .map(|header| request.headers_mut().set(XSpanId(header.clone())));
+
+ let hyper_client = (self.hyper_client)(&*self.handle);
+ Box::new(
+ hyper_client
+ .call(request)
+ .map_err(|e| ApiError(format!("No response received: {}", e)))
+ .and_then(|mut response| match response.status().as_u16() {
+ 201 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Editgroup>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| EditgroupPostResponse::SuccessfullyCreated(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 400 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ EditgroupPostResponse::InvalidRequestParameters(body)
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 0 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| EditgroupPostResponse::GenericErrorResponse(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ code => {
+ let headers = response.headers().clone();
+ Box::new(response.body().take(100).concat2().then(move |body| {
+ future::err(ApiError(format!(
+ "Unexpected response code {}:\n{:?}\n\n{}",
+ code,
+ headers,
+ match body {
+ Ok(ref body) => match str::from_utf8(body) {
+ Ok(body) => Cow::from(body),
+ Err(e) => {
+ Cow::from(format!("<Body was not UTF8: {:?}>", e))
+ }
+ },
+ Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
+ }
+ )))
+ })) as Box<Future<Item = _, Error = _>>
+ }
+ }),
+ )
+ }
+
+ fn editor_username_changelog_get(
+ &self,
+ param_username: String,
+ context: &Context,
+ ) -> Box<Future<Item = EditorUsernameChangelogGetResponse, Error = ApiError>> {
+ let uri = format!(
+ "{}/v0/editor/{username}/changelog",
+ self.base_path,
+ username = utf8_percent_encode(&param_username.to_string(), PATH_SEGMENT_ENCODE_SET)
+ );
+
+ let uri = match Uri::from_str(&uri) {
+ Ok(uri) => uri,
+ Err(err) => {
+ return Box::new(futures::done(Err(ApiError(format!(
+ "Unable to build URI: {}",
+ err
+ )))))
+ }
+ };
+
+ let mut request = hyper::Request::new(hyper::Method::Get, uri);
+
+ context
+ .x_span_id
+ .as_ref()
+ .map(|header| request.headers_mut().set(XSpanId(header.clone())));
+
+ let hyper_client = (self.hyper_client)(&*self.handle);
+ Box::new(
+ hyper_client
+ .call(request)
+ .map_err(|e| ApiError(format!("No response received: {}", e)))
+ .and_then(|mut response| match response.status().as_u16() {
+ 200 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Changelogentry>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ EditorUsernameChangelogGetResponse::FindChanges_(body)
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 404 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ EditorUsernameChangelogGetResponse::UsernameNotFound(body)
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 0 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ EditorUsernameChangelogGetResponse::GenericErrorResponse(body)
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ code => {
+ let headers = response.headers().clone();
+ Box::new(response.body().take(100).concat2().then(move |body| {
+ future::err(ApiError(format!(
+ "Unexpected response code {}:\n{:?}\n\n{}",
+ code,
+ headers,
+ match body {
+ Ok(ref body) => match str::from_utf8(body) {
+ Ok(body) => Cow::from(body),
+ Err(e) => {
+ Cow::from(format!("<Body was not UTF8: {:?}>", e))
+ }
+ },
+ Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
+ }
+ )))
+ })) as Box<Future<Item = _, Error = _>>
+ }
+ }),
+ )
+ }
+
+ fn editor_username_get(
+ &self,
+ param_username: String,
+ context: &Context,
+ ) -> Box<Future<Item = EditorUsernameGetResponse, Error = ApiError>> {
+ let uri = format!(
+ "{}/v0/editor/{username}",
+ self.base_path,
+ username = utf8_percent_encode(&param_username.to_string(), PATH_SEGMENT_ENCODE_SET)
+ );
+
+ let uri = match Uri::from_str(&uri) {
+ Ok(uri) => uri,
+ Err(err) => {
+ return Box::new(futures::done(Err(ApiError(format!(
+ "Unable to build URI: {}",
+ err
+ )))))
+ }
+ };
+
+ let mut request = hyper::Request::new(hyper::Method::Get, uri);
+
+ context
+ .x_span_id
+ .as_ref()
+ .map(|header| request.headers_mut().set(XSpanId(header.clone())));
+
+ let hyper_client = (self.hyper_client)(&*self.handle);
+ Box::new(
+ hyper_client
+ .call(request)
+ .map_err(|e| ApiError(format!("No response received: {}", e)))
+ .and_then(|mut response| match response.status().as_u16() {
+ 200 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Editor>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ EditorUsernameGetResponse::FetchGenericInformationAboutAnEditor(
+ body,
+ )
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 404 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| EditorUsernameGetResponse::UsernameNotFound(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 0 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ EditorUsernameGetResponse::GenericErrorResponse(body)
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ code => {
+ let headers = response.headers().clone();
+ Box::new(response.body().take(100).concat2().then(move |body| {
+ future::err(ApiError(format!(
+ "Unexpected response code {}:\n{:?}\n\n{}",
+ code,
+ headers,
+ match body {
+ Ok(ref body) => match str::from_utf8(body) {
+ Ok(body) => Cow::from(body),
+ Err(e) => {
+ Cow::from(format!("<Body was not UTF8: {:?}>", e))
+ }
+ },
+ Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
+ }
+ )))
+ })) as Box<Future<Item = _, Error = _>>
+ }
+ }),
+ )
+ }
+
+ fn file_id_get(
+ &self,
+ param_id: String,
+ context: &Context,
+ ) -> Box<Future<Item = FileIdGetResponse, Error = ApiError>> {
+ let uri = format!(
+ "{}/v0/file/{id}",
+ self.base_path,
+ id = utf8_percent_encode(&param_id.to_string(), PATH_SEGMENT_ENCODE_SET)
+ );
+
+ let uri = match Uri::from_str(&uri) {
+ Ok(uri) => uri,
+ Err(err) => {
+ return Box::new(futures::done(Err(ApiError(format!(
+ "Unable to build URI: {}",
+ err
+ )))))
+ }
+ };
+
+ let mut request = hyper::Request::new(hyper::Method::Get, uri);
+
+ context
+ .x_span_id
+ .as_ref()
+ .map(|header| request.headers_mut().set(XSpanId(header.clone())));
+
+ let hyper_client = (self.hyper_client)(&*self.handle);
+ Box::new(
+ hyper_client
+ .call(request)
+ .map_err(|e| ApiError(format!("No response received: {}", e)))
+ .and_then(|mut response| match response.status().as_u16() {
+ 200 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::FileEntity>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| FileIdGetResponse::FetchASingleFileById(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 400 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| FileIdGetResponse::BadRequest(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 0 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| FileIdGetResponse::GenericErrorResponse(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ code => {
+ let headers = response.headers().clone();
+ Box::new(response.body().take(100).concat2().then(move |body| {
+ future::err(ApiError(format!(
+ "Unexpected response code {}:\n{:?}\n\n{}",
+ code,
+ headers,
+ match body {
+ Ok(ref body) => match str::from_utf8(body) {
+ Ok(body) => Cow::from(body),
+ Err(e) => {
+ Cow::from(format!("<Body was not UTF8: {:?}>", e))
+ }
+ },
+ Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
+ }
+ )))
+ })) as Box<Future<Item = _, Error = _>>
+ }
+ }),
+ )
+ }
+
+ fn file_lookup_get(
+ &self,
+ param_sha1: String,
+ context: &Context,
+ ) -> Box<Future<Item = FileLookupGetResponse, Error = ApiError>> {
+ // Query parameters
+ let query_sha1 = format!("sha1={sha1}&", sha1 = param_sha1.to_string());
+
+ let uri = format!(
+ "{}/v0/file/lookup?{sha1}",
+ self.base_path,
+ sha1 = utf8_percent_encode(&query_sha1, QUERY_ENCODE_SET)
+ );
+
+ let uri = match Uri::from_str(&uri) {
+ Ok(uri) => uri,
+ Err(err) => {
+ return Box::new(futures::done(Err(ApiError(format!(
+ "Unable to build URI: {}",
+ err
+ )))))
+ }
+ };
+
+ let mut request = hyper::Request::new(hyper::Method::Get, uri);
+
+ context
+ .x_span_id
+ .as_ref()
+ .map(|header| request.headers_mut().set(XSpanId(header.clone())));
+
+ let hyper_client = (self.hyper_client)(&*self.handle);
+ Box::new(
+ hyper_client
+ .call(request)
+ .map_err(|e| ApiError(format!("No response received: {}", e)))
+ .and_then(|mut response| match response.status().as_u16() {
+ 200 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::FileEntity>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ FileLookupGetResponse::FindASingleFileByExternalIdentifer(body)
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 400 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| FileLookupGetResponse::BadRequest(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 404 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| FileLookupGetResponse::NoSuchFile(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 0 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| FileLookupGetResponse::GenericErrorResponse(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ code => {
+ let headers = response.headers().clone();
+ Box::new(response.body().take(100).concat2().then(move |body| {
+ future::err(ApiError(format!(
+ "Unexpected response code {}:\n{:?}\n\n{}",
+ code,
+ headers,
+ match body {
+ Ok(ref body) => match str::from_utf8(body) {
+ Ok(body) => Cow::from(body),
+ Err(e) => {
+ Cow::from(format!("<Body was not UTF8: {:?}>", e))
+ }
+ },
+ Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
+ }
+ )))
+ })) as Box<Future<Item = _, Error = _>>
+ }
+ }),
+ )
+ }
+
+ fn file_post(
+ &self,
+ param_body: Option<models::FileEntity>,
+ context: &Context,
+ ) -> Box<Future<Item = FilePostResponse, Error = ApiError>> {
+ let uri = format!("{}/v0/file", self.base_path);
+
+ let uri = match Uri::from_str(&uri) {
+ Ok(uri) => uri,
+ Err(err) => {
+ return Box::new(futures::done(Err(ApiError(format!(
+ "Unable to build URI: {}",
+ err
+ )))))
+ }
+ };
+
+ let mut request = hyper::Request::new(hyper::Method::Post, uri);
+
+ let body = param_body
+ .map(|ref body| serde_json::to_string(body).expect("impossible to fail to serialize"));
+
+ if let Some(body) = body {
+ request.set_body(body.into_bytes());
+ }
+
+ request
+ .headers_mut()
+ .set(ContentType(mimetypes::requests::FILE_POST.clone()));
+ context
+ .x_span_id
+ .as_ref()
+ .map(|header| request.headers_mut().set(XSpanId(header.clone())));
+
+ let hyper_client = (self.hyper_client)(&*self.handle);
+ Box::new(
+ hyper_client
+ .call(request)
+ .map_err(|e| ApiError(format!("No response received: {}", e)))
+ .and_then(|mut response| match response.status().as_u16() {
+ 201 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::EntityEdit>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| FilePostResponse::Created(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 400 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| FilePostResponse::BadRequest(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 0 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| FilePostResponse::GenericErrorResponse(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ code => {
+ let headers = response.headers().clone();
+ Box::new(response.body().take(100).concat2().then(move |body| {
+ future::err(ApiError(format!(
+ "Unexpected response code {}:\n{:?}\n\n{}",
+ code,
+ headers,
+ match body {
+ Ok(ref body) => match str::from_utf8(body) {
+ Ok(body) => Cow::from(body),
+ Err(e) => {
+ Cow::from(format!("<Body was not UTF8: {:?}>", e))
+ }
+ },
+ Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
+ }
+ )))
+ })) as Box<Future<Item = _, Error = _>>
+ }
+ }),
+ )
+ }
+
+ fn release_id_get(
+ &self,
+ param_id: String,
+ context: &Context,
+ ) -> Box<Future<Item = ReleaseIdGetResponse, Error = ApiError>> {
+ let uri = format!(
+ "{}/v0/release/{id}",
+ self.base_path,
+ id = utf8_percent_encode(&param_id.to_string(), PATH_SEGMENT_ENCODE_SET)
+ );
+
+ let uri = match Uri::from_str(&uri) {
+ Ok(uri) => uri,
+ Err(err) => {
+ return Box::new(futures::done(Err(ApiError(format!(
+ "Unable to build URI: {}",
+ err
+ )))))
+ }
+ };
+
+ let mut request = hyper::Request::new(hyper::Method::Get, uri);
+
+ context
+ .x_span_id
+ .as_ref()
+ .map(|header| request.headers_mut().set(XSpanId(header.clone())));
+
+ let hyper_client = (self.hyper_client)(&*self.handle);
+ Box::new(
+ hyper_client
+ .call(request)
+ .map_err(|e| ApiError(format!("No response received: {}", e)))
+ .and_then(|mut response| match response.status().as_u16() {
+ 200 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::ReleaseEntity>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ ReleaseIdGetResponse::FetchASingleReleaseById(body)
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 400 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| ReleaseIdGetResponse::BadRequest(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 0 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| ReleaseIdGetResponse::GenericErrorResponse(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ code => {
+ let headers = response.headers().clone();
+ Box::new(response.body().take(100).concat2().then(move |body| {
+ future::err(ApiError(format!(
+ "Unexpected response code {}:\n{:?}\n\n{}",
+ code,
+ headers,
+ match body {
+ Ok(ref body) => match str::from_utf8(body) {
+ Ok(body) => Cow::from(body),
+ Err(e) => {
+ Cow::from(format!("<Body was not UTF8: {:?}>", e))
+ }
+ },
+ Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
+ }
+ )))
+ })) as Box<Future<Item = _, Error = _>>
+ }
+ }),
+ )
+ }
+
+ fn release_lookup_get(
+ &self,
+ param_doi: String,
+ context: &Context,
+ ) -> Box<Future<Item = ReleaseLookupGetResponse, Error = ApiError>> {
+ // Query parameters
+ let query_doi = format!("doi={doi}&", doi = param_doi.to_string());
+
+ let uri = format!(
+ "{}/v0/release/lookup?{doi}",
+ self.base_path,
+ doi = utf8_percent_encode(&query_doi, QUERY_ENCODE_SET)
+ );
+
+ let uri = match Uri::from_str(&uri) {
+ Ok(uri) => uri,
+ Err(err) => {
+ return Box::new(futures::done(Err(ApiError(format!(
+ "Unable to build URI: {}",
+ err
+ )))))
+ }
+ };
+
+ let mut request = hyper::Request::new(hyper::Method::Get, uri);
+
+ context
+ .x_span_id
+ .as_ref()
+ .map(|header| request.headers_mut().set(XSpanId(header.clone())));
+
+ let hyper_client = (self.hyper_client)(&*self.handle);
+ Box::new(
+ hyper_client
+ .call(request)
+ .map_err(|e| ApiError(format!("No response received: {}", e)))
+ .and_then(|mut response| match response.status().as_u16() {
+ 200 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::ReleaseEntity>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ ReleaseLookupGetResponse::FindASingleReleaseByExternalIdentifer(
+ body,
+ )
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 400 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| ReleaseLookupGetResponse::BadRequest(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 404 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| ReleaseLookupGetResponse::NoSuchRelease(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 0 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| {
+ ReleaseLookupGetResponse::GenericErrorResponse(body)
+ }),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ code => {
+ let headers = response.headers().clone();
+ Box::new(response.body().take(100).concat2().then(move |body| {
+ future::err(ApiError(format!(
+ "Unexpected response code {}:\n{:?}\n\n{}",
+ code,
+ headers,
+ match body {
+ Ok(ref body) => match str::from_utf8(body) {
+ Ok(body) => Cow::from(body),
+ Err(e) => {
+ Cow::from(format!("<Body was not UTF8: {:?}>", e))
+ }
+ },
+ Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
+ }
+ )))
+ })) as Box<Future<Item = _, Error = _>>
+ }
+ }),
+ )
+ }
+
+ fn release_post(
+ &self,
+ param_body: Option<models::ReleaseEntity>,
+ context: &Context,
+ ) -> Box<Future<Item = ReleasePostResponse, Error = ApiError>> {
+ let uri = format!("{}/v0/release", self.base_path);
+
+ let uri = match Uri::from_str(&uri) {
+ Ok(uri) => uri,
+ Err(err) => {
+ return Box::new(futures::done(Err(ApiError(format!(
+ "Unable to build URI: {}",
+ err
+ )))))
+ }
+ };
+
+ let mut request = hyper::Request::new(hyper::Method::Post, uri);
+
+ let body = param_body
+ .map(|ref body| serde_json::to_string(body).expect("impossible to fail to serialize"));
+
+ if let Some(body) = body {
+ request.set_body(body.into_bytes());
+ }
+
+ request
+ .headers_mut()
+ .set(ContentType(mimetypes::requests::RELEASE_POST.clone()));
+ context
+ .x_span_id
+ .as_ref()
+ .map(|header| request.headers_mut().set(XSpanId(header.clone())));
+
+ let hyper_client = (self.hyper_client)(&*self.handle);
+ Box::new(
+ hyper_client
+ .call(request)
+ .map_err(|e| ApiError(format!("No response received: {}", e)))
+ .and_then(|mut response| match response.status().as_u16() {
+ 201 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::EntityEdit>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| ReleasePostResponse::Created(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 400 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| ReleasePostResponse::BadRequest(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 0 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| ReleasePostResponse::GenericErrorResponse(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ code => {
+ let headers = response.headers().clone();
+ Box::new(response.body().take(100).concat2().then(move |body| {
+ future::err(ApiError(format!(
+ "Unexpected response code {}:\n{:?}\n\n{}",
+ code,
+ headers,
+ match body {
+ Ok(ref body) => match str::from_utf8(body) {
+ Ok(body) => Cow::from(body),
+ Err(e) => {
+ Cow::from(format!("<Body was not UTF8: {:?}>", e))
+ }
+ },
+ Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
+ }
+ )))
+ })) as Box<Future<Item = _, Error = _>>
+ }
+ }),
+ )
+ }
+
+ fn work_id_get(
+ &self,
+ param_id: String,
+ context: &Context,
+ ) -> Box<Future<Item = WorkIdGetResponse, Error = ApiError>> {
+ let uri = format!(
+ "{}/v0/work/{id}",
+ self.base_path,
+ id = utf8_percent_encode(&param_id.to_string(), PATH_SEGMENT_ENCODE_SET)
+ );
+
+ let uri = match Uri::from_str(&uri) {
+ Ok(uri) => uri,
+ Err(err) => {
+ return Box::new(futures::done(Err(ApiError(format!(
+ "Unable to build URI: {}",
+ err
+ )))))
+ }
+ };
+
+ let mut request = hyper::Request::new(hyper::Method::Get, uri);
+
+ context
+ .x_span_id
+ .as_ref()
+ .map(|header| request.headers_mut().set(XSpanId(header.clone())));
+
+ let hyper_client = (self.hyper_client)(&*self.handle);
+ Box::new(
+ hyper_client
+ .call(request)
+ .map_err(|e| ApiError(format!("No response received: {}", e)))
+ .and_then(|mut response| match response.status().as_u16() {
+ 200 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::WorkEntity>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| WorkIdGetResponse::FetchASingleWorkById(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 400 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| WorkIdGetResponse::BadRequest(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 0 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| WorkIdGetResponse::GenericErrorResponse(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ code => {
+ let headers = response.headers().clone();
+ Box::new(response.body().take(100).concat2().then(move |body| {
+ future::err(ApiError(format!(
+ "Unexpected response code {}:\n{:?}\n\n{}",
+ code,
+ headers,
+ match body {
+ Ok(ref body) => match str::from_utf8(body) {
+ Ok(body) => Cow::from(body),
+ Err(e) => {
+ Cow::from(format!("<Body was not UTF8: {:?}>", e))
+ }
+ },
+ Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
+ }
+ )))
+ })) as Box<Future<Item = _, Error = _>>
+ }
+ }),
+ )
+ }
+
+ fn work_post(
+ &self,
+ param_body: Option<models::WorkEntity>,
+ context: &Context,
+ ) -> Box<Future<Item = WorkPostResponse, Error = ApiError>> {
+ let uri = format!("{}/v0/work", self.base_path);
+
+ let uri = match Uri::from_str(&uri) {
+ Ok(uri) => uri,
+ Err(err) => {
+ return Box::new(futures::done(Err(ApiError(format!(
+ "Unable to build URI: {}",
+ err
+ )))))
+ }
+ };
+
+ let mut request = hyper::Request::new(hyper::Method::Post, uri);
+
+ let body = param_body
+ .map(|ref body| serde_json::to_string(body).expect("impossible to fail to serialize"));
+
+ if let Some(body) = body {
+ request.set_body(body.into_bytes());
+ }
+
+ request
+ .headers_mut()
+ .set(ContentType(mimetypes::requests::WORK_POST.clone()));
+ context
+ .x_span_id
+ .as_ref()
+ .map(|header| request.headers_mut().set(XSpanId(header.clone())));
+
+ let hyper_client = (self.hyper_client)(&*self.handle);
+ Box::new(
+ hyper_client
+ .call(request)
+ .map_err(|e| ApiError(format!("No response received: {}", e)))
+ .and_then(|mut response| match response.status().as_u16() {
+ 201 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::EntityEdit>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| WorkPostResponse::Created(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 400 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| WorkPostResponse::BadRequest(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ 0 => {
+ let body = response.body();
+ Box::new(
+ body.concat2()
+ .map_err(|e| ApiError(format!("Failed to read response: {}", e)))
+ .and_then(|body| {
+ str::from_utf8(&body)
+ .map_err(|e| {
+ ApiError(format!("Response was not valid UTF8: {}", e))
+ })
+ .and_then(|body| {
+ serde_json::from_str::<models::Error>(body)
+ .map_err(|e| e.into())
+ })
+ })
+ .map(move |body| WorkPostResponse::GenericErrorResponse(body)),
+ ) as Box<Future<Item = _, Error = _>>
+ }
+ code => {
+ let headers = response.headers().clone();
+ Box::new(response.body().take(100).concat2().then(move |body| {
+ future::err(ApiError(format!(
+ "Unexpected response code {}:\n{:?}\n\n{}",
+ code,
+ headers,
+ match body {
+ Ok(ref body) => match str::from_utf8(body) {
+ Ok(body) => Cow::from(body),
+ Err(e) => {
+ Cow::from(format!("<Body was not UTF8: {:?}>", e))
+ }
+ },
+ Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
+ }
+ )))
+ })) as Box<Future<Item = _, Error = _>>
+ }
+ }),
+ )
+ }
+}
+
+#[derive(Debug)]
+pub enum ClientInitError {
+ InvalidScheme,
+ InvalidUri(hyper::error::UriError),
+ MissingHost,
+ SslError(openssl::error::ErrorStack),
+}
+
+impl From<hyper::error::UriError> for ClientInitError {
+ fn from(err: hyper::error::UriError) -> ClientInitError {
+ ClientInitError::InvalidUri(err)
+ }
+}
+
+impl From<openssl::error::ErrorStack> for ClientInitError {
+ fn from(err: openssl::error::ErrorStack) -> ClientInitError {
+ ClientInitError::SslError(err)
+ }
+}
+
+impl fmt::Display for ClientInitError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ (self as &fmt::Debug).fmt(f)
+ }
+}
+
+impl error::Error for ClientInitError {
+ fn description(&self) -> &str {
+ "Failed to produce a hyper client."
+ }
+}
diff --git a/rust/fatcat-api/src/lib.rs b/rust/fatcat-api/src/lib.rs
new file mode 100644
index 00000000..36573537
--- /dev/null
+++ b/rust/fatcat-api/src/lib.rs
@@ -0,0 +1,599 @@
+#![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports,
+ unused_extern_crates, non_camel_case_types)]
+extern crate serde;
+#[macro_use]
+extern crate serde_derive;
+extern crate serde_json;
+
+extern crate chrono;
+extern crate futures;
+#[macro_use]
+extern crate lazy_static;
+#[macro_use]
+extern crate log;
+
+// Logically this should be in the client and server modules, but rust doesn't allow `macro_use` from a module.
+#[cfg(any(feature = "client", feature = "server"))]
+#[macro_use]
+extern crate hyper;
+
+extern crate swagger;
+
+use futures::Stream;
+use std::io::Error;
+
+#[allow(unused_imports)]
+use std::collections::HashMap;
+
+pub use futures::Future;
+
+#[cfg(any(feature = "client", feature = "server"))]
+mod mimetypes;
+
+pub use swagger::{ApiError, Context, ContextWrapper};
+
+pub const BASE_PATH: &'static str = "/v0";
+pub const API_VERSION: &'static str = "0.1.0";
+
+#[derive(Debug, PartialEq)]
+pub enum ContainerIdGetResponse {
+ /// fetch a single container by id
+ FetchASingleContainerById(models::ContainerEntity),
+ /// bad request
+ BadRequest(models::Error),
+ /// generic error response
+ GenericErrorResponse(models::Error),
+}
+
+#[derive(Debug, PartialEq)]
+pub enum ContainerLookupGetResponse {
+ /// find a single container by external identifer
+ FindASingleContainerByExternalIdentifer(models::ContainerEntity),
+ /// bad request
+ BadRequest(models::Error),
+ /// no such container
+ NoSuchContainer(models::Error),
+ /// generic error response
+ GenericErrorResponse(models::Error),
+}
+
+#[derive(Debug, PartialEq)]
+pub enum ContainerPostResponse {
+ /// created
+ Created(models::EntityEdit),
+ /// bad request
+ BadRequest(models::Error),
+ /// generic error response
+ GenericErrorResponse(models::Error),
+}
+
+#[derive(Debug, PartialEq)]
+pub enum CreatorIdGetResponse {
+ /// fetch a single creator by id
+ FetchASingleCreatorById(models::CreatorEntity),
+ /// bad request
+ BadRequest(models::Error),
+ /// generic error response
+ GenericErrorResponse(models::Error),
+}
+
+#[derive(Debug, PartialEq)]
+pub enum CreatorLookupGetResponse {
+ /// find a single creator by external identifer
+ FindASingleCreatorByExternalIdentifer(models::CreatorEntity),
+ /// bad request
+ BadRequest(models::Error),
+ /// no such creator
+ NoSuchCreator(models::Error),
+ /// generic error response
+ GenericErrorResponse(models::Error),
+}
+
+#[derive(Debug, PartialEq)]
+pub enum CreatorPostResponse {
+ /// created
+ Created(models::EntityEdit),
+ /// bad request
+ BadRequest(models::Error),
+ /// generic error response
+ GenericErrorResponse(models::Error),
+}
+
+#[derive(Debug, PartialEq)]
+pub enum EditgroupIdAcceptPostResponse {
+ /// merged editgroup successfully (\&quot;live\&quot;)
+ MergedEditgroupSuccessfully_(models::Success),
+ /// editgroup is in an unmergable state
+ EditgroupIsInAnUnmergableState(models::Error),
+ /// no such editgroup
+ NoSuchEditgroup(models::Error),
+ /// generic error response
+ GenericErrorResponse(models::Error),
+}
+
+#[derive(Debug, PartialEq)]
+pub enum EditgroupIdGetResponse {
+ /// fetch editgroup by identifier
+ FetchEditgroupByIdentifier(models::Editgroup),
+ /// no such editgroup
+ NoSuchEditgroup(models::Error),
+ /// generic error response
+ GenericErrorResponse(models::Error),
+}
+
+#[derive(Debug, PartialEq)]
+pub enum EditgroupPostResponse {
+ /// successfully created
+ SuccessfullyCreated(models::Editgroup),
+ /// invalid request parameters
+ InvalidRequestParameters(models::Error),
+ /// generic error response
+ GenericErrorResponse(models::Error),
+}
+
+#[derive(Debug, PartialEq)]
+pub enum EditorUsernameChangelogGetResponse {
+ /// find changes (editgroups) by this editor which have been merged
+ FindChanges_(models::Changelogentry),
+ /// username not found
+ UsernameNotFound(models::Error),
+ /// generic error response
+ GenericErrorResponse(models::Error),
+}
+
+#[derive(Debug, PartialEq)]
+pub enum EditorUsernameGetResponse {
+ /// fetch generic information about an editor
+ FetchGenericInformationAboutAnEditor(models::Editor),
+ /// username not found
+ UsernameNotFound(models::Error),
+ /// generic error response
+ GenericErrorResponse(models::Error),
+}
+
+#[derive(Debug, PartialEq)]
+pub enum FileIdGetResponse {
+ /// fetch a single file by id
+ FetchASingleFileById(models::FileEntity),
+ /// bad request
+ BadRequest(models::Error),
+ /// generic error response
+ GenericErrorResponse(models::Error),
+}
+
+#[derive(Debug, PartialEq)]
+pub enum FileLookupGetResponse {
+ /// find a single file by external identifer
+ FindASingleFileByExternalIdentifer(models::FileEntity),
+ /// bad request
+ BadRequest(models::Error),
+ /// no such file
+ NoSuchFile(models::Error),
+ /// generic error response
+ GenericErrorResponse(models::Error),
+}
+
+#[derive(Debug, PartialEq)]
+pub enum FilePostResponse {
+ /// created
+ Created(models::EntityEdit),
+ /// bad request
+ BadRequest(models::Error),
+ /// generic error response
+ GenericErrorResponse(models::Error),
+}
+
+#[derive(Debug, PartialEq)]
+pub enum ReleaseIdGetResponse {
+ /// fetch a single release by id
+ FetchASingleReleaseById(models::ReleaseEntity),
+ /// bad request
+ BadRequest(models::Error),
+ /// generic error response
+ GenericErrorResponse(models::Error),
+}
+
+#[derive(Debug, PartialEq)]
+pub enum ReleaseLookupGetResponse {
+ /// find a single release by external identifer
+ FindASingleReleaseByExternalIdentifer(models::ReleaseEntity),
+ /// bad request
+ BadRequest(models::Error),
+ /// no such release
+ NoSuchRelease(models::Error),
+ /// generic error response
+ GenericErrorResponse(models::Error),
+}
+
+#[derive(Debug, PartialEq)]
+pub enum ReleasePostResponse {
+ /// created
+ Created(models::EntityEdit),
+ /// bad request
+ BadRequest(models::Error),
+ /// generic error response
+ GenericErrorResponse(models::Error),
+}
+
+#[derive(Debug, PartialEq)]
+pub enum WorkIdGetResponse {
+ /// fetch a single work by id
+ FetchASingleWorkById(models::WorkEntity),
+ /// bad request
+ BadRequest(models::Error),
+ /// generic error response
+ GenericErrorResponse(models::Error),
+}
+
+#[derive(Debug, PartialEq)]
+pub enum WorkPostResponse {
+ /// created
+ Created(models::EntityEdit),
+ /// bad request
+ BadRequest(models::Error),
+ /// generic error response
+ GenericErrorResponse(models::Error),
+}
+
+/// API
+pub trait Api {
+ fn container_id_get(
+ &self,
+ id: String,
+ context: &Context,
+ ) -> Box<Future<Item = ContainerIdGetResponse, Error = ApiError>>;
+
+ fn container_lookup_get(
+ &self,
+ issn: String,
+ context: &Context,
+ ) -> Box<Future<Item = ContainerLookupGetResponse, Error = ApiError>>;
+
+ fn container_post(
+ &self,
+ body: Option<models::ContainerEntity>,
+ context: &Context,
+ ) -> Box<Future<Item = ContainerPostResponse, Error = ApiError>>;
+
+ fn creator_id_get(
+ &self,
+ id: String,
+ context: &Context,
+ ) -> Box<Future<Item = CreatorIdGetResponse, Error = ApiError>>;
+
+ fn creator_lookup_get(
+ &self,
+ orcid: String,
+ context: &Context,
+ ) -> Box<Future<Item = CreatorLookupGetResponse, Error = ApiError>>;
+
+ fn creator_post(
+ &self,
+ body: Option<models::CreatorEntity>,
+ context: &Context,
+ ) -> Box<Future<Item = CreatorPostResponse, Error = ApiError>>;
+
+ fn editgroup_id_accept_post(
+ &self,
+ id: i32,
+ context: &Context,
+ ) -> Box<Future<Item = EditgroupIdAcceptPostResponse, Error = ApiError>>;
+
+ fn editgroup_id_get(
+ &self,
+ id: i32,
+ context: &Context,
+ ) -> Box<Future<Item = EditgroupIdGetResponse, Error = ApiError>>;
+
+ fn editgroup_post(
+ &self,
+ context: &Context,
+ ) -> Box<Future<Item = EditgroupPostResponse, Error = ApiError>>;
+
+ fn editor_username_changelog_get(
+ &self,
+ username: String,
+ context: &Context,
+ ) -> Box<Future<Item = EditorUsernameChangelogGetResponse, Error = ApiError>>;
+
+ fn editor_username_get(
+ &self,
+ username: String,
+ context: &Context,
+ ) -> Box<Future<Item = EditorUsernameGetResponse, Error = ApiError>>;
+
+ fn file_id_get(
+ &self,
+ id: String,
+ context: &Context,
+ ) -> Box<Future<Item = FileIdGetResponse, Error = ApiError>>;
+
+ fn file_lookup_get(
+ &self,
+ sha1: String,
+ context: &Context,
+ ) -> Box<Future<Item = FileLookupGetResponse, Error = ApiError>>;
+
+ fn file_post(
+ &self,
+ body: Option<models::FileEntity>,
+ context: &Context,
+ ) -> Box<Future<Item = FilePostResponse, Error = ApiError>>;
+
+ fn release_id_get(
+ &self,
+ id: String,
+ context: &Context,
+ ) -> Box<Future<Item = ReleaseIdGetResponse, Error = ApiError>>;
+
+ fn release_lookup_get(
+ &self,
+ doi: String,
+ context: &Context,
+ ) -> Box<Future<Item = ReleaseLookupGetResponse, Error = ApiError>>;
+
+ fn release_post(
+ &self,
+ body: Option<models::ReleaseEntity>,
+ context: &Context,
+ ) -> Box<Future<Item = ReleasePostResponse, Error = ApiError>>;
+
+ fn work_id_get(
+ &self,
+ id: String,
+ context: &Context,
+ ) -> Box<Future<Item = WorkIdGetResponse, Error = ApiError>>;
+
+ fn work_post(
+ &self,
+ body: Option<models::WorkEntity>,
+ context: &Context,
+ ) -> Box<Future<Item = WorkPostResponse, Error = ApiError>>;
+}
+
+/// API without a `Context`
+pub trait ApiNoContext {
+ fn container_id_get(
+ &self,
+ id: String,
+ ) -> Box<Future<Item = ContainerIdGetResponse, Error = ApiError>>;
+
+ fn container_lookup_get(
+ &self,
+ issn: String,
+ ) -> Box<Future<Item = ContainerLookupGetResponse, Error = ApiError>>;
+
+ fn container_post(
+ &self,
+ body: Option<models::ContainerEntity>,
+ ) -> Box<Future<Item = ContainerPostResponse, Error = ApiError>>;
+
+ fn creator_id_get(
+ &self,
+ id: String,
+ ) -> Box<Future<Item = CreatorIdGetResponse, Error = ApiError>>;
+
+ fn creator_lookup_get(
+ &self,
+ orcid: String,
+ ) -> Box<Future<Item = CreatorLookupGetResponse, Error = ApiError>>;
+
+ fn creator_post(
+ &self,
+ body: Option<models::CreatorEntity>,
+ ) -> Box<Future<Item = CreatorPostResponse, Error = ApiError>>;
+
+ fn editgroup_id_accept_post(
+ &self,
+ id: i32,
+ ) -> Box<Future<Item = EditgroupIdAcceptPostResponse, Error = ApiError>>;
+
+ fn editgroup_id_get(
+ &self,
+ id: i32,
+ ) -> Box<Future<Item = EditgroupIdGetResponse, Error = ApiError>>;
+
+ fn editgroup_post(&self) -> Box<Future<Item = EditgroupPostResponse, Error = ApiError>>;
+
+ fn editor_username_changelog_get(
+ &self,
+ username: String,
+ ) -> Box<Future<Item = EditorUsernameChangelogGetResponse, Error = ApiError>>;
+
+ fn editor_username_get(
+ &self,
+ username: String,
+ ) -> Box<Future<Item = EditorUsernameGetResponse, Error = ApiError>>;
+
+ fn file_id_get(&self, id: String) -> Box<Future<Item = FileIdGetResponse, Error = ApiError>>;
+
+ fn file_lookup_get(
+ &self,
+ sha1: String,
+ ) -> Box<Future<Item = FileLookupGetResponse, Error = ApiError>>;
+
+ fn file_post(
+ &self,
+ body: Option<models::FileEntity>,
+ ) -> Box<Future<Item = FilePostResponse, Error = ApiError>>;
+
+ fn release_id_get(
+ &self,
+ id: String,
+ ) -> Box<Future<Item = ReleaseIdGetResponse, Error = ApiError>>;
+
+ fn release_lookup_get(
+ &self,
+ doi: String,
+ ) -> Box<Future<Item = ReleaseLookupGetResponse, Error = ApiError>>;
+
+ fn release_post(
+ &self,
+ body: Option<models::ReleaseEntity>,
+ ) -> Box<Future<Item = ReleasePostResponse, Error = ApiError>>;
+
+ fn work_id_get(&self, id: String) -> Box<Future<Item = WorkIdGetResponse, Error = ApiError>>;
+
+ fn work_post(
+ &self,
+ body: Option<models::WorkEntity>,
+ ) -> Box<Future<Item = WorkPostResponse, Error = ApiError>>;
+}
+
+/// Trait to extend an API to make it easy to bind it to a context.
+pub trait ContextWrapperExt<'a>
+where
+ Self: Sized,
+{
+ /// Binds this API to a context.
+ fn with_context(self: &'a Self, context: Context) -> ContextWrapper<'a, Self>;
+}
+
+impl<'a, T: Api + Sized> ContextWrapperExt<'a> for T {
+ fn with_context(self: &'a T, context: Context) -> ContextWrapper<'a, T> {
+ ContextWrapper::<T>::new(self, context)
+ }
+}
+
+impl<'a, T: Api> ApiNoContext for ContextWrapper<'a, T> {
+ fn container_id_get(
+ &self,
+ id: String,
+ ) -> Box<Future<Item = ContainerIdGetResponse, Error = ApiError>> {
+ self.api().container_id_get(id, &self.context())
+ }
+
+ fn container_lookup_get(
+ &self,
+ issn: String,
+ ) -> Box<Future<Item = ContainerLookupGetResponse, Error = ApiError>> {
+ self.api().container_lookup_get(issn, &self.context())
+ }
+
+ fn container_post(
+ &self,
+ body: Option<models::ContainerEntity>,
+ ) -> Box<Future<Item = ContainerPostResponse, Error = ApiError>> {
+ self.api().container_post(body, &self.context())
+ }
+
+ fn creator_id_get(
+ &self,
+ id: String,
+ ) -> Box<Future<Item = CreatorIdGetResponse, Error = ApiError>> {
+ self.api().creator_id_get(id, &self.context())
+ }
+
+ fn creator_lookup_get(
+ &self,
+ orcid: String,
+ ) -> Box<Future<Item = CreatorLookupGetResponse, Error = ApiError>> {
+ self.api().creator_lookup_get(orcid, &self.context())
+ }
+
+ fn creator_post(
+ &self,
+ body: Option<models::CreatorEntity>,
+ ) -> Box<Future<Item = CreatorPostResponse, Error = ApiError>> {
+ self.api().creator_post(body, &self.context())
+ }
+
+ fn editgroup_id_accept_post(
+ &self,
+ id: i32,
+ ) -> Box<Future<Item = EditgroupIdAcceptPostResponse, Error = ApiError>> {
+ self.api().editgroup_id_accept_post(id, &self.context())
+ }
+
+ fn editgroup_id_get(
+ &self,
+ id: i32,
+ ) -> Box<Future<Item = EditgroupIdGetResponse, Error = ApiError>> {
+ self.api().editgroup_id_get(id, &self.context())
+ }
+
+ fn editgroup_post(&self) -> Box<Future<Item = EditgroupPostResponse, Error = ApiError>> {
+ self.api().editgroup_post(&self.context())
+ }
+
+ fn editor_username_changelog_get(
+ &self,
+ username: String,
+ ) -> Box<Future<Item = EditorUsernameChangelogGetResponse, Error = ApiError>> {
+ self.api()
+ .editor_username_changelog_get(username, &self.context())
+ }
+
+ fn editor_username_get(
+ &self,
+ username: String,
+ ) -> Box<Future<Item = EditorUsernameGetResponse, Error = ApiError>> {
+ self.api().editor_username_get(username, &self.context())
+ }
+
+ fn file_id_get(&self, id: String) -> Box<Future<Item = FileIdGetResponse, Error = ApiError>> {
+ self.api().file_id_get(id, &self.context())
+ }
+
+ fn file_lookup_get(
+ &self,
+ sha1: String,
+ ) -> Box<Future<Item = FileLookupGetResponse, Error = ApiError>> {
+ self.api().file_lookup_get(sha1, &self.context())
+ }
+
+ fn file_post(
+ &self,
+ body: Option<models::FileEntity>,
+ ) -> Box<Future<Item = FilePostResponse, Error = ApiError>> {
+ self.api().file_post(body, &self.context())
+ }
+
+ fn release_id_get(
+ &self,
+ id: String,
+ ) -> Box<Future<Item = ReleaseIdGetResponse, Error = ApiError>> {
+ self.api().release_id_get(id, &self.context())
+ }
+
+ fn release_lookup_get(
+ &self,
+ doi: String,
+ ) -> Box<Future<Item = ReleaseLookupGetResponse, Error = ApiError>> {
+ self.api().release_lookup_get(doi, &self.context())
+ }
+
+ fn release_post(
+ &self,
+ body: Option<models::ReleaseEntity>,
+ ) -> Box<Future<Item = ReleasePostResponse, Error = ApiError>> {
+ self.api().release_post(body, &self.context())
+ }
+
+ fn work_id_get(&self, id: String) -> Box<Future<Item = WorkIdGetResponse, Error = ApiError>> {
+ self.api().work_id_get(id, &self.context())
+ }
+
+ fn work_post(
+ &self,
+ body: Option<models::WorkEntity>,
+ ) -> Box<Future<Item = WorkPostResponse, Error = ApiError>> {
+ self.api().work_post(body, &self.context())
+ }
+}
+
+#[cfg(feature = "client")]
+pub mod client;
+
+// Re-export Client as a top-level name
+#[cfg(feature = "client")]
+pub use self::client::Client;
+
+#[cfg(feature = "server")]
+pub mod server;
+
+// Re-export router() as a top-level name
+#[cfg(feature = "server")]
+pub use self::server::Service;
+
+pub mod models;
diff --git a/rust/fatcat-api/src/mimetypes.rs b/rust/fatcat-api/src/mimetypes.rs
new file mode 100644
index 00000000..dfbff890
--- /dev/null
+++ b/rust/fatcat-api/src/mimetypes.rs
@@ -0,0 +1,313 @@
+/// mime types for requests and responses
+
+pub mod responses {
+ use hyper::mime::*;
+
+ // The macro is called per-operation to beat the recursion limit
+ /// Create Mime objects for the response content types for ContainerIdGet
+ lazy_static! {
+ pub static ref CONTAINER_ID_GET_FETCH_A_SINGLE_CONTAINER_BY_ID: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for ContainerIdGet
+ lazy_static! {
+ pub static ref CONTAINER_ID_GET_BAD_REQUEST: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for ContainerIdGet
+ lazy_static! {
+ pub static ref CONTAINER_ID_GET_GENERIC_ERROR_RESPONSE: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for ContainerLookupGet
+ lazy_static! {
+ pub static ref CONTAINER_LOOKUP_GET_FIND_A_SINGLE_CONTAINER_BY_EXTERNAL_IDENTIFER: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for ContainerLookupGet
+ lazy_static! {
+ pub static ref CONTAINER_LOOKUP_GET_BAD_REQUEST: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for ContainerLookupGet
+ lazy_static! {
+ pub static ref CONTAINER_LOOKUP_GET_NO_SUCH_CONTAINER: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for ContainerLookupGet
+ lazy_static! {
+ pub static ref CONTAINER_LOOKUP_GET_GENERIC_ERROR_RESPONSE: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for ContainerPost
+ lazy_static! {
+ pub static ref CONTAINER_POST_CREATED: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for ContainerPost
+ lazy_static! {
+ pub static ref CONTAINER_POST_BAD_REQUEST: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for ContainerPost
+ lazy_static! {
+ pub static ref CONTAINER_POST_GENERIC_ERROR_RESPONSE: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for CreatorIdGet
+ lazy_static! {
+ pub static ref CREATOR_ID_GET_FETCH_A_SINGLE_CREATOR_BY_ID: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for CreatorIdGet
+ lazy_static! {
+ pub static ref CREATOR_ID_GET_BAD_REQUEST: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for CreatorIdGet
+ lazy_static! {
+ pub static ref CREATOR_ID_GET_GENERIC_ERROR_RESPONSE: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for CreatorLookupGet
+ lazy_static! {
+ pub static ref CREATOR_LOOKUP_GET_FIND_A_SINGLE_CREATOR_BY_EXTERNAL_IDENTIFER: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for CreatorLookupGet
+ lazy_static! {
+ pub static ref CREATOR_LOOKUP_GET_BAD_REQUEST: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for CreatorLookupGet
+ lazy_static! {
+ pub static ref CREATOR_LOOKUP_GET_NO_SUCH_CREATOR: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for CreatorLookupGet
+ lazy_static! {
+ pub static ref CREATOR_LOOKUP_GET_GENERIC_ERROR_RESPONSE: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for CreatorPost
+ lazy_static! {
+ pub static ref CREATOR_POST_CREATED: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for CreatorPost
+ lazy_static! {
+ pub static ref CREATOR_POST_BAD_REQUEST: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for CreatorPost
+ lazy_static! {
+ pub static ref CREATOR_POST_GENERIC_ERROR_RESPONSE: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for EditgroupIdAcceptPost
+ lazy_static! {
+ pub static ref EDITGROUP_ID_ACCEPT_POST_MERGED_EDITGROUP_SUCCESSFULLY_: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for EditgroupIdAcceptPost
+ lazy_static! {
+ pub static ref EDITGROUP_ID_ACCEPT_POST_EDITGROUP_IS_IN_AN_UNMERGABLE_STATE: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for EditgroupIdAcceptPost
+ lazy_static! {
+ pub static ref EDITGROUP_ID_ACCEPT_POST_NO_SUCH_EDITGROUP: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for EditgroupIdAcceptPost
+ lazy_static! {
+ pub static ref EDITGROUP_ID_ACCEPT_POST_GENERIC_ERROR_RESPONSE: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for EditgroupIdGet
+ lazy_static! {
+ pub static ref EDITGROUP_ID_GET_FETCH_EDITGROUP_BY_IDENTIFIER: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for EditgroupIdGet
+ lazy_static! {
+ pub static ref EDITGROUP_ID_GET_NO_SUCH_EDITGROUP: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for EditgroupIdGet
+ lazy_static! {
+ pub static ref EDITGROUP_ID_GET_GENERIC_ERROR_RESPONSE: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for EditgroupPost
+ lazy_static! {
+ pub static ref EDITGROUP_POST_SUCCESSFULLY_CREATED: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for EditgroupPost
+ lazy_static! {
+ pub static ref EDITGROUP_POST_INVALID_REQUEST_PARAMETERS: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for EditgroupPost
+ lazy_static! {
+ pub static ref EDITGROUP_POST_GENERIC_ERROR_RESPONSE: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for EditorUsernameChangelogGet
+ lazy_static! {
+ pub static ref EDITOR_USERNAME_CHANGELOG_GET_FIND_CHANGES_: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for EditorUsernameChangelogGet
+ lazy_static! {
+ pub static ref EDITOR_USERNAME_CHANGELOG_GET_USERNAME_NOT_FOUND: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for EditorUsernameChangelogGet
+ lazy_static! {
+ pub static ref EDITOR_USERNAME_CHANGELOG_GET_GENERIC_ERROR_RESPONSE: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for EditorUsernameGet
+ lazy_static! {
+ pub static ref EDITOR_USERNAME_GET_FETCH_GENERIC_INFORMATION_ABOUT_AN_EDITOR: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for EditorUsernameGet
+ lazy_static! {
+ pub static ref EDITOR_USERNAME_GET_USERNAME_NOT_FOUND: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for EditorUsernameGet
+ lazy_static! {
+ pub static ref EDITOR_USERNAME_GET_GENERIC_ERROR_RESPONSE: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for FileIdGet
+ lazy_static! {
+ pub static ref FILE_ID_GET_FETCH_A_SINGLE_FILE_BY_ID: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for FileIdGet
+ lazy_static! {
+ pub static ref FILE_ID_GET_BAD_REQUEST: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for FileIdGet
+ lazy_static! {
+ pub static ref FILE_ID_GET_GENERIC_ERROR_RESPONSE: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for FileLookupGet
+ lazy_static! {
+ pub static ref FILE_LOOKUP_GET_FIND_A_SINGLE_FILE_BY_EXTERNAL_IDENTIFER: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for FileLookupGet
+ lazy_static! {
+ pub static ref FILE_LOOKUP_GET_BAD_REQUEST: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for FileLookupGet
+ lazy_static! {
+ pub static ref FILE_LOOKUP_GET_NO_SUCH_FILE: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for FileLookupGet
+ lazy_static! {
+ pub static ref FILE_LOOKUP_GET_GENERIC_ERROR_RESPONSE: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for FilePost
+ lazy_static! {
+ pub static ref FILE_POST_CREATED: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for FilePost
+ lazy_static! {
+ pub static ref FILE_POST_BAD_REQUEST: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for FilePost
+ lazy_static! {
+ pub static ref FILE_POST_GENERIC_ERROR_RESPONSE: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for ReleaseIdGet
+ lazy_static! {
+ pub static ref RELEASE_ID_GET_FETCH_A_SINGLE_RELEASE_BY_ID: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for ReleaseIdGet
+ lazy_static! {
+ pub static ref RELEASE_ID_GET_BAD_REQUEST: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for ReleaseIdGet
+ lazy_static! {
+ pub static ref RELEASE_ID_GET_GENERIC_ERROR_RESPONSE: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for ReleaseLookupGet
+ lazy_static! {
+ pub static ref RELEASE_LOOKUP_GET_FIND_A_SINGLE_RELEASE_BY_EXTERNAL_IDENTIFER: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for ReleaseLookupGet
+ lazy_static! {
+ pub static ref RELEASE_LOOKUP_GET_BAD_REQUEST: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for ReleaseLookupGet
+ lazy_static! {
+ pub static ref RELEASE_LOOKUP_GET_NO_SUCH_RELEASE: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for ReleaseLookupGet
+ lazy_static! {
+ pub static ref RELEASE_LOOKUP_GET_GENERIC_ERROR_RESPONSE: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for ReleasePost
+ lazy_static! {
+ pub static ref RELEASE_POST_CREATED: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for ReleasePost
+ lazy_static! {
+ pub static ref RELEASE_POST_BAD_REQUEST: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for ReleasePost
+ lazy_static! {
+ pub static ref RELEASE_POST_GENERIC_ERROR_RESPONSE: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for WorkIdGet
+ lazy_static! {
+ pub static ref WORK_ID_GET_FETCH_A_SINGLE_WORK_BY_ID: Mime =
+ "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for WorkIdGet
+ lazy_static! {
+ pub static ref WORK_ID_GET_BAD_REQUEST: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for WorkIdGet
+ lazy_static! {
+ pub static ref WORK_ID_GET_GENERIC_ERROR_RESPONSE: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for WorkPost
+ lazy_static! {
+ pub static ref WORK_POST_CREATED: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for WorkPost
+ lazy_static! {
+ pub static ref WORK_POST_BAD_REQUEST: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the response content types for WorkPost
+ lazy_static! {
+ pub static ref WORK_POST_GENERIC_ERROR_RESPONSE: Mime = "application/json".parse().unwrap();
+ }
+
+}
+
+pub mod requests {
+ use hyper::mime::*;
+ /// Create Mime objects for the request content types for ContainerPost
+ lazy_static! {
+ pub static ref CONTAINER_POST: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the request content types for CreatorPost
+ lazy_static! {
+ pub static ref CREATOR_POST: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the request content types for FilePost
+ lazy_static! {
+ pub static ref FILE_POST: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the request content types for ReleasePost
+ lazy_static! {
+ pub static ref RELEASE_POST: Mime = "application/json".parse().unwrap();
+ }
+ /// Create Mime objects for the request content types for WorkPost
+ lazy_static! {
+ pub static ref WORK_POST: Mime = "application/json".parse().unwrap();
+ }
+
+}
diff --git a/rust/fatcat-api/src/models.rs b/rust/fatcat-api/src/models.rs
new file mode 100644
index 00000000..3d104b78
--- /dev/null
+++ b/rust/fatcat-api/src/models.rs
@@ -0,0 +1,392 @@
+#![allow(unused_imports, unused_qualifications, unused_extern_crates)]
+extern crate chrono;
+extern crate uuid;
+
+use serde::ser::Serializer;
+
+use models;
+use std::collections::HashMap;
+use swagger;
+
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+pub struct Changelogentry {
+ #[serde(rename = "index")]
+ pub index: isize,
+
+ #[serde(rename = "editgroup_id")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub editgroup_id: Option<isize>,
+
+ #[serde(rename = "timestamp")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub timestamp: Option<chrono::DateTime<chrono::Utc>>,
+}
+
+impl Changelogentry {
+ pub fn new(index: isize) -> Changelogentry {
+ Changelogentry {
+ index: index,
+ editgroup_id: None,
+ timestamp: None,
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+pub struct ContainerEntity {
+ #[serde(rename = "issn")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub issn: Option<String>,
+
+ #[serde(rename = "publisher")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub publisher: Option<String>,
+
+ #[serde(rename = "parent")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub parent: Option<String>,
+
+ #[serde(rename = "name")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub name: Option<String>,
+
+ // Note: inline enums are not fully supported by swagger-codegen
+ #[serde(rename = "state")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub state: Option<String>,
+
+ #[serde(rename = "ident")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub ident: Option<String>,
+
+ #[serde(rename = "revision")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub revision: Option<isize>,
+
+ #[serde(rename = "redirect")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub redirect: Option<String>,
+
+ #[serde(rename = "editgroup")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub editgroup: Option<isize>,
+}
+
+impl ContainerEntity {
+ pub fn new() -> ContainerEntity {
+ ContainerEntity {
+ issn: None,
+ publisher: None,
+ parent: None,
+ name: None,
+ state: None,
+ ident: None,
+ revision: None,
+ redirect: None,
+ editgroup: None,
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+pub struct CreatorEntity {
+ #[serde(rename = "orcid")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub orcid: Option<String>,
+
+ #[serde(rename = "name")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub name: Option<String>,
+
+ #[serde(rename = "editgroup")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub editgroup: Option<isize>,
+
+ #[serde(rename = "redirect")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub redirect: Option<String>,
+
+ #[serde(rename = "revision")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub revision: Option<isize>,
+
+ #[serde(rename = "ident")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub ident: Option<String>,
+
+ // Note: inline enums are not fully supported by swagger-codegen
+ #[serde(rename = "state")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub state: Option<String>,
+}
+
+impl CreatorEntity {
+ pub fn new() -> CreatorEntity {
+ CreatorEntity {
+ orcid: None,
+ name: None,
+ editgroup: None,
+ redirect: None,
+ revision: None,
+ ident: None,
+ state: None,
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+pub struct Editgroup {
+ #[serde(rename = "id")]
+ pub id: isize,
+
+ #[serde(rename = "editor_id")]
+ pub editor_id: isize,
+}
+
+impl Editgroup {
+ pub fn new(id: isize, editor_id: isize) -> Editgroup {
+ Editgroup {
+ id: id,
+ editor_id: editor_id,
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+pub struct Editor {
+ #[serde(rename = "username")]
+ pub username: String,
+}
+
+impl Editor {
+ pub fn new(username: String) -> Editor {
+ Editor { username: username }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+pub struct EntityEdit {
+ #[serde(rename = "editgroup_id")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub editgroup_id: Option<isize>,
+
+ #[serde(rename = "revision")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub revision: Option<isize>,
+
+ #[serde(rename = "ident")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub ident: Option<String>,
+
+ #[serde(rename = "id")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub id: Option<isize>,
+}
+
+impl EntityEdit {
+ pub fn new() -> EntityEdit {
+ EntityEdit {
+ editgroup_id: None,
+ revision: None,
+ ident: None,
+ id: None,
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+pub struct Error {
+ #[serde(rename = "message")]
+ pub message: String,
+}
+
+impl Error {
+ pub fn new(message: String) -> Error {
+ Error { message: message }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+pub struct FileEntity {
+ #[serde(rename = "url")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub url: Option<String>,
+
+ #[serde(rename = "sha1")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub sha1: Option<String>,
+
+ #[serde(rename = "size")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub size: Option<isize>,
+
+ #[serde(rename = "editgroup")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub editgroup: Option<isize>,
+
+ #[serde(rename = "redirect")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub redirect: Option<String>,
+
+ #[serde(rename = "revision")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub revision: Option<isize>,
+
+ #[serde(rename = "ident")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub ident: Option<String>,
+
+ // Note: inline enums are not fully supported by swagger-codegen
+ #[serde(rename = "state")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub state: Option<String>,
+}
+
+impl FileEntity {
+ pub fn new() -> FileEntity {
+ FileEntity {
+ url: None,
+ sha1: None,
+ size: None,
+ editgroup: None,
+ redirect: None,
+ revision: None,
+ ident: None,
+ state: None,
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+pub struct ReleaseEntity {
+ #[serde(rename = "issue")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub issue: Option<String>,
+
+ #[serde(rename = "pages")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub pages: Option<String>,
+
+ #[serde(rename = "volume")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub volume: Option<String>,
+
+ #[serde(rename = "doi")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub doi: Option<String>,
+
+ #[serde(rename = "release_type")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub release_type: Option<String>,
+
+ #[serde(rename = "license")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub license: Option<String>,
+
+ #[serde(rename = "container")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub container: Option<String>,
+
+ #[serde(rename = "work")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub work: Option<String>,
+
+ // Note: inline enums are not fully supported by swagger-codegen
+ #[serde(rename = "state")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub state: Option<String>,
+
+ #[serde(rename = "ident")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub ident: Option<String>,
+
+ #[serde(rename = "revision")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub revision: Option<isize>,
+
+ #[serde(rename = "redirect")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub redirect: Option<String>,
+
+ #[serde(rename = "editgroup")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub editgroup: Option<isize>,
+}
+
+impl ReleaseEntity {
+ pub fn new() -> ReleaseEntity {
+ ReleaseEntity {
+ issue: None,
+ pages: None,
+ volume: None,
+ doi: None,
+ release_type: None,
+ license: None,
+ container: None,
+ work: None,
+ state: None,
+ ident: None,
+ revision: None,
+ redirect: None,
+ editgroup: None,
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+pub struct Success {
+ #[serde(rename = "message")]
+ pub message: String,
+}
+
+impl Success {
+ pub fn new(message: String) -> Success {
+ Success { message: message }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+pub struct WorkEntity {
+ #[serde(rename = "work_type")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub work_type: Option<String>,
+
+ #[serde(rename = "title")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub title: Option<String>,
+
+ #[serde(rename = "editgroup")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub editgroup: Option<isize>,
+
+ #[serde(rename = "redirect")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub redirect: Option<String>,
+
+ #[serde(rename = "revision")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub revision: Option<isize>,
+
+ #[serde(rename = "ident")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub ident: Option<String>,
+
+ // Note: inline enums are not fully supported by swagger-codegen
+ #[serde(rename = "state")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub state: Option<String>,
+}
+
+impl WorkEntity {
+ pub fn new() -> WorkEntity {
+ WorkEntity {
+ work_type: None,
+ title: None,
+ editgroup: None,
+ redirect: None,
+ revision: None,
+ ident: None,
+ state: None,
+ }
+ }
+}
diff --git a/rust/fatcat-api/src/server/auth.rs b/rust/fatcat-api/src/server/auth.rs
new file mode 100644
index 00000000..48dfd7d6
--- /dev/null
+++ b/rust/fatcat-api/src/server/auth.rs
@@ -0,0 +1,93 @@
+use Api;
+use hyper;
+use hyper::{Error, Request, Response, StatusCode};
+use server::url::form_urlencoded;
+use std::io;
+use swagger::auth::{AuthData, Authorization, Scopes};
+
+pub struct NewService<T>
+where
+ T: hyper::server::NewService<
+ Request = (Request, Option<AuthData>),
+ Response = Response,
+ Error = Error,
+ >,
+{
+ inner: T,
+}
+
+impl<T> NewService<T>
+where
+ T: hyper::server::NewService<
+ Request = (Request, Option<AuthData>),
+ Response = Response,
+ Error = Error,
+ >
+ + 'static,
+{
+ pub fn new(inner: T) -> NewService<T> {
+ NewService { inner }
+ }
+}
+
+impl<T> hyper::server::NewService for NewService<T>
+where
+ T: hyper::server::NewService<
+ Request = (Request, Option<AuthData>),
+ Response = Response,
+ Error = Error,
+ >
+ + 'static,
+{
+ type Request = Request;
+ type Response = Response;
+ type Error = Error;
+ type Instance = Service<T::Instance>;
+
+ fn new_service(&self) -> Result<Self::Instance, io::Error> {
+ self.inner.new_service().map(|s| Service::new(s))
+ }
+}
+
+/// Middleware to extract authentication data from request
+pub struct Service<T>
+where
+ T: hyper::server::Service<
+ Request = (Request, Option<AuthData>),
+ Response = Response,
+ Error = Error,
+ >,
+{
+ inner: T,
+}
+
+impl<T> Service<T>
+where
+ T: hyper::server::Service<
+ Request = (Request, Option<AuthData>),
+ Response = Response,
+ Error = Error,
+ >,
+{
+ pub fn new(inner: T) -> Service<T> {
+ Service { inner }
+ }
+}
+
+impl<T> hyper::server::Service for Service<T>
+where
+ T: hyper::server::Service<
+ Request = (Request, Option<AuthData>),
+ Response = Response,
+ Error = Error,
+ >,
+{
+ type Request = Request;
+ type Response = Response;
+ type Error = Error;
+ type Future = T::Future;
+
+ fn call(&self, req: Self::Request) -> Self::Future {
+ return self.inner.call((req, None));
+ }
+}
diff --git a/rust/fatcat-api/src/server/mod.rs b/rust/fatcat-api/src/server/mod.rs
new file mode 100644
index 00000000..186f8272
--- /dev/null
+++ b/rust/fatcat-api/src/server/mod.rs
@@ -0,0 +1,2485 @@
+#![allow(unused_extern_crates)]
+extern crate chrono;
+extern crate hyper_tls;
+extern crate mime;
+extern crate native_tls;
+extern crate openssl;
+extern crate serde_ignored;
+extern crate tokio_core;
+extern crate uuid;
+
+extern crate percent_encoding;
+extern crate url;
+
+use self::url::form_urlencoded;
+use futures::{future, stream, Future, Stream};
+use hyper;
+use hyper::header::{ContentType, Headers};
+use hyper::{Error, Request, Response, StatusCode};
+use mimetypes;
+use std::sync::Arc;
+
+use serde_json;
+
+#[allow(unused_imports)]
+use std::collections::{BTreeMap, HashMap};
+use std::io;
+#[allow(unused_imports)]
+use swagger;
+
+#[allow(unused_imports)]
+use std::collections::BTreeSet;
+
+pub use swagger::auth::Authorization;
+use swagger::auth::Scopes;
+use swagger::{ApiError, Context, XSpanId};
+
+#[allow(unused_imports)]
+use models;
+use {Api, ContainerIdGetResponse, ContainerLookupGetResponse, ContainerPostResponse,
+ CreatorIdGetResponse, CreatorLookupGetResponse, CreatorPostResponse,
+ EditgroupIdAcceptPostResponse, EditgroupIdGetResponse, EditgroupPostResponse,
+ EditorUsernameChangelogGetResponse, EditorUsernameGetResponse, FileIdGetResponse,
+ FileLookupGetResponse, FilePostResponse, ReleaseIdGetResponse, ReleaseLookupGetResponse,
+ ReleasePostResponse, WorkIdGetResponse, WorkPostResponse};
+
+pub mod auth;
+
+header! { (Warning, "Warning") => [String] }
+
+mod paths {
+ extern crate regex;
+
+ lazy_static! {
+ pub static ref GLOBAL_REGEX_SET: regex::RegexSet = regex::RegexSet::new(&[
+ r"^/v0/container$",
+ r"^/v0/container/lookup$",
+ r"^/v0/container/(?P<id>[^/?#]*)$",
+ r"^/v0/creator$",
+ r"^/v0/creator/lookup$",
+ r"^/v0/creator/(?P<id>[^/?#]*)$",
+ r"^/v0/editgroup$",
+ r"^/v0/editgroup/(?P<id>[^/?#]*)$",
+ r"^/v0/editgroup/(?P<id>[^/?#]*)/accept$",
+ r"^/v0/editor/(?P<username>[^/?#]*)$",
+ r"^/v0/editor/(?P<username>[^/?#]*)/changelog$",
+ r"^/v0/file$",
+ r"^/v0/file/lookup$",
+ r"^/v0/file/(?P<id>[^/?#]*)$",
+ r"^/v0/release$",
+ r"^/v0/release/lookup$",
+ r"^/v0/release/(?P<id>[^/?#]*)$",
+ r"^/v0/work$",
+ r"^/v0/work/(?P<id>[^/?#]*)$"
+ ]).unwrap();
+ }
+ pub static ID_CONTAINER: usize = 0;
+ pub static ID_CONTAINER_LOOKUP: usize = 1;
+ pub static ID_CONTAINER_ID: usize = 2;
+ lazy_static! {
+ pub static ref REGEX_CONTAINER_ID: regex::Regex =
+ regex::Regex::new(r"^/v0/container/(?P<id>[^/?#]*)$").unwrap();
+ }
+ pub static ID_CREATOR: usize = 3;
+ pub static ID_CREATOR_LOOKUP: usize = 4;
+ pub static ID_CREATOR_ID: usize = 5;
+ lazy_static! {
+ pub static ref REGEX_CREATOR_ID: regex::Regex =
+ regex::Regex::new(r"^/v0/creator/(?P<id>[^/?#]*)$").unwrap();
+ }
+ pub static ID_EDITGROUP: usize = 6;
+ pub static ID_EDITGROUP_ID: usize = 7;
+ lazy_static! {
+ pub static ref REGEX_EDITGROUP_ID: regex::Regex =
+ regex::Regex::new(r"^/v0/editgroup/(?P<id>[^/?#]*)$").unwrap();
+ }
+ pub static ID_EDITGROUP_ID_ACCEPT: usize = 8;
+ lazy_static! {
+ pub static ref REGEX_EDITGROUP_ID_ACCEPT: regex::Regex =
+ regex::Regex::new(r"^/v0/editgroup/(?P<id>[^/?#]*)/accept$").unwrap();
+ }
+ pub static ID_EDITOR_USERNAME: usize = 9;
+ lazy_static! {
+ pub static ref REGEX_EDITOR_USERNAME: regex::Regex =
+ regex::Regex::new(r"^/v0/editor/(?P<username>[^/?#]*)$").unwrap();
+ }
+ pub static ID_EDITOR_USERNAME_CHANGELOG: usize = 10;
+ lazy_static! {
+ pub static ref REGEX_EDITOR_USERNAME_CHANGELOG: regex::Regex =
+ regex::Regex::new(r"^/v0/editor/(?P<username>[^/?#]*)/changelog$").unwrap();
+ }
+ pub static ID_FILE: usize = 11;
+ pub static ID_FILE_LOOKUP: usize = 12;
+ pub static ID_FILE_ID: usize = 13;
+ lazy_static! {
+ pub static ref REGEX_FILE_ID: regex::Regex =
+ regex::Regex::new(r"^/v0/file/(?P<id>[^/?#]*)$").unwrap();
+ }
+ pub static ID_RELEASE: usize = 14;
+ pub static ID_RELEASE_LOOKUP: usize = 15;
+ pub static ID_RELEASE_ID: usize = 16;
+ lazy_static! {
+ pub static ref REGEX_RELEASE_ID: regex::Regex =
+ regex::Regex::new(r"^/v0/release/(?P<id>[^/?#]*)$").unwrap();
+ }
+ pub static ID_WORK: usize = 17;
+ pub static ID_WORK_ID: usize = 18;
+ lazy_static! {
+ pub static ref REGEX_WORK_ID: regex::Regex =
+ regex::Regex::new(r"^/v0/work/(?P<id>[^/?#]*)$").unwrap();
+ }
+}
+
+pub struct NewService<T> {
+ api_impl: Arc<T>,
+}
+
+impl<T> NewService<T>
+where
+ T: Api + Clone + 'static,
+{
+ pub fn new<U: Into<Arc<T>>>(api_impl: U) -> NewService<T> {
+ NewService {
+ api_impl: api_impl.into(),
+ }
+ }
+}
+
+impl<T> hyper::server::NewService for NewService<T>
+where
+ T: Api + Clone + 'static,
+{
+ type Request = (Request, Context);
+ type Response = Response;
+ type Error = Error;
+ type Instance = Service<T>;
+
+ fn new_service(&self) -> Result<Self::Instance, io::Error> {
+ Ok(Service::new(self.api_impl.clone()))
+ }
+}
+
+pub struct Service<T> {
+ api_impl: Arc<T>,
+}
+
+impl<T> Service<T>
+where
+ T: Api + Clone + 'static,
+{
+ pub fn new<U: Into<Arc<T>>>(api_impl: U) -> Service<T> {
+ Service {
+ api_impl: api_impl.into(),
+ }
+ }
+}
+
+impl<T> hyper::server::Service for Service<T>
+where
+ T: Api + Clone + 'static,
+{
+ type Request = (Request, Context);
+ type Response = Response;
+ type Error = Error;
+ type Future = Box<Future<Item = Response, Error = Error>>;
+
+ fn call(&self, (req, mut context): Self::Request) -> Self::Future {
+ let api_impl = self.api_impl.clone();
+ let (method, uri, _, headers, body) = req.deconstruct();
+ let path = paths::GLOBAL_REGEX_SET.matches(uri.path());
+ match &method {
+ // ContainerIdGet - GET /container/{id}
+ &hyper::Method::Get if path.matched(paths::ID_CONTAINER_ID) => {
+ if context.x_span_id.is_none() {
+ context.x_span_id = Some(
+ headers
+ .get::<XSpanId>()
+ .map(XSpanId::to_string)
+ .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()),
+ );
+ }
+
+ // Path parameters
+ let path = uri.path().to_string();
+ let path_params = paths::REGEX_CONTAINER_ID
+ .captures(&path)
+ .unwrap_or_else(|| {
+ panic!("Path {} matched RE CONTAINER_ID in set but failed match against \"{}\"", path, paths::REGEX_CONTAINER_ID.as_str())
+ });
+
+ let param_id = match percent_encoding::percent_decode(path_params["id"].as_bytes())
+ .decode_utf8()
+ {
+ Ok(param_id) => match param_id.parse::<String>() {
+ Ok(param_id) => param_id,
+ Err(e) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!("Couldn't parse path parameter id: {}", e)),
+ ))
+ }
+ },
+ Err(_) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!(
+ "Couldn't percent-decode path parameter as UTF-8: {}",
+ &path_params["id"]
+ )),
+ ))
+ }
+ };
+
+ Box::new(
+ ({
+ {
+ {
+ Box::new(api_impl.container_id_get(param_id, &context).then(
+ move |result| {
+ let mut response = Response::new();
+ context.x_span_id.as_ref().map(|header| {
+ response.headers_mut().set(XSpanId(header.clone()))
+ });
+
+ match result {
+ Ok(rsp) => match rsp {
+ ContainerIdGetResponse::FetchASingleContainerById
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(200).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::CONTAINER_ID_GET_FETCH_A_SINGLE_CONTAINER_BY_ID.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ ContainerIdGetResponse::BadRequest
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(400).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::CONTAINER_ID_GET_BAD_REQUEST.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ ContainerIdGetResponse::GenericErrorResponse
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(0).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::CONTAINER_ID_GET_GENERIC_ERROR_RESPONSE.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ },
+ Err(_) => {
+ // Application code returned an error. This should not happen, as the implementation should
+ // return a valid response.
+ response.set_status(StatusCode::InternalServerError);
+ response.set_body("An internal error occurred");
+ },
+ }
+
+ future::ok(response)
+ },
+ ))
+ }
+ }
+ }),
+ ) as Box<Future<Item = Response, Error = Error>>
+ }
+
+ // ContainerLookupGet - GET /container/lookup
+ &hyper::Method::Get if path.matched(paths::ID_CONTAINER_LOOKUP) => {
+ if context.x_span_id.is_none() {
+ context.x_span_id = Some(
+ headers
+ .get::<XSpanId>()
+ .map(XSpanId::to_string)
+ .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()),
+ );
+ }
+
+ // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response)
+ let query_params = form_urlencoded::parse(
+ uri.query().unwrap_or_default().as_bytes(),
+ ).collect::<Vec<_>>();
+ let param_issn = query_params
+ .iter()
+ .filter(|e| e.0 == "issn")
+ .map(|e| e.1.to_owned())
+ .nth(0);
+ let param_issn = match param_issn {
+ Some(param_issn) => match param_issn.parse::<String>() {
+ Ok(param_issn) => param_issn,
+ Err(e) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!(
+ "Couldn't parse query parameter issn - doesn't match schema: {}",
+ e
+ )),
+ ))
+ }
+ },
+ None => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body("Missing required query parameter issn"),
+ ))
+ }
+ };
+
+ Box::new(
+ ({
+ {
+ {
+ Box::new(api_impl.container_lookup_get(param_issn, &context).then(
+ move |result| {
+ let mut response = Response::new();
+ context.x_span_id.as_ref().map(|header| {
+ response.headers_mut().set(XSpanId(header.clone()))
+ });
+
+ match result {
+ Ok(rsp) => match rsp {
+ ContainerLookupGetResponse::FindASingleContainerByExternalIdentifer
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(200).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::CONTAINER_LOOKUP_GET_FIND_A_SINGLE_CONTAINER_BY_EXTERNAL_IDENTIFER.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ ContainerLookupGetResponse::BadRequest
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(400).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::CONTAINER_LOOKUP_GET_BAD_REQUEST.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ ContainerLookupGetResponse::NoSuchContainer
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(404).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::CONTAINER_LOOKUP_GET_NO_SUCH_CONTAINER.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ ContainerLookupGetResponse::GenericErrorResponse
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(0).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::CONTAINER_LOOKUP_GET_GENERIC_ERROR_RESPONSE.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ },
+ Err(_) => {
+ // Application code returned an error. This should not happen, as the implementation should
+ // return a valid response.
+ response.set_status(StatusCode::InternalServerError);
+ response.set_body("An internal error occurred");
+ },
+ }
+
+ future::ok(response)
+ },
+ ))
+ }
+ }
+ }),
+ ) as Box<Future<Item = Response, Error = Error>>
+ }
+
+ // ContainerPost - POST /container
+ &hyper::Method::Post if path.matched(paths::ID_CONTAINER) => {
+ if context.x_span_id.is_none() {
+ context.x_span_id = Some(
+ headers
+ .get::<XSpanId>()
+ .map(XSpanId::to_string)
+ .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()),
+ );
+ }
+
+ // Body parameters (note that non-required body parameters will ignore garbage
+ // values, rather than causing a 400 response). Produce warning header and logs for
+ // any unused fields.
+ Box::new(body.concat2().then(
+ move |result| -> Box<Future<Item = Response, Error = Error>> {
+ match result {
+ Ok(body) => {
+ let mut unused_elements = Vec::new();
+ let param_body: Option<
+ models::ContainerEntity,
+ > = if !body.is_empty() {
+ let deserializer =
+ &mut serde_json::Deserializer::from_slice(&*body);
+
+ match serde_ignored::deserialize(deserializer, |path| {
+ warn!("Ignoring unknown field in body: {}", path);
+ unused_elements.push(path.to_string());
+ }) {
+ Ok(param_body) => param_body,
+
+ Err(_) => None,
+ }
+ } else {
+ None
+ };
+
+ Box::new(api_impl.container_post(param_body, &context).then(
+ move |result| {
+ let mut response = Response::new();
+ context.x_span_id.as_ref().map(|header| {
+ response.headers_mut().set(XSpanId(header.clone()))
+ });
+
+ if !unused_elements.is_empty() {
+ response.headers_mut().set(Warning(format!(
+ "Ignoring unknown fields in body: {:?}",
+ unused_elements
+ )));
+ }
+
+ match result {
+ Ok(rsp) => match rsp {
+ ContainerPostResponse::Created(body) => {
+ response.set_status(
+ StatusCode::try_from(201).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(mimetypes::responses::CONTAINER_POST_CREATED.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ ContainerPostResponse::BadRequest(body) => {
+ response.set_status(
+ StatusCode::try_from(400).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(mimetypes::responses::CONTAINER_POST_BAD_REQUEST.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ ContainerPostResponse::GenericErrorResponse(
+ body,
+ ) => {
+ response.set_status(
+ StatusCode::try_from(0).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(mimetypes::responses::CONTAINER_POST_GENERIC_ERROR_RESPONSE.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ },
+ Err(_) => {
+ // Application code returned an error. This should not happen, as the implementation should
+ // return a valid response.
+ response
+ .set_status(StatusCode::InternalServerError);
+ response.set_body("An internal error occurred");
+ }
+ }
+
+ future::ok(response)
+ },
+ ))
+ }
+ Err(e) => Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!("Couldn't read body parameter body: {}", e)),
+ )),
+ }
+ },
+ )) as Box<Future<Item = Response, Error = Error>>
+ }
+
+ // CreatorIdGet - GET /creator/{id}
+ &hyper::Method::Get if path.matched(paths::ID_CREATOR_ID) => {
+ if context.x_span_id.is_none() {
+ context.x_span_id = Some(
+ headers
+ .get::<XSpanId>()
+ .map(XSpanId::to_string)
+ .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()),
+ );
+ }
+
+ // Path parameters
+ let path = uri.path().to_string();
+ let path_params = paths::REGEX_CREATOR_ID.captures(&path).unwrap_or_else(|| {
+ panic!(
+ "Path {} matched RE CREATOR_ID in set but failed match against \"{}\"",
+ path,
+ paths::REGEX_CREATOR_ID.as_str()
+ )
+ });
+
+ let param_id = match percent_encoding::percent_decode(path_params["id"].as_bytes())
+ .decode_utf8()
+ {
+ Ok(param_id) => match param_id.parse::<String>() {
+ Ok(param_id) => param_id,
+ Err(e) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!("Couldn't parse path parameter id: {}", e)),
+ ))
+ }
+ },
+ Err(_) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!(
+ "Couldn't percent-decode path parameter as UTF-8: {}",
+ &path_params["id"]
+ )),
+ ))
+ }
+ };
+
+ Box::new(
+ ({
+ {
+ {
+ Box::new(api_impl.creator_id_get(param_id, &context).then(
+ move |result| {
+ let mut response = Response::new();
+ context.x_span_id.as_ref().map(|header| {
+ response.headers_mut().set(XSpanId(header.clone()))
+ });
+
+ match result {
+ Ok(rsp) => match rsp {
+ CreatorIdGetResponse::FetchASingleCreatorById(
+ body,
+ ) => {
+ response.set_status(
+ StatusCode::try_from(200).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(mimetypes::responses::CREATOR_ID_GET_FETCH_A_SINGLE_CREATOR_BY_ID.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ CreatorIdGetResponse::BadRequest(body) => {
+ response.set_status(
+ StatusCode::try_from(400).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(mimetypes::responses::CREATOR_ID_GET_BAD_REQUEST.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ CreatorIdGetResponse::GenericErrorResponse(
+ body,
+ ) => {
+ response.set_status(
+ StatusCode::try_from(0).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(mimetypes::responses::CREATOR_ID_GET_GENERIC_ERROR_RESPONSE.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ },
+ Err(_) => {
+ // Application code returned an error. This should not happen, as the implementation should
+ // return a valid response.
+ response
+ .set_status(StatusCode::InternalServerError);
+ response.set_body("An internal error occurred");
+ }
+ }
+
+ future::ok(response)
+ },
+ ))
+ }
+ }
+ }),
+ ) as Box<Future<Item = Response, Error = Error>>
+ }
+
+ // CreatorLookupGet - GET /creator/lookup
+ &hyper::Method::Get if path.matched(paths::ID_CREATOR_LOOKUP) => {
+ if context.x_span_id.is_none() {
+ context.x_span_id = Some(
+ headers
+ .get::<XSpanId>()
+ .map(XSpanId::to_string)
+ .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()),
+ );
+ }
+
+ // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response)
+ let query_params = form_urlencoded::parse(
+ uri.query().unwrap_or_default().as_bytes(),
+ ).collect::<Vec<_>>();
+ let param_orcid = query_params
+ .iter()
+ .filter(|e| e.0 == "orcid")
+ .map(|e| e.1.to_owned())
+ .nth(0);
+ let param_orcid = match param_orcid {
+ Some(param_orcid) => match param_orcid.parse::<String>() {
+ Ok(param_orcid) => param_orcid,
+ Err(e) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!(
+ "Couldn't parse query parameter orcid - doesn't match schema: {}",
+ e
+ )),
+ ))
+ }
+ },
+ None => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body("Missing required query parameter orcid"),
+ ))
+ }
+ };
+
+ Box::new(
+ ({
+ {
+ {
+ Box::new(api_impl.creator_lookup_get(param_orcid, &context).then(
+ move |result| {
+ let mut response = Response::new();
+ context.x_span_id.as_ref().map(|header| {
+ response.headers_mut().set(XSpanId(header.clone()))
+ });
+
+ match result {
+ Ok(rsp) => match rsp {
+ CreatorLookupGetResponse::FindASingleCreatorByExternalIdentifer
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(200).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::CREATOR_LOOKUP_GET_FIND_A_SINGLE_CREATOR_BY_EXTERNAL_IDENTIFER.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ CreatorLookupGetResponse::BadRequest
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(400).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::CREATOR_LOOKUP_GET_BAD_REQUEST.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ CreatorLookupGetResponse::NoSuchCreator
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(404).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::CREATOR_LOOKUP_GET_NO_SUCH_CREATOR.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ CreatorLookupGetResponse::GenericErrorResponse
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(0).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::CREATOR_LOOKUP_GET_GENERIC_ERROR_RESPONSE.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ },
+ Err(_) => {
+ // Application code returned an error. This should not happen, as the implementation should
+ // return a valid response.
+ response.set_status(StatusCode::InternalServerError);
+ response.set_body("An internal error occurred");
+ },
+ }
+
+ future::ok(response)
+ },
+ ))
+ }
+ }
+ }),
+ ) as Box<Future<Item = Response, Error = Error>>
+ }
+
+ // CreatorPost - POST /creator
+ &hyper::Method::Post if path.matched(paths::ID_CREATOR) => {
+ if context.x_span_id.is_none() {
+ context.x_span_id = Some(
+ headers
+ .get::<XSpanId>()
+ .map(XSpanId::to_string)
+ .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()),
+ );
+ }
+
+ // Body parameters (note that non-required body parameters will ignore garbage
+ // values, rather than causing a 400 response). Produce warning header and logs for
+ // any unused fields.
+ Box::new(body.concat2().then(
+ move |result| -> Box<Future<Item = Response, Error = Error>> {
+ match result {
+ Ok(body) => {
+ let mut unused_elements = Vec::new();
+ let param_body: Option<
+ models::CreatorEntity,
+ > = if !body.is_empty() {
+ let deserializer =
+ &mut serde_json::Deserializer::from_slice(&*body);
+
+ match serde_ignored::deserialize(deserializer, |path| {
+ warn!("Ignoring unknown field in body: {}", path);
+ unused_elements.push(path.to_string());
+ }) {
+ Ok(param_body) => param_body,
+
+ Err(_) => None,
+ }
+ } else {
+ None
+ };
+
+ Box::new(api_impl.creator_post(param_body, &context).then(
+ move |result| {
+ let mut response = Response::new();
+ context.x_span_id.as_ref().map(|header| {
+ response.headers_mut().set(XSpanId(header.clone()))
+ });
+
+ if !unused_elements.is_empty() {
+ response.headers_mut().set(Warning(format!(
+ "Ignoring unknown fields in body: {:?}",
+ unused_elements
+ )));
+ }
+
+ match result {
+ Ok(rsp) => match rsp {
+ CreatorPostResponse::Created(body) => {
+ response.set_status(
+ StatusCode::try_from(201).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(
+ mimetypes::responses::CREATOR_POST_CREATED
+ .clone(),
+ ));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ CreatorPostResponse::BadRequest(body) => {
+ response.set_status(
+ StatusCode::try_from(400).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(mimetypes::responses::CREATOR_POST_BAD_REQUEST.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ CreatorPostResponse::GenericErrorResponse(body) => {
+ response.set_status(
+ StatusCode::try_from(0).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(mimetypes::responses::CREATOR_POST_GENERIC_ERROR_RESPONSE.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ },
+ Err(_) => {
+ // Application code returned an error. This should not happen, as the implementation should
+ // return a valid response.
+ response
+ .set_status(StatusCode::InternalServerError);
+ response.set_body("An internal error occurred");
+ }
+ }
+
+ future::ok(response)
+ },
+ ))
+ }
+ Err(e) => Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!("Couldn't read body parameter body: {}", e)),
+ )),
+ }
+ },
+ )) as Box<Future<Item = Response, Error = Error>>
+ }
+
+ // EditgroupIdAcceptPost - POST /editgroup/{id}/accept
+ &hyper::Method::Post if path.matched(paths::ID_EDITGROUP_ID_ACCEPT) => {
+ if context.x_span_id.is_none() {
+ context.x_span_id = Some(
+ headers
+ .get::<XSpanId>()
+ .map(XSpanId::to_string)
+ .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()),
+ );
+ }
+
+ // Path parameters
+ let path = uri.path().to_string();
+ let path_params = paths::REGEX_EDITGROUP_ID_ACCEPT
+ .captures(&path)
+ .unwrap_or_else(|| {
+ panic!("Path {} matched RE EDITGROUP_ID_ACCEPT in set but failed match against \"{}\"", path, paths::REGEX_EDITGROUP_ID_ACCEPT.as_str())
+ });
+
+ let param_id = match percent_encoding::percent_decode(path_params["id"].as_bytes())
+ .decode_utf8()
+ {
+ Ok(param_id) => match param_id.parse::<i32>() {
+ Ok(param_id) => param_id,
+ Err(e) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!("Couldn't parse path parameter id: {}", e)),
+ ))
+ }
+ },
+ Err(_) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!(
+ "Couldn't percent-decode path parameter as UTF-8: {}",
+ &path_params["id"]
+ )),
+ ))
+ }
+ };
+
+ Box::new(
+ ({
+ {
+ {
+ Box::new(
+ api_impl.editgroup_id_accept_post(param_id, &context).then(
+ move |result| {
+ let mut response = Response::new();
+ context.x_span_id.as_ref().map(|header| {
+ response.headers_mut().set(XSpanId(header.clone()))
+ });
+
+ match result {
+ Ok(rsp) => match rsp {
+ EditgroupIdAcceptPostResponse::MergedEditgroupSuccessfully_
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(200).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::EDITGROUP_ID_ACCEPT_POST_MERGED_EDITGROUP_SUCCESSFULLY_.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ EditgroupIdAcceptPostResponse::EditgroupIsInAnUnmergableState
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(400).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::EDITGROUP_ID_ACCEPT_POST_EDITGROUP_IS_IN_AN_UNMERGABLE_STATE.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ EditgroupIdAcceptPostResponse::NoSuchEditgroup
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(404).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::EDITGROUP_ID_ACCEPT_POST_NO_SUCH_EDITGROUP.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ EditgroupIdAcceptPostResponse::GenericErrorResponse
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(0).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::EDITGROUP_ID_ACCEPT_POST_GENERIC_ERROR_RESPONSE.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ },
+ Err(_) => {
+ // Application code returned an error. This should not happen, as the implementation should
+ // return a valid response.
+ response.set_status(StatusCode::InternalServerError);
+ response.set_body("An internal error occurred");
+ },
+ }
+
+ future::ok(response)
+ },
+ ),
+ )
+ }
+ }
+ }),
+ ) as Box<Future<Item = Response, Error = Error>>
+ }
+
+ // EditgroupIdGet - GET /editgroup/{id}
+ &hyper::Method::Get if path.matched(paths::ID_EDITGROUP_ID) => {
+ if context.x_span_id.is_none() {
+ context.x_span_id = Some(
+ headers
+ .get::<XSpanId>()
+ .map(XSpanId::to_string)
+ .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()),
+ );
+ }
+
+ // Path parameters
+ let path = uri.path().to_string();
+ let path_params = paths::REGEX_EDITGROUP_ID
+ .captures(&path)
+ .unwrap_or_else(|| {
+ panic!("Path {} matched RE EDITGROUP_ID in set but failed match against \"{}\"", path, paths::REGEX_EDITGROUP_ID.as_str())
+ });
+
+ let param_id = match percent_encoding::percent_decode(path_params["id"].as_bytes())
+ .decode_utf8()
+ {
+ Ok(param_id) => match param_id.parse::<i32>() {
+ Ok(param_id) => param_id,
+ Err(e) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!("Couldn't parse path parameter id: {}", e)),
+ ))
+ }
+ },
+ Err(_) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!(
+ "Couldn't percent-decode path parameter as UTF-8: {}",
+ &path_params["id"]
+ )),
+ ))
+ }
+ };
+
+ Box::new(
+ ({
+ {
+ {
+ Box::new(api_impl.editgroup_id_get(param_id, &context).then(
+ move |result| {
+ let mut response = Response::new();
+ context.x_span_id.as_ref().map(|header| {
+ response.headers_mut().set(XSpanId(header.clone()))
+ });
+
+ match result {
+ Ok(rsp) => match rsp {
+ EditgroupIdGetResponse::FetchEditgroupByIdentifier
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(200).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::EDITGROUP_ID_GET_FETCH_EDITGROUP_BY_IDENTIFIER.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ EditgroupIdGetResponse::NoSuchEditgroup
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(404).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::EDITGROUP_ID_GET_NO_SUCH_EDITGROUP.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ EditgroupIdGetResponse::GenericErrorResponse
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(0).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::EDITGROUP_ID_GET_GENERIC_ERROR_RESPONSE.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ },
+ Err(_) => {
+ // Application code returned an error. This should not happen, as the implementation should
+ // return a valid response.
+ response.set_status(StatusCode::InternalServerError);
+ response.set_body("An internal error occurred");
+ },
+ }
+
+ future::ok(response)
+ },
+ ))
+ }
+ }
+ }),
+ ) as Box<Future<Item = Response, Error = Error>>
+ }
+
+ // EditgroupPost - POST /editgroup
+ &hyper::Method::Post if path.matched(paths::ID_EDITGROUP) => {
+ if context.x_span_id.is_none() {
+ context.x_span_id = Some(
+ headers
+ .get::<XSpanId>()
+ .map(XSpanId::to_string)
+ .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()),
+ );
+ }
+
+ Box::new(
+ ({
+ {
+ {
+ Box::new(api_impl.editgroup_post(&context).then(move |result| {
+ let mut response = Response::new();
+ context.x_span_id.as_ref().map(|header| {
+ response.headers_mut().set(XSpanId(header.clone()))
+ });
+
+ match result {
+ Ok(rsp) => match rsp {
+ EditgroupPostResponse::SuccessfullyCreated(body) => {
+ response
+ .set_status(StatusCode::try_from(201).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::EDITGROUP_POST_SUCCESSFULLY_CREATED.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ EditgroupPostResponse::InvalidRequestParameters(
+ body,
+ ) => {
+ response
+ .set_status(StatusCode::try_from(400).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::EDITGROUP_POST_INVALID_REQUEST_PARAMETERS.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ EditgroupPostResponse::GenericErrorResponse(body) => {
+ response
+ .set_status(StatusCode::try_from(0).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::EDITGROUP_POST_GENERIC_ERROR_RESPONSE.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ },
+ Err(_) => {
+ // Application code returned an error. This should not happen, as the implementation should
+ // return a valid response.
+ response.set_status(StatusCode::InternalServerError);
+ response.set_body("An internal error occurred");
+ }
+ }
+
+ future::ok(response)
+ }))
+ }
+ }
+ }),
+ ) as Box<Future<Item = Response, Error = Error>>
+ }
+
+ // EditorUsernameChangelogGet - GET /editor/{username}/changelog
+ &hyper::Method::Get if path.matched(paths::ID_EDITOR_USERNAME_CHANGELOG) => {
+ if context.x_span_id.is_none() {
+ context.x_span_id = Some(
+ headers
+ .get::<XSpanId>()
+ .map(XSpanId::to_string)
+ .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()),
+ );
+ }
+
+ // Path parameters
+ let path = uri.path().to_string();
+ let path_params = paths::REGEX_EDITOR_USERNAME_CHANGELOG
+ .captures(&path)
+ .unwrap_or_else(|| {
+ panic!("Path {} matched RE EDITOR_USERNAME_CHANGELOG in set but failed match against \"{}\"", path, paths::REGEX_EDITOR_USERNAME_CHANGELOG.as_str())
+ });
+
+ let param_username = match percent_encoding::percent_decode(
+ path_params["username"].as_bytes(),
+ ).decode_utf8()
+ {
+ Ok(param_username) => match param_username.parse::<String>() {
+ Ok(param_username) => param_username,
+ Err(e) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!(
+ "Couldn't parse path parameter username: {}",
+ e
+ )),
+ ))
+ }
+ },
+ Err(_) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!(
+ "Couldn't percent-decode path parameter as UTF-8: {}",
+ &path_params["username"]
+ )),
+ ))
+ }
+ };
+
+ Box::new(
+ ({
+ {
+ {
+ Box::new(
+ api_impl
+ .editor_username_changelog_get(param_username, &context)
+ .then(move |result| {
+ let mut response = Response::new();
+ context.x_span_id.as_ref().map(|header| {
+ response.headers_mut().set(XSpanId(header.clone()))
+ });
+
+ match result {
+ Ok(rsp) => match rsp {
+ EditorUsernameChangelogGetResponse::FindChanges_
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(200).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::EDITOR_USERNAME_CHANGELOG_GET_FIND_CHANGES_.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ EditorUsernameChangelogGetResponse::UsernameNotFound
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(404).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::EDITOR_USERNAME_CHANGELOG_GET_USERNAME_NOT_FOUND.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ EditorUsernameChangelogGetResponse::GenericErrorResponse
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(0).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::EDITOR_USERNAME_CHANGELOG_GET_GENERIC_ERROR_RESPONSE.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ },
+ Err(_) => {
+ // Application code returned an error. This should not happen, as the implementation should
+ // return a valid response.
+ response.set_status(StatusCode::InternalServerError);
+ response.set_body("An internal error occurred");
+ },
+ }
+
+ future::ok(response)
+ }),
+ )
+ }
+ }
+ }),
+ ) as Box<Future<Item = Response, Error = Error>>
+ }
+
+ // EditorUsernameGet - GET /editor/{username}
+ &hyper::Method::Get if path.matched(paths::ID_EDITOR_USERNAME) => {
+ if context.x_span_id.is_none() {
+ context.x_span_id = Some(
+ headers
+ .get::<XSpanId>()
+ .map(XSpanId::to_string)
+ .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()),
+ );
+ }
+
+ // Path parameters
+ let path = uri.path().to_string();
+ let path_params = paths::REGEX_EDITOR_USERNAME.captures(&path).unwrap_or_else(
+ || {
+ panic!("Path {} matched RE EDITOR_USERNAME in set but failed match against \"{}\"", path, paths::REGEX_EDITOR_USERNAME.as_str())
+ },
+ );
+
+ let param_username = match percent_encoding::percent_decode(
+ path_params["username"].as_bytes(),
+ ).decode_utf8()
+ {
+ Ok(param_username) => match param_username.parse::<String>() {
+ Ok(param_username) => param_username,
+ Err(e) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!(
+ "Couldn't parse path parameter username: {}",
+ e
+ )),
+ ))
+ }
+ },
+ Err(_) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!(
+ "Couldn't percent-decode path parameter as UTF-8: {}",
+ &path_params["username"]
+ )),
+ ))
+ }
+ };
+
+ Box::new(
+ ({
+ {
+ {
+ Box::new(
+ api_impl.editor_username_get(param_username, &context).then(
+ move |result| {
+ let mut response = Response::new();
+ context.x_span_id.as_ref().map(|header| {
+ response.headers_mut().set(XSpanId(header.clone()))
+ });
+
+ match result {
+ Ok(rsp) => match rsp {
+ EditorUsernameGetResponse::FetchGenericInformationAboutAnEditor
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(200).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::EDITOR_USERNAME_GET_FETCH_GENERIC_INFORMATION_ABOUT_AN_EDITOR.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ EditorUsernameGetResponse::UsernameNotFound
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(404).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::EDITOR_USERNAME_GET_USERNAME_NOT_FOUND.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ EditorUsernameGetResponse::GenericErrorResponse
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(0).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::EDITOR_USERNAME_GET_GENERIC_ERROR_RESPONSE.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ },
+ Err(_) => {
+ // Application code returned an error. This should not happen, as the implementation should
+ // return a valid response.
+ response.set_status(StatusCode::InternalServerError);
+ response.set_body("An internal error occurred");
+ },
+ }
+
+ future::ok(response)
+ },
+ ),
+ )
+ }
+ }
+ }),
+ ) as Box<Future<Item = Response, Error = Error>>
+ }
+
+ // FileIdGet - GET /file/{id}
+ &hyper::Method::Get if path.matched(paths::ID_FILE_ID) => {
+ if context.x_span_id.is_none() {
+ context.x_span_id = Some(
+ headers
+ .get::<XSpanId>()
+ .map(XSpanId::to_string)
+ .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()),
+ );
+ }
+
+ // Path parameters
+ let path = uri.path().to_string();
+ let path_params = paths::REGEX_FILE_ID.captures(&path).unwrap_or_else(|| {
+ panic!(
+ "Path {} matched RE FILE_ID in set but failed match against \"{}\"",
+ path,
+ paths::REGEX_FILE_ID.as_str()
+ )
+ });
+
+ let param_id = match percent_encoding::percent_decode(path_params["id"].as_bytes())
+ .decode_utf8()
+ {
+ Ok(param_id) => match param_id.parse::<String>() {
+ Ok(param_id) => param_id,
+ Err(e) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!("Couldn't parse path parameter id: {}", e)),
+ ))
+ }
+ },
+ Err(_) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!(
+ "Couldn't percent-decode path parameter as UTF-8: {}",
+ &path_params["id"]
+ )),
+ ))
+ }
+ };
+
+ Box::new(
+ ({
+ {
+ {
+ Box::new(api_impl.file_id_get(param_id, &context).then(
+ move |result| {
+ let mut response = Response::new();
+ context.x_span_id.as_ref().map(|header| {
+ response.headers_mut().set(XSpanId(header.clone()))
+ });
+
+ match result {
+ Ok(rsp) => match rsp {
+ FileIdGetResponse::FetchASingleFileById(body) => {
+ response.set_status(
+ StatusCode::try_from(200).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(mimetypes::responses::FILE_ID_GET_FETCH_A_SINGLE_FILE_BY_ID.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ FileIdGetResponse::BadRequest(body) => {
+ response.set_status(
+ StatusCode::try_from(400).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(mimetypes::responses::FILE_ID_GET_BAD_REQUEST.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ FileIdGetResponse::GenericErrorResponse(body) => {
+ response.set_status(
+ StatusCode::try_from(0).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(mimetypes::responses::FILE_ID_GET_GENERIC_ERROR_RESPONSE.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ },
+ Err(_) => {
+ // Application code returned an error. This should not happen, as the implementation should
+ // return a valid response.
+ response
+ .set_status(StatusCode::InternalServerError);
+ response.set_body("An internal error occurred");
+ }
+ }
+
+ future::ok(response)
+ },
+ ))
+ }
+ }
+ }),
+ ) as Box<Future<Item = Response, Error = Error>>
+ }
+
+ // FileLookupGet - GET /file/lookup
+ &hyper::Method::Get if path.matched(paths::ID_FILE_LOOKUP) => {
+ if context.x_span_id.is_none() {
+ context.x_span_id = Some(
+ headers
+ .get::<XSpanId>()
+ .map(XSpanId::to_string)
+ .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()),
+ );
+ }
+
+ // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response)
+ let query_params = form_urlencoded::parse(
+ uri.query().unwrap_or_default().as_bytes(),
+ ).collect::<Vec<_>>();
+ let param_sha1 = query_params
+ .iter()
+ .filter(|e| e.0 == "sha1")
+ .map(|e| e.1.to_owned())
+ .nth(0);
+ let param_sha1 = match param_sha1 {
+ Some(param_sha1) => match param_sha1.parse::<String>() {
+ Ok(param_sha1) => param_sha1,
+ Err(e) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!(
+ "Couldn't parse query parameter sha1 - doesn't match schema: {}",
+ e
+ )),
+ ))
+ }
+ },
+ None => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body("Missing required query parameter sha1"),
+ ))
+ }
+ };
+
+ Box::new(
+ ({
+ {
+ {
+ Box::new(api_impl.file_lookup_get(param_sha1, &context).then(
+ move |result| {
+ let mut response = Response::new();
+ context.x_span_id.as_ref().map(|header| {
+ response.headers_mut().set(XSpanId(header.clone()))
+ });
+
+ match result {
+ Ok(rsp) => match rsp {
+ FileLookupGetResponse::FindASingleFileByExternalIdentifer
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(200).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::FILE_LOOKUP_GET_FIND_A_SINGLE_FILE_BY_EXTERNAL_IDENTIFER.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ FileLookupGetResponse::BadRequest
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(400).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::FILE_LOOKUP_GET_BAD_REQUEST.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ FileLookupGetResponse::NoSuchFile
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(404).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::FILE_LOOKUP_GET_NO_SUCH_FILE.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ FileLookupGetResponse::GenericErrorResponse
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(0).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::FILE_LOOKUP_GET_GENERIC_ERROR_RESPONSE.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ },
+ Err(_) => {
+ // Application code returned an error. This should not happen, as the implementation should
+ // return a valid response.
+ response.set_status(StatusCode::InternalServerError);
+ response.set_body("An internal error occurred");
+ },
+ }
+
+ future::ok(response)
+ },
+ ))
+ }
+ }
+ }),
+ ) as Box<Future<Item = Response, Error = Error>>
+ }
+
+ // FilePost - POST /file
+ &hyper::Method::Post if path.matched(paths::ID_FILE) => {
+ if context.x_span_id.is_none() {
+ context.x_span_id = Some(
+ headers
+ .get::<XSpanId>()
+ .map(XSpanId::to_string)
+ .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()),
+ );
+ }
+
+ // Body parameters (note that non-required body parameters will ignore garbage
+ // values, rather than causing a 400 response). Produce warning header and logs for
+ // any unused fields.
+ Box::new(body.concat2().then(
+ move |result| -> Box<Future<Item = Response, Error = Error>> {
+ match result {
+ Ok(body) => {
+ let mut unused_elements = Vec::new();
+ let param_body: Option<
+ models::FileEntity,
+ > = if !body.is_empty() {
+ let deserializer =
+ &mut serde_json::Deserializer::from_slice(&*body);
+
+ match serde_ignored::deserialize(deserializer, |path| {
+ warn!("Ignoring unknown field in body: {}", path);
+ unused_elements.push(path.to_string());
+ }) {
+ Ok(param_body) => param_body,
+
+ Err(_) => None,
+ }
+ } else {
+ None
+ };
+
+ Box::new(api_impl.file_post(param_body, &context).then(
+ move |result| {
+ let mut response = Response::new();
+ context.x_span_id.as_ref().map(|header| {
+ response.headers_mut().set(XSpanId(header.clone()))
+ });
+
+ if !unused_elements.is_empty() {
+ response.headers_mut().set(Warning(format!(
+ "Ignoring unknown fields in body: {:?}",
+ unused_elements
+ )));
+ }
+
+ match result {
+ Ok(rsp) => match rsp {
+ FilePostResponse::Created(body) => {
+ response.set_status(
+ StatusCode::try_from(201).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(
+ mimetypes::responses::FILE_POST_CREATED
+ .clone(),
+ ));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ FilePostResponse::BadRequest(body) => {
+ response.set_status(
+ StatusCode::try_from(400).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(
+ mimetypes::responses::FILE_POST_BAD_REQUEST
+ .clone(),
+ ));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ FilePostResponse::GenericErrorResponse(body) => {
+ response.set_status(
+ StatusCode::try_from(0).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(mimetypes::responses::FILE_POST_GENERIC_ERROR_RESPONSE.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ },
+ Err(_) => {
+ // Application code returned an error. This should not happen, as the implementation should
+ // return a valid response.
+ response
+ .set_status(StatusCode::InternalServerError);
+ response.set_body("An internal error occurred");
+ }
+ }
+
+ future::ok(response)
+ },
+ ))
+ }
+ Err(e) => Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!("Couldn't read body parameter body: {}", e)),
+ )),
+ }
+ },
+ )) as Box<Future<Item = Response, Error = Error>>
+ }
+
+ // ReleaseIdGet - GET /release/{id}
+ &hyper::Method::Get if path.matched(paths::ID_RELEASE_ID) => {
+ if context.x_span_id.is_none() {
+ context.x_span_id = Some(
+ headers
+ .get::<XSpanId>()
+ .map(XSpanId::to_string)
+ .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()),
+ );
+ }
+
+ // Path parameters
+ let path = uri.path().to_string();
+ let path_params = paths::REGEX_RELEASE_ID.captures(&path).unwrap_or_else(|| {
+ panic!(
+ "Path {} matched RE RELEASE_ID in set but failed match against \"{}\"",
+ path,
+ paths::REGEX_RELEASE_ID.as_str()
+ )
+ });
+
+ let param_id = match percent_encoding::percent_decode(path_params["id"].as_bytes())
+ .decode_utf8()
+ {
+ Ok(param_id) => match param_id.parse::<String>() {
+ Ok(param_id) => param_id,
+ Err(e) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!("Couldn't parse path parameter id: {}", e)),
+ ))
+ }
+ },
+ Err(_) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!(
+ "Couldn't percent-decode path parameter as UTF-8: {}",
+ &path_params["id"]
+ )),
+ ))
+ }
+ };
+
+ Box::new(
+ ({
+ {
+ {
+ Box::new(api_impl.release_id_get(param_id, &context).then(
+ move |result| {
+ let mut response = Response::new();
+ context.x_span_id.as_ref().map(|header| {
+ response.headers_mut().set(XSpanId(header.clone()))
+ });
+
+ match result {
+ Ok(rsp) => match rsp {
+ ReleaseIdGetResponse::FetchASingleReleaseById(
+ body,
+ ) => {
+ response.set_status(
+ StatusCode::try_from(200).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(mimetypes::responses::RELEASE_ID_GET_FETCH_A_SINGLE_RELEASE_BY_ID.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ ReleaseIdGetResponse::BadRequest(body) => {
+ response.set_status(
+ StatusCode::try_from(400).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(mimetypes::responses::RELEASE_ID_GET_BAD_REQUEST.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ ReleaseIdGetResponse::GenericErrorResponse(
+ body,
+ ) => {
+ response.set_status(
+ StatusCode::try_from(0).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(mimetypes::responses::RELEASE_ID_GET_GENERIC_ERROR_RESPONSE.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ },
+ Err(_) => {
+ // Application code returned an error. This should not happen, as the implementation should
+ // return a valid response.
+ response
+ .set_status(StatusCode::InternalServerError);
+ response.set_body("An internal error occurred");
+ }
+ }
+
+ future::ok(response)
+ },
+ ))
+ }
+ }
+ }),
+ ) as Box<Future<Item = Response, Error = Error>>
+ }
+
+ // ReleaseLookupGet - GET /release/lookup
+ &hyper::Method::Get if path.matched(paths::ID_RELEASE_LOOKUP) => {
+ if context.x_span_id.is_none() {
+ context.x_span_id = Some(
+ headers
+ .get::<XSpanId>()
+ .map(XSpanId::to_string)
+ .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()),
+ );
+ }
+
+ // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response)
+ let query_params = form_urlencoded::parse(
+ uri.query().unwrap_or_default().as_bytes(),
+ ).collect::<Vec<_>>();
+ let param_doi = query_params
+ .iter()
+ .filter(|e| e.0 == "doi")
+ .map(|e| e.1.to_owned())
+ .nth(0);
+ let param_doi = match param_doi {
+ Some(param_doi) => match param_doi.parse::<String>() {
+ Ok(param_doi) => param_doi,
+ Err(e) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!(
+ "Couldn't parse query parameter doi - doesn't match schema: {}",
+ e
+ )),
+ ))
+ }
+ },
+ None => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body("Missing required query parameter doi"),
+ ))
+ }
+ };
+
+ Box::new(
+ ({
+ {
+ {
+ Box::new(api_impl.release_lookup_get(param_doi, &context).then(
+ move |result| {
+ let mut response = Response::new();
+ context.x_span_id.as_ref().map(|header| {
+ response.headers_mut().set(XSpanId(header.clone()))
+ });
+
+ match result {
+ Ok(rsp) => match rsp {
+ ReleaseLookupGetResponse::FindASingleReleaseByExternalIdentifer
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(200).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::RELEASE_LOOKUP_GET_FIND_A_SINGLE_RELEASE_BY_EXTERNAL_IDENTIFER.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ ReleaseLookupGetResponse::BadRequest
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(400).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::RELEASE_LOOKUP_GET_BAD_REQUEST.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ ReleaseLookupGetResponse::NoSuchRelease
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(404).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::RELEASE_LOOKUP_GET_NO_SUCH_RELEASE.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ ReleaseLookupGetResponse::GenericErrorResponse
+
+ (body)
+
+
+ => {
+ response.set_status(StatusCode::try_from(0).unwrap());
+
+ response.headers_mut().set(ContentType(mimetypes::responses::RELEASE_LOOKUP_GET_GENERIC_ERROR_RESPONSE.clone()));
+
+
+ let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ },
+ },
+ Err(_) => {
+ // Application code returned an error. This should not happen, as the implementation should
+ // return a valid response.
+ response.set_status(StatusCode::InternalServerError);
+ response.set_body("An internal error occurred");
+ },
+ }
+
+ future::ok(response)
+ },
+ ))
+ }
+ }
+ }),
+ ) as Box<Future<Item = Response, Error = Error>>
+ }
+
+ // ReleasePost - POST /release
+ &hyper::Method::Post if path.matched(paths::ID_RELEASE) => {
+ if context.x_span_id.is_none() {
+ context.x_span_id = Some(
+ headers
+ .get::<XSpanId>()
+ .map(XSpanId::to_string)
+ .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()),
+ );
+ }
+
+ // Body parameters (note that non-required body parameters will ignore garbage
+ // values, rather than causing a 400 response). Produce warning header and logs for
+ // any unused fields.
+ Box::new(body.concat2().then(
+ move |result| -> Box<Future<Item = Response, Error = Error>> {
+ match result {
+ Ok(body) => {
+ let mut unused_elements = Vec::new();
+ let param_body: Option<
+ models::ReleaseEntity,
+ > = if !body.is_empty() {
+ let deserializer =
+ &mut serde_json::Deserializer::from_slice(&*body);
+
+ match serde_ignored::deserialize(deserializer, |path| {
+ warn!("Ignoring unknown field in body: {}", path);
+ unused_elements.push(path.to_string());
+ }) {
+ Ok(param_body) => param_body,
+
+ Err(_) => None,
+ }
+ } else {
+ None
+ };
+
+ Box::new(api_impl.release_post(param_body, &context).then(
+ move |result| {
+ let mut response = Response::new();
+ context.x_span_id.as_ref().map(|header| {
+ response.headers_mut().set(XSpanId(header.clone()))
+ });
+
+ if !unused_elements.is_empty() {
+ response.headers_mut().set(Warning(format!(
+ "Ignoring unknown fields in body: {:?}",
+ unused_elements
+ )));
+ }
+
+ match result {
+ Ok(rsp) => match rsp {
+ ReleasePostResponse::Created(body) => {
+ response.set_status(
+ StatusCode::try_from(201).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(
+ mimetypes::responses::RELEASE_POST_CREATED
+ .clone(),
+ ));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ ReleasePostResponse::BadRequest(body) => {
+ response.set_status(
+ StatusCode::try_from(400).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(mimetypes::responses::RELEASE_POST_BAD_REQUEST.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ ReleasePostResponse::GenericErrorResponse(body) => {
+ response.set_status(
+ StatusCode::try_from(0).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(mimetypes::responses::RELEASE_POST_GENERIC_ERROR_RESPONSE.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ },
+ Err(_) => {
+ // Application code returned an error. This should not happen, as the implementation should
+ // return a valid response.
+ response
+ .set_status(StatusCode::InternalServerError);
+ response.set_body("An internal error occurred");
+ }
+ }
+
+ future::ok(response)
+ },
+ ))
+ }
+ Err(e) => Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!("Couldn't read body parameter body: {}", e)),
+ )),
+ }
+ },
+ )) as Box<Future<Item = Response, Error = Error>>
+ }
+
+ // WorkIdGet - GET /work/{id}
+ &hyper::Method::Get if path.matched(paths::ID_WORK_ID) => {
+ if context.x_span_id.is_none() {
+ context.x_span_id = Some(
+ headers
+ .get::<XSpanId>()
+ .map(XSpanId::to_string)
+ .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()),
+ );
+ }
+
+ // Path parameters
+ let path = uri.path().to_string();
+ let path_params = paths::REGEX_WORK_ID.captures(&path).unwrap_or_else(|| {
+ panic!(
+ "Path {} matched RE WORK_ID in set but failed match against \"{}\"",
+ path,
+ paths::REGEX_WORK_ID.as_str()
+ )
+ });
+
+ let param_id = match percent_encoding::percent_decode(path_params["id"].as_bytes())
+ .decode_utf8()
+ {
+ Ok(param_id) => match param_id.parse::<String>() {
+ Ok(param_id) => param_id,
+ Err(e) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!("Couldn't parse path parameter id: {}", e)),
+ ))
+ }
+ },
+ Err(_) => {
+ return Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!(
+ "Couldn't percent-decode path parameter as UTF-8: {}",
+ &path_params["id"]
+ )),
+ ))
+ }
+ };
+
+ Box::new(
+ ({
+ {
+ {
+ Box::new(api_impl.work_id_get(param_id, &context).then(
+ move |result| {
+ let mut response = Response::new();
+ context.x_span_id.as_ref().map(|header| {
+ response.headers_mut().set(XSpanId(header.clone()))
+ });
+
+ match result {
+ Ok(rsp) => match rsp {
+ WorkIdGetResponse::FetchASingleWorkById(body) => {
+ response.set_status(
+ StatusCode::try_from(200).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(mimetypes::responses::WORK_ID_GET_FETCH_A_SINGLE_WORK_BY_ID.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ WorkIdGetResponse::BadRequest(body) => {
+ response.set_status(
+ StatusCode::try_from(400).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(mimetypes::responses::WORK_ID_GET_BAD_REQUEST.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ WorkIdGetResponse::GenericErrorResponse(body) => {
+ response.set_status(
+ StatusCode::try_from(0).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(mimetypes::responses::WORK_ID_GET_GENERIC_ERROR_RESPONSE.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ },
+ Err(_) => {
+ // Application code returned an error. This should not happen, as the implementation should
+ // return a valid response.
+ response
+ .set_status(StatusCode::InternalServerError);
+ response.set_body("An internal error occurred");
+ }
+ }
+
+ future::ok(response)
+ },
+ ))
+ }
+ }
+ }),
+ ) as Box<Future<Item = Response, Error = Error>>
+ }
+
+ // WorkPost - POST /work
+ &hyper::Method::Post if path.matched(paths::ID_WORK) => {
+ if context.x_span_id.is_none() {
+ context.x_span_id = Some(
+ headers
+ .get::<XSpanId>()
+ .map(XSpanId::to_string)
+ .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()),
+ );
+ }
+
+ // Body parameters (note that non-required body parameters will ignore garbage
+ // values, rather than causing a 400 response). Produce warning header and logs for
+ // any unused fields.
+ Box::new(body.concat2().then(
+ move |result| -> Box<Future<Item = Response, Error = Error>> {
+ match result {
+ Ok(body) => {
+ let mut unused_elements = Vec::new();
+ let param_body: Option<
+ models::WorkEntity,
+ > = if !body.is_empty() {
+ let deserializer =
+ &mut serde_json::Deserializer::from_slice(&*body);
+
+ match serde_ignored::deserialize(deserializer, |path| {
+ warn!("Ignoring unknown field in body: {}", path);
+ unused_elements.push(path.to_string());
+ }) {
+ Ok(param_body) => param_body,
+
+ Err(_) => None,
+ }
+ } else {
+ None
+ };
+
+ Box::new(api_impl.work_post(param_body, &context).then(
+ move |result| {
+ let mut response = Response::new();
+ context.x_span_id.as_ref().map(|header| {
+ response.headers_mut().set(XSpanId(header.clone()))
+ });
+
+ if !unused_elements.is_empty() {
+ response.headers_mut().set(Warning(format!(
+ "Ignoring unknown fields in body: {:?}",
+ unused_elements
+ )));
+ }
+
+ match result {
+ Ok(rsp) => match rsp {
+ WorkPostResponse::Created(body) => {
+ response.set_status(
+ StatusCode::try_from(201).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(
+ mimetypes::responses::WORK_POST_CREATED
+ .clone(),
+ ));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ WorkPostResponse::BadRequest(body) => {
+ response.set_status(
+ StatusCode::try_from(400).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(
+ mimetypes::responses::WORK_POST_BAD_REQUEST
+ .clone(),
+ ));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ WorkPostResponse::GenericErrorResponse(body) => {
+ response.set_status(
+ StatusCode::try_from(0).unwrap(),
+ );
+
+ response.headers_mut().set(ContentType(mimetypes::responses::WORK_POST_GENERIC_ERROR_RESPONSE.clone()));
+
+ let body = serde_json::to_string(&body)
+ .expect("impossible to fail to serialize");
+
+ response.set_body(body);
+ }
+ },
+ Err(_) => {
+ // Application code returned an error. This should not happen, as the implementation should
+ // return a valid response.
+ response
+ .set_status(StatusCode::InternalServerError);
+ response.set_body("An internal error occurred");
+ }
+ }
+
+ future::ok(response)
+ },
+ ))
+ }
+ Err(e) => Box::new(future::ok(
+ Response::new()
+ .with_status(StatusCode::BadRequest)
+ .with_body(format!("Couldn't read body parameter body: {}", e)),
+ )),
+ }
+ },
+ )) as Box<Future<Item = Response, Error = Error>>
+ }
+
+ _ => Box::new(future::ok(
+ Response::new().with_status(StatusCode::NotFound),
+ )) as Box<Future<Item = Response, Error = Error>>,
+ }
+ }
+}
diff --git a/rust/src/api_lib.rs b/rust/src/api_lib.rs
new file mode 100644
index 00000000..cc8c2313
--- /dev/null
+++ b/rust/src/api_lib.rs
@@ -0,0 +1,34 @@
+//! Main library entry point for fatcat implementation.
+
+// Imports required by server library.
+// extern crate fatcat;
+// extern crate swagger;
+extern crate futures;
+extern crate chrono;
+#[macro_use]
+extern crate error_chain;
+
+mod server;
+
+mod errors {
+ error_chain!{}
+}
+
+pub use self::errors::*;
+use std::io;
+use hyper;
+use fatcat;
+
+pub struct NewService;
+
+impl hyper::server::NewService for NewService {
+ type Request = (hyper::Request, fatcat::Context);
+ type Response = hyper::Response;
+ type Error = hyper::Error;
+ type Instance = fatcat::server::Service<server::Server>;
+
+ /// Instantiate a new server.
+ fn new_service(&self) -> io::Result<Self::Instance> {
+ Ok(fatcat::server::Service::new(server::Server))
+ }
+}
diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs
new file mode 100644
index 00000000..16701b27
--- /dev/null
+++ b/rust/src/api_server.rs
@@ -0,0 +1,288 @@
+//! Server implementation of fatcat.
+
+#![allow(unused_imports)]
+
+use chrono;
+use futures::{self, Future};
+
+use std::collections::HashMap;
+
+use swagger;
+
+use fatcat_api::models;
+use fatcat_api::{Api, ApiError, ContainerIdGetResponse, ContainerLookupGetResponse,
+ ContainerPostResponse, Context, CreatorIdGetResponse, CreatorLookupGetResponse,
+ CreatorPostResponse, EditgroupIdAcceptPostResponse, EditgroupIdGetResponse,
+ EditgroupPostResponse, EditorUsernameChangelogGetResponse,
+ EditorUsernameGetResponse, FileIdGetResponse, FileLookupGetResponse,
+ FilePostResponse, ReleaseIdGetResponse, ReleaseLookupGetResponse,
+ ReleasePostResponse, WorkIdGetResponse, WorkPostResponse};
+
+#[derive(Copy, Clone)]
+pub struct Server;
+
+impl Api for Server {
+ fn container_id_get(
+ &self,
+ id: String,
+ context: &Context,
+ ) -> Box<Future<Item = ContainerIdGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "container_id_get(\"{}\") - X-Span-ID: {:?}",
+ id,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn container_lookup_get(
+ &self,
+ issn: String,
+ context: &Context,
+ ) -> Box<Future<Item = ContainerLookupGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "container_lookup_get(\"{}\") - X-Span-ID: {:?}",
+ issn,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn container_post(
+ &self,
+ body: Option<models::ContainerEntity>,
+ context: &Context,
+ ) -> Box<Future<Item = ContainerPostResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "container_post({:?}) - X-Span-ID: {:?}",
+ body,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn creator_id_get(
+ &self,
+ id: String,
+ context: &Context,
+ ) -> Box<Future<Item = CreatorIdGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "creator_id_get(\"{}\") - X-Span-ID: {:?}",
+ id,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn creator_lookup_get(
+ &self,
+ orcid: String,
+ context: &Context,
+ ) -> Box<Future<Item = CreatorLookupGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "creator_lookup_get(\"{}\") - X-Span-ID: {:?}",
+ orcid,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn creator_post(
+ &self,
+ body: Option<models::CreatorEntity>,
+ context: &Context,
+ ) -> Box<Future<Item = CreatorPostResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "creator_post({:?}) - X-Span-ID: {:?}",
+ body,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn editgroup_id_accept_post(
+ &self,
+ id: i32,
+ context: &Context,
+ ) -> Box<Future<Item = EditgroupIdAcceptPostResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "editgroup_id_accept_post({}) - X-Span-ID: {:?}",
+ id,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn editgroup_id_get(
+ &self,
+ id: i32,
+ context: &Context,
+ ) -> Box<Future<Item = EditgroupIdGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "editgroup_id_get({}) - X-Span-ID: {:?}",
+ id,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn editgroup_post(
+ &self,
+ context: &Context,
+ ) -> Box<Future<Item = EditgroupPostResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "editgroup_post() - X-Span-ID: {:?}",
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn editor_username_changelog_get(
+ &self,
+ username: String,
+ context: &Context,
+ ) -> Box<Future<Item = EditorUsernameChangelogGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "editor_username_changelog_get(\"{}\") - X-Span-ID: {:?}",
+ username,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn editor_username_get(
+ &self,
+ username: String,
+ context: &Context,
+ ) -> Box<Future<Item = EditorUsernameGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "editor_username_get(\"{}\") - X-Span-ID: {:?}",
+ username,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn file_id_get(
+ &self,
+ id: String,
+ context: &Context,
+ ) -> Box<Future<Item = FileIdGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "file_id_get(\"{}\") - X-Span-ID: {:?}",
+ id,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn file_lookup_get(
+ &self,
+ sha1: String,
+ context: &Context,
+ ) -> Box<Future<Item = FileLookupGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "file_lookup_get(\"{}\") - X-Span-ID: {:?}",
+ sha1,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn file_post(
+ &self,
+ body: Option<models::FileEntity>,
+ context: &Context,
+ ) -> Box<Future<Item = FilePostResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "file_post({:?}) - X-Span-ID: {:?}",
+ body,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn release_id_get(
+ &self,
+ id: String,
+ context: &Context,
+ ) -> Box<Future<Item = ReleaseIdGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "release_id_get(\"{}\") - X-Span-ID: {:?}",
+ id,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn release_lookup_get(
+ &self,
+ doi: String,
+ context: &Context,
+ ) -> Box<Future<Item = ReleaseLookupGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "release_lookup_get(\"{}\") - X-Span-ID: {:?}",
+ doi,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn release_post(
+ &self,
+ body: Option<models::ReleaseEntity>,
+ context: &Context,
+ ) -> Box<Future<Item = ReleasePostResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "release_post({:?}) - X-Span-ID: {:?}",
+ body,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn work_id_get(
+ &self,
+ id: String,
+ context: &Context,
+ ) -> Box<Future<Item = WorkIdGetResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "work_id_get(\"{}\") - X-Span-ID: {:?}",
+ id,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn work_post(
+ &self,
+ body: Option<models::WorkEntity>,
+ context: &Context,
+ ) -> Box<Future<Item = WorkPostResponse, Error = ApiError>> {
+ let context = context.clone();
+ println!(
+ "work_post({:?}) - X-Span-ID: {:?}",
+ body,
+ context.x_span_id.unwrap_or(String::from("<none>")).clone()
+ );
+ Box::new(futures::failed("Generic failure".into()))
+ }
+}
diff --git a/rust/src/bin/fatcat-tokio.rs b/rust/src/bin/fatcat-tokio.rs
new file mode 100644
index 00000000..cc603908
--- /dev/null
+++ b/rust/src/bin/fatcat-tokio.rs
@@ -0,0 +1,67 @@
+//! Main binary entry point for fatcat implementation.
+
+#![allow(missing_docs)]
+
+// Imports required by this file.
+extern crate fatcat;
+extern crate fatcat_api;
+extern crate hyper;
+extern crate swagger;
+//extern crate openssl;
+//extern crate native_tls;
+extern crate tokio_proto;
+//extern crate tokio_tls;
+extern crate clap;
+
+//use openssl::x509::X509_FILETYPE_PEM;
+//use openssl::ssl::{SslAcceptorBuilder, SslMethod};
+//use openssl::error::ErrorStack;
+use clap::{App, Arg};
+use hyper::server::Http;
+use swagger::auth::AllowAllAuthenticator;
+use tokio_proto::TcpServer;
+
+// Builds an SSL implementation for Simple HTTPS from some hard-coded file names
+/*
+fn ssl() -> Result<SslAcceptorBuilder, ErrorStack> {
+ let mut ssl = SslAcceptorBuilder::mozilla_intermediate_raw(SslMethod::tls())?;
+
+ // Server authentication
+ ssl.set_private_key_file("examples/server-key.pem", X509_FILETYPE_PEM)?;
+ ssl.set_certificate_chain_file("examples/server-chain.pem")?;
+ ssl.check_private_key()?;
+
+ Ok(ssl)
+}
+*/
+
+/// Create custom server, wire it to the autogenerated router,
+/// and pass it to the web server.
+fn main() {
+ let matches = App::new("server")
+ .arg(
+ Arg::with_name("https")
+ .long("https")
+ .help("Whether to use HTTPS or not"),
+ )
+ .get_matches();
+
+ let service_fn = fatcat_api::server::auth::NewService::new(AllowAllAuthenticator::new(
+ fatcat::NewService,
+ "cosmo",
+ ));
+
+ let addr = "127.0.0.1:8080"
+ .parse()
+ .expect("Failed to parse bind address");
+ if matches.is_present("https") {
+ unimplemented!()
+ //let ssl = ssl().expect("Failed to load SSL keys");
+ //let builder: native_tls::TlsAcceptorBuilder = native_tls::backend::openssl::TlsAcceptorBuilderExt::from_openssl(ssl);
+ //let tls_acceptor = builder.build().expect("Failed to build TLS acceptor");
+ //TcpServer::new(tokio_tls::proto::Server::new(Http::new(), tls_acceptor), addr).serve(service_fn);
+ } else {
+ // Using HTTP
+ TcpServer::new(Http::new(), addr).serve(service_fn);
+ }
+}
diff --git a/rust/src/bin/show_creators.rs b/rust/src/bin/show_creators.rs
deleted file mode 100644
index 968d2542..00000000
--- a/rust/src/bin/show_creators.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-
-extern crate fatcat;
-extern crate diesel;
-
-use self::fatcat::*;
-use self::models::*;
-use self::diesel::prelude::*;
-
-fn main() {
- use diesel_demo::database_schema::creators::dsl::*;
-
- let connection = establish_connection();
- let results = creators.filter(published.eq(true))
- .limit(5)
- .load::<CreatorRev>(&connection)
- .expect("Error loading creators");
-
- println!("Displaying {} creators", results.len());
- for creator in results {
- println!("{}", creator.title);
- println!("----------\n");
- println!("{}", creator.body);
- }
-}
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index 679b9ed3..ec7193e6 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -1,18 +1,46 @@
-
#[macro_use]
+extern crate fatcat_api;
+extern crate chrono;
extern crate diesel;
extern crate dotenv;
+extern crate futures;
+extern crate hyper;
+extern crate swagger;
+#[macro_use]
+extern crate error_chain;
-use diesel::prelude::*;
+pub mod api_server;
+
+mod errors {
+ error_chain!{}
+}
+
+pub use self::errors::*;
use diesel::pg::PgConnection;
+use diesel::prelude::*;
use dotenv::dotenv;
use std::env;
+use std::io;
+//use hyper;
+//use fatcat_api;
pub fn establish_connection() -> PgConnection {
dotenv().ok();
- let database_url = env::var("DATABASE_URL")
- .expect("DATABASE_URL must be set");
- PgConnection::establish(&database_url)
- .expect(&format!("Error connecting to {}", database_url))
+ let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
+ PgConnection::establish(&database_url).expect(&format!("Error connecting to {}", database_url))
+}
+
+pub struct NewService;
+
+impl hyper::server::NewService for NewService {
+ type Request = (hyper::Request, fatcat_api::Context);
+ type Response = hyper::Response;
+ type Error = hyper::Error;
+ type Instance = fatcat_api::server::Service<api_server::Server>;
+
+ /// Instantiate a new server.
+ fn new_service(&self) -> io::Result<Self::Instance> {
+ Ok(fatcat_api::server::Service::new(api_server::Server))
+ }
}