├── .env ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── migrations ├── .gitkeep ├── 00000000000000_diesel_initial_setup │ ├── down.sql │ └── up.sql └── 2018-04-01-161756_create_people │ ├── down.sql │ └── up.sql ├── rust-web-with-rocket-wordpress.md ├── rust-web-with-rocket.md └── src ├── connection.rs ├── main.rs ├── people ├── handler.rs ├── mod.rs ├── repository.rs └── router.rs └── schema.rs /.env: -------------------------------------------------------------------------------- 1 | DATABASE_URL=postgres://postgres:password@localhost/rust-web-with-rocket 2 | ROCKET_ADDRESS=localhost 3 | ROCKET_PORT=8001 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target 3 | **/*.rs.bk 4 | .idea 5 | *.iml 6 | /.env 7 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "aho-corasick" 3 | version = "0.6.4" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | dependencies = [ 6 | "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 7 | ] 8 | 9 | [[package]] 10 | name = "antidote" 11 | version = "1.0.0" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | 14 | [[package]] 15 | name = "backtrace" 16 | version = "0.3.12" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | dependencies = [ 19 | "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", 20 | "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 21 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 22 | "rustc-demangle 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", 23 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 24 | ] 25 | 26 | [[package]] 27 | name = "backtrace-sys" 28 | version = "0.1.28" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | dependencies = [ 31 | "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", 32 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 33 | ] 34 | 35 | [[package]] 36 | name = "base64" 37 | version = "0.6.0" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | dependencies = [ 40 | "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 41 | "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 42 | ] 43 | 44 | [[package]] 45 | name = "base64" 46 | version = "0.9.3" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | dependencies = [ 49 | "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 50 | "safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 51 | ] 52 | 53 | [[package]] 54 | name = "base64" 55 | version = "0.10.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | dependencies = [ 58 | "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 59 | ] 60 | 61 | [[package]] 62 | name = "bitflags" 63 | version = "0.7.0" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | 66 | [[package]] 67 | name = "bitflags" 68 | version = "1.0.4" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | 71 | [[package]] 72 | name = "byteorder" 73 | version = "1.2.7" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | 76 | [[package]] 77 | name = "bytes" 78 | version = "0.4.11" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | dependencies = [ 81 | "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 82 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 83 | ] 84 | 85 | [[package]] 86 | name = "cc" 87 | version = "1.0.28" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | 90 | [[package]] 91 | name = "cfg-if" 92 | version = "0.1.2" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | 95 | [[package]] 96 | name = "cloudabi" 97 | version = "0.0.3" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | dependencies = [ 100 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 101 | ] 102 | 103 | [[package]] 104 | name = "cookie" 105 | version = "0.11.0" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | dependencies = [ 108 | "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", 109 | "ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", 110 | "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", 111 | "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 112 | ] 113 | 114 | [[package]] 115 | name = "crossbeam-utils" 116 | version = "0.6.3" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | dependencies = [ 119 | "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 120 | ] 121 | 122 | [[package]] 123 | name = "devise" 124 | version = "0.2.0" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | dependencies = [ 127 | "devise_codegen 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 128 | "devise_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 129 | ] 130 | 131 | [[package]] 132 | name = "devise_codegen" 133 | version = "0.2.0" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | dependencies = [ 136 | "devise_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 137 | "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 138 | ] 139 | 140 | [[package]] 141 | name = "devise_core" 142 | version = "0.2.0" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | dependencies = [ 145 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 146 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 147 | "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 148 | "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", 149 | ] 150 | 151 | [[package]] 152 | name = "diesel" 153 | version = "1.3.3" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | dependencies = [ 156 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 157 | "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 158 | "diesel_derives 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 159 | "pq-sys 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 160 | ] 161 | 162 | [[package]] 163 | name = "diesel_derives" 164 | version = "1.3.0" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | dependencies = [ 167 | "proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 168 | "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 169 | "syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", 170 | ] 171 | 172 | [[package]] 173 | name = "dotenv" 174 | version = "0.13.0" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | dependencies = [ 177 | "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 178 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 179 | "regex 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 180 | ] 181 | 182 | [[package]] 183 | name = "failure" 184 | version = "0.1.3" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | dependencies = [ 187 | "backtrace 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", 188 | "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 189 | ] 190 | 191 | [[package]] 192 | name = "failure_derive" 193 | version = "0.1.3" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | dependencies = [ 196 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 197 | "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 198 | "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", 199 | "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", 200 | ] 201 | 202 | [[package]] 203 | name = "filetime" 204 | version = "0.2.4" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | dependencies = [ 207 | "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 208 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 209 | "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 210 | ] 211 | 212 | [[package]] 213 | name = "fsevent" 214 | version = "0.2.17" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | dependencies = [ 217 | "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 218 | "fsevent-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 219 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 220 | ] 221 | 222 | [[package]] 223 | name = "fsevent-sys" 224 | version = "0.1.6" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | dependencies = [ 227 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 228 | ] 229 | 230 | [[package]] 231 | name = "fuchsia-zircon" 232 | version = "0.3.3" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | dependencies = [ 235 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 236 | "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 237 | ] 238 | 239 | [[package]] 240 | name = "fuchsia-zircon-sys" 241 | version = "0.3.3" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | 244 | [[package]] 245 | name = "futures" 246 | version = "0.1.25" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | 249 | [[package]] 250 | name = "httparse" 251 | version = "1.2.4" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | 254 | [[package]] 255 | name = "hyper" 256 | version = "0.10.13" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | dependencies = [ 259 | "base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 260 | "httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", 261 | "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 262 | "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 263 | "mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 264 | "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 265 | "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", 266 | "traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 267 | "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 268 | "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 269 | "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 270 | ] 271 | 272 | [[package]] 273 | name = "idna" 274 | version = "0.1.4" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | dependencies = [ 277 | "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 278 | "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 279 | "unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 280 | ] 281 | 282 | [[package]] 283 | name = "indexmap" 284 | version = "1.0.2" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | 287 | [[package]] 288 | name = "inotify" 289 | version = "0.6.1" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | dependencies = [ 292 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 293 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 294 | "inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 295 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 296 | "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", 297 | "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 298 | "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 299 | ] 300 | 301 | [[package]] 302 | name = "inotify-sys" 303 | version = "0.1.3" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | dependencies = [ 306 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 307 | ] 308 | 309 | [[package]] 310 | name = "iovec" 311 | version = "0.1.2" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | dependencies = [ 314 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 315 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 316 | ] 317 | 318 | [[package]] 319 | name = "isatty" 320 | version = "0.1.7" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | dependencies = [ 323 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 324 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 325 | "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 326 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 327 | ] 328 | 329 | [[package]] 330 | name = "itoa" 331 | version = "0.4.3" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | 334 | [[package]] 335 | name = "kernel32-sys" 336 | version = "0.2.2" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | dependencies = [ 339 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 340 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 341 | ] 342 | 343 | [[package]] 344 | name = "language-tags" 345 | version = "0.2.2" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | 348 | [[package]] 349 | name = "lazy_static" 350 | version = "1.2.0" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | 353 | [[package]] 354 | name = "lazycell" 355 | version = "1.2.1" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | 358 | [[package]] 359 | name = "libc" 360 | version = "0.2.45" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | 363 | [[package]] 364 | name = "lock_api" 365 | version = "0.1.5" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | dependencies = [ 368 | "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 369 | "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 370 | ] 371 | 372 | [[package]] 373 | name = "log" 374 | version = "0.3.9" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | dependencies = [ 377 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 378 | ] 379 | 380 | [[package]] 381 | name = "log" 382 | version = "0.4.1" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | dependencies = [ 385 | "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 386 | ] 387 | 388 | [[package]] 389 | name = "matches" 390 | version = "0.1.6" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | 393 | [[package]] 394 | name = "memchr" 395 | version = "2.0.1" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | dependencies = [ 398 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 399 | ] 400 | 401 | [[package]] 402 | name = "mime" 403 | version = "0.2.6" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | dependencies = [ 406 | "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 407 | ] 408 | 409 | [[package]] 410 | name = "mio" 411 | version = "0.6.16" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | dependencies = [ 414 | "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 415 | "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 416 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 417 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 418 | "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 419 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 420 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 421 | "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 422 | "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 423 | "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 424 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 425 | ] 426 | 427 | [[package]] 428 | name = "mio-extras" 429 | version = "2.0.5" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | dependencies = [ 432 | "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 433 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 434 | "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", 435 | "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 436 | ] 437 | 438 | [[package]] 439 | name = "miow" 440 | version = "0.2.1" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | dependencies = [ 443 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 444 | "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 445 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 446 | "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 447 | ] 448 | 449 | [[package]] 450 | name = "net2" 451 | version = "0.2.33" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | dependencies = [ 454 | "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 455 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 456 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 457 | ] 458 | 459 | [[package]] 460 | name = "notify" 461 | version = "4.0.6" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | dependencies = [ 464 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 465 | "filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", 466 | "fsevent 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", 467 | "fsevent-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 468 | "inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", 469 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 470 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 471 | "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", 472 | "mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 473 | "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 474 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 475 | ] 476 | 477 | [[package]] 478 | name = "num_cpus" 479 | version = "1.8.0" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | dependencies = [ 482 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 483 | ] 484 | 485 | [[package]] 486 | name = "owning_ref" 487 | version = "0.4.0" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | dependencies = [ 490 | "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 491 | ] 492 | 493 | [[package]] 494 | name = "parking_lot" 495 | version = "0.6.4" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | dependencies = [ 498 | "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 499 | "parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 500 | ] 501 | 502 | [[package]] 503 | name = "parking_lot_core" 504 | version = "0.3.1" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | dependencies = [ 507 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 508 | "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", 509 | "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 510 | "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", 511 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 512 | ] 513 | 514 | [[package]] 515 | name = "pear" 516 | version = "0.1.2" 517 | source = "registry+https://github.com/rust-lang/crates.io-index" 518 | dependencies = [ 519 | "pear_codegen 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 520 | ] 521 | 522 | [[package]] 523 | name = "pear_codegen" 524 | version = "0.1.2" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | dependencies = [ 527 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 528 | "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 529 | "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", 530 | "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 531 | "yansi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 532 | ] 533 | 534 | [[package]] 535 | name = "percent-encoding" 536 | version = "1.0.1" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | 539 | [[package]] 540 | name = "pq-sys" 541 | version = "0.4.4" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | dependencies = [ 544 | "vcpkg 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 545 | ] 546 | 547 | [[package]] 548 | name = "proc-macro2" 549 | version = "0.3.6" 550 | source = "registry+https://github.com/rust-lang/crates.io-index" 551 | dependencies = [ 552 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 553 | ] 554 | 555 | [[package]] 556 | name = "proc-macro2" 557 | version = "0.4.24" 558 | source = "registry+https://github.com/rust-lang/crates.io-index" 559 | dependencies = [ 560 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 561 | ] 562 | 563 | [[package]] 564 | name = "quote" 565 | version = "0.5.2" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | dependencies = [ 568 | "proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 569 | ] 570 | 571 | [[package]] 572 | name = "quote" 573 | version = "0.6.10" 574 | source = "registry+https://github.com/rust-lang/crates.io-index" 575 | dependencies = [ 576 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 577 | ] 578 | 579 | [[package]] 580 | name = "r2d2" 581 | version = "0.8.3" 582 | source = "registry+https://github.com/rust-lang/crates.io-index" 583 | dependencies = [ 584 | "antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 585 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 586 | "scheduled-thread-pool 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 587 | ] 588 | 589 | [[package]] 590 | name = "r2d2-diesel" 591 | version = "1.0.0" 592 | source = "registry+https://github.com/rust-lang/crates.io-index" 593 | dependencies = [ 594 | "diesel 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 595 | "r2d2 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", 596 | ] 597 | 598 | [[package]] 599 | name = "rand" 600 | version = "0.5.5" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | dependencies = [ 603 | "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 604 | "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 605 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 606 | "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 607 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 608 | ] 609 | 610 | [[package]] 611 | name = "rand_core" 612 | version = "0.2.2" 613 | source = "registry+https://github.com/rust-lang/crates.io-index" 614 | dependencies = [ 615 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 616 | ] 617 | 618 | [[package]] 619 | name = "rand_core" 620 | version = "0.3.0" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | 623 | [[package]] 624 | name = "redox_syscall" 625 | version = "0.1.37" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | 628 | [[package]] 629 | name = "regex" 630 | version = "1.0.3" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | dependencies = [ 633 | "aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", 634 | "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 635 | "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", 636 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 637 | "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 638 | ] 639 | 640 | [[package]] 641 | name = "regex-syntax" 642 | version = "0.6.4" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | dependencies = [ 645 | "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 646 | ] 647 | 648 | [[package]] 649 | name = "ring" 650 | version = "0.13.5" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | dependencies = [ 653 | "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", 654 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 655 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 656 | "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", 657 | ] 658 | 659 | [[package]] 660 | name = "rocket" 661 | version = "0.4.0" 662 | source = "registry+https://github.com/rust-lang/crates.io-index" 663 | dependencies = [ 664 | "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 665 | "isatty 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 666 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 667 | "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 668 | "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 669 | "pear 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 670 | "rocket_codegen 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 671 | "rocket_http 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 672 | "state 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 673 | "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", 674 | "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", 675 | "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 676 | "yansi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 677 | ] 678 | 679 | [[package]] 680 | name = "rocket_codegen" 681 | version = "0.4.0" 682 | source = "registry+https://github.com/rust-lang/crates.io-index" 683 | dependencies = [ 684 | "devise 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 685 | "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 686 | "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 687 | "rocket_http 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 688 | "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 689 | "yansi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 690 | ] 691 | 692 | [[package]] 693 | name = "rocket_contrib" 694 | version = "0.4.0" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | dependencies = [ 697 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 698 | "notify 4.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 699 | "rocket 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 700 | "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", 701 | "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", 702 | ] 703 | 704 | [[package]] 705 | name = "rocket_http" 706 | version = "0.4.0" 707 | source = "registry+https://github.com/rust-lang/crates.io-index" 708 | dependencies = [ 709 | "cookie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 710 | "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)", 711 | "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 712 | "pear 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 713 | "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 714 | "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", 715 | "state 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 716 | "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", 717 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 718 | ] 719 | 720 | [[package]] 721 | name = "rust-web-with-rocket" 722 | version = "0.1.0" 723 | dependencies = [ 724 | "diesel 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 725 | "dotenv 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", 726 | "r2d2 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", 727 | "r2d2-diesel 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 728 | "rocket 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 729 | "rocket_codegen 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 730 | "rocket_contrib 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 731 | "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", 732 | "serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", 733 | "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", 734 | ] 735 | 736 | [[package]] 737 | name = "rustc-demangle" 738 | version = "0.1.11" 739 | source = "registry+https://github.com/rust-lang/crates.io-index" 740 | 741 | [[package]] 742 | name = "rustc_version" 743 | version = "0.2.3" 744 | source = "registry+https://github.com/rust-lang/crates.io-index" 745 | dependencies = [ 746 | "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 747 | ] 748 | 749 | [[package]] 750 | name = "ryu" 751 | version = "0.2.7" 752 | source = "registry+https://github.com/rust-lang/crates.io-index" 753 | 754 | [[package]] 755 | name = "safemem" 756 | version = "0.2.0" 757 | source = "registry+https://github.com/rust-lang/crates.io-index" 758 | 759 | [[package]] 760 | name = "safemem" 761 | version = "0.3.0" 762 | source = "registry+https://github.com/rust-lang/crates.io-index" 763 | 764 | [[package]] 765 | name = "same-file" 766 | version = "1.0.4" 767 | source = "registry+https://github.com/rust-lang/crates.io-index" 768 | dependencies = [ 769 | "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 770 | ] 771 | 772 | [[package]] 773 | name = "scheduled-thread-pool" 774 | version = "0.2.0" 775 | source = "registry+https://github.com/rust-lang/crates.io-index" 776 | dependencies = [ 777 | "antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 778 | ] 779 | 780 | [[package]] 781 | name = "scopeguard" 782 | version = "0.3.3" 783 | source = "registry+https://github.com/rust-lang/crates.io-index" 784 | 785 | [[package]] 786 | name = "semver" 787 | version = "0.9.0" 788 | source = "registry+https://github.com/rust-lang/crates.io-index" 789 | dependencies = [ 790 | "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 791 | ] 792 | 793 | [[package]] 794 | name = "semver-parser" 795 | version = "0.7.0" 796 | source = "registry+https://github.com/rust-lang/crates.io-index" 797 | 798 | [[package]] 799 | name = "serde" 800 | version = "1.0.82" 801 | source = "registry+https://github.com/rust-lang/crates.io-index" 802 | 803 | [[package]] 804 | name = "serde_derive" 805 | version = "1.0.82" 806 | source = "registry+https://github.com/rust-lang/crates.io-index" 807 | dependencies = [ 808 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 809 | "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 810 | "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", 811 | ] 812 | 813 | [[package]] 814 | name = "serde_json" 815 | version = "1.0.33" 816 | source = "registry+https://github.com/rust-lang/crates.io-index" 817 | dependencies = [ 818 | "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 819 | "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 820 | "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", 821 | ] 822 | 823 | [[package]] 824 | name = "slab" 825 | version = "0.4.1" 826 | source = "registry+https://github.com/rust-lang/crates.io-index" 827 | 828 | [[package]] 829 | name = "smallvec" 830 | version = "0.6.7" 831 | source = "registry+https://github.com/rust-lang/crates.io-index" 832 | dependencies = [ 833 | "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 834 | ] 835 | 836 | [[package]] 837 | name = "stable_deref_trait" 838 | version = "1.1.1" 839 | source = "registry+https://github.com/rust-lang/crates.io-index" 840 | 841 | [[package]] 842 | name = "state" 843 | version = "0.4.1" 844 | source = "registry+https://github.com/rust-lang/crates.io-index" 845 | 846 | [[package]] 847 | name = "syn" 848 | version = "0.13.1" 849 | source = "registry+https://github.com/rust-lang/crates.io-index" 850 | dependencies = [ 851 | "proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 852 | "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 853 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 854 | ] 855 | 856 | [[package]] 857 | name = "syn" 858 | version = "0.15.23" 859 | source = "registry+https://github.com/rust-lang/crates.io-index" 860 | dependencies = [ 861 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 862 | "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 863 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 864 | ] 865 | 866 | [[package]] 867 | name = "synstructure" 868 | version = "0.10.1" 869 | source = "registry+https://github.com/rust-lang/crates.io-index" 870 | dependencies = [ 871 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 872 | "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 873 | "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", 874 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 875 | ] 876 | 877 | [[package]] 878 | name = "thread_local" 879 | version = "0.3.6" 880 | source = "registry+https://github.com/rust-lang/crates.io-index" 881 | dependencies = [ 882 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 883 | ] 884 | 885 | [[package]] 886 | name = "time" 887 | version = "0.1.39" 888 | source = "registry+https://github.com/rust-lang/crates.io-index" 889 | dependencies = [ 890 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 891 | "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 892 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 893 | ] 894 | 895 | [[package]] 896 | name = "tokio-executor" 897 | version = "0.1.5" 898 | source = "registry+https://github.com/rust-lang/crates.io-index" 899 | dependencies = [ 900 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 901 | ] 902 | 903 | [[package]] 904 | name = "tokio-io" 905 | version = "0.1.10" 906 | source = "registry+https://github.com/rust-lang/crates.io-index" 907 | dependencies = [ 908 | "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 909 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 910 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 911 | ] 912 | 913 | [[package]] 914 | name = "tokio-reactor" 915 | version = "0.1.7" 916 | source = "registry+https://github.com/rust-lang/crates.io-index" 917 | dependencies = [ 918 | "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 919 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 920 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 921 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 922 | "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", 923 | "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 924 | "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", 925 | "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 926 | "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 927 | "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 928 | ] 929 | 930 | [[package]] 931 | name = "toml" 932 | version = "0.4.10" 933 | source = "registry+https://github.com/rust-lang/crates.io-index" 934 | dependencies = [ 935 | "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", 936 | ] 937 | 938 | [[package]] 939 | name = "traitobject" 940 | version = "0.1.0" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | 943 | [[package]] 944 | name = "typeable" 945 | version = "0.1.2" 946 | source = "registry+https://github.com/rust-lang/crates.io-index" 947 | 948 | [[package]] 949 | name = "ucd-util" 950 | version = "0.1.1" 951 | source = "registry+https://github.com/rust-lang/crates.io-index" 952 | 953 | [[package]] 954 | name = "unicase" 955 | version = "1.4.2" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | dependencies = [ 958 | "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 959 | ] 960 | 961 | [[package]] 962 | name = "unicode-bidi" 963 | version = "0.3.4" 964 | source = "registry+https://github.com/rust-lang/crates.io-index" 965 | dependencies = [ 966 | "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 967 | ] 968 | 969 | [[package]] 970 | name = "unicode-normalization" 971 | version = "0.1.5" 972 | source = "registry+https://github.com/rust-lang/crates.io-index" 973 | 974 | [[package]] 975 | name = "unicode-xid" 976 | version = "0.1.0" 977 | source = "registry+https://github.com/rust-lang/crates.io-index" 978 | 979 | [[package]] 980 | name = "unreachable" 981 | version = "1.0.0" 982 | source = "registry+https://github.com/rust-lang/crates.io-index" 983 | dependencies = [ 984 | "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 985 | ] 986 | 987 | [[package]] 988 | name = "untrusted" 989 | version = "0.6.2" 990 | source = "registry+https://github.com/rust-lang/crates.io-index" 991 | 992 | [[package]] 993 | name = "url" 994 | version = "1.7.0" 995 | source = "registry+https://github.com/rust-lang/crates.io-index" 996 | dependencies = [ 997 | "idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 998 | "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 999 | "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 1000 | ] 1001 | 1002 | [[package]] 1003 | name = "utf8-ranges" 1004 | version = "1.0.0" 1005 | source = "registry+https://github.com/rust-lang/crates.io-index" 1006 | 1007 | [[package]] 1008 | name = "vcpkg" 1009 | version = "0.2.3" 1010 | source = "registry+https://github.com/rust-lang/crates.io-index" 1011 | 1012 | [[package]] 1013 | name = "version_check" 1014 | version = "0.1.3" 1015 | source = "registry+https://github.com/rust-lang/crates.io-index" 1016 | 1017 | [[package]] 1018 | name = "void" 1019 | version = "1.0.2" 1020 | source = "registry+https://github.com/rust-lang/crates.io-index" 1021 | 1022 | [[package]] 1023 | name = "walkdir" 1024 | version = "2.2.7" 1025 | source = "registry+https://github.com/rust-lang/crates.io-index" 1026 | dependencies = [ 1027 | "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 1028 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 1029 | "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 1030 | ] 1031 | 1032 | [[package]] 1033 | name = "winapi" 1034 | version = "0.2.8" 1035 | source = "registry+https://github.com/rust-lang/crates.io-index" 1036 | 1037 | [[package]] 1038 | name = "winapi" 1039 | version = "0.3.6" 1040 | source = "registry+https://github.com/rust-lang/crates.io-index" 1041 | dependencies = [ 1042 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 1043 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 1044 | ] 1045 | 1046 | [[package]] 1047 | name = "winapi-build" 1048 | version = "0.1.1" 1049 | source = "registry+https://github.com/rust-lang/crates.io-index" 1050 | 1051 | [[package]] 1052 | name = "winapi-i686-pc-windows-gnu" 1053 | version = "0.4.0" 1054 | source = "registry+https://github.com/rust-lang/crates.io-index" 1055 | 1056 | [[package]] 1057 | name = "winapi-util" 1058 | version = "0.1.1" 1059 | source = "registry+https://github.com/rust-lang/crates.io-index" 1060 | dependencies = [ 1061 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 1062 | ] 1063 | 1064 | [[package]] 1065 | name = "winapi-x86_64-pc-windows-gnu" 1066 | version = "0.4.0" 1067 | source = "registry+https://github.com/rust-lang/crates.io-index" 1068 | 1069 | [[package]] 1070 | name = "ws2_32-sys" 1071 | version = "0.2.1" 1072 | source = "registry+https://github.com/rust-lang/crates.io-index" 1073 | dependencies = [ 1074 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 1075 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 1076 | ] 1077 | 1078 | [[package]] 1079 | name = "yansi" 1080 | version = "0.4.0" 1081 | source = "registry+https://github.com/rust-lang/crates.io-index" 1082 | 1083 | [[package]] 1084 | name = "yansi" 1085 | version = "0.5.0" 1086 | source = "registry+https://github.com/rust-lang/crates.io-index" 1087 | 1088 | [metadata] 1089 | "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" 1090 | "checksum antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5" 1091 | "checksum backtrace 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "a2eff3830839471718ef8522b9025b399bfb713e25bc220da721364efb660d7d" 1092 | "checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" 1093 | "checksum base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "621fc7ecb8008f86d7fb9b95356cd692ce9514b80a86d85b397f32a22da7b9e2" 1094 | "checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9" 1095 | "checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" 1096 | "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" 1097 | "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" 1098 | "checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d" 1099 | "checksum bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "40ade3d27603c2cb345eb0912aec461a6dec7e06a4ae48589904e808335c7afa" 1100 | "checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749" 1101 | "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" 1102 | "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 1103 | "checksum cookie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1465f8134efa296b4c19db34d909637cb2bf0f7aaf21299e23e18fa29ac557cf" 1104 | "checksum crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "41ee4864f4797060e52044376f7d107429ce1fb43460021b126424b7180ee21a" 1105 | "checksum devise 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "74e04ba2d03c5fa0d954c061fc8c9c288badadffc272ebb87679a89846de3ed3" 1106 | "checksum devise_codegen 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "066ceb7928ca93a9bedc6d0e612a8a0424048b0ab1f75971b203d01420c055d7" 1107 | "checksum devise_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf41c59b22b5e3ec0ea55c7847e5f358d340f3a8d6d53a5cf4f1564967f96487" 1108 | "checksum diesel 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "164080ac16a4d1d80a50f0a623e4ddef41cb2779eee85bcc76907d340dfc98cc" 1109 | "checksum diesel_derives 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "03bcaf77491f53e400d5ee3bdd57142ea4e1c47fe9217b3361ff9a76ca0e3d37" 1110 | "checksum dotenv 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d0a1279c96732bc6800ce6337b6a614697b0e74ae058dc03c62ebeb78b4d86" 1111 | "checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7" 1112 | "checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596" 1113 | "checksum filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a2df5c1a8c4be27e7707789dc42ae65976e60b394afd293d1419ab915833e646" 1114 | "checksum fsevent 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "c4bbbf71584aeed076100b5665ac14e3d85eeb31fdbb45fbd41ef9a682b5ec05" 1115 | "checksum fsevent-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1a772d36c338d07a032d5375a36f15f9a7043bf0cb8ce7cee658e037c6032874" 1116 | "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 1117 | "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 1118 | "checksum futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "49e7653e374fe0d0c12de4250f0bdb60680b8c80eed558c5c7538eec9c89e21b" 1119 | "checksum httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f407128745b78abc95c0ffbe4e5d37427fdc0d45470710cfef8c44522a2e37" 1120 | "checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2" 1121 | "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" 1122 | "checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" 1123 | "checksum inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40b54539f3910d6f84fbf9a643efd6e3aa6e4f001426c0329576128255994718" 1124 | "checksum inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0" 1125 | "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" 1126 | "checksum isatty 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a118a53ba42790ef25c82bb481ecf36e2da892646cccd361e69a6bb881e19398" 1127 | "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" 1128 | "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 1129 | "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" 1130 | "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" 1131 | "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" 1132 | "checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74" 1133 | "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" 1134 | "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" 1135 | "checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" 1136 | "checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376" 1137 | "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" 1138 | "checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" 1139 | "checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432" 1140 | "checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40" 1141 | "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" 1142 | "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" 1143 | "checksum notify 4.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "873ecfd8c174964ae30f401329d140142312c8e5590719cf1199d5f1717d8078" 1144 | "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" 1145 | "checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" 1146 | "checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" 1147 | "checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" 1148 | "checksum pear 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c26d2b92e47063ffce70d3e3b1bd097af121a9e0db07ca38a6cc1cf0cc85ff25" 1149 | "checksum pear_codegen 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "336db4a192cc7f54efeb0c4e11a9245394824cc3bcbd37ba3ff51240c35d7a6e" 1150 | "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" 1151 | "checksum pq-sys 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4dfb5e575ef93a1b7b2a381d47ba7c5d4e4f73bff37cee932195de769aad9a54" 1152 | "checksum proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "49b6a521dc81b643e9a51e0d1cf05df46d5a2f3c0280ea72bcb68276ba64a118" 1153 | "checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09" 1154 | "checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8" 1155 | "checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c" 1156 | "checksum r2d2 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5d746fc8a0dab19ccea7ff73ad535854e90ddb3b4b8cdce953dd5cd0b2e7bd22" 1157 | "checksum r2d2-diesel 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9c29bad92da76d02bc2c020452ebc3a3fe6fa74cfab91e711c43116e4fb1a3" 1158 | "checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" 1159 | "checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372" 1160 | "checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db" 1161 | "checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" 1162 | "checksum regex 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3d8c9f33201f46669484bacc312b00e7541bed6aaf296dffe2bb4e0ac6b8ce2a" 1163 | "checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1" 1164 | "checksum ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2c4db68a2e35f3497146b7e4563df7d4773a2433230c5e4b448328e31740458a" 1165 | "checksum rocket 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "242154377a85c2a9e036fc31ffc8c200b9e1f22a196e47baa3b57716606ca89d" 1166 | "checksum rocket_codegen 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d907d6d458c859651c1cf4c8fa99b77685082bde0561db6a4600b365058f710" 1167 | "checksum rocket_contrib 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f73e161dad5730435f51c815a5c6831d2e57b6b4299b1bf609d31b09aa9a2fa7" 1168 | "checksum rocket_http 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba9d4f2ce5bba6e1b6d3100493bbad63879e99bbf6b4365d61e6f781daab324d" 1169 | "checksum rustc-demangle 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "01b90379b8664dd83460d59bdc5dd1fd3172b8913788db483ed1325171eab2f7" 1170 | "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 1171 | "checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" 1172 | "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" 1173 | "checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9" 1174 | "checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" 1175 | "checksum scheduled-thread-pool 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a2ff3fc5223829be817806c6441279c676e454cc7da608faf03b0ccc09d3889" 1176 | "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" 1177 | "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 1178 | "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 1179 | "checksum serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)" = "6fa52f19aee12441d5ad11c9a00459122bd8f98707cadf9778c540674f1935b6" 1180 | "checksum serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)" = "96a7f9496ac65a2db5929afa087b54f8fc5008dcfbe48a8874ed20049b0d6154" 1181 | "checksum serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)" = "c37ccd6be3ed1fdf419ee848f7c758eb31b054d7cd3ae3600e3bae0adf569811" 1182 | "checksum slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f9776d6b986f77b35c6cf846c11ad986ff128fe0b2b63a3628e3755e8d3102d" 1183 | "checksum smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b73ea3738b47563803ef814925e69be00799a8c07420be8b996f8e98fb2336db" 1184 | "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" 1185 | "checksum state 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7345c971d1ef21ffdbd103a75990a15eb03604fc8b8852ca8cb418ee1a099028" 1186 | "checksum syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91b52877572087400e83d24b9178488541e3d535259e04ff17a63df1e5ceff59" 1187 | "checksum syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9545a6a093a3f0bd59adb472700acc08cad3776f860f16a897dfce8c88721cbc" 1188 | "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" 1189 | "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" 1190 | "checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098" 1191 | "checksum tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c117b6cf86bb730aab4834f10df96e4dd586eff2c3c27d3781348da49e255bde" 1192 | "checksum tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "7392fe0a70d5ce0c882c4778116c519bd5dbaa8a7c3ae3d04578b3afafdcda21" 1193 | "checksum tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "502b625acb4ee13cbb3b90b8ca80e0addd263ddacf6931666ef751e610b07fb5" 1194 | "checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" 1195 | "checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" 1196 | "checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" 1197 | "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" 1198 | "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" 1199 | "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 1200 | "checksum unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f" 1201 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 1202 | "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" 1203 | "checksum untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f" 1204 | "checksum url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f808aadd8cfec6ef90e4a14eb46f24511824d1ac596b9682703c87056c8678b7" 1205 | "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" 1206 | "checksum vcpkg 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7ed0f6789c8a85ca41bbc1c9d175422116a9869bd1cf31bb08e1493ecce60380" 1207 | "checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d" 1208 | "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 1209 | "checksum walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1" 1210 | "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 1211 | "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" 1212 | "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 1213 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1214 | "checksum winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "afc5508759c5bf4285e61feb862b6083c8480aec864fa17a81fdec6f69b461ab" 1215 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1216 | "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 1217 | "checksum yansi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d60c3b48c9cdec42fb06b3b84b5b087405e1fa1c644a1af3930e4dfafe93de48" 1218 | "checksum yansi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71" 1219 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-web-with-rocket" 3 | version = "0.1.0" 4 | authors = ["Dan Newton "] 5 | 6 | [dependencies] 7 | rocket = "0.4.0" 8 | rocket_codegen = "0.4.0" 9 | diesel = { version = "1.3.3", features = ["postgres"] } 10 | dotenv = "0.13.0" 11 | r2d2-diesel = "1.0.0" 12 | r2d2 = "0.8.3" 13 | serde = "1.0.82" 14 | serde_derive = "1.0.82" 15 | serde_json = "1.0.33" 16 | 17 | [dependencies.rocket_contrib] 18 | version = "0.4.0" 19 | default-features = false 20 | features = ["json"] 21 | -------------------------------------------------------------------------------- /migrations/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lankydan/rust-web-with-rocket/da31b9753d9fd7d612b6360a8c510e0d509c9c36/migrations/.gitkeep -------------------------------------------------------------------------------- /migrations/00000000000000_diesel_initial_setup/down.sql: -------------------------------------------------------------------------------- 1 | -- This file was automatically created by Diesel to setup helper functions 2 | -- and other internal bookkeeping. This file is safe to edit, any future 3 | -- changes will be added to existing projects as new migrations. 4 | 5 | DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass); 6 | DROP FUNCTION IF EXISTS diesel_set_updated_at(); 7 | -------------------------------------------------------------------------------- /migrations/00000000000000_diesel_initial_setup/up.sql: -------------------------------------------------------------------------------- 1 | -- This file was automatically created by Diesel to setup helper functions 2 | -- and other internal bookkeeping. This file is safe to edit, any future 3 | -- changes will be added to existing projects as new migrations. 4 | 5 | 6 | 7 | 8 | -- Sets up a trigger for the given table to automatically set a column called 9 | -- `updated_at` whenever the row is modified (unless `updated_at` was included 10 | -- in the modified columns) 11 | -- 12 | -- # Example 13 | -- 14 | -- ```sql 15 | -- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW()); 16 | -- 17 | -- SELECT diesel_manage_updated_at('users'); 18 | -- ``` 19 | CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$ 20 | BEGIN 21 | EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s 22 | FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl); 23 | END; 24 | $$ LANGUAGE plpgsql; 25 | 26 | CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$ 27 | BEGIN 28 | IF ( 29 | NEW IS DISTINCT FROM OLD AND 30 | NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at 31 | ) THEN 32 | NEW.updated_at := current_timestamp; 33 | END IF; 34 | RETURN NEW; 35 | END; 36 | $$ LANGUAGE plpgsql; 37 | -------------------------------------------------------------------------------- /migrations/2018-04-01-161756_create_people/down.sql: -------------------------------------------------------------------------------- 1 | -- This file should undo anything in `up.sql` 2 | DROP TABLE people; -------------------------------------------------------------------------------- /migrations/2018-04-01-161756_create_people/up.sql: -------------------------------------------------------------------------------- 1 | -- Your SQL goes here 2 | CREATE TABLE people( 3 | id SERIAL PRIMARY KEY, 4 | first_name VARCHAR NOT NULL, 5 | last_name VARCHAR NOT NULL, 6 | age INT NOT NULL, 7 | profession VARCHAR NOT NULL, 8 | salary INT NOT NULL 9 | ) -------------------------------------------------------------------------------- /rust-web-with-rocket-wordpress.md: -------------------------------------------------------------------------------- 1 | Sorry if that title seemed stupid. The super serious title would be "Creating a REST API in Rust using Rocket and Diesel", but thats boring. Anyway... 2 | 3 | Here I go with my first post that fully focuses on Rust. After spending a few months doing a bit here and there I decided to just dive right in as I was going through the Rust book at too slow a pace to keep myself interested. So, in this post I decided to write about setting up a simple REST API which is something that I have done in Java plenty of times but with Rust it is a different story. 4 | 5 | Anyway, enough with this personal backstory and onto the actual tutorial. 6 | 7 | In this post we will be looking creating a REST API in Rust. To do this we will useRocket to setup the API and Diesel to deal with the database. 8 | 9 | At the time of writing this post the only databases that Diesel accommodates are Postgres, MySql and Sqlite. 10 | 11 |

Dependencies

12 | Before we can begin coding we need to sort out our dependencies. 13 | 14 | [gist https://gist.github.com/lankydan/2b72b641b905ff37af99a512fa5638bd /] 15 | 16 | As you can see there's a reasonable amount of crates being used here. Obviously we need the rocket and diesel crates whereas the rest are not yet clear. rocket_codegen is pulled in for some macros. dotenv to allow us to retrieve environment variables from an external file. r2d2 and r2d2-diesel for connection pooling to connect to the database, specifically via diesel. Finally, serde, serde_derive, serde_json for serialisation and deserialisation of data that is sent and received by the REST API. One extra note about the diesel dependency, postgres has been specified explicitly to include only Postgres modules in the Diesel crate, if we wanted to use a different database or even multiple types within the project we just need to specify them or remove the features list all together. 17 | 18 | There is one last piece of information we need before we can continue. To use Rocket, we must be using a nightly build of Rust since it relies on features not yet included in the stable builds. 19 |

Doing database stuff with Diesel

20 | I think the best place to start with is setting up Diesel. Once that's done we will have our schema defined (only one table for this post) which we can then use to build up our application. 21 | 22 | For the purpose of this post I will assume that you have already setup the Diesel CLI. A quick example on how to use it can be found in Diesel's getting started guide along with other information in how to use it. I personally used Postgres solely due to not being able to get everything I needed to run MySQL, which seemed to stem from me using Windows... Postgres on the other hand was nice and easy to get going. 23 |

Creating a table

24 | First set the DATABASE_URL to connect to Postgres with the below command or by adding it to the .env file manually: 25 |
 26 | echo DATABASE_URL=postgres://postgres:password@localhost/rust-web-with-rocket > .env
 27 | 
28 | Hopefully your username and password differs from mine! 29 | 30 | Then run diesel setup to create a database for the project and an empty migrations folder for later use. 31 | 32 | For this post, we will be modelling people who can be: inserted, retrieved, updated and deleted from the database. To do this we are going to first need a table to store them in. So lets create our first migration. 33 |
 34 | diesel migration generate create_people
 35 | 
36 | This creates two new files within a single folder which are then placed in the migrations directory. up.sql is for upgrading and is where we want to put the SQL to create the table. down.sql is for downgrading so we can undo the upgrade if necessary, therefore for this example it will drop the people table. 37 | 38 | To create the people table we run: 39 | 40 | [gist https://gist.github.com/lankydan/40800defe7f02485988bddb5b97477a3 /] 41 | 42 | And to undo this creation: 43 | 44 | [gist https://gist.github.com/lankydan/7b7bbaa18e8ea1e05fe754f5e4d9f179 /] 45 | 46 | To apply this migration we need to run: 47 |
 48 | diesel migration run
 49 | 
50 | And if we need to undo it right away: 51 |
 52 | diesel migration redo
 53 | 
54 |

Mapping to structs

55 | At this point we have a people table which we can start inserting data into. Since Diesel is an ORM we are obviously going to start mapping the table to something that represents the it in Rust. To do just that we will use a struct. 56 | 57 | [gist https://gist.github.com/lankydan/6ca1eb5d800ce430432e476061a88034 /] 58 | 59 | Below is the struct that represents each record in the people table; otherwise named a person. Since I only want this struct to represent a record in the table I decided to provide it with no logic and therefore it does not have a impl section. There are three Diesel specific attributes here: #[derive(Queryable)], #[derive(AsChangeSet)] and #[table_name]. #[derive(Queryable)] will generate the code to retrieve a person from the database and #[derive(AsChangeSet)] to allow us to use update.set later on. Finally, #[table_name = "people"] is required since the plural of person in not people. If this struct was called post and the table posts, like in the Diesel getting started example, the attribute can be removed since the plural of post is posts which matches the table name. 60 | 61 | The other attributes are aptly named; #[derive(Serialize)] and #[derive(Deserialize)]. These are for accepting/returning JSON into/from the REST API. They both come from the serde crate. We will look at this more later on in the post. 62 | 63 | Before we move any further, we should look at creating our schema. Not a database schema for Postgres, a Rust schema file that uses the table! macro that does the actual Rust to database mappings for us. If we run the following command: 64 |
 65 | diesel print-schema > src/schema.rs
 66 | 
67 | The following file is generated: 68 |
 69 | table! {
 70 |     people (id) {
 71 |         id -> Int4,
 72 |         first_name -> Varchar,
 73 |         last_name -> Varchar,
 74 |         age -> Int4,
 75 |         profession -> Varchar,
 76 |         salary -> Int4,
 77 |     }
 78 | }
 79 | 
80 | For now we can just ignore this file and carry on going. 81 | 82 | Using the Person struct defined above, we can execute SELECT and UPDATE queries. DELETE doesn't require a struct to map to since we just require the record's ID. Then what about INSERT? For convenience, Diesel suggests doing it this way, we will use another struct with the sole purpose of being used for inserts. 83 | 84 | [gist https://gist.github.com/lankydan/99adc54f48c4cf066446a1fd73e9d11f /] 85 | 86 | InsertablePerson is nearly identical to the Person struct but with one difference, the id field is missing. This is because the ID of the record will be generated automatically when inserted, so we have no need to set it ourselves. Other fields could also differ slightly, if we don't want some other fields being set on creation. Similar to the Person's attributes #[derive(Insertable)] is added generate the code to insert a new record. 87 | 88 | I have also included an utility function from_person which takes a Person struct's values and converts it into an InsertablePerson. This simply removes the id field in this scenario and allows me to have tidier code in other places. This function isn't 100% necessary and is added due to my coding preferences. 89 |

Executing queries

90 | At this point we have created our table and the structs that map to it. Now we need to put them to use. Below are all the methods needed to implement the basic REST API: 91 | 92 | [gist https://gist.github.com/lankydan/bbf521dfb3d5f5380326274a51db49e3 /] 93 | 94 | The diesel module is used to access the insert_into, update and delete functions. diesel::prelude::* provides access to a range of modules and structs that are generally useful when using Diesel, for this example; PgConnection and QueryResult are included in this list. schema::people is included so we can access the people table from within Rust and execute methods on it. Note that schema::people is referring back to the people table defined in the schema.rs file we generated earlier. 95 | 96 | Let's look at one of the functions more closely: 97 | 98 | [gist https://gist.github.com/lankydan/84ccaf17a2bb8db3fb5abb9acdc0a2d1 /] 99 | 100 | As mentioned above, we can access the people table via people::table thanks to including schema::people. This example is nice and easy, find is specified as the query that selects a single record with the provided ID and get_result executes the query with the connection provided to it. 101 | 102 | In my examples QueryResult is returned from all functions. Diesel returns QueryResult<T> from most methods and is shorthand for Result due to the following line: 103 | 104 | [gist https://gist.github.com/lankydan/702de21e950da0c0711040b706fd869f /] 105 | 106 | Returning QueryResult allows us to determine what happens if the query fails in whatever way is suitable for where the function is used. If we wanted to return a Person directly out of the function we could call expect to log the error there and then. 107 | 108 | Also, since I have used Postgres for this post, PgConnection is used. If we were using one of the other databases Diesel support; MySql for example, MysqlConnection would be used instead. 109 | 110 | Let's look at another one: 111 | 112 | [gist https://gist.github.com/lankydan/2a530bf568823ce7724ba3721e1a58d2 /] 113 | 114 | This works slightly differently to the earlier get function. Rather than accessing a function on the people::table it is passed into another Diesel function, insert_into. As I mentioned earlier in the post, InsertablePerson was defined specifically for new records, therefore the values from person are extracted thanks to the from_person helper function. Remember that no ID is included on this struct. Like before, get_result is called again to execute the statement. 115 |

Connection pooling - A bit of everything

116 | You might have a question following on from the previous section. I'm hoping it's the question I'm about to answer... Where did the PgConnection come from? Well, let's have a look. 117 | 118 | The code below shows how a connection pool is created: 119 | 120 | [gist https://gist.github.com/lankydan/d17aff3353380e3f3992f105e91b40b2 /] 121 | 122 | Now, I'm not going to lie. This is a straight up copy from the Rocket documentation. That link will probably provide a better explanation than I would but I'll give you a quick run through it. init_pool creates a new pool of connections for our database which we have specified as PgConnections. DbConn wraps the actual PgConnection. Finally, FromRequest allows a DbConn to be retrieved from Rocket handler functions when included in the input parameters, we will look at an example of this soon. 123 |

Rocket

124 | All of the database magic has been implemented at this point. All we now need to do is create the REST API and hook it up to the back-end that we've created. In Rocket this consists of routes that map incoming requests to handler functions which will then deal with the requests. So we have got two clear things still to do, define the routes and create the handler functions. 125 |

Handlers

126 | It makes sense to start with the handlers first so we actually have an idea of what the routes are mapping to. Below are all the handlers that are needed to implement the typical REST verbs of GET, POST, PUT, DELETE: 127 | 128 | [gist https://gist.github.com/lankydan/c3e9fe301d87c270ba947ad9dbc3d130 /] 129 | 130 | Each method is marked with an attribute that specifies what REST verb it accepts along with the path needed to get there. Part of the path is missing as the rest will be defined when the routes are created, so just hold on for a bit... The attributes can also accept a few extra properties to properly specify the behavior of the handler. 131 | 132 | Until we look at routing, just assume the base path to these handler methods are localhost:8000/people. 133 | 134 | Let's look at one of the simpler handlers: 135 | 136 | [gist https://gist.github.com/lankydan/c5e8a203cad89fad50d686c072a7a1b8 /] 137 | 138 | This function returns all the person records stored in the database. It accepts a GET request thanks to the #[get("/")] attribute on the function. The path it accepts requests from is localhost:8000/people as denoted by the "/". 139 | 140 | To use cURL to send a request to this function we need to execute: 141 |
142 | curl localhost:8000/people
143 | 
144 | This will then return a JSON list of people as specified by the return type of Result<Json<Vec<Person>>, Failure>. To do this, records are retrieved from database and mapped into their JSON representation. Thanks to the return type of QueryResult<Vec<Person>> from the all function, if anything goes wrong at the database level we can then map this to a HTTP status code to represent the error properly. This is why the return type is a Result. It provides us with an option to either return the records when nothing goes wrong but if anything does, it can return a error status code instead. 145 | 146 | Serde finally pops up here, although it does so behind the scenes. Without the #[derive(Serialize)] attribute we added onto the Person struct earlier we would not be able to return Json<Vec<Person>> from this function; the same applies for Json<Person>. 147 | 148 | The error_status function isn't particularly interesting and doesn't help for this specific example. It simply converts an Error contained within QueryResult into a status code. I was only particularly interested in these two scenarios, hence why it either returns NotFound or InternalServerError for anything else since I'm lazy (plus most of the other errors would honestly be classed as internal server errors). 149 | 150 | The last point to touch on before we look at another handler function, the appearance of DbConn. The code we wrote earlier for connection pooling allows this. At this point all we need to do is include it in the function parameters and it will retrieve a connection for us. 151 | 152 | Let's look at the PUT handler next: 153 | 154 | [gist https://gist.github.com/lankydan/344731e805bd7a9982648fe4f185c0cd /] 155 | 156 | The first difference between this function and the previous ALL example (ignoring request type) is the id and person being passed in. The "</id>" represents the path variable id and data = "<person"> represents that request body that maps to person in the functions arguments. The format property specifies the content of the request body, in other words, the data property should contain JSON (indicated by application/json). We can see that it does indeed do just that since person is of type Json<Person>. 157 | 158 | Serde again shows up here. It is needed to retrieve the Json<Person> from the request body. 159 | 160 | To retrieve the contents of person we must call into_inner(), revealing the Person that was waiting to break out all along... update is called and the result or error is mapped and returned in the Result enum. Due to the implementation of error_status, an error will be thrown if an existing record does not exist with the passed in ID. Whether this is how it should work seems to vary from person to person (according to my googling anyway). If we instead wanted to insert the record if it did not already exist, we would need to handle the Error::NotFound and instead call similar code to that in the POST function. 161 | 162 | Well we just mentioned it, so we need to look at it now. Below is the POST function: 163 | 164 | [gist https://gist.github.com/lankydan/4c873148524fa652d7b99f660d874f07 /] 165 | 166 | This contains similar components to the PUT function we just looked at. The main difference is the return type. The status code that should be returned from a successful POST request is 201 Created rather than 200 Ok which was used by the previous functions that we looked at. To return a different status code, the Result should contain status::Created instead of Json<Person> directly. This change is what makes it return a 201 status code. 167 | 168 | To create the status::Created struct, the created record along with the path to retrieve it (via a GET request) must be passed into it's constructor. Passing in the path as an absolute string isn't ideal so I have retrieved the host and port number from the environment variables. This might not be the best way to get this to work... But I spent ages trying to figure out how to get them out of Rocket and gave up in the end. 169 | 170 | We should probably also look at Responders in Rocket and how they enrich the returned responses, but this post is already so long so I will instead refer you to the Rocket documentation on the subject. 171 |

Routing

172 | We are nearly at the end now... Don't give up yet! 173 | 174 | The handlers are setup to accept requests to the server but before we can use them the we need to set the routes to the different functions. Since all of the functions in this post are related to people it will be mapped to /people. See the code below on how to do this: 175 | 176 | [gist https://gist.github.com/lankydan/426ede1ffedafe52aa965f6a00ca9120 /] 177 | 178 | create_routes is called by the main function to get everything rolling. ignite creates a new instance of Rocket. The handler functions are then mounted onto a base request path of /people by specifying all of the them inside of routes!. Finally, launch starts the application server. 179 |

Configuration

180 | Earlier in this post I made use of environment variables to retrieve the host and port of the running server. So let's have a brief look at the configuration required to change the host and port in Rocket. There are two ways to do this from within configuration files. Either specify values within a .env file or create a Rocket.toml file. 181 | 182 | When using a .env file, the values must follow the format of ROCKET_{PARAM} where PARAM is the property you are trying to set. {ADDRESS} represents the host and {PORT} is obviously the port number. Taking this information, below is the .env file used in this post (removing unrelated configuration): 183 |
184 | ROCKET_ADDRESS=localhost
185 | ROCKET_PORT=8000
186 | 
187 | If instead you wanted to use a Rocket.toml file, it would look like the below. 188 |
189 | [development]
190 | address = "localhost"
191 | port = 8000
192 | 
193 | In this situation, these values are only applicable for development, which is handy since thats all I'm doing. 194 | 195 | If you choose to include neither of these Rocket will instead fall back to it's default configuration. So don't worry about needing to do loads of configuration when playing around with Rocket; for local development the defaults are most likely good enough. 196 | 197 | For more (and better) explanations of Rocket Configuration, I again recommend looking at their documentation. 198 |

The last step

199 | Finally we have reached the end. All that is left to do now is create the main method so the application can be run. 200 | 201 | [gist https://gist.github.com/lankydan/3efb9383579a23a1d40d502a2c450068 /] 202 | 203 | All main does is load in the environment variables and starts Rocket by calling create_routes. The rest of this file just pulls in a load of crates so they don't need to be scattered throughout the rest of the code. 204 | 205 | Now you can rest. That was a pretty long post. I'd write a conclusion but honestly, I'm tired and don't want to write anymore. So for a short summary, in this post we have created a simple REST API using Rocket to run an application server that responds to requests and used Diesel to connect to a database to manage the state of the application. 206 | 207 | The code used in this post can be on my GitHub. 208 | 209 | If you liked this post, then follow me on Twitter at @LankyDanDev to be able to keep up with my new posts as I write them. 210 | 211 | -------------------------------------------------------------------------------- /rust-web-with-rocket.md: -------------------------------------------------------------------------------- 1 | Creating a Rusty Rocket fuelled with Diesel 2 | 3 | Sorry if that title seemed stupid. The super serious title would be "Creating a REST API in Rust using Rocket and Diesel", but thats boring. Anyway... 4 | 5 | Here I go with my first post that fully focuses on Rust. After spending a few months doing a bit here and there I decided to just dive right in as I was going through the Rust book at too slow a pace to keep myself interested. So, in this post I decided to write about setting up a simple REST API which is something that I have done in Java plenty of times but with Rust it is a different story. 6 | 7 | Anyway, enough with this personal backstory and onto the actual tutorial. 8 | 9 | In this post we will be looking creating a REST API in Rust. To do this we will use [Rocket](https://rocket.rs/) to setup the API and [Diesel](http://diesel.rs/) to deal with the database. 10 | 11 | At the time of writing this post the only databases that Diesel accommodates are Postgres, MySql and Sqlite. 12 | 13 | ## Dependencies 14 | 15 | Before we can begin coding we need to sort out our dependencies. 16 | ```toml 17 | [dependencies] 18 | rocket = "0.3.6" 19 | rocket_codegen = "0.3.6" 20 | diesel = { version = "1.0.0", features = ["postgres"] } 21 | dotenv = "0.9.0" 22 | r2d2-diesel = "1.0" 23 | r2d2 = "0.8" 24 | serde = "1.0" 25 | serde_derive = "1.0" 26 | serde_json = "1.0" 27 | 28 | [dependencies.rocket_contrib] 29 | version = "*" 30 | default-features = false 31 | features = ["json"] 32 | ``` 33 | As you can see there's a reasonable amount of crates being used here. Obviously we need the `rocket` and `diesel` crates whereas the rest are not yet clear. `rocket_codegen` is pulled in for some macros. `dotenv` to allow us to retrieve environment variables from an external file. `r2d2` and `r2d2-diesel` for connection pooling to connect to the database, specifically via diesel. Finally, `serde`, `serde_derive`, `serde_json` for serialisation and deserialisation of data that is sent and received by the REST API. One extra note about the `diesel` dependency, `postgres` has been specified explicitly to include only Postgres modules in the Diesel crate, if we wanted to use a different database or even multiple types within the project we just need to specify them or remove the `features` list all together. 34 | 35 | There is one last piece of information we need before we can continue. To use Rocket, we must be using a nightly build of Rust since it relies on features not yet included in the stable builds. 36 | 37 | ## Doing database stuff with Diesel 38 | 39 | I think the best place to start with is setting up Diesel. Once that's done we will have our schema defined (only one table for this post) which we can then use to build up our application. 40 | 41 | For the purpose of this post I will assume that you have already setup the Diesel CLI. A quick example on how to use it can be found in Diesel's [getting started guide](http://diesel.rs/guides/getting-started/) along with other information in how to use it. I personally used Postgres solely due to not being able to get everything I needed to run MySQL, which seemed to stem from me using Windows... Postgres on the other hand was nice and easy to get going. 42 | 43 | ### Creating a table 44 | 45 | First set the `DATABASE_URL` to connect to Postgres with the below command or by adding it to the `.env` file manually: 46 | ``` 47 | echo DATABASE_URL=postgres://postgres:password@localhost/rust-web-with-rocket > .env 48 | ``` 49 | Hopefully your username and password differs from mine! 50 | 51 | Then run `diesel setup` to create a database for the project and an empty migrations folder for later use. 52 | 53 | For this post, we will be modelling people who can be: inserted, retrieved, updated and deleted from the database. To do this we are going to first need a table to store them in. So lets create our first migration. 54 | ``` 55 | diesel migration generate create_people 56 | ``` 57 | This creates two new files within a single folder which are then placed in the migrations directory. `up.sql` is for upgrading and is where we want to put the SQL to create the table. `down.sql` is for downgrading so we can undo the upgrade if necessary, therefore for this example it will drop the people table. 58 | 59 | To create the people table we run: 60 | ```sql 61 | CREATE TABLE people( 62 | id SERIAL PRIMARY KEY, 63 | first_name VARCHAR NOT NULL, 64 | last_name VARCHAR NOT NULL, 65 | age INT NOT NULL, 66 | profession VARCHAR NOT NULL, 67 | salary INT NOT NULL 68 | ) 69 | ``` 70 | And to undo this creation: 71 | ```sql 72 | DROP TABLE people 73 | ``` 74 | To apply this migration we need to run: 75 | ``` 76 | diesel migration run 77 | ``` 78 | And if we need to undo it right away: 79 | ``` 80 | diesel migration redo 81 | ``` 82 | ### Mapping to structs 83 | 84 | At this point we have a people table which we can start inserting data into. Since Diesel is an ORM we are obviously going to start mapping the table to something that represents the it in Rust. To do just that we will use a struct. 85 | ```rust 86 | use super::schema::people; 87 | 88 | #[derive(Queryable, AsChangeset, Serialize, Deserialize)] 89 | #[table_name = "people"] 90 | pub struct Person { 91 | pub id: i32, 92 | pub first_name: String, 93 | pub last_name: String, 94 | pub age: i32, 95 | pub profession: String, 96 | pub salary: i32, 97 | } 98 | ``` 99 | Below is the struct that represents each record in the people table; otherwise named a person. Since I only want this struct to represent a record in the table I decided to provide it with no logic and therefore it does not have a `impl` section. There are three Diesel specific attributes here: `#[derive(Queryable)]`, `#[derive(AsChangeSet)]` and `#[table_name]`. `#[derive(Queryable)]` will generate the code to retrieve a person from the database and `#[derive(AsChangeSet)]` to allow us to use `update.set` later on. Finally, `#[table_name = "people"]` is required since the plural of person in not people. If this struct was called post and the table posts, like in the Diesel [getting started example](http://diesel.rs/guides/getting-started/), the attribute can be removed since the plural of post is posts which matches the table name. 100 | 101 | The other attributes are aptly named; `#[derive(Serialize)]` and `#[derive(Deserialize)]`. These are for accepting/returning JSON into/from the REST API. They both come from the `serde` crate. We will look at this more later on in the post. 102 | 103 | Before we move any further, we should look at creating our schema. Not a database schema for Postgres, a Rust schema file that uses the `table!` macro that does the actual Rust to database mappings for us. If we run the following command: 104 | ``` 105 | diesel print-schema > src/schema.rs 106 | ``` 107 | The following file is generated: 108 | ``` 109 | table! { 110 | people (id) { 111 | id -> Int4, 112 | first_name -> Varchar, 113 | last_name -> Varchar, 114 | age -> Int4, 115 | profession -> Varchar, 116 | salary -> Int4, 117 | } 118 | } 119 | ``` 120 | For now we can just ignore this file and carry on going. 121 | 122 | Using the `Person` struct defined above, we can execute `SELECT` and `UPDATE` queries. `DELETE` doesn't require a struct to map to since we just require the record's ID. Then what about `INSERT`? For convenience, Diesel suggests doing it this way, we will use another struct with the sole purpose of being used for inserts. 123 | ```rust 124 | #[derive(Insertable)] 125 | #[table_name = "people"] 126 | struct InsertablePerson { 127 | first_name: String, 128 | last_name: String, 129 | age: i32, 130 | profession: String, 131 | salary: i32, 132 | } 133 | 134 | impl InsertablePerson { 135 | 136 | fn from_person(person: Person) -> InsertablePerson { 137 | InsertablePerson { 138 | first_name: person.first_name, 139 | last_name: person.last_name, 140 | age: person.age, 141 | profession: person.profession, 142 | salary: person.salary, 143 | } 144 | } 145 | } 146 | ``` 147 | `InsertablePerson` is nearly identical to the `Person` struct but with one difference, the `id` field is missing. This is because the ID of the record will be generated automatically when inserted, so we have no need to set it ourselves. Other fields could also differ slightly, if we don't want some other fields being set on creation. Similar to the `Person`'s attributes `#[derive(Insertable)]` is added generate the code to insert a new record. 148 | 149 | I have also included an utility function `from_person` which takes a `Person` struct's values and converts it into an `InsertablePerson`. This simply removes the `id` field in this scenario and allows me to have tidier code in other places. This function isn't 100% necessary and is added due to my coding preferences. 150 | 151 | ### Executing queries 152 | 153 | At this point we have created our table and the structs that map to it. Now we need to put them to use. Below are all the methods needed to implement the basic REST API: 154 | ```rust 155 | use diesel; 156 | use diesel::prelude::*; 157 | use schema::people; 158 | use people::Person; 159 | 160 | pub fn all(connection: &PgConnection) -> QueryResult> { 161 | people::table.load::(&*connection) 162 | } 163 | 164 | pub fn get(id: i32, connection: &PgConnection) -> QueryResult { 165 | people::table.find(id).get_result::(connection) 166 | } 167 | 168 | pub fn insert(person: Person, connection: &PgConnection) -> QueryResult { 169 | diesel::insert_into(people::table) 170 | .values(&InsertablePerson::from_person(person)) 171 | .get_result(connection) 172 | } 173 | 174 | pub fn update(id: i32, person: Person, connection: &PgConnection) -> QueryResult { 175 | diesel::update(people::table.find(id)) 176 | .set(&person) 177 | .get_result(connection) 178 | } 179 | 180 | pub fn delete(id: i32, connection: &PgConnection) -> QueryResult { 181 | diesel::delete(people::table.find(id)) 182 | .execute(connection) 183 | } 184 | ``` 185 | The `diesel` module is used to access the `insert_into`, `update` and `delete` functions. `diesel::prelude::*` provides access to a range of modules and structs that are generally useful when using Diesel, for this example; `PgConnection` and `QueryResult` are included in this list. `schema::people` is included so we can access the people table from within Rust and execute methods on it. Note that `schema::people` is referring back to the people table defined in the `schema.rs` file we generated earlier. 186 | 187 | Let's look at one of the functions more closely: 188 | ```rust 189 | pub fn get(id: i32, connection: &PgConnection) -> QueryResult { 190 | people::table.find(id).get_result::(connection) 191 | } 192 | ``` 193 | As mentioned above, we can access the people table via `people::table` thanks to including `schema::people`. This example is nice and easy, `find` is specified as the query that selects a single record with the provided ID and `get_result` executes the query with the connection provided to it. 194 | 195 | In my examples `QueryResult` is returned from all functions. Diesel returns `QueryResult<T>` from most methods and is shorthand for `Result` due to the following line: 196 | ```rust 197 | pub type QueryResult = Result; 198 | ``` 199 | Returning `QueryResult` allows us to determine what happens if the query fails in whatever way is suitable for where the function is used. If we wanted to return a `Person` directly out of the function we could call `expect` to log the error there and then. 200 | 201 | Also, since I have used Postgres for this post, `PgConnection` is used. If we were using one of the other databases Diesel support; MySql for example, `MysqlConnection` would be used instead. 202 | 203 | Let's look at another one: 204 | ```rust 205 | pub fn insert(person: Person, connection: &PgConnection) -> QueryResult { 206 | diesel::insert_into(people::table) 207 | .values(&InsertablePerson::from_person(person)) 208 | .get_result(connection) 209 | } 210 | ``` 211 | This works slightly differently to the earlier `get` function. Rather than accessing a function on the `people::table` it is passed into another Diesel function, `insert_into`. As I mentioned earlier in the post, `InsertablePerson` was defined specifically for new records, therefore the values from `person` are extracted thanks to the `from_person` helper function. Remember that no ID is included on this struct. Like before, `get_result` is called again to execute the statement. 212 | 213 | ## Connection pooling - A bit of everything 214 | 215 | You might have a question following on from the previous section. I'm hoping it's the question I'm about to answer... Where did the `PgConnection` come from? Well, let's have a look. 216 | 217 | The code below shows how a connection pool is created: 218 | ```rust 219 | use diesel::pg::PgConnection; 220 | use r2d2; 221 | use r2d2_diesel::ConnectionManager; 222 | use rocket::{Outcome, Request, State}; 223 | use rocket::http::Status; 224 | use rocket::request::{self, FromRequest}; 225 | use std::env; 226 | use std::ops::Deref; 227 | 228 | type Pool = r2d2::Pool>; 229 | 230 | pub fn init_pool() -> Pool { 231 | let manager = ConnectionManager::::new(database_url()); 232 | Pool::new(manager).expect("db pool") 233 | } 234 | 235 | fn database_url() -> String { 236 | env::var("DATABASE_URL").expect("DATABASE_URL must be set") 237 | } 238 | 239 | pub struct DbConn(pub r2d2::PooledConnection>); 240 | 241 | impl<'a, 'r> FromRequest<'a, 'r> for DbConn { 242 | type Error = (); 243 | 244 | fn from_request(request: &'a Request<'r>) -> request::Outcome { 245 | let pool = request.guard::>()?; 246 | match pool.get() { 247 | Ok(conn) => Outcome::Success(DbConn(conn)), 248 | Err(_) => Outcome::Failure((Status::ServiceUnavailable, ())), 249 | } 250 | } 251 | } 252 | 253 | impl Deref for DbConn { 254 | type Target = PgConnection; 255 | 256 | fn deref(&self) -> &Self::Target { 257 | &self.0 258 | } 259 | } 260 | ``` 261 | Now, I'm not going to lie. This is a straight up copy from the [Rocket documentation](https://rocket.rs/guide/state/#databases). That link will probably provide a better explanation than I would but I'll give you a quick run through it. `init_pool` creates a new pool of connections for our database which we have specified as `PgConnection`s. `DbConn` wraps the actual `PgConnection`. Finally, `FromRequest` allows a `DbConn` to be retrieved from Rocket handler functions when included in the input parameters, we will look at an example of this soon. 262 | 263 | ## Rocket 264 | 265 | All of the database magic has been implemented at this point. All we now need to do is create the REST API and hook it up to the back-end that we've created. In Rocket this consists of routes that map incoming requests to handler functions which will then deal with the requests. So we have got two clear things still to do, define the routes and create the handler functions. 266 | 267 | ### Handlers 268 | 269 | It makes sense to start with the handlers first so we actually have an idea of what the routes are mapping to. Below are all the handlers that are needed to implement the typical REST verbs of `GET`, `POST`, `PUT`, `DELETE`: 270 | ```rust 271 | use connection::DbConn; 272 | use diesel::result::Error; 273 | use std::env; 274 | use people; 275 | use people::Person; 276 | use rocket::http::Status; 277 | use rocket::response::{Failure, status}; 278 | use rocket_contrib::Json; 279 | 280 | #[get("/")] 281 | fn all(connection: DbConn) -> Result>, Failure> { 282 | people::repository::all(&connection) 283 | .map(|people| Json(people)) 284 | .map_err(|error| error_status(error)) 285 | } 286 | 287 | fn error_status(error: Error) -> Failure { 288 | Failure(match error { 289 | Error::NotFound => Status::NotFound, 290 | _ => Status::InternalServerError 291 | }) 292 | } 293 | 294 | #[get("/")] 295 | fn get(id: i32, connection: DbConn) -> Result, Failure> { 296 | people::repository::get(id, &connection) 297 | .map(|person| Json(person)) 298 | .map_err(|error| error_status(error)) 299 | } 300 | 301 | #[post("/", format = "application/json", data = "")] 302 | fn post(person: Json, connection: DbConn) -> Result>, Failure> { 303 | people::repository::insert(person.into_inner(), &connection) 304 | .map(|person| person_created(person)) 305 | .map_err(|error| error_status(error)) 306 | } 307 | 308 | fn person_created(person: Person) -> status::Created> { 309 | let host = env::var("ROCKET_ADDRESS").expect("ROCKET_ADDRESS must be set"); 310 | let port = env::var("ROCKET_PORT").expect("ROCKET_PORT must be set"); 311 | status::Created( 312 | format!("{host}:{port}/people/{id}", host = host, port = port, id = person.id).to_string(), 313 | Some(Json(person))) 314 | } 315 | 316 | #[put("/", format = "application/json", data = "")] 317 | fn put(id: i32, person: Json, connection: DbConn) -> Result, Failure> { 318 | people::repository::update(id, person.into_inner(), &connection) 319 | .map(|person| Json(person)) 320 | .map_err(|error| error_status(error)) 321 | } 322 | 323 | #[delete("/")] 324 | fn delete(id: i32, connection: DbConn) -> Result { 325 | match people::repository::get(id, &connection) { 326 | Ok(_) => people::repository::delete(id, &connection) 327 | .map(|_| status::NoContent) 328 | .map_err(|error| error_status(error)), 329 | Err(error) => Err(error_status(error)) 330 | } 331 | } 332 | ``` 333 | Each method is marked with an attribute that specifies what REST verb it accepts along with the path needed to get there. Part of the path is missing as the rest will be defined when the routes are created, so just hold on for a bit... The attributes can also accept a few extra properties to properly specify the behavior of the handler. 334 | 335 | Until we look at routing, just assume the base path to these handler methods are `localhost:8000/people`. 336 | 337 | Let's look at one of the simpler handlers: 338 | ```rust 339 | #[get("/")] 340 | fn all(connection: DbConn) -> Result>, Failure> { 341 | people::repository::all(&connection) 342 | .map(|people| Json(people)) 343 | .map_err(|error| error_status(error)) 344 | } 345 | 346 | fn error_status(error: Error) -> Failure { 347 | Failure(match error { 348 | Error::NotFound => Status::NotFound, 349 | _ => Status::InternalServerError 350 | }) 351 | } 352 | ``` 353 | This function returns all the person records stored in the database. It accepts a `GET` request thanks to the `#[get("/")]` attribute on the function. The path it accepts requests from is `localhost:8000/people` as denoted by the `"/"`. 354 | 355 | To use cURL to send a request to this function we need to execute: 356 | ``` 357 | curl localhost:8000/people 358 | ``` 359 | This will then return a JSON list of people as specified by the return type of `Result<Json<Vec<Person>>, Failure>`. To do this, records are retrieved from database and mapped into their JSON representation. Thanks to the return type of `QueryResult<Vec<Person>>` from the `all` function, if anything goes wrong at the database level we can then map this to a HTTP status code to represent the error properly. This is why the return type is a `Result`. It provides us with an option to either return the records when nothing goes wrong but if anything does, it can return a error status code instead. 360 | 361 | Serde finally pops up here, although it does so behind the scenes. Without the `#[derive(Serialize)]` attribute we added onto the `Person` struct earlier we would not be able to return `Json<Vec<Person>>` from this function; the same applies for `Json<Person>`. 362 | 363 | The `error_status` function isn't particularly interesting and doesn't help for this specific example. It simply converts an `Error` contained within `QueryResult` into a status code. I was only particularly interested in these two scenarios, hence why it either returns `NotFound` or `InternalServerError` for anything else since I'm lazy (plus most of the other errors would honestly be classed as internal server errors). 364 | 365 | The last point to touch on before we look at another handler function, the appearance of `DbConn`. The code we wrote earlier for connection pooling allows this. At this point all we need to do is include it in the function parameters and it will retrieve a connection for us. 366 | 367 | Let's look at the `PUT` handler next: 368 | ```rust 369 | #[put("/", format = "application/json", data = "")] 370 | fn put(id: i32, person: Json, connection: DbConn) -> Result, Failure> { 371 | people::repository::update(id, person.into_inner(), &connection) 372 | .map(|person| Json(person)) 373 | .map_err(|error| error_status(error)) 374 | } 375 | ``` 376 | The first difference between this function and the previous `ALL` example (ignoring request type) is the `id` and `person` being passed in. The `"</id>"` represents the path variable `id` and `data = "<person">` represents that request body that maps to `person` in the functions arguments. The `format` property specifies the content of the request body, in other words, the `data` property should contain JSON (indicated by `application/json`). We can see that it does indeed do just that since `person` is of type `Json<Person>`. 377 | 378 | Serde again shows up here. It is needed to retrieve the `Json<Person>` from the request body. 379 | 380 | To retrieve the contents of `person` we must call `into_inner()`, revealing the `Person` that was waiting to break out all along... `update` is called and the result or error is mapped and returned in the `Result` enum. Due to the implementation of `error_status`, an error will be thrown if an existing record does not exist with the passed in ID. Whether this is how it should work seems to vary from person to person (according to my googling anyway). If we instead wanted to insert the record if it did not already exist, we would need to handle the `Error::NotFound` and instead call similar code to that in the `POST` function. 381 | 382 | Well we just mentioned it, so we need to look at it now. Below is the `POST` function: 383 | ```rust 384 | #[post("/", format = "application/json", data = "")] 385 | fn post(person: Json, connection: DbConn) -> Result>, Failure> { 386 | people::repository::insert(person.into_inner(), &connection) 387 | .map(|person| person_created(person)) 388 | .map_err(|error| error_status(error)) 389 | } 390 | 391 | fn person_created(person: Person) -> status::Created> { 392 | status::Created( 393 | format!("{host}:{port}/people/{id}", host = host(), port = port(), id = person.id).to_string(), 394 | Some(Json(person))) 395 | } 396 | 397 | fn host() -> String { 398 | env::var("ROCKET_ADDRESS").expect("ROCKET_ADDRESS must be set") 399 | } 400 | 401 | fn port() -> String { 402 | env::var("ROCKET_PORT").expect("ROCKET_PORT must be set") 403 | } 404 | ``` 405 | This contains similar components to the `PUT` function we just looked at. The main difference is the return type. The status code that should be returned from a successful `POST` request is `201 Created` rather than `200 Ok` which was used by the previous functions that we looked at. To return a different status code, the `Result` should contain `status::Created` instead of `Json<Person>` directly. This change is what makes it return a `201` status code. 406 | 407 | To create the `status::Created` struct, the created record along with the path to retrieve it (via a `GET` request) must be passed into it's constructor. Passing in the path as an absolute string isn't ideal so I have retrieved the host and port number from the environment variables. This might not be the best way to get this to work... But I spent ages trying to figure out how to get them out of Rocket and gave up in the end. 408 | 409 | We should probably also look at Responders in Rocket and how they enrich the returned responses, but this post is already so long so I will instead refer you to the [Rocket documentation](https://rocket.rs/guide/responses/#rocket-responders) on the subject. 410 | 411 | ### Routing 412 | 413 | We are nearly at the end now... Don't give up yet! 414 | 415 | The handlers are setup to accept requests to the server but before we can use them the we need to set the routes to the different functions. Since all of the functions in this post are related to people it will be mapped to `/people`. See the code below on how to do this: 416 | ```rust 417 | use people; 418 | use rocket; 419 | use connection; 420 | 421 | pub fn create_routes() { 422 | rocket::ignite() 423 | .manage(connection::init_pool()) 424 | .mount("/people", 425 | routes![people::handler::all, 426 | people::handler::get, 427 | people::handler::post, 428 | people::handler::put, 429 | people::handler::delete], 430 | ).launch(); 431 | } 432 | ``` 433 | `create_routes` is called by the `main` function to get everything rolling. `ignite` creates a new instance of `Rocket`. The handler functions are then mounted onto a base request path of `/people` by specifying all of the them inside of `routes!`. Finally, `launch` starts the application server. 434 | 435 | ### Configuration 436 | 437 | Earlier in this post I made use of environment variables to retrieve the host and port of the running server. So let's have a brief look at the configuration required to change the host and port in Rocket. There are two ways to do this from within configuration files. Either specify values within a `.env` file or create a `Rocket.toml` file. 438 | 439 | When using a `.env` file, the values must follow the format of `ROCKET_{PARAM}` where `PARAM` is the property you are trying to set. `{ADDRESS}` represents the host and `{PORT}` is obviously the port number. Taking this information, below is the `.env` file used in this post (removing unrelated configuration): 440 | ```.env 441 | ROCKET_ADDRESS=localhost 442 | ROCKET_PORT=8000 443 | ``` 444 | If instead you wanted to use a `Rocket.toml` file, it would look like the below. 445 | ```toml 446 | [development] 447 | address = "localhost" 448 | port = 8000 449 | ``` 450 | In this situation, these values are only applicable for development, which is handy since thats all I'm doing. 451 | 452 | If you choose to include neither of these Rocket will instead fall back to it's default configuration. So don't worry about needing to do loads of configuration when playing around with Rocket; for local development the defaults are most likely good enough. 453 | 454 | For more (and better) explanations of Rocket Configuration, I again recommend looking at their [documentation](https://rocket.rs/guide/configuration/#configuration). 455 | 456 | ## The last step 457 | 458 | Finally we have reached the end. All that is left to do now is create the `main` method so the application can be run. 459 | ```rust 460 | #![feature(plugin, decl_macro, custom_derive)] 461 | #![plugin(rocket_codegen)] 462 | #[macro_use] 463 | extern crate diesel; 464 | extern crate dotenv; 465 | extern crate r2d2; 466 | extern crate r2d2_diesel; 467 | extern crate rocket; 468 | extern crate rocket_contrib; 469 | #[macro_use] 470 | extern crate serde_derive; 471 | 472 | use dotenv::dotenv; 473 | 474 | mod people; 475 | mod schema; 476 | mod connection; 477 | 478 | fn main() { 479 | dotenv().ok(); 480 | people::router::create_routes(); 481 | } 482 | ``` 483 | All `main` does is load in the environment variables and starts Rocket by calling `create_routes`. The rest of this file just pulls in a load of crates so they don't need to be scattered throughout the rest of the code. 484 | 485 | Now you can rest. That was a pretty long post. I'd write a conclusion but honestly, I'm tired and don't want to write anymore. So for a short summary, in this post we have created a simple REST API using Rocket to run an application server that responds to requests and used Diesel to connect to a database to manage the state of the application. 486 | 487 | The code used in this post can be on my [GitHub](https://github.com/lankydan/rust-web-with-rocket). 488 | 489 | If you liked this post, then follow me on Twitter at [@LankyDanDev](https://twitter.com/LankyDanDev) to be able to keep up with my new posts as I write them. 490 | 491 | -------------------------------------------------------------------------------- /src/connection.rs: -------------------------------------------------------------------------------- 1 | use diesel::pg::PgConnection; 2 | use r2d2; 3 | use r2d2_diesel::ConnectionManager; 4 | use rocket::{Outcome, Request, State}; 5 | use rocket::http::Status; 6 | use rocket::request::{self, FromRequest}; 7 | use std::env; 8 | use std::ops::Deref; 9 | 10 | type Pool = r2d2::Pool>; 11 | 12 | pub fn init_pool() -> Pool { 13 | let manager = ConnectionManager::::new(database_url()); 14 | Pool::new(manager).expect("db pool") 15 | } 16 | 17 | fn database_url() -> String { 18 | env::var("DATABASE_URL").expect("DATABASE_URL must be set") 19 | } 20 | 21 | pub struct DbConn(pub r2d2::PooledConnection>); 22 | 23 | impl<'a, 'r> FromRequest<'a, 'r> for DbConn { 24 | type Error = (); 25 | 26 | fn from_request(request: &'a Request<'r>) -> request::Outcome { 27 | let pool = request.guard::>()?; 28 | match pool.get() { 29 | Ok(conn) => Outcome::Success(DbConn(conn)), 30 | Err(_) => Outcome::Failure((Status::ServiceUnavailable, ())), 31 | } 32 | } 33 | } 34 | 35 | impl Deref for DbConn { 36 | type Target = PgConnection; 37 | 38 | fn deref(&self) -> &Self::Target { 39 | &self.0 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(decl_macro, proc_macro_hygiene)] 2 | #[macro_use] extern crate rocket; 3 | #[macro_use] extern crate diesel; 4 | extern crate dotenv; 5 | extern crate r2d2; 6 | extern crate r2d2_diesel; 7 | extern crate rocket_contrib; 8 | #[macro_use] 9 | extern crate serde_derive; 10 | 11 | use dotenv::dotenv; 12 | 13 | mod people; 14 | mod schema; 15 | mod connection; 16 | 17 | fn main() { 18 | dotenv().ok(); 19 | people::router::create_routes(); 20 | } 21 | -------------------------------------------------------------------------------- /src/people/handler.rs: -------------------------------------------------------------------------------- 1 | use connection::DbConn; 2 | use diesel::result::Error; 3 | use std::env; 4 | use people; 5 | use people::Person; 6 | use rocket::http::Status; 7 | use rocket::response::status; 8 | use rocket_contrib::json::Json; 9 | 10 | #[get("/")] 11 | pub fn all(connection: DbConn) -> Result>, Status> { 12 | people::repository::all(&connection) 13 | .map(|people| Json(people)) 14 | .map_err(|error| error_status(error)) 15 | } 16 | 17 | fn error_status(error: Error) -> Status { 18 | match error { 19 | Error::NotFound => Status::NotFound, 20 | _ => Status::InternalServerError 21 | } 22 | } 23 | 24 | #[get("/")] 25 | pub fn get(id: i32, connection: DbConn) -> Result, Status> { 26 | people::repository::get(id, &connection) 27 | .map(|person| Json(person)) 28 | .map_err(|error| error_status(error)) 29 | } 30 | 31 | #[post("/", format = "application/json", data = "")] 32 | pub fn post(person: Json, connection: DbConn) -> Result>, Status> { 33 | people::repository::insert(person.into_inner(), &connection) 34 | .map(|person| person_created(person)) 35 | .map_err(|error| error_status(error)) 36 | } 37 | 38 | fn person_created(person: Person) -> status::Created> { 39 | status::Created( 40 | format!("{host}:{port}/people/{id}", host = host(), port = port(), id = person.id).to_string(), 41 | Some(Json(person))) 42 | } 43 | 44 | fn host() -> String { 45 | env::var("ROCKET_ADDRESS").expect("ROCKET_ADDRESS must be set") 46 | } 47 | 48 | fn port() -> String { 49 | env::var("ROCKET_PORT").expect("ROCKET_PORT must be set") 50 | } 51 | 52 | #[put("/", format = "application/json", data = "")] 53 | pub fn put(id: i32, person: Json, connection: DbConn) -> Result, Status> { 54 | people::repository::update(id, person.into_inner(), &connection) 55 | .map(|person| Json(person)) 56 | .map_err(|error| error_status(error)) 57 | } 58 | 59 | #[delete("/")] 60 | pub fn delete(id: i32, connection: DbConn) -> Result { 61 | match people::repository::get(id, &connection) { 62 | Ok(_) => people::repository::delete(id, &connection) 63 | .map(|_| Status::NoContent) 64 | .map_err(|error| error_status(error)), 65 | Err(error) => Err(error_status(error)) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/people/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(proc_macro_derive_resolution_fallback)] 2 | use super::schema::people; 3 | 4 | pub mod handler; 5 | pub mod router; 6 | pub mod repository; 7 | 8 | #[derive(Queryable, AsChangeset, Serialize, Deserialize)] 9 | #[table_name = "people"] 10 | pub struct Person { 11 | pub id: i32, 12 | pub first_name: String, 13 | pub last_name: String, 14 | pub age: i32, 15 | pub profession: String, 16 | pub salary: i32, 17 | } 18 | -------------------------------------------------------------------------------- /src/people/repository.rs: -------------------------------------------------------------------------------- 1 | #![allow(proc_macro_derive_resolution_fallback)] 2 | 3 | use diesel; 4 | use diesel::prelude::*; 5 | use schema::people; 6 | use people::Person; 7 | 8 | pub fn all(connection: &PgConnection) -> QueryResult> { 9 | people::table.load::(&*connection) 10 | } 11 | 12 | pub fn get(id: i32, connection: &PgConnection) -> QueryResult { 13 | people::table.find(id).get_result::(connection) 14 | } 15 | 16 | pub fn insert(person: Person, connection: &PgConnection) -> QueryResult { 17 | diesel::insert_into(people::table) 18 | .values(&InsertablePerson::from_person(person)) 19 | .get_result(connection) 20 | } 21 | 22 | pub fn update(id: i32, person: Person, connection: &PgConnection) -> QueryResult { 23 | diesel::update(people::table.find(id)) 24 | .set(&person) 25 | .get_result(connection) 26 | } 27 | 28 | pub fn delete(id: i32, connection: &PgConnection) -> QueryResult { 29 | diesel::delete(people::table.find(id)) 30 | .execute(connection) 31 | } 32 | 33 | #[derive(Insertable)] 34 | #[table_name = "people"] 35 | struct InsertablePerson { 36 | first_name: String, 37 | last_name: String, 38 | age: i32, 39 | profession: String, 40 | salary: i32, 41 | } 42 | 43 | impl InsertablePerson { 44 | 45 | fn from_person(person: Person) -> InsertablePerson { 46 | InsertablePerson { 47 | first_name: person.first_name, 48 | last_name: person.last_name, 49 | age: person.age, 50 | profession: person.profession, 51 | salary: person.salary, 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/people/router.rs: -------------------------------------------------------------------------------- 1 | use people; 2 | use rocket; 3 | use connection; 4 | 5 | pub fn create_routes() { 6 | rocket::ignite() 7 | .manage(connection::init_pool()) 8 | .mount("/people", 9 | routes![people::handler::all, 10 | people::handler::get, 11 | people::handler::post, 12 | people::handler::put, 13 | people::handler::delete], 14 | ).launch(); 15 | } 16 | -------------------------------------------------------------------------------- /src/schema.rs: -------------------------------------------------------------------------------- 1 | table! { 2 | people (id) { 3 | id -> Int4, 4 | first_name -> Varchar, 5 | last_name -> Varchar, 6 | age -> Int4, 7 | profession -> Varchar, 8 | salary -> Int4, 9 | } 10 | } 11 | --------------------------------------------------------------------------------