diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..3dfba382 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,32 @@ +# Include any files or directories that you don't want to be copied to your +# container here (e.g., local build artifacts, temporary files, etc.). +# +# For more help, visit the .dockerignore file reference guide at +# https://docs.docker.com/engine/reference/builder/#dockerignore-file + +**/.DS_Store +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/charts +**/docker-compose* +**/compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/secrets.dev.yaml +**/values.dev.yaml +/bin +/target +LICENSE +README.md diff --git a/.github/workflows/linters-cargo.yml b/.github/workflows/linters-cargo.yml new file mode 100644 index 00000000..96172230 --- /dev/null +++ b/.github/workflows/linters-cargo.yml @@ -0,0 +1,23 @@ +--- +name: Task - Linters Cargo + +on: + workflow_dispatch: + workflow_call: + +jobs: + cargo-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + # selecting a toolchain either by action or manual `rustup` calls should happen + # before the plugin, as the cache uses the current rustc version as its cache key + - run: rustup show + + - uses: Swatinem/rust-cache@v2 + - name: Format and clippy + run: | + cargo fmt -- --check + cargo clippy --no-deps -- -D warnings + cargo clippy --tests --no-deps -- -D warnings diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml new file mode 100644 index 00000000..42f8c8de --- /dev/null +++ b/.github/workflows/linters.yml @@ -0,0 +1,33 @@ +--- +name: Task - Linters + +on: + workflow_dispatch: + workflow_call: + +jobs: + prettier: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Run prettier + run: |- + npx prettier --check . + + markdown-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: avto-dev/markdown-lint@v1 + with: + config: "./.markdownlint.json" + args: "." + ignore: "./target" + + toml-lint: + runs-on: ubuntu-latest + steps: + - name: Checkout toml files + uses: actions/checkout@v3 + - name: Run toml check + run: npx @taplo/cli fmt --config ./taplo/taplo.toml --check diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 00000000..588c3afb --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,28 @@ +--- +name: Workflow - Pull Request + +on: + workflow_dispatch: + pull_request: + branches: [main] + push: + branches: [main] + +jobs: + linters: + name: Run linters + uses: ./.github/workflows/linters.yml + + rust_build: + name: Build Rust project + uses: ./.github/workflows/rust-build.yml + + linters_cargo: + name: Run Cargo linters + uses: ./.github/workflows/linters-cargo.yml + needs: rust_build + + coverage: + name: Run coverage + uses: ./.github/workflows/coverage.yml + needs: rust_build diff --git a/.github/workflows/rust-build.yml b/.github/workflows/rust-build.yml new file mode 100644 index 00000000..f7cb3e08 --- /dev/null +++ b/.github/workflows/rust-build.yml @@ -0,0 +1,22 @@ +--- +name: Task - Build Rust + +on: + workflow_dispatch: + workflow_call: + +jobs: + rust_build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + # selecting a toolchain either by action or manual `rustup` calls should happen + # before the plugin, as the cache uses the current rustc version as its cache key + - run: rustup show + + - uses: Swatinem/rust-cache@v2 + + - name: Build the project + run: | + cargo build --release --workspace diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..4f96098e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +.idea +.env diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..0d27813f --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +quentin.piot@proton,mail.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..2e5ad79a --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1480 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "async-trait" +version = "0.1.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core", + "axum-macros", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-diesel-real-world" +version = "0.1.0" +dependencies = [ + "axum", + "axum-macros", + "bigdecimal", + "chrono", + "deadpool-diesel", + "diesel", + "diesel_migrations", + "dotenvy", + "serde", + "serde_json", + "tokio", + "tracing", + "tracing-subscriber", + "uuid", +] + +[[package]] +name = "axum-macros" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdca6a10ecad987bda04e95606ef85a5417dcaac1a78455242d72e031e2b6b62" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bigdecimal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "454bca3db10617b88b566f205ed190aedb0e0e6dd4cad61d3988a72e8c5594cb" +dependencies = [ + "autocfg", + "libm", + "num-bigint", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ed24df0632f708f5f6d8082675bef2596f7084dee3dd55f632290bf35bfe0f" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "time 0.1.45", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "deadpool" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "421fe0f90f2ab22016f32a9881be5134fdd71c65298917084b0c7477cbc3856e" +dependencies = [ + "async-trait", + "deadpool-runtime", + "num_cpus", + "retain_mut", + "tokio", +] + +[[package]] +name = "deadpool-diesel" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9ce884fff09b610fd0bbd9e9447327fda9f613d5bd1fa114f57905cbcfd8d27" +dependencies = [ + "deadpool", + "deadpool-sync", + "diesel", +] + +[[package]] +name = "deadpool-runtime" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaa37046cc0f6c3cc6090fbdbf73ef0b8ef4cfcc37f6befc0020f63e8cf121e1" +dependencies = [ + "tokio", +] + +[[package]] +name = "deadpool-sync" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1bea344b64b32537fde6e0f0179b1ede34d435636719dd40fe6a0f28218a61c" +dependencies = [ + "deadpool", +] + +[[package]] +name = "deranged" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" + +[[package]] +name = "diesel" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98235fdc2f355d330a8244184ab6b4b33c28679c0b4158f63138e51d6cf7e88" +dependencies = [ + "bigdecimal", + "bitflags 2.4.0", + "byteorder", + "chrono", + "diesel_derives", + "ipnetwork", + "itoa", + "libc", + "num-bigint", + "num-integer", + "num-traits", + "pq-sys", + "r2d2", + "serde_json", + "time 0.3.29", + "uuid", +] + +[[package]] +name = "diesel_derives" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e054665eaf6d97d1e7125512bb2d35d07c73ac86cc6920174cb42d1ab697a554" +dependencies = [ + "diesel_table_macro_syntax", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "diesel_migrations" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6036b3f0120c5961381b570ee20a02432d7e2d27ea60de9578799cf9156914ac" +dependencies = [ + "diesel", + "migrations_internals", + "migrations_macros", +] + +[[package]] +name = "diesel_table_macro_syntax" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" +dependencies = [ + "syn", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.4.9", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ipnetwork" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" +dependencies = [ + "serde", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matchit" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed1202b2a6f884ae56f04cff409ab315c5ce26b5e58d7412e484f01fd52f52ef" + +[[package]] +name = "memchr" +version = "2.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5486aed0026218e61b8a01d5fbd5a0a134649abb71a0e53b7bc088529dced86e" + +[[package]] +name = "migrations_internals" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f23f71580015254b020e856feac3df5878c2c7a8812297edd6c0a485ac9dada" +dependencies = [ + "serde", + "toml", +] + +[[package]] +name = "migrations_macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08" +dependencies = [ + "migrations_internals", + "proc-macro2", + "quote", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "pq-sys" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c0052426df997c0cbd30789eb44ca097e3541717a7b8fa36b1c464ee7edebd" +dependencies = [ + "vcpkg", +] + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r2d2" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" +dependencies = [ + "log", + "parking_lot", + "scheduled-thread-pool", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.3.7", + "regex-syntax 0.7.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.5", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + +[[package]] +name = "retain_mut" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0" + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "scheduled-thread-pool" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "smallvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "syn" +version = "2.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" +dependencies = [ + "deranged", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +dependencies = [ + "time-core", +] + +[[package]] +name = "tokio" +version = "1.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +dependencies = [ + "backtrace", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2 0.5.3", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "uuid" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +dependencies = [ + "getrandom", + "rand", + "serde", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winnow" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..9f2a5b35 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "axum-diesel-real-world" +version = "0.1.0" +edition = "2021" + + +[dependencies] +axum = { version = "0.6", features = ["macros"] } +axum-macros = "0.3" +chrono = { version = "0.4.26", features = ["serde"] } +diesel = { version = "2.1", features = ["postgres", "extras"] } +diesel_migrations = "2" +deadpool-diesel = { version = "0.4", features = ["postgres"] } +dotenvy = "0.15" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +tokio = { version = "1.0", features = ["sync", "macros", "rt-multi-thread"] } +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +uuid = { version = "1.4", features = ["fast-rng", "v4", "serde"] } +bigdecimal = { version = "0.4.1", features = ["serde"] } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..518da596 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,82 @@ +# syntax=docker/dockerfile:1 + +# Comments are provided throughout this file to help you get started. +# If you need more help, visit the Dockerfile reference guide at +# https://docs.docker.com/engine/reference/builder/ + +################################################################################ +# Create a stage for building the application. + +ARG RUST_VERSION=1.72.0 +ARG APP_NAME=axum-diesel-real-world +FROM rust:${RUST_VERSION}-slim-bullseye AS build +ARG APP_NAME +WORKDIR /app + + + +RUN apt update +RUN apt install -y libpq-dev + + + +# Build the application. +# Leverage a cache mount to /usr/local/cargo/registry/ +# for downloaded dependencies and a cache mount to /app/target/ for +# compiled dependencies which will speed up subsequent builds. +# Leverage a bind mount to the src directory to avoid having to copy the +# source code into the container. Once built, copy the executable to an +# output directory before the cache mounted /app/target is unmounted. +RUN --mount=type=bind,source=src,target=src \ + --mount=type=bind,source=Cargo.toml,target=Cargo.toml \ + --mount=type=bind,source=Cargo.lock,target=Cargo.lock \ + --mount=type=bind,source=migrations,target=migrations \ + --mount=type=cache,target=/app/target/ \ + --mount=type=cache,target=/usr/local/cargo/registry/ \ + < &str { + &self.db.url + } + + pub fn server_host(&self) -> &str { + &self.server.host + } + + pub fn server_port(&self) -> u16 { + self.server.port + } +} + +pub static CONFIG: OnceCell = OnceCell::const_new(); + +async fn init_config() -> Config { + dotenv().ok(); + let server_config = ServerConfig { + host: env::var("HOST").unwrap_or_else(|_| String::from("127.0.0.1")), + port: env::var("PORT") + .unwrap_or_else(|_| String::from("3000")) + .parse::() + .unwrap(), + }; + + let database_config = DatabaseConfig { + url: env::var("DATABASE_URL").expect("DATABASE_URL must be set"), + }; + + Config { + server: server_config, + db: database_config, + } +} + +pub async fn config() -> &'static Config { + CONFIG.get_or_init(init_config).await +} diff --git a/src/domain/mod.rs b/src/domain/mod.rs new file mode 100644 index 00000000..c446ac88 --- /dev/null +++ b/src/domain/mod.rs @@ -0,0 +1 @@ +pub mod models; diff --git a/src/domain/models/entry.rs b/src/domain/models/entry.rs new file mode 100644 index 00000000..029b00bb --- /dev/null +++ b/src/domain/models/entry.rs @@ -0,0 +1,49 @@ +use axum::http::StatusCode; +use axum::response::IntoResponse; +use axum::Json; +use serde_json::json; +use uuid::Uuid; + +use crate::infra::errors::InfraError; + +#[derive(Clone, Debug, PartialEq)] +pub struct EntryModel { + pub id: Uuid, + pub pair_id: String, + pub publisher: String, + pub timestamp: u64, + pub price: u128, +} + +#[derive(Debug)] +pub enum EntryError { + InternalServerError, + NotFound(String), + InfraError(InfraError), +} + +impl IntoResponse for EntryError { + fn into_response(self) -> axum::response::Response { + let (status, err_msg) = match self { + Self::NotFound(pair_id) => ( + StatusCode::NOT_FOUND, + format!("EntryModel with pair id {} has not been found", pair_id), + ), + Self::InfraError(db_error) => ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Internal server error: {}", db_error), + ), + _ => ( + StatusCode::INTERNAL_SERVER_ERROR, + String::from("Internal server error"), + ), + }; + ( + status, + Json( + json!({"resource":"EntryModel", "message": err_msg, "happened_at" : chrono::Utc::now() }), + ), + ) + .into_response() + } +} diff --git a/src/domain/models/mod.rs b/src/domain/models/mod.rs new file mode 100644 index 00000000..eb5becf4 --- /dev/null +++ b/src/domain/models/mod.rs @@ -0,0 +1 @@ +pub mod entry; diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 00000000..d3e69d38 --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,30 @@ +use axum::http::StatusCode; +use axum::response::IntoResponse; +use axum::Json; +use serde_json::json; + +#[derive(Debug)] +pub enum AppError { + InternalServerError, + BodyParsingError(String), +} + +pub fn internal_error(_err: E) -> AppError { + AppError::InternalServerError +} + +impl IntoResponse for AppError { + fn into_response(self) -> axum::response::Response { + let (status, err_msg) = match self { + Self::InternalServerError => ( + StatusCode::INTERNAL_SERVER_ERROR, + String::from("Internal Server Error"), + ), + Self::BodyParsingError(message) => ( + StatusCode::BAD_REQUEST, + format!("Bad request error: {}", message), + ), + }; + (status, Json(json!({ "message": err_msg }))).into_response() + } +} diff --git a/src/handlers/entries/create_entry.rs b/src/handlers/entries/create_entry.rs new file mode 100644 index 00000000..710ff68a --- /dev/null +++ b/src/handlers/entries/create_entry.rs @@ -0,0 +1,36 @@ +use axum::extract::State; +use axum::Json; +use chrono::NaiveDateTime; + +use crate::domain::models::entry::EntryError; +use crate::handlers::entries::{CreateEntryRequest, EntryResponse}; +use crate::infra::repositories::entry_repository; +use crate::utils::JsonExtractor; +use crate::AppState; + +pub async fn create_entry( + State(state): State, + JsonExtractor(new_entry): JsonExtractor, +) -> Result, EntryError> { + // TODO: Verify Signature + + let new_entry_db = entry_repository::NewEntryDb { + pair_id: new_entry.pair_id, + publisher: new_entry.publisher, + timestamp: NaiveDateTime::from_timestamp_opt(new_entry.timestamp as i64, 0).unwrap(), // TODO: remove unwrap + price: new_entry.price.into(), + }; + + let created_entry = entry_repository::insert(&state.pool, new_entry_db) + .await + .map_err(EntryError::InfraError)?; + + let entry_response = EntryResponse { + pair_id: created_entry.pair_id, + timestamp: created_entry.timestamp, + num_sources_aggregated: 0, + price: created_entry.price, + }; + + Ok(Json(entry_response)) +} diff --git a/src/handlers/entries/get_entry.rs b/src/handlers/entries/get_entry.rs new file mode 100644 index 00000000..df8a2f4f --- /dev/null +++ b/src/handlers/entries/get_entry.rs @@ -0,0 +1,38 @@ +use axum::extract::State; +use axum::Json; + +use crate::domain::models::entry::{EntryError, EntryModel}; +use crate::handlers::entries::EntryResponse; +use crate::infra::errors::InfraError; +use crate::infra::repositories::entry_repository; +use crate::utils::PathExtractor; +use crate::AppState; + +/// Converts a currency pair to a pair id. +fn currency_pair_to_pair_id(quote: &str, base: &str) -> String { + format!("{}/{}", quote.to_uppercase(), base.to_uppercase()) +} + +pub async fn get_entry( + State(state): State, + PathExtractor(pair): PathExtractor<(String, String)>, +) -> Result, EntryError> { + let pair_id = currency_pair_to_pair_id(&pair.0, &pair.1); + let entry = entry_repository::get(&state.pool, &pair_id) + .await + .map_err(|db_error| match db_error { + InfraError::InternalServerError => EntryError::InternalServerError, + InfraError::NotFound => EntryError::NotFound(pair_id), + })?; + + Ok(Json(adapt_entry_to_entry_response(entry))) +} + +fn adapt_entry_to_entry_response(entry: EntryModel) -> EntryResponse { + EntryResponse { + pair_id: entry.pair_id, + timestamp: entry.timestamp, + num_sources_aggregated: 0, // TODO: add real value + price: entry.price, + } +} diff --git a/src/handlers/entries/mod.rs b/src/handlers/entries/mod.rs new file mode 100644 index 00000000..67a217a2 --- /dev/null +++ b/src/handlers/entries/mod.rs @@ -0,0 +1,24 @@ +use serde::{Deserialize, Serialize}; + +pub use create_entry::create_entry; +pub use get_entry::get_entry; + +mod create_entry; +mod get_entry; + +#[derive(Debug, Deserialize)] +pub struct CreateEntryRequest { + signature: Vec, + publisher: String, + pair_id: String, + timestamp: u64, + price: u128, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct EntryResponse { + pair_id: String, + timestamp: u64, + num_sources_aggregated: u8, + price: u128, +} diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs new file mode 100644 index 00000000..f700be77 --- /dev/null +++ b/src/handlers/mod.rs @@ -0,0 +1 @@ +pub mod entries; diff --git a/src/infra/db/mod.rs b/src/infra/db/mod.rs new file mode 100644 index 00000000..1ce7e176 --- /dev/null +++ b/src/infra/db/mod.rs @@ -0,0 +1 @@ +pub mod schema; diff --git a/src/infra/db/schema.rs b/src/infra/db/schema.rs new file mode 100644 index 00000000..3a8c6078 --- /dev/null +++ b/src/infra/db/schema.rs @@ -0,0 +1,11 @@ +// @generated automatically by Diesel CLI. + +diesel::table! { + entries (id) { + id -> Int4, + pair_id -> Varchar, + publisher -> Text, + timestamp -> Timestamp, + price -> Numeric, + } +} diff --git a/src/infra/errors.rs b/src/infra/errors.rs new file mode 100644 index 00000000..dd726453 --- /dev/null +++ b/src/infra/errors.rs @@ -0,0 +1,47 @@ +use std::fmt; + +use deadpool_diesel::InteractError; + +#[derive(Debug)] +pub enum InfraError { + InternalServerError, + NotFound, +} + +pub fn adapt_infra_error(error: T) -> InfraError { + error.as_infra_error() +} + +impl fmt::Display for InfraError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + InfraError::NotFound => write!(f, "Not found"), + InfraError::InternalServerError => write!(f, "Internal server error"), + } + } +} + +pub trait Error { + fn as_infra_error(&self) -> InfraError; +} + +impl Error for diesel::result::Error { + fn as_infra_error(&self) -> InfraError { + match self { + diesel::result::Error::NotFound => InfraError::NotFound, + _ => InfraError::InternalServerError, + } + } +} + +impl Error for deadpool_diesel::PoolError { + fn as_infra_error(&self) -> InfraError { + InfraError::InternalServerError + } +} + +impl Error for InteractError { + fn as_infra_error(&self) -> InfraError { + InfraError::InternalServerError + } +} diff --git a/src/infra/mod.rs b/src/infra/mod.rs new file mode 100644 index 00000000..63bd3a61 --- /dev/null +++ b/src/infra/mod.rs @@ -0,0 +1,3 @@ +pub mod db; +pub mod errors; +pub mod repositories; diff --git a/src/infra/repositories/entry_repository.rs b/src/infra/repositories/entry_repository.rs new file mode 100644 index 00000000..050f4d52 --- /dev/null +++ b/src/infra/repositories/entry_repository.rs @@ -0,0 +1,117 @@ +use bigdecimal::{BigDecimal, ToPrimitive}; +use chrono::NaiveDateTime; +use diesel::{ + ExpressionMethods, Insertable, PgTextExpressionMethods, QueryDsl, Queryable, RunQueryDsl, + Selectable, SelectableHelper, +}; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use crate::domain::models::entry::EntryModel; +use crate::infra::db::schema::entries; +use crate::infra::errors::{adapt_infra_error, InfraError}; + +#[derive(Serialize, Queryable, Selectable)] +#[diesel(table_name = entries)] +#[diesel(check_for_backend(diesel::pg::Pg))] +pub struct EntryDb { + pub id: Uuid, + pub pair_id: String, + pub publisher: String, + pub timestamp: NaiveDateTime, + pub price: BigDecimal, +} + +#[derive(Deserialize, Insertable)] +#[diesel(table_name = entries)] +pub struct NewEntryDb { + pub pair_id: String, + pub publisher: String, + pub timestamp: NaiveDateTime, + pub price: BigDecimal, +} + +#[derive(Deserialize)] +pub struct EntriesFilter { + pair_id: Option, + publisher_contains: Option, +} + +pub async fn insert( + pool: &deadpool_diesel::postgres::Pool, + new_entry: NewEntryDb, +) -> Result { + let conn = pool.get().await.map_err(adapt_infra_error)?; + let res = conn + .interact(|conn| { + diesel::insert_into(entries::table) + .values(new_entry) + .returning(EntryDb::as_returning()) + .get_result(conn) + }) + .await + .map_err(adapt_infra_error)? + .map_err(adapt_infra_error)?; + + Ok(adapt_entry_db_to_entry(res)) +} + +pub async fn get( + pool: &deadpool_diesel::postgres::Pool, + pair_id: &str, +) -> Result { + let conn = pool.get().await.map_err(adapt_infra_error)?; + let res = conn + .interact(move |conn| { + entries::table + .filter(entries::pair_id.eq(pair_id)) + .select(EntryDb::as_select()) + .get_result(conn) + }) + .await + .map_err(adapt_infra_error)? + .map_err(adapt_infra_error)?; + + Ok(adapt_entry_db_to_entry(res)) +} + +pub async fn get_all( + pool: &deadpool_diesel::postgres::Pool, + filter: EntriesFilter, +) -> Result, InfraError> { + let conn = pool.get().await.map_err(adapt_infra_error)?; + let res = conn + .interact(move |conn| { + let mut query = entries::table.into_boxed::(); + + if let Some(pair_id) = filter.pair_id { + query = query.filter(entries::pair_id.eq(pair_id)); + } + + if let Some(publisher_contains) = filter.publisher_contains { + query = query.filter(entries::publisher.ilike(format!("%{}%", publisher_contains))); + } + + query.select(EntryDb::as_select()).load::(conn) + }) + .await + .map_err(adapt_infra_error)? + .map_err(adapt_infra_error)?; + + let entries: Vec = res + .into_iter() + .map(|entry_db| adapt_entry_db_to_entry(entry_db)) + .collect(); + + Ok(entries) +} + +fn adapt_entry_db_to_entry(entry_db: EntryDb) -> EntryModel { + EntryModel { + id: entry_db.id, + pair_id: entry_db.pair_id, + publisher: entry_db.publisher, + timestamp: entry_db.timestamp.timestamp() as u64, + price: entry_db.price.to_u128().unwrap(), // TODO: remove unwrap + } +} diff --git a/src/infra/repositories/mod.rs b/src/infra/repositories/mod.rs new file mode 100644 index 00000000..bc8875e8 --- /dev/null +++ b/src/infra/repositories/mod.rs @@ -0,0 +1 @@ +pub mod entry_repository; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 00000000..14351f01 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,78 @@ +#![feature(trivial_bounds)] +use std::net::SocketAddr; + +use deadpool_diesel::postgres::{Manager, Pool}; +use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + +use crate::config::config; +use crate::errors::internal_error; +use crate::routes::app_router; + +mod config; +mod domain; +mod errors; +mod handlers; +mod infra; +mod routes; +mod utils; + +pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations/"); + +#[derive(Clone)] +pub struct AppState { + pool: Pool, +} + +#[tokio::main] +async fn main() { + init_tracing(); + + let config = config().await; + + let manager = Manager::new( + config.db_url().to_string(), + deadpool_diesel::Runtime::Tokio1, + ); + let pool = Pool::builder(manager).build().unwrap(); + + { + run_migrations(&pool).await; + } + + let state = AppState { pool }; + + let app = app_router(state.clone()).with_state(state); + + let host = config.server_host(); + let port = config.server_port(); + + let address = format!("{}:{}", host, port); + + let socket_addr: SocketAddr = address.parse().unwrap(); + + tracing::info!("listening on http://{}", socket_addr); + axum::Server::bind(&socket_addr) + .serve(app.into_make_service()) + .await + .map_err(internal_error) + .unwrap() +} + +fn init_tracing() { + tracing_subscriber::registry() + .with( + tracing_subscriber::EnvFilter::try_from_default_env() + .unwrap_or_else(|_| "example_tokio_postgres=debug".into()), + ) + .with(tracing_subscriber::fmt::layer()) + .init(); +} + +async fn run_migrations(pool: &Pool) { + let conn = pool.get().await.unwrap(); + conn.interact(|conn| conn.run_pending_migrations(MIGRATIONS).map(|_| ())) + .await + .unwrap() + .unwrap(); +} diff --git a/src/routes.rs b/src/routes.rs new file mode 100644 index 00000000..5f3ebb8e --- /dev/null +++ b/src/routes.rs @@ -0,0 +1,32 @@ +use axum::http::StatusCode; +use axum::response::IntoResponse; +use axum::routing::{get, post}; +use axum::Router; + +use crate::handlers::entries::{create_entry, get_entry}; +use crate::AppState; + +pub fn app_router(state: AppState) -> Router { + Router::new() + .route("/", get(root)) + .nest("/v1/data", data_routes(state.clone())) + .fallback(handler_404) +} + +async fn root() -> &'static str { + "Server is running!" +} + +async fn handler_404() -> impl IntoResponse { + ( + StatusCode::NOT_FOUND, + "The requested resource was not found", + ) +} + +fn data_routes(state: AppState) -> Router { + Router::new() + .route("/publish", post(create_entry)) + .route("/:quote/:base", get(get_entry)) + .with_state(state) +} diff --git a/src/utils/custom_extractors/json_extractor.rs b/src/utils/custom_extractors/json_extractor.rs new file mode 100644 index 00000000..b65efbf9 --- /dev/null +++ b/src/utils/custom_extractors/json_extractor.rs @@ -0,0 +1,14 @@ +use axum::extract::rejection::JsonRejection; +use axum_macros::FromRequest; + +use crate::errors::AppError; + +#[derive(FromRequest)] +#[from_request(via(axum::Json), rejection(AppError))] +pub struct JsonExtractor(pub T); + +impl From for AppError { + fn from(rejection: JsonRejection) -> Self { + AppError::BodyParsingError(rejection.to_string()) + } +} diff --git a/src/utils/custom_extractors/mod.rs b/src/utils/custom_extractors/mod.rs new file mode 100644 index 00000000..eaec8484 --- /dev/null +++ b/src/utils/custom_extractors/mod.rs @@ -0,0 +1,2 @@ +pub mod json_extractor; +pub mod path_extractor; diff --git a/src/utils/custom_extractors/path_extractor.rs b/src/utils/custom_extractors/path_extractor.rs new file mode 100644 index 00000000..d796c8cb --- /dev/null +++ b/src/utils/custom_extractors/path_extractor.rs @@ -0,0 +1,14 @@ +use axum::extract::rejection::PathRejection; +use axum_macros::FromRequestParts; + +use crate::errors::AppError; + +#[derive(FromRequestParts, Debug)] +#[from_request(via(axum::extract::Path), rejection(AppError))] +pub struct PathExtractor(pub T); + +impl From for AppError { + fn from(rejection: PathRejection) -> Self { + AppError::BodyParsingError(rejection.to_string()) + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 00000000..a5552295 --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1,4 @@ +pub use custom_extractors::json_extractor::JsonExtractor; +pub use custom_extractors::path_extractor::PathExtractor; + +mod custom_extractors;