From 46d76f11b9d1c6a35cbb21db28be8104ff4ff2c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 23 Nov 2022 14:46:26 -0500 Subject: [PATCH 01/31] Remove unused asset. --- assets/CornucopiaDiagram.png | Bin 59952 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 assets/CornucopiaDiagram.png diff --git a/assets/CornucopiaDiagram.png b/assets/CornucopiaDiagram.png deleted file mode 100644 index c1e6753831d835b1149d300c3fab0c80ad388f82..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59952 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 From 77d28fb7ecda09103cfc0014f852fd2cd17113d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 23 Nov 2022 16:25:12 -0500 Subject: [PATCH 02/31] Rename `bench` crate to `benches`. --- {bench => benches}/Cargo.toml | 24 ++++++++++++------- {bench => benches}/codegen.rs | 0 .../cornucopia_benches/generated_async.rs | 0 .../cornucopia_benches/generated_sync.rs | 0 .../usage/cornucopia_benches/mod.rs | 0 .../cornucopia_benches/queries/bench.sql | 0 .../usage/cornucopia_benches/schema.sql | 0 {bench => benches}/usage/diesel_benches.rs | 0 {bench => benches}/usage/main.rs | 6 ++++- {bench => benches}/usage/postgres_benches.rs | 3 +-- .../usage/tokio_postgres_benches.rs | 0 integration/fixtures/codegen/benchmarks.toml | 4 ++-- 12 files changed, 24 insertions(+), 13 deletions(-) rename {bench => benches}/Cargo.toml (62%) rename {bench => benches}/codegen.rs (100%) rename {bench => benches}/usage/cornucopia_benches/generated_async.rs (100%) rename {bench => benches}/usage/cornucopia_benches/generated_sync.rs (100%) rename {bench => benches}/usage/cornucopia_benches/mod.rs (100%) rename {bench => benches}/usage/cornucopia_benches/queries/bench.sql (100%) rename {bench => benches}/usage/cornucopia_benches/schema.sql (100%) rename {bench => benches}/usage/diesel_benches.rs (100%) rename {bench => benches}/usage/main.rs (98%) rename {bench => benches}/usage/postgres_benches.rs (98%) rename {bench => benches}/usage/tokio_postgres_benches.rs (100%) diff --git a/bench/Cargo.toml b/benches/Cargo.toml similarity index 62% rename from bench/Cargo.toml rename to benches/Cargo.toml index 069a6527..0edcde8f 100644 --- a/bench/Cargo.toml +++ b/benches/Cargo.toml @@ -1,27 +1,35 @@ [package] -name = "bench" +name = "benches" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +# Path dependencies +cornucopia = { path = "../crates/cornucopia" } +cornucopia_sync = { path = "../crates/client_sync" } +cornucopia_async = { path = "../crates/client_async" } + +# benchmarking +criterion = { version = "0.4.0", features = ["html_reports"] } + +# async tokio = { version = "1.21.2", features = ["full"] } futures = "0.3.25" -criterion = { version = "0.4.0", features = ["html_reports"] } + +# rust-postgres interaction postgres = "0.19.4" tokio-postgres = "0.7.7" postgres-types = "0.2.4" -diesel = { version = "2.0.2", features = ["postgres"] } -cornucopia = { path = "../cornucopia" } -cornucopia_sync = { path = "../clients/sync" } -cornucopia_async = { path = "../clients/async" } +# diesel +diesel = { version = "2.0.2", features = ["postgres"] } [[bench]] -name = "usage" +name = "execution" harness = false -path = "usage/main.rs" +path = "execution/main.rs" [[bench]] name = "codegen" diff --git a/bench/codegen.rs b/benches/codegen.rs similarity index 100% rename from bench/codegen.rs rename to benches/codegen.rs diff --git a/bench/usage/cornucopia_benches/generated_async.rs b/benches/usage/cornucopia_benches/generated_async.rs similarity index 100% rename from bench/usage/cornucopia_benches/generated_async.rs rename to benches/usage/cornucopia_benches/generated_async.rs diff --git a/bench/usage/cornucopia_benches/generated_sync.rs b/benches/usage/cornucopia_benches/generated_sync.rs similarity index 100% rename from bench/usage/cornucopia_benches/generated_sync.rs rename to benches/usage/cornucopia_benches/generated_sync.rs diff --git a/bench/usage/cornucopia_benches/mod.rs b/benches/usage/cornucopia_benches/mod.rs similarity index 100% rename from bench/usage/cornucopia_benches/mod.rs rename to benches/usage/cornucopia_benches/mod.rs diff --git a/bench/usage/cornucopia_benches/queries/bench.sql b/benches/usage/cornucopia_benches/queries/bench.sql similarity index 100% rename from bench/usage/cornucopia_benches/queries/bench.sql rename to benches/usage/cornucopia_benches/queries/bench.sql diff --git a/bench/usage/cornucopia_benches/schema.sql b/benches/usage/cornucopia_benches/schema.sql similarity index 100% rename from bench/usage/cornucopia_benches/schema.sql rename to benches/usage/cornucopia_benches/schema.sql diff --git a/bench/usage/diesel_benches.rs b/benches/usage/diesel_benches.rs similarity index 100% rename from bench/usage/diesel_benches.rs rename to benches/usage/diesel_benches.rs diff --git a/bench/usage/main.rs b/benches/usage/main.rs similarity index 98% rename from bench/usage/main.rs rename to benches/usage/main.rs index f6b1722c..548bbdc9 100644 --- a/bench/usage/main.rs +++ b/benches/usage/main.rs @@ -143,7 +143,11 @@ fn bench(c: &mut Criterion) { let conn = &mut PgConnection::establish("postgresql://postgres:postgres@127.0.0.1:5435/postgres") .unwrap(); - cornucopia::load_schema(client, vec!["usage/cornucopia_benches/schema.sql".into()]).unwrap(); + cornucopia::load_schema( + client, + vec!["execution/cornucopia_benches/schema.sql".into()], + ) + .unwrap(); { let mut group = c.benchmark_group("bench_trivial_query"); for size in QUERY_SIZE { diff --git a/bench/usage/postgres_benches.rs b/benches/usage/postgres_benches.rs similarity index 98% rename from bench/usage/postgres_benches.rs rename to benches/usage/postgres_benches.rs index 1d4dd8ce..ea10dbfc 100644 --- a/bench/usage/postgres_benches.rs +++ b/benches/usage/postgres_benches.rs @@ -1,7 +1,6 @@ use criterion::Bencher; -use postgres::fallible_iterator::FallibleIterator; use postgres::types::ToSql; -use postgres::Client; +use postgres::{fallible_iterator::FallibleIterator, Client}; use std::collections::HashMap; use std::fmt::Write; diff --git a/bench/usage/tokio_postgres_benches.rs b/benches/usage/tokio_postgres_benches.rs similarity index 100% rename from bench/usage/tokio_postgres_benches.rs rename to benches/usage/tokio_postgres_benches.rs diff --git a/integration/fixtures/codegen/benchmarks.toml b/integration/fixtures/codegen/benchmarks.toml index 36153a21..829ca3b6 100644 --- a/integration/fixtures/codegen/benchmarks.toml +++ b/integration/fixtures/codegen/benchmarks.toml @@ -1,10 +1,10 @@ [[codegen]] name = "Sync" -base_path = "bench/usage/cornucopia_benches" +base_path = "benches/execution/cornucopia_benches" destination = "generated_sync.rs" sync = true [[codegen]] name = "Async" -base_path = "bench/usage/cornucopia_benches" +base_path = "benches/execution/cornucopia_benches" destination = "generated_async.rs" From 4de39af15c296b68c7025a997876f9ef0bef41f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 23 Nov 2022 16:35:34 -0500 Subject: [PATCH 03/31] Remove ad-hoc `moving` function and replcace with `std::drop`, which is exactly the same. --- codegen_test/src/main.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/codegen_test/src/main.rs b/codegen_test/src/main.rs index 5e0beb55..0913af8a 100644 --- a/codegen_test/src/main.rs +++ b/codegen_test/src/main.rs @@ -1,7 +1,8 @@ mod cornucopia_async; mod cornucopia_sync; -use ::cornucopia_async::IterSql; +use ::cornucopia_sync::IterSql; + use eui48::MacAddress; use postgres::{Client, Config, NoTls}; use rust_decimal::Decimal; @@ -64,8 +65,6 @@ pub fn main() { test_keyword_escaping(client); } -pub fn moving(_item: T) {} - pub fn test_params(client: &mut Client) { assert_eq!( 1, @@ -265,11 +264,11 @@ pub fn test_copy(client: &mut Client) { first: 42, second: 4.2, }; - moving(copy_params); // Ignore if copied + drop(copy_params); // Ignore if copied insert_copy().bind(client, ©_params).unwrap(); let copy_row = select_copy().bind(client).one().unwrap(); - moving(copy_row); // Ignore if copied - moving(copy_row); + drop(copy_row); // Ignore if copied + drop(copy_row); // Test clone let clone_params = CloneCompositeBorrowed { From 73c17b007cab42d2152b3aa0940537d4acbd34ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 23 Nov 2022 16:36:22 -0500 Subject: [PATCH 04/31] Add some documentation for benchmarks. --- benches/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 benches/README.md diff --git a/benches/README.md b/benches/README.md new file mode 100644 index 00000000..d2e6a2e5 --- /dev/null +++ b/benches/README.md @@ -0,0 +1,3 @@ +Benchmarking suite for code generation and code execution, heavily based on [the diesel benchmarking suite](https://github.com/diesel-rs/diesel/tree/master/diesel_bench). + +Note that the benchmarks use the `diesel` crate which links directly against `libpq`, so you will need to install it. On debian-based distros, the package is `libpq-dev`, and on RHEl-based distros, it is named `libpq-devel`. \ No newline at end of file From d7d6493b9ed868f160820d334d36285bb5783219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 23 Nov 2022 18:42:43 -0500 Subject: [PATCH 05/31] Remove unused lock file. --- examples/auto_build/Cargo.lock | 816 --------------------------------- 1 file changed, 816 deletions(-) delete mode 100644 examples/auto_build/Cargo.lock diff --git a/examples/auto_build/Cargo.lock b/examples/auto_build/Cargo.lock deleted file mode 100644 index 7901ad1f..00000000 --- a/examples/auto_build/Cargo.lock +++ /dev/null @@ -1,816 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "async-trait" -version = "0.1.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" -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 = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "block-buffer" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" -dependencies = [ - "generic-array", -] - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cornucopia-example-auto-build" -version = "0.1.0" -dependencies = [ - "cornucopia_client", - "deadpool-postgres", - "tokio", - "tokio-postgres", -] - -[[package]] -name = "cornucopia_client" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48ef849bf6ca73ece1605e5b3dc4a957007839700892627a2f2dc506510d7af8" -dependencies = [ - "async-trait", - "deadpool-postgres", - "tokio", - "tokio-postgres", -] - -[[package]] -name = "cpufeatures" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" -dependencies = [ - "libc", -] - -[[package]] -name = "crypto-common" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" -dependencies = [ - "generic-array", - "typenum", -] - -[[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-postgres" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c668a58063c6331e3437e3146970943ad82b1b36169fd979bb2645ac2088209a" -dependencies = [ - "deadpool", - "log", - "tokio", - "tokio-postgres", -] - -[[package]] -name = "deadpool-runtime" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaa37046cc0f6c3cc6090fbdbf73ef0b8ef4cfcc37f6befc0020f63e8cf121e1" -dependencies = [ - "tokio", -] - -[[package]] -name = "digest" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" -dependencies = [ - "block-buffer", - "crypto-common", - "subtle", -] - -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - -[[package]] -name = "futures" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" - -[[package]] -name = "futures-executor" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" - -[[package]] -name = "futures-macro" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" - -[[package]] -name = "futures-task" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" - -[[package]] -name = "futures-util" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.10.2+wasi-snapshot-preview1", -] - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[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.126" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" - -[[package]] -name = "lock_api" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "md-5" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" -dependencies = [ - "digest", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "mio" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" -dependencies = [ - "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", -] - -[[package]] -name = "num_cpus" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "once_cell" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" - -[[package]] -name = "parking_lot" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-sys", -] - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - -[[package]] -name = "phf" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" -dependencies = [ - "phf_shared", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "postgres-protocol" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "878c6cbf956e03af9aa8204b407b9cbf47c072164800aa918c516cd4b056c50c" -dependencies = [ - "base64", - "byteorder", - "bytes", - "fallible-iterator", - "hmac", - "md-5", - "memchr", - "rand", - "sha2", - "stringprep", -] - -[[package]] -name = "postgres-types" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd6e8b7189a73169290e89bd24c771071f1012d8fe6f738f5226531f0b03d89" -dependencies = [ - "bytes", - "fallible-iterator", - "postgres-protocol", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - -[[package]] -name = "proc-macro2" -version = "1.0.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" -dependencies = [ - "proc-macro2", -] - -[[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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - -[[package]] -name = "redox_syscall" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" -dependencies = [ - "bitflags", -] - -[[package]] -name = "retain_mut" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0" - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "sha2" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" -dependencies = [ - "libc", -] - -[[package]] -name = "siphasher" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" - -[[package]] -name = "slab" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" - -[[package]] -name = "smallvec" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" - -[[package]] -name = "socket2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "stringprep" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "syn" -version = "1.0.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - -[[package]] -name = "tokio" -version = "1.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4903bf0427cf68dddd5aa6a93220756f8be0c34fcfa9f5e6191e103e15a31395" -dependencies = [ - "bytes", - "libc", - "memchr", - "mio", - "num_cpus", - "once_cell", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "winapi", -] - -[[package]] -name = "tokio-macros" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-postgres" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19c88a47a23c5d2dc9ecd28fb38fba5fc7e5ddc1fe64488ec145076b0c71c8ae" -dependencies = [ - "async-trait", - "byteorder", - "bytes", - "fallible-iterator", - "futures", - "log", - "parking_lot", - "percent-encoding", - "phf", - "pin-project-lite", - "postgres-protocol", - "postgres-types", - "socket2", - "tokio", - "tokio-util", -] - -[[package]] -name = "tokio-util" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - -[[package]] -name = "tracing" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" -dependencies = [ - "cfg-if", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "typenum" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" - -[[package]] -name = "unicode-bidi" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" - -[[package]] -name = "unicode-ident" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" - -[[package]] -name = "unicode-normalization" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[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-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - -[[package]] -name = "windows_i686_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" From 6bd282985f9f8ef491f9d0841e6d065ef04c545e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 23 Nov 2022 18:48:29 -0500 Subject: [PATCH 06/31] Add optional podman support to integration test. --- integration/src/main.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/integration/src/main.rs b/integration/src/main.rs index eb42a1b2..2f55f31d 100644 --- a/integration/src/main.rs +++ b/integration/src/main.rs @@ -19,6 +19,9 @@ struct Args { /// Update the project's generated code #[clap(long)] apply_codegen: bool, + /// Use `podman` instead of `docker` + #[clap(short, long)] + podman: bool, } #[derive(serde::Deserialize, serde::Serialize)] @@ -82,17 +85,18 @@ fn test( Args { apply_errors, apply_codegen, + podman, }: Args, ) -> bool { // Start by removing previous container if it was left open - container::cleanup(false).ok(); - container::setup(false).unwrap(); + container::cleanup(podman).ok(); + container::setup(podman).unwrap(); let successful = std::panic::catch_unwind(|| { let mut client = cornucopia::conn::cornucopia_conn().unwrap(); display(run_errors_test(&mut client, apply_errors)).unwrap() && display(run_codegen_test(&mut client, apply_codegen)).unwrap() }); - container::cleanup(false).unwrap(); + container::cleanup(podman).unwrap(); successful.unwrap() } @@ -321,7 +325,8 @@ mod test { fn run() { assert!(test(crate::Args { apply_errors: false, - apply_codegen: false + apply_codegen: false, + podman: false })) } } From fe3b5ebd80d6c111ed1c0dc2477d0777687d0ab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 23 Nov 2022 18:56:38 -0500 Subject: [PATCH 07/31] Prevent running sync `codegen_test` twice (until we add a real async `codegen_test`) --- integration/fixtures/codegen/codegen_test.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/integration/fixtures/codegen/codegen_test.toml b/integration/fixtures/codegen/codegen_test.toml index 3867a0e2..dd7af2e6 100644 --- a/integration/fixtures/codegen/codegen_test.toml +++ b/integration/fixtures/codegen/codegen_test.toml @@ -10,5 +10,4 @@ run = "codegen_test" name = "Async" base_path = "codegen_test" destination = "src/cornucopia_async.rs" -run = "codegen_test" derive_ser = true From 6126ca29de9c84b42308c029b484012bd88098b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 23 Nov 2022 20:15:33 -0500 Subject: [PATCH 08/31] Document dependencies. --- codegen_test/Cargo.toml | 26 +++++++++++------ examples/auto_build/Cargo.toml | 18 +++++++++--- examples/basic_async/Cargo.toml | 14 ++++++++-- examples/basic_sync/Cargo.toml | 9 ++++-- integration/Cargo.toml | 49 ++++++++++++++++++++++++++++++--- 5 files changed, 95 insertions(+), 21 deletions(-) diff --git a/codegen_test/Cargo.toml b/codegen_test/Cargo.toml index 534dfae9..e3ab7c1c 100644 --- a/codegen_test/Cargo.toml +++ b/codegen_test/Cargo.toml @@ -6,6 +6,18 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +# Path dependencies +cornucopia_sync = { path = "../crates/client_sync", features = [ + "with-serde_json-1", +] } +cornucopia_async = { path = "../crates/client_async", features = [ + "with-serde_json-1", +] } + +# async +futures = "0.3.25" + +# rust-postgres interaction postgres = { version = "0.19.4", features = [ "with-serde_json-1", "with-time-0_3", @@ -19,15 +31,13 @@ tokio-postgres = { version = "0.7.7", features = [ "with-eui48-1", ] } postgres-types = { version = "0.2.4", features = ["derive"] } -serde = { version = "1.0.147", features = ["derive"], package = "serde" } -serde_json = { version = "1.0.87", features = ["raw_value"], package = "serde_json" } + +# serde +serde = { version = "1.0.147", features = ["derive"] } + +# extra types +serde_json = { version = "1.0.87", features = ["raw_value"] } time = { version = "0.3.17", features = ["parsing", "serde"] } uuid = { version = "1.2.1", features = ["serde"] } eui48 = { version = "1.1.0", features = ["serde"] } rust_decimal = { version = "1.26.1", features = ["db-postgres"] } -futures = "0.3.25" - -cornucopia_sync = { path = "../clients/sync", features = ["with-serde_json-1"] } -cornucopia_async = { path = "../clients/async", features = [ - "with-serde_json-1", -] } diff --git a/examples/auto_build/Cargo.toml b/examples/auto_build/Cargo.toml index cb6f0d50..850b5fe8 100644 --- a/examples/auto_build/Cargo.toml +++ b/examples/auto_build/Cargo.toml @@ -6,13 +6,23 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +# Async tokio = { version = "1.21.2", features = ["full"] } -tokio-postgres = "0.7.7" -deadpool-postgres = "0.10.3" futures = "0.3.25" + +# Postgres interaction +tokio-postgres = "0.7.7" postgres-types = "0.2.4" +## Connection pooling +deadpool-postgres = "0.10.3" -cornucopia_async = { path = "../../clients/async" } +# Cornucopia async client +## If you're trying this example as a standalone crate, +## replace the path with the latest current version +cornucopia_async = { path = "../../crates/client_async" } [build-dependencies] -cornucopia = { path = "../../cornucopia" } +# Cornucopia library to automatically +## If you're trying this example as a standalone crate, +## replace the path with the latest current version +cornucopia = { path = "../../crates/cornucopia" } diff --git a/examples/basic_async/Cargo.toml b/examples/basic_async/Cargo.toml index e0077c75..3fd6fa75 100644 --- a/examples/basic_async/Cargo.toml +++ b/examples/basic_async/Cargo.toml @@ -4,9 +4,17 @@ name = "basic-async" version = "0.1.0" [dependencies] +# Async tokio = { version = "1.21.2", features = ["full"] } -deadpool-postgres = "0.10.3" futures = "0.3.25" -postgres-types = { version = "0.2.4", features = ["derive"] } + +# Postgres interaction tokio-postgres = "0.7.7" -cornucopia_async = { path = "../../clients/async" } +postgres-types = { version = "0.2.4", features = ["derive"] } +## Connection pooling +deadpool-postgres = "0.10.3" + +# cornucopia async client +## If you're trying this example as a standalone crate, +## replace the path with the latest current version +cornucopia_async = { path = "../../crates/client_async" } diff --git a/examples/basic_sync/Cargo.toml b/examples/basic_sync/Cargo.toml index 8ba6b95d..3d567636 100644 --- a/examples/basic_sync/Cargo.toml +++ b/examples/basic_sync/Cargo.toml @@ -6,6 +6,11 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -postgres-types = { version = "0.2.4", features = ["derive"] } +# Postgres interaction postgres = "0.19.4" -cornucopia_sync = { path = "../../clients/sync" } +postgres-types = { version = "0.2.4", features = ["derive"] } + +# cornucopia async client +## If you're trying this example as a standalone crate, +## replace the path with the latest current version +cornucopia_sync = { path = "../../crates/client_sync" } diff --git a/integration/Cargo.toml b/integration/Cargo.toml index 7eb3e1a8..cbd0d786 100644 --- a/integration/Cargo.toml +++ b/integration/Cargo.toml @@ -6,10 +6,51 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +# Path dependencies +cornucopia = { path = "../crates/cornucopia" } +cornucopia_sync = { path = "../crates/client_sync", features = [ + "with-serde_json-1", +] } +cornucopia_async = { path = "../crates/client_async", features = [ + "with-serde_json-1", +] } + +# Create temporary projects for error tests. tempfile = "3.3.0" -toml = "0.5.9" + +# Colored output owo-colors = "3.5.0" -serde = { version = "1.0.147", features = ["derive"], package = "serde" } + +# CLI handling clap = { version = "4.0.19", features = ["derive"] } -cornucopia = { path = "../cornucopia" } -postgres = "0.19.4" + +# async +futures = "0.3.25" + +# rust-postgres interaction +postgres = { version = "0.19.4", features = [ + "with-serde_json-1", + "with-time-0_3", + "with-uuid-1", + "with-eui48-1", +] } +tokio-postgres = { version = "0.7.7", features = [ + "with-serde_json-1", + "with-time-0_3", + "with-uuid-1", + "with-eui48-1", +] } +postgres-types = { version = "0.2.4", features = ["derive"] } + +# serde +## Both for test helpers ser/de and codegen `Serialize` derives +serde = { version = "1.0.147", features = ["derive"] } +## Read fixture files +toml = "0.5.9" + +# extra types +serde_json = { version = "1.0.87", features = ["raw_value"] } +time = { version = "0.3.17", features = ["parsing", "serde"] } +uuid = { version = "1.2.1", features = ["serde"] } +eui48 = { version = "1.1.0", features = ["serde"] } +rust_decimal = { version = "1.26.1", features = ["db-postgres"] } From c049d852f770372789f28bf0bf4f12bab4ee8a58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 23 Nov 2022 20:25:38 -0500 Subject: [PATCH 09/31] Move published crates into a dedicated `crates/` folder. --- Cargo.toml | 10 +--------- codegen_template/Cargo.toml | 13 ------------- .../async => crates/client_async}/Cargo.toml | 12 +++++++++--- .../async => crates/client_async}/README.md | 0 .../client_async}/src/deadpool.rs | 0 .../client_async}/src/generic_client.rs | 0 .../async => crates/client_async}/src/lib.rs | 0 .../client_async}/src/private.rs | 0 .../core => crates/client_core}/Cargo.toml | 19 +++++++++++-------- .../core => crates/client_core}/README.md | 0 .../client_core}/src/array_iterator.rs | 0 .../core => crates/client_core}/src/domain.rs | 0 .../core => crates/client_core}/src/lib.rs | 0 .../client_core}/src/type_traits.rs | 7 ++----- .../core => crates/client_core}/src/utils.rs | 0 .../sync => crates/client_sync}/Cargo.toml | 12 +++++++----- .../sync => crates/client_sync}/README.md | 0 .../sync => crates/client_sync}/src/lib.rs | 0 .../client_sync}/src/private.rs | 0 crates/codegen_template/Cargo.toml | 16 ++++++++++++++++ .../codegen_template}/src/lib.rs | 0 {cornucopia => crates/cornucopia}/Cargo.toml | 14 +++++++++++++- {cornucopia => crates/cornucopia}/src/cli.rs | 0 .../cornucopia}/src/codegen.rs | 0 {cornucopia => crates/cornucopia}/src/conn.rs | 0 .../cornucopia}/src/container.rs | 2 +- .../cornucopia}/src/error.rs | 0 {cornucopia => crates/cornucopia}/src/lib.rs | 0 .../cornucopia}/src/load_schema.rs | 0 {cornucopia => crates/cornucopia}/src/main.rs | 0 .../cornucopia}/src/parser.rs | 0 .../cornucopia}/src/prepare_queries.rs | 0 .../cornucopia}/src/read_queries.rs | 0 .../cornucopia}/src/type_registrar.rs | 0 .../cornucopia}/src/utils.rs | 0 .../cornucopia}/src/validation.rs | 0 36 files changed, 60 insertions(+), 45 deletions(-) delete mode 100644 codegen_template/Cargo.toml rename {clients/async => crates/client_async}/Cargo.toml (83%) rename {clients/async => crates/client_async}/README.md (100%) rename {clients/async => crates/client_async}/src/deadpool.rs (100%) rename {clients/async => crates/client_async}/src/generic_client.rs (100%) rename {clients/async => crates/client_async}/src/lib.rs (100%) rename {clients/async => crates/client_async}/src/private.rs (100%) rename {clients/core => crates/client_core}/Cargo.toml (56%) rename {clients/core => crates/client_core}/README.md (100%) rename {clients/core => crates/client_core}/src/array_iterator.rs (100%) rename {clients/core => crates/client_core}/src/domain.rs (100%) rename {clients/core => crates/client_core}/src/lib.rs (100%) rename {clients/core => crates/client_core}/src/type_traits.rs (96%) rename {clients/core => crates/client_core}/src/utils.rs (100%) rename {clients/sync => crates/client_sync}/Cargo.toml (83%) rename {clients/sync => crates/client_sync}/README.md (100%) rename {clients/sync => crates/client_sync}/src/lib.rs (100%) rename {clients/sync => crates/client_sync}/src/private.rs (100%) create mode 100644 crates/codegen_template/Cargo.toml rename {codegen_template => crates/codegen_template}/src/lib.rs (100%) rename {cornucopia => crates/cornucopia}/Cargo.toml (80%) rename {cornucopia => crates/cornucopia}/src/cli.rs (100%) rename {cornucopia => crates/cornucopia}/src/codegen.rs (100%) rename {cornucopia => crates/cornucopia}/src/conn.rs (100%) rename {cornucopia => crates/cornucopia}/src/container.rs (98%) rename {cornucopia => crates/cornucopia}/src/error.rs (100%) rename {cornucopia => crates/cornucopia}/src/lib.rs (100%) rename {cornucopia => crates/cornucopia}/src/load_schema.rs (100%) rename {cornucopia => crates/cornucopia}/src/main.rs (100%) rename {cornucopia => crates/cornucopia}/src/parser.rs (100%) rename {cornucopia => crates/cornucopia}/src/prepare_queries.rs (100%) rename {cornucopia => crates/cornucopia}/src/read_queries.rs (100%) rename {cornucopia => crates/cornucopia}/src/type_registrar.rs (100%) rename {cornucopia => crates/cornucopia}/src/utils.rs (100%) rename {cornucopia => crates/cornucopia}/src/validation.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 44fea166..1a087039 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,2 @@ [workspace] -members = [ - "examples/*", - "clients/*", - "integration", - "codegen_test", - "bench", - "cornucopia", - "codegen_template", -] +members = ["examples/*", "integration", "codegen_test", "benches", "crates/*"] diff --git a/codegen_template/Cargo.toml b/codegen_template/Cargo.toml deleted file mode 100644 index 08235026..00000000 --- a/codegen_template/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "codegen_template" -version = "0.1.0" -edition = "2021" -description = "A Rust code templating macro for code generation." -license = "MIT OR Apache-2.0" - -[lib] -proc-macro = true - -[dependencies] -unscanny = "0.1.0" -unicode-xid = "0.2.4" \ No newline at end of file diff --git a/clients/async/Cargo.toml b/crates/client_async/Cargo.toml similarity index 83% rename from clients/async/Cargo.toml rename to crates/client_async/Cargo.toml index 9c220eb9..2364c01f 100644 --- a/clients/async/Cargo.toml +++ b/crates/client_async/Cargo.toml @@ -18,8 +18,14 @@ with-serde_json-1 = ["cornucopia_client_core/with-serde_json-1"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tokio-postgres = "0.7.7" +# Path dependencies +cornucopia_client_core = { path = "../client_core", version = "0.4.0" } + +# async async-trait = "0.1.58" -deadpool-postgres = { version = "0.10.3", optional = true } -cornucopia_client_core = { path = "../core", version = "0.4.0" } +# rust-postgres interaction +tokio-postgres = "0.7.7" + +# connection pooling +deadpool-postgres = { version = "0.10.3", optional = true } diff --git a/clients/async/README.md b/crates/client_async/README.md similarity index 100% rename from clients/async/README.md rename to crates/client_async/README.md diff --git a/clients/async/src/deadpool.rs b/crates/client_async/src/deadpool.rs similarity index 100% rename from clients/async/src/deadpool.rs rename to crates/client_async/src/deadpool.rs diff --git a/clients/async/src/generic_client.rs b/crates/client_async/src/generic_client.rs similarity index 100% rename from clients/async/src/generic_client.rs rename to crates/client_async/src/generic_client.rs diff --git a/clients/async/src/lib.rs b/crates/client_async/src/lib.rs similarity index 100% rename from clients/async/src/lib.rs rename to crates/client_async/src/lib.rs diff --git a/clients/async/src/private.rs b/crates/client_async/src/private.rs similarity index 100% rename from clients/async/src/private.rs rename to crates/client_async/src/private.rs diff --git a/clients/core/Cargo.toml b/crates/client_core/Cargo.toml similarity index 56% rename from clients/core/Cargo.toml rename to crates/client_core/Cargo.toml index f4f3f486..6e894c50 100644 --- a/clients/core/Cargo.toml +++ b/crates/client_core/Cargo.toml @@ -12,16 +12,19 @@ keywords = ["postgresql", "query", "generator", "sql", "tokio-postgres"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +with-serde_json-1 = ["postgres-types/with-serde_json-1", "serde", "serde_json"] + [dependencies] +# Postgres interaction postgres-protocol = "0.6.4" postgres-types = "0.2.4" +## Iterator utils required for working with `postgres_protocol::types::ArrayValues` fallible-iterator = "0.2.0" -serde-1 = { version = "1.0.147", package = "serde", optional = true } -serde_json-1 = { version = "1.0.87", package = "serde_json", optional = true } -[features] -with-serde_json-1 = [ - "postgres-types/with-serde_json-1", - "serde-1", - "serde_json-1", -] +# json +## This crate implements the "ergonomic paramters" for +## `serde_json::Value` and `serde_json::raw::RawValue`. +serde_json = { version = "1.0.87", optional = true } +## Used for `postgres_types::Json` `Serialize` trait bounds +serde = { version = "1.0.147", optional = true } diff --git a/clients/core/README.md b/crates/client_core/README.md similarity index 100% rename from clients/core/README.md rename to crates/client_core/README.md diff --git a/clients/core/src/array_iterator.rs b/crates/client_core/src/array_iterator.rs similarity index 100% rename from clients/core/src/array_iterator.rs rename to crates/client_core/src/array_iterator.rs diff --git a/clients/core/src/domain.rs b/crates/client_core/src/domain.rs similarity index 100% rename from clients/core/src/domain.rs rename to crates/client_core/src/domain.rs diff --git a/clients/core/src/lib.rs b/crates/client_core/src/lib.rs similarity index 100% rename from clients/core/src/lib.rs rename to crates/client_core/src/lib.rs diff --git a/clients/core/src/type_traits.rs b/crates/client_core/src/type_traits.rs similarity index 96% rename from clients/core/src/type_traits.rs rename to crates/client_core/src/type_traits.rs index fe950939..e818a1cb 100644 --- a/clients/core/src/type_traits.rs +++ b/crates/client_core/src/type_traits.rs @@ -21,12 +21,9 @@ pub trait JsonSql: std::fmt::Debug + ToSql + Sync + Send {} #[cfg(feature = "with-serde_json-1")] impl JsonSql for &T {} #[cfg(feature = "with-serde_json-1")] -impl JsonSql for serde_json_1::value::Value {} +impl JsonSql for serde_json::value::Value {} #[cfg(feature = "with-serde_json-1")] -impl JsonSql - for postgres_types::Json -{ -} +impl JsonSql for postgres_types::Json {} pub trait ArraySql: std::fmt::Debug + ToSql + Send + Sync { type Item; diff --git a/clients/core/src/utils.rs b/crates/client_core/src/utils.rs similarity index 100% rename from clients/core/src/utils.rs rename to crates/client_core/src/utils.rs diff --git a/clients/sync/Cargo.toml b/crates/client_sync/Cargo.toml similarity index 83% rename from clients/sync/Cargo.toml rename to crates/client_sync/Cargo.toml index 76dcdf51..8662bab6 100644 --- a/clients/sync/Cargo.toml +++ b/crates/client_sync/Cargo.toml @@ -12,10 +12,12 @@ keywords = ["postgresql", "query", "generator", "sql", "tokio-postgres"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] -postgres = "0.19.4" - -cornucopia_client_core = { path = "../core", version = "0.4.0" } - [features] with-serde_json-1 = ["cornucopia_client_core/with-serde_json-1"] + +[dependencies] +# Path dependencies +cornucopia_client_core = { path = "../client_core", version = "0.4.0" } + +# postgres interaction +postgres = "0.19.4" diff --git a/clients/sync/README.md b/crates/client_sync/README.md similarity index 100% rename from clients/sync/README.md rename to crates/client_sync/README.md diff --git a/clients/sync/src/lib.rs b/crates/client_sync/src/lib.rs similarity index 100% rename from clients/sync/src/lib.rs rename to crates/client_sync/src/lib.rs diff --git a/clients/sync/src/private.rs b/crates/client_sync/src/private.rs similarity index 100% rename from clients/sync/src/private.rs rename to crates/client_sync/src/private.rs diff --git a/crates/codegen_template/Cargo.toml b/crates/codegen_template/Cargo.toml new file mode 100644 index 00000000..5e42ea8f --- /dev/null +++ b/crates/codegen_template/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "codegen_template" +version = "0.1.0" +edition = "2021" +description = "A templating macro to generate Rust code into a buffer." +readme = "README.md" +license = "MIT OR Apache-2.0" + +[lib] +proc-macro = true + +[dependencies] +# String scanner +unscanny = "0.1.0" +# Utility to parse rust identifiers +unicode-xid = "0.2.4" diff --git a/codegen_template/src/lib.rs b/crates/codegen_template/src/lib.rs similarity index 100% rename from codegen_template/src/lib.rs rename to crates/codegen_template/src/lib.rs diff --git a/cornucopia/Cargo.toml b/crates/cornucopia/Cargo.toml similarity index 80% rename from cornucopia/Cargo.toml rename to crates/cornucopia/Cargo.toml index 722baa8f..3a8310c7 100644 --- a/cornucopia/Cargo.toml +++ b/crates/cornucopia/Cargo.toml @@ -11,13 +11,25 @@ categories = ["database"] keywords = ["postgresql", "query", "generator", "sql", "tokio-postgres"] [dependencies] +# Path dependencies codegen_template = { path = "../codegen_template", version = "0.1.0" } +# Postgres interaction postgres = "0.19.4" postgres-types = "0.2.4" + +# Error handling and reporting thiserror = "1.0.37" miette = { version = "5.4.1", features = ["fancy"] } + +# Parser +chumsky = "0.8.0" + +# CLI handling clap = { version = "4.0.19", features = ["derive"] } + +# Word case heck = "0.4.0" + +# Order-preserving map to work around borrowing issues indexmap = "1.9.1" -chumsky = "0.8.0" diff --git a/cornucopia/src/cli.rs b/crates/cornucopia/src/cli.rs similarity index 100% rename from cornucopia/src/cli.rs rename to crates/cornucopia/src/cli.rs diff --git a/cornucopia/src/codegen.rs b/crates/cornucopia/src/codegen.rs similarity index 100% rename from cornucopia/src/codegen.rs rename to crates/cornucopia/src/codegen.rs diff --git a/cornucopia/src/conn.rs b/crates/cornucopia/src/conn.rs similarity index 100% rename from cornucopia/src/conn.rs rename to crates/cornucopia/src/conn.rs diff --git a/cornucopia/src/container.rs b/crates/cornucopia/src/container.rs similarity index 98% rename from cornucopia/src/container.rs rename to crates/cornucopia/src/container.rs index ecaa6385..5ef00506 100644 --- a/cornucopia/src/container.rs +++ b/crates/cornucopia/src/container.rs @@ -29,7 +29,7 @@ fn spawn_container(podman: bool) -> Result<(), Error> { "5435:5432", "-e", "POSTGRES_PASSWORD=postgres", - "postgres", + "docker.io/library/postgres:latest", ], "spawn container", ) diff --git a/cornucopia/src/error.rs b/crates/cornucopia/src/error.rs similarity index 100% rename from cornucopia/src/error.rs rename to crates/cornucopia/src/error.rs diff --git a/cornucopia/src/lib.rs b/crates/cornucopia/src/lib.rs similarity index 100% rename from cornucopia/src/lib.rs rename to crates/cornucopia/src/lib.rs diff --git a/cornucopia/src/load_schema.rs b/crates/cornucopia/src/load_schema.rs similarity index 100% rename from cornucopia/src/load_schema.rs rename to crates/cornucopia/src/load_schema.rs diff --git a/cornucopia/src/main.rs b/crates/cornucopia/src/main.rs similarity index 100% rename from cornucopia/src/main.rs rename to crates/cornucopia/src/main.rs diff --git a/cornucopia/src/parser.rs b/crates/cornucopia/src/parser.rs similarity index 100% rename from cornucopia/src/parser.rs rename to crates/cornucopia/src/parser.rs diff --git a/cornucopia/src/prepare_queries.rs b/crates/cornucopia/src/prepare_queries.rs similarity index 100% rename from cornucopia/src/prepare_queries.rs rename to crates/cornucopia/src/prepare_queries.rs diff --git a/cornucopia/src/read_queries.rs b/crates/cornucopia/src/read_queries.rs similarity index 100% rename from cornucopia/src/read_queries.rs rename to crates/cornucopia/src/read_queries.rs diff --git a/cornucopia/src/type_registrar.rs b/crates/cornucopia/src/type_registrar.rs similarity index 100% rename from cornucopia/src/type_registrar.rs rename to crates/cornucopia/src/type_registrar.rs diff --git a/cornucopia/src/utils.rs b/crates/cornucopia/src/utils.rs similarity index 100% rename from cornucopia/src/utils.rs rename to crates/cornucopia/src/utils.rs diff --git a/cornucopia/src/validation.rs b/crates/cornucopia/src/validation.rs similarity index 100% rename from cornucopia/src/validation.rs rename to crates/cornucopia/src/validation.rs From b431b72e46e358eb646571c074a19ad11ed29779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 23 Nov 2022 20:26:19 -0500 Subject: [PATCH 10/31] Copy documentation into a README file. --- crates/codegen_template/README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 crates/codegen_template/README.md diff --git a/crates/codegen_template/README.md b/crates/codegen_template/README.md new file mode 100644 index 00000000..f730cfbc --- /dev/null +++ b/crates/codegen_template/README.md @@ -0,0 +1,25 @@ +Performs variable interpolation against the input and store the result into +a writable output. + +# Display + +You can interpolate any type implementing the [`Display`](std::fmt::Display) trait using `$var` +or `${var}`. This grabs the `var` variable that is currently in scope and +format it into the output. + +# Lazy + +You can interpolate formatting closure implementing the [`Fn(&mut W)`] trait +using `$!lazy` or `$!{lazy}`. This grabs the `lazy` variable that is currently +in scope and call it with th output as arg in the right time. + +# Repetition + +Repetition is done using `$(...)`. This iterates through the elements of any variable +interpolated within the repetition and inserts a copy of the repetition body +for each one. The variables in an interpolation must implement the [`Iterator`] and the +[`Clone`] traits. + +- `$($var)` — simple repetition +- `$( struct ${var}; )` — the repetition can contain other tokens +- `$( $k => println!("{}", $!v), )` — even multiple interpolations \ No newline at end of file From c3dac7e153df5e803d75f6a70c4a8b397828098b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 23 Nov 2022 20:30:05 -0500 Subject: [PATCH 11/31] Reword error message. --- crates/cornucopia/src/validation.rs | 2 +- integration/fixtures/errors/validation.toml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/cornucopia/src/validation.rs b/crates/cornucopia/src/validation.rs index 2edb7db1..cb5b4e0f 100644 --- a/crates/cornucopia/src/validation.rs +++ b/crates/cornucopia/src/validation.rs @@ -414,7 +414,7 @@ pub mod error { pos: SourceSpan, known: String, }, - #[error("named type `{name}` as conflicting usage")] + #[error("conflicting uses of named type `{name}`")] #[diagnostic(help("use a different named type for each query"))] IncompatibleNamedType { #[source_code] diff --git a/integration/fixtures/errors/validation.toml b/integration/fixtures/errors/validation.toml index ad7bfea1..c318cbc7 100644 --- a/integration/fixtures/errors/validation.toml +++ b/integration/fixtures/errors/validation.toml @@ -190,7 +190,7 @@ SELECT name FROM Author; SELECT id FROM Author; ''' error = ''' -× named type `Row` as conflicting usage +× conflicting uses of named type `Row` ╭─[queries/test.sql:1:1] 1 │ --: Row() 2 │ --! author_names: Row @@ -214,7 +214,7 @@ SELECT name FROM Author; SELECT name, id FROM Author; ''' error = ''' -× named type `Row` as conflicting usage +× conflicting uses of named type `Row` ╭─[queries/test.sql:1:1] 1 │ --: Row() 2 │ --! author_names: Row @@ -238,7 +238,7 @@ SELECT name FROM Author; SELECT id as name FROM Author; ''' error = ''' -× named type `Row` as conflicting usage +× conflicting uses of named type `Row` ╭─[queries/test.sql:1:1] 1 │ --: Row() 2 │ --! author_names: Row From f18150b4fcd16249daa8208b5a26fcb3c52e7b38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 23 Nov 2022 21:16:32 -0500 Subject: [PATCH 12/31] Renamed `usage` bench to `execution` --- .../cornucopia_benches/mod.rs => execution/cornucopia_benches.rs} | 0 .../{usage => execution}/cornucopia_benches/generated_async.rs | 0 benches/{usage => execution}/cornucopia_benches/generated_sync.rs | 0 benches/{usage => execution}/cornucopia_benches/queries/bench.sql | 0 benches/{usage => execution}/cornucopia_benches/schema.sql | 0 benches/{usage => execution}/diesel_benches.rs | 0 benches/{usage => execution}/main.rs | 0 benches/{usage => execution}/postgres_benches.rs | 0 benches/{usage => execution}/tokio_postgres_benches.rs | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename benches/{usage/cornucopia_benches/mod.rs => execution/cornucopia_benches.rs} (100%) rename benches/{usage => execution}/cornucopia_benches/generated_async.rs (100%) rename benches/{usage => execution}/cornucopia_benches/generated_sync.rs (100%) rename benches/{usage => execution}/cornucopia_benches/queries/bench.sql (100%) rename benches/{usage => execution}/cornucopia_benches/schema.sql (100%) rename benches/{usage => execution}/diesel_benches.rs (100%) rename benches/{usage => execution}/main.rs (100%) rename benches/{usage => execution}/postgres_benches.rs (100%) rename benches/{usage => execution}/tokio_postgres_benches.rs (100%) diff --git a/benches/usage/cornucopia_benches/mod.rs b/benches/execution/cornucopia_benches.rs similarity index 100% rename from benches/usage/cornucopia_benches/mod.rs rename to benches/execution/cornucopia_benches.rs diff --git a/benches/usage/cornucopia_benches/generated_async.rs b/benches/execution/cornucopia_benches/generated_async.rs similarity index 100% rename from benches/usage/cornucopia_benches/generated_async.rs rename to benches/execution/cornucopia_benches/generated_async.rs diff --git a/benches/usage/cornucopia_benches/generated_sync.rs b/benches/execution/cornucopia_benches/generated_sync.rs similarity index 100% rename from benches/usage/cornucopia_benches/generated_sync.rs rename to benches/execution/cornucopia_benches/generated_sync.rs diff --git a/benches/usage/cornucopia_benches/queries/bench.sql b/benches/execution/cornucopia_benches/queries/bench.sql similarity index 100% rename from benches/usage/cornucopia_benches/queries/bench.sql rename to benches/execution/cornucopia_benches/queries/bench.sql diff --git a/benches/usage/cornucopia_benches/schema.sql b/benches/execution/cornucopia_benches/schema.sql similarity index 100% rename from benches/usage/cornucopia_benches/schema.sql rename to benches/execution/cornucopia_benches/schema.sql diff --git a/benches/usage/diesel_benches.rs b/benches/execution/diesel_benches.rs similarity index 100% rename from benches/usage/diesel_benches.rs rename to benches/execution/diesel_benches.rs diff --git a/benches/usage/main.rs b/benches/execution/main.rs similarity index 100% rename from benches/usage/main.rs rename to benches/execution/main.rs diff --git a/benches/usage/postgres_benches.rs b/benches/execution/postgres_benches.rs similarity index 100% rename from benches/usage/postgres_benches.rs rename to benches/execution/postgres_benches.rs diff --git a/benches/usage/tokio_postgres_benches.rs b/benches/execution/tokio_postgres_benches.rs similarity index 100% rename from benches/usage/tokio_postgres_benches.rs rename to benches/execution/tokio_postgres_benches.rs From 1aa41d8144c9e455246cb9af96794a62d03f4acd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Thu, 24 Nov 2022 15:37:50 -0500 Subject: [PATCH 13/31] Add `codegen_test` documentation. --- codegen_test/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 codegen_test/README.md diff --git a/codegen_test/README.md b/codegen_test/README.md new file mode 100644 index 00000000..1e206ecb --- /dev/null +++ b/codegen_test/README.md @@ -0,0 +1,3 @@ +This crate is associated with the integrations tests. **You should not run it stand-alone** - use the dedicated `integration` crate instead. + +This crate is a scaffold containing a schema and queries stress-testing as many features as possible. The generated code is kept alongside to monitor breaking changes and to prevent unintentional changes to our generated interfaces. \ No newline at end of file From 4d5580970557d81177a8d2e7735c69a216b6ed2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Thu, 24 Nov 2022 15:38:10 -0500 Subject: [PATCH 14/31] Add `integration` documentation. --- integration/README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 integration/README.md diff --git a/integration/README.md b/integration/README.md new file mode 100644 index 00000000..3d850788 --- /dev/null +++ b/integration/README.md @@ -0,0 +1,19 @@ +Integration testing entrypoint. + +This acts like a testing harness. It manages the database required for testing, coordinates the execution of tests and reports the results of the test suite. Internally, it uses many of our workspace crates, notably `codegen_test`, but also `examples` and `benches`. + +Our integration testing not only checks that Cornucopia is able to generate the code, but it also tests that the right error messages are reported in case of user errors. It also runs the generated code to ensure its correctness. + +The test cases are auto-described using TOML fixtures. These files are deserialized when the integration tests are run and describe what should be generated, where it shoulld be generated, etc. + +# How to use + +The crate can be executed directly with `cargo run`, but it will also automatically be invoked when running workspace tests. + +When executed directly, the crate accepts CLI arguments to *update* the generated code or error messages. This is useful when you made changes (either to the generated code or to error message) and want to propagate them to the rest of the workspace: +* Update generated code: --apply-codegen +* Update errors: --apply-errors + +Note that if you made modifications that affect generated code or errors and you don't update the workspace code, the integration tests will fail. + +By default, the tests run using `docker`, but you can pass a `--podman` CLI argument. \ No newline at end of file From 235ec40c233a0f6fe047a0bc149b243041a91096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Thu, 24 Nov 2022 16:34:10 -0500 Subject: [PATCH 15/31] Rename `codegen_test` to `test_codegen` and `integration` to `test_integration` for better visibility. --- Cargo.lock | 219 ++++++++++++------ Cargo.toml | 8 +- benches/codegen.rs | 6 +- {codegen_test => test_codegen}/Cargo.toml | 2 +- {codegen_test => test_codegen}/README.md | 0 .../queries/copy.sql | 0 .../queries/domain.sql | 0 .../queries/named.sql | 0 .../queries/nullity.sql | 0 .../queries/params.sql | 0 .../queries/stress.sql | 0 .../queries/syntax.sql | 0 {codegen_test => test_codegen}/schema.sql | 0 .../src/cornucopia_async.rs | 0 .../src/cornucopia_sync.rs | 0 {codegen_test => test_codegen}/src/main.rs | 0 {integration => test_integration}/Cargo.toml | 2 +- {integration => test_integration}/README.md | 2 +- .../fixtures/codegen/benchmarks.toml | 0 .../fixtures/codegen/examples.toml | 0 .../fixtures/codegen/test_codegen.toml | 6 +- .../fixtures/errors/codegen.toml | 0 .../fixtures/errors/prepare.toml | 0 .../fixtures/errors/schema.toml | 0 .../fixtures/errors/validation.toml | 0 {integration => test_integration}/src/main.rs | 0 26 files changed, 166 insertions(+), 79 deletions(-) rename {codegen_test => test_codegen}/Cargo.toml (98%) rename {codegen_test => test_codegen}/README.md (100%) rename {codegen_test => test_codegen}/queries/copy.sql (100%) rename {codegen_test => test_codegen}/queries/domain.sql (100%) rename {codegen_test => test_codegen}/queries/named.sql (100%) rename {codegen_test => test_codegen}/queries/nullity.sql (100%) rename {codegen_test => test_codegen}/queries/params.sql (100%) rename {codegen_test => test_codegen}/queries/stress.sql (100%) rename {codegen_test => test_codegen}/queries/syntax.sql (100%) rename {codegen_test => test_codegen}/schema.sql (100%) rename {codegen_test => test_codegen}/src/cornucopia_async.rs (100%) rename {codegen_test => test_codegen}/src/cornucopia_sync.rs (100%) rename {codegen_test => test_codegen}/src/main.rs (100%) rename {integration => test_integration}/Cargo.toml (98%) rename {integration => test_integration}/README.md (95%) rename {integration => test_integration}/fixtures/codegen/benchmarks.toml (100%) rename {integration => test_integration}/fixtures/codegen/examples.toml (100%) rename integration/fixtures/codegen/codegen_test.toml => test_integration/fixtures/codegen/test_codegen.toml (70%) rename {integration => test_integration}/fixtures/errors/codegen.toml (100%) rename {integration => test_integration}/fixtures/errors/prepare.toml (100%) rename {integration => test_integration}/fixtures/errors/schema.toml (100%) rename {integration => test_integration}/fixtures/errors/validation.toml (100%) rename {integration => test_integration}/src/main.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index cfcf8d12..7b58f3a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,9 +39,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.19" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] @@ -75,7 +75,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -142,7 +142,7 @@ dependencies = [ ] [[package]] -name = "bench" +name = "benches" version = "0.1.0" dependencies = [ "cornucopia", @@ -186,9 +186,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" [[package]] name = "cast" @@ -198,9 +198,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.74" +version = "1.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574" +checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" [[package]] name = "cfg-if" @@ -258,14 +258,14 @@ dependencies = [ [[package]] name = "clap" -version = "4.0.19" +version = "4.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e67816e006b17427c9b4386915109b494fec2d929c63e3bd3561234cbf1bf1e" +checksum = "0acbd8d28a0a60d7108d7ae850af6ba34cf2d1257fc646980e5f97ce14275966" dependencies = [ - "atty", "bitflags", "clap_derive", "clap_lex 0.3.0", + "is-terminal", "once_cell", "strsim", "termcolor", @@ -273,9 +273,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.0.18" +version = "4.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16a1b0f6422af32d5da0c58e2703320f379216ee70198241c84173a8c5ac28f3" +checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" dependencies = [ "heck", "proc-macro-error", @@ -310,24 +310,6 @@ dependencies = [ "unscanny", ] -[[package]] -name = "codegen_test" -version = "0.1.0" -dependencies = [ - "cornucopia_async", - "cornucopia_sync", - "eui48", - "futures", - "postgres", - "postgres-types", - "rust_decimal", - "serde", - "serde_json", - "time", - "tokio-postgres", - "uuid", -] - [[package]] name = "const-random" version = "0.1.15" @@ -355,7 +337,7 @@ name = "cornucopia" version = "0.9.0" dependencies = [ "chumsky", - "clap 4.0.19", + "clap 4.0.27", "codegen_template", "heck", "indexmap", @@ -462,9 +444,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.11" +version = "0.9.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" +checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" dependencies = [ "autocfg", "cfg-if", @@ -475,9 +457,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.12" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" dependencies = [ "cfg-if", ] @@ -559,9 +541,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer", "crypto-common", @@ -574,6 +556,27 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "eui48" version = "1.1.0" @@ -746,6 +749,15 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "hmac" version = "0.12.1" @@ -757,9 +769,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", "hashbrown", @@ -775,16 +787,25 @@ dependencies = [ ] [[package]] -name = "integration" -version = "0.1.0" +name = "io-lifetimes" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7d367024b3f3414d8e01f437f704f41a9f64ab36f9067fa73e526ad4c763c87" dependencies = [ - "clap 4.0.19", - "cornucopia", - "owo-colors", - "postgres", - "serde", - "tempfile", - "toml", + "libc", + "windows-sys", +] + +[[package]] +name = "is-terminal" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae5bc6e2eb41c9def29a3e0f1306382807764b9b53112030eff57435667352d" +dependencies = [ + "hermit-abi 0.2.6", + "io-lifetimes", + "rustix", + "windows-sys", ] [[package]] @@ -829,6 +850,12 @@ version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +[[package]] +name = "linux-raw-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f" + [[package]] name = "lock_api" version = "0.4.9" @@ -865,18 +892,18 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" -version = "0.6.5" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" dependencies = [ "autocfg", ] [[package]] name = "miette" -version = "5.4.1" +version = "5.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a24c4b4063c21e037dffb4de388ee85e400bff299803aba9513d9c52de8116b" +checksum = "4afd9b301defa984bbdbe112b4763e093ed191750a0d914a78c1106b2d0fe703" dependencies = [ "atty", "backtrace", @@ -894,9 +921,9 @@ dependencies = [ [[package]] name = "miette-derive" -version = "5.4.1" +version = "5.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "827d18edee5d43dc309eb0ac565f2b8e2fdc89b986b2d929e924a0f6e7f23835" +checksum = "97c2401ab7ac5282ca5c8b518a87635b1a93762b0b90b9990c509888eeccba29" dependencies = [ "proc-macro2", "quote", @@ -939,7 +966,7 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", ] @@ -966,9 +993,9 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "os_str_bytes" -version = "6.3.1" +version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" [[package]] name = "owo-colors" @@ -1218,11 +1245,10 @@ dependencies = [ [[package]] name = "rayon" -version = "1.5.3" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b" dependencies = [ - "autocfg", "crossbeam-deque", "either", "rayon-core", @@ -1230,9 +1256,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.9.3" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -1307,6 +1333,20 @@ version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" +[[package]] +name = "rustix" +version = "0.36.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b1fbb4dfc4eb1d390c02df47760bb19a84bb80b301ecc947ab5406394d8223e" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "ryu" version = "1.0.11" @@ -1350,9 +1390,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.87" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" dependencies = [ "itoa", "ryu", @@ -1510,6 +1550,47 @@ dependencies = [ "winapi", ] +[[package]] +name = "test_codegen" +version = "0.1.0" +dependencies = [ + "cornucopia_async", + "cornucopia_sync", + "eui48", + "futures", + "postgres", + "postgres-types", + "rust_decimal", + "serde", + "serde_json", + "time", + "tokio-postgres", + "uuid", +] + +[[package]] +name = "test_integration" +version = "0.1.0" +dependencies = [ + "clap 4.0.27", + "cornucopia", + "cornucopia_async", + "cornucopia_sync", + "eui48", + "futures", + "owo-colors", + "postgres", + "postgres-types", + "rust_decimal", + "serde", + "serde_json", + "tempfile", + "time", + "tokio-postgres", + "toml", + "uuid", +] + [[package]] name = "textwrap" version = "0.15.2" @@ -1609,9 +1690,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.21.2" +version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3" dependencies = [ "autocfg", "bytes", @@ -1762,9 +1843,9 @@ checksum = "e9df2af067a7953e9c3831320f35c1cc0600c30d44d9f7a12b01db1cd88d6b47" [[package]] name = "uuid" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" +checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" dependencies = [ "serde", ] diff --git a/Cargo.toml b/Cargo.toml index 1a087039..e3b158b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,8 @@ [workspace] -members = ["examples/*", "integration", "codegen_test", "benches", "crates/*"] +members = [ + "examples/*", + "test_integration", + "test_codegen", + "benches", + "crates/*", +] diff --git a/benches/codegen.rs b/benches/codegen.rs index c9578333..96ceb3a0 100644 --- a/benches/codegen.rs +++ b/benches/codegen.rs @@ -6,12 +6,12 @@ fn bench(c: &mut Criterion) { cornucopia::container::setup(false).unwrap(); let client = &mut cornucopia_conn().unwrap(); - cornucopia::load_schema(client, vec!["../codegen_test/schema.sql".into()]).unwrap(); + cornucopia::load_schema(client, vec!["../test_codegen/schema.sql".into()]).unwrap(); c.bench_function("codegen_sync", |b| { b.iter(|| { cornucopia::generate_live( client, - "../codegen_test/queries", + "../test_codegen/queries", None, CodegenSettings { is_async: false, @@ -25,7 +25,7 @@ fn bench(c: &mut Criterion) { b.iter(|| { cornucopia::generate_live( client, - "../codegen_test/queries", + "../test_codegen/queries", None, CodegenSettings { is_async: true, diff --git a/codegen_test/Cargo.toml b/test_codegen/Cargo.toml similarity index 98% rename from codegen_test/Cargo.toml rename to test_codegen/Cargo.toml index e3ab7c1c..83bbc1db 100644 --- a/codegen_test/Cargo.toml +++ b/test_codegen/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "codegen_test" +name = "test_codegen" version = "0.1.0" edition = "2021" diff --git a/codegen_test/README.md b/test_codegen/README.md similarity index 100% rename from codegen_test/README.md rename to test_codegen/README.md diff --git a/codegen_test/queries/copy.sql b/test_codegen/queries/copy.sql similarity index 100% rename from codegen_test/queries/copy.sql rename to test_codegen/queries/copy.sql diff --git a/codegen_test/queries/domain.sql b/test_codegen/queries/domain.sql similarity index 100% rename from codegen_test/queries/domain.sql rename to test_codegen/queries/domain.sql diff --git a/codegen_test/queries/named.sql b/test_codegen/queries/named.sql similarity index 100% rename from codegen_test/queries/named.sql rename to test_codegen/queries/named.sql diff --git a/codegen_test/queries/nullity.sql b/test_codegen/queries/nullity.sql similarity index 100% rename from codegen_test/queries/nullity.sql rename to test_codegen/queries/nullity.sql diff --git a/codegen_test/queries/params.sql b/test_codegen/queries/params.sql similarity index 100% rename from codegen_test/queries/params.sql rename to test_codegen/queries/params.sql diff --git a/codegen_test/queries/stress.sql b/test_codegen/queries/stress.sql similarity index 100% rename from codegen_test/queries/stress.sql rename to test_codegen/queries/stress.sql diff --git a/codegen_test/queries/syntax.sql b/test_codegen/queries/syntax.sql similarity index 100% rename from codegen_test/queries/syntax.sql rename to test_codegen/queries/syntax.sql diff --git a/codegen_test/schema.sql b/test_codegen/schema.sql similarity index 100% rename from codegen_test/schema.sql rename to test_codegen/schema.sql diff --git a/codegen_test/src/cornucopia_async.rs b/test_codegen/src/cornucopia_async.rs similarity index 100% rename from codegen_test/src/cornucopia_async.rs rename to test_codegen/src/cornucopia_async.rs diff --git a/codegen_test/src/cornucopia_sync.rs b/test_codegen/src/cornucopia_sync.rs similarity index 100% rename from codegen_test/src/cornucopia_sync.rs rename to test_codegen/src/cornucopia_sync.rs diff --git a/codegen_test/src/main.rs b/test_codegen/src/main.rs similarity index 100% rename from codegen_test/src/main.rs rename to test_codegen/src/main.rs diff --git a/integration/Cargo.toml b/test_integration/Cargo.toml similarity index 98% rename from integration/Cargo.toml rename to test_integration/Cargo.toml index cbd0d786..6765503e 100644 --- a/integration/Cargo.toml +++ b/test_integration/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "integration" +name = "test_integration" version = "0.1.0" edition = "2021" diff --git a/integration/README.md b/test_integration/README.md similarity index 95% rename from integration/README.md rename to test_integration/README.md index 3d850788..b04c6974 100644 --- a/integration/README.md +++ b/test_integration/README.md @@ -1,6 +1,6 @@ Integration testing entrypoint. -This acts like a testing harness. It manages the database required for testing, coordinates the execution of tests and reports the results of the test suite. Internally, it uses many of our workspace crates, notably `codegen_test`, but also `examples` and `benches`. +This acts like a testing harness. It manages the database required for testing, coordinates the execution of tests and reports the results of the test suite. Internally, it uses many of our workspace crates, notably `test_codegen`, but also `examples` and `benches`. Our integration testing not only checks that Cornucopia is able to generate the code, but it also tests that the right error messages are reported in case of user errors. It also runs the generated code to ensure its correctness. diff --git a/integration/fixtures/codegen/benchmarks.toml b/test_integration/fixtures/codegen/benchmarks.toml similarity index 100% rename from integration/fixtures/codegen/benchmarks.toml rename to test_integration/fixtures/codegen/benchmarks.toml diff --git a/integration/fixtures/codegen/examples.toml b/test_integration/fixtures/codegen/examples.toml similarity index 100% rename from integration/fixtures/codegen/examples.toml rename to test_integration/fixtures/codegen/examples.toml diff --git a/integration/fixtures/codegen/codegen_test.toml b/test_integration/fixtures/codegen/test_codegen.toml similarity index 70% rename from integration/fixtures/codegen/codegen_test.toml rename to test_integration/fixtures/codegen/test_codegen.toml index dd7af2e6..cf928505 100644 --- a/integration/fixtures/codegen/codegen_test.toml +++ b/test_integration/fixtures/codegen/test_codegen.toml @@ -1,13 +1,13 @@ [[codegen]] name = "Sync" -base_path = "codegen_test" +base_path = "test_codegen" destination = "src/cornucopia_sync.rs" derive_ser = true sync = true -run = "codegen_test" +run = "test_codegen" [[codegen]] name = "Async" -base_path = "codegen_test" +base_path = "test_codegen" destination = "src/cornucopia_async.rs" derive_ser = true diff --git a/integration/fixtures/errors/codegen.toml b/test_integration/fixtures/errors/codegen.toml similarity index 100% rename from integration/fixtures/errors/codegen.toml rename to test_integration/fixtures/errors/codegen.toml diff --git a/integration/fixtures/errors/prepare.toml b/test_integration/fixtures/errors/prepare.toml similarity index 100% rename from integration/fixtures/errors/prepare.toml rename to test_integration/fixtures/errors/prepare.toml diff --git a/integration/fixtures/errors/schema.toml b/test_integration/fixtures/errors/schema.toml similarity index 100% rename from integration/fixtures/errors/schema.toml rename to test_integration/fixtures/errors/schema.toml diff --git a/integration/fixtures/errors/validation.toml b/test_integration/fixtures/errors/validation.toml similarity index 100% rename from integration/fixtures/errors/validation.toml rename to test_integration/fixtures/errors/validation.toml diff --git a/integration/src/main.rs b/test_integration/src/main.rs similarity index 100% rename from integration/src/main.rs rename to test_integration/src/main.rs From 4f5aa5d26f9b011a9158ec15d86f5838ce915c66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Thu, 24 Nov 2022 18:23:33 -0500 Subject: [PATCH 16/31] Slightly simplify `run` feature in integration tests. --- .../fixtures/codegen/test_codegen.toml | 2 +- test_integration/src/main.rs | 30 +++++++------------ 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/test_integration/fixtures/codegen/test_codegen.toml b/test_integration/fixtures/codegen/test_codegen.toml index cf928505..3a56d174 100644 --- a/test_integration/fixtures/codegen/test_codegen.toml +++ b/test_integration/fixtures/codegen/test_codegen.toml @@ -4,7 +4,7 @@ base_path = "test_codegen" destination = "src/cornucopia_sync.rs" derive_ser = true sync = true -run = "test_codegen" +run = true [[codegen]] name = "Async" diff --git a/test_integration/src/main.rs b/test_integration/src/main.rs index 2f55f31d..0d07e574 100644 --- a/test_integration/src/main.rs +++ b/test_integration/src/main.rs @@ -53,14 +53,7 @@ struct CodegenTest<'a> { destination: Option<&'a str>, sync: Option, derive_ser: Option, - run: Option, -} - -#[derive(serde::Deserialize)] -#[serde(untagged)] -enum Run { - Bool(bool), - Path(String), + run: Option, } fn main() -> ExitCode { @@ -278,22 +271,21 @@ fn run_codegen_test( // If the newly generated file differs from // the currently checked in one, return an error. if old_codegen != formated_new_codegen { - Err("\"{destination}\" is outdated")?; + Err(format!("\"{destination}\" is outdated"))?; } } println!("(generate) {} {}", codegen_test.name, "OK".green()); - // Run code - let run = match codegen_test.run.unwrap_or(Run::Bool(false)) { - Run::Bool(bool) => bool, - Run::Path(path) => { - // Switch directory - std::env::set_current_dir(&original_pwd)?; - std::env::set_current_dir(&format!("../{}", path))?; - true - } + // Use the base path as run path if `run` `Some(true)`. + let run_path = match codegen_test.run { + Some(true) => Some(codegen_test.base_path), + _ => None, }; - if run { + if let Some(path) = run_path { + // Change current directory + std::env::set_current_dir(&original_pwd)?; + std::env::set_current_dir(&format!("../{}", path))?; + // Run let result = Command::new("cargo").arg("run").output()?; if result.status.success() { println!("(run) {} {}", codegen_test.name, "OK".green()); From 39aa5c09a5700097b18429509014775280115f83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Mon, 28 Nov 2022 22:54:45 -0500 Subject: [PATCH 17/31] Allow drop copy in our copy test. --- test_codegen/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/test_codegen/src/main.rs b/test_codegen/src/main.rs index 0913af8a..427a272e 100644 --- a/test_codegen/src/main.rs +++ b/test_codegen/src/main.rs @@ -258,6 +258,7 @@ pub fn test_named(client: &mut Client) { } // Test we correctly implement borrowed version and copy derive +#[allow(clippy::drop_copy)] pub fn test_copy(client: &mut Client) { // Test copy let copy_params = CopyComposite { From 81e03d6c9bacd119e495e4e600050ec9e91349df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Mon, 28 Nov 2022 23:55:52 -0500 Subject: [PATCH 18/31] clippy fixes --- benches/execution/cornucopia_benches.rs | 12 ++++-------- benches/execution/postgres_benches.rs | 17 +++++++---------- benches/execution/tokio_postgres_benches.rs | 16 ++++++---------- 3 files changed, 17 insertions(+), 28 deletions(-) diff --git a/benches/execution/cornucopia_benches.rs b/benches/execution/cornucopia_benches.rs index 79814d7f..84f73d88 100644 --- a/benches/execution/cornucopia_benches.rs +++ b/benches/execution/cornucopia_benches.rs @@ -47,15 +47,11 @@ pub fn bench_insert(b: &mut Bencher, client: &mut Client, size: usize) { let mut stmt = insert_user(); b.iter(|| { block_on(async { - let mut tx = client.transaction().await.unwrap(); + let tx = client.transaction().await.unwrap(); for x in 0..size { - stmt.bind( - &mut tx, - &format!("User {}", x).as_str(), - &Some("hair_color"), - ) - .await - .unwrap(); + stmt.bind(&tx, &format!("User {}", x).as_str(), &Some("hair_color")) + .await + .unwrap(); } tx.commit().await.unwrap(); }) diff --git a/benches/execution/postgres_benches.rs b/benches/execution/postgres_benches.rs index ea10dbfc..99606d90 100644 --- a/benches/execution/postgres_benches.rs +++ b/benches/execution/postgres_benches.rs @@ -64,16 +64,13 @@ pub fn bench_medium_complex_query(b: &mut Bencher, client: &mut Client) { name: row.get(1), hair_color: row.get(2), }; - let post = if let Some(id) = row.get(3) { - Some(Post { - id, - user_id: row.get(4), - title: row.get(5), - body: row.get(6), - }) - } else { - None - }; + let post = row.get::<_, Option>(3).map(|id| Post { + id, + user_id: row.get(4), + title: row.get(5), + body: row.get(6), + }); + Ok((user, post)) }) .collect::>() diff --git a/benches/execution/tokio_postgres_benches.rs b/benches/execution/tokio_postgres_benches.rs index ba7b3dbb..866302a1 100644 --- a/benches/execution/tokio_postgres_benches.rs +++ b/benches/execution/tokio_postgres_benches.rs @@ -80,16 +80,12 @@ pub fn bench_medium_complex_query(b: &mut Bencher, client: &mut Client) { name: row.get(1), hair_color: row.get(2), }, - if let Some(id) = row.get(3) { - Some(Post { - id, - user_id: row.get(4), - title: row.get(5), - body: row.get(6), - }) - } else { - None - }, + row.get::<_, Option>(3).map(|id| Post { + id, + user_id: row.get(4), + title: row.get(5), + body: row.get(6), + }), ) }) }) From 5d4c244d5c8d6c19aec9a6c54fd97a85e7c9c9cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 30 Nov 2022 19:04:33 -0500 Subject: [PATCH 19/31] Improve `test_integration` internal organization. --- .../fixtures/codegen/benchmarks.toml | 4 +- .../fixtures/codegen/examples.toml | 6 +- .../fixtures/codegen/test_codegen.toml | 4 +- test_integration/src/codegen.rs | 93 ++++++ test_integration/src/errors.rs | 96 ++++++ test_integration/src/fixtures.rs | 86 ++++++ test_integration/src/main.rs | 281 ++---------------- test_integration/src/utils.rs | 35 +++ 8 files changed, 334 insertions(+), 271 deletions(-) create mode 100644 test_integration/src/codegen.rs create mode 100644 test_integration/src/errors.rs create mode 100644 test_integration/src/fixtures.rs create mode 100644 test_integration/src/utils.rs diff --git a/test_integration/fixtures/codegen/benchmarks.toml b/test_integration/fixtures/codegen/benchmarks.toml index 829ca3b6..e88023d4 100644 --- a/test_integration/fixtures/codegen/benchmarks.toml +++ b/test_integration/fixtures/codegen/benchmarks.toml @@ -1,10 +1,10 @@ -[[codegen]] +[[test]] name = "Sync" base_path = "benches/execution/cornucopia_benches" destination = "generated_sync.rs" sync = true -[[codegen]] +[[test]] name = "Async" base_path = "benches/execution/cornucopia_benches" destination = "generated_async.rs" diff --git a/test_integration/fixtures/codegen/examples.toml b/test_integration/fixtures/codegen/examples.toml index 1f1d538d..da1972b7 100644 --- a/test_integration/fixtures/codegen/examples.toml +++ b/test_integration/fixtures/codegen/examples.toml @@ -1,15 +1,15 @@ -[[codegen]] +[[test]] name = "Auto build" base_path = "examples/auto_build" run = true -[[codegen]] +[[test]] name = "Basic sync" base_path = "examples/basic_sync" sync = true run = true -[[codegen]] +[[test]] name = "Basic async" base_path = "examples/basic_async" run = true diff --git a/test_integration/fixtures/codegen/test_codegen.toml b/test_integration/fixtures/codegen/test_codegen.toml index 3a56d174..48f502b6 100644 --- a/test_integration/fixtures/codegen/test_codegen.toml +++ b/test_integration/fixtures/codegen/test_codegen.toml @@ -1,4 +1,4 @@ -[[codegen]] +[[test]] name = "Sync" base_path = "test_codegen" destination = "src/cornucopia_sync.rs" @@ -6,7 +6,7 @@ derive_ser = true sync = true run = true -[[codegen]] +[[test]] name = "Async" base_path = "test_codegen" destination = "src/cornucopia_async.rs" diff --git a/test_integration/src/codegen.rs b/test_integration/src/codegen.rs new file mode 100644 index 00000000..5a937e5e --- /dev/null +++ b/test_integration/src/codegen.rs @@ -0,0 +1,93 @@ +use crate::{ + fixtures::{CodegenTest, TestSuite}, + utils::{reset_db, rustfmt_file, rustfmt_string}, +}; + +use cornucopia::{CodegenSettings, Error}; +use owo_colors::OwoColorize; +use std::{env::set_current_dir, process::Command}; + +// Run codegen test, return true if all test are successful +pub(crate) fn run_codegen_test( + client: &mut postgres::Client, + apply: bool, +) -> Result> { + let mut successful = true; + let original_pwd = std::env::current_dir()?; + let fixture_path = "fixtures/codegen"; + + let test_suites = TestSuite::::read(fixture_path); + for suite in test_suites { + println!("{}", format!("[codegen] {}", suite.name).magenta()); + for test in suite.tests { + set_current_dir(format!("../{}", test.base_path))?; + + // Load schema + reset_db(client)?; + cornucopia::load_schema(client, vec!["schema.sql".to_string()])?; + + // If `--apply`, then the code will be regenerated. + // Otherwise, it is only checked. + if apply { + // Generate + cornucopia::generate_live( + client, + test.queries_path.to_str().unwrap(), // TODO: Update this once our API accepts paths + Some(test.destination.to_str().unwrap()), // TODO: Update this once our API accepts paths + CodegenSettings::from(&test), + ) + .map_err(Error::report)?; + // Format the generated file + rustfmt_file(&test.destination); + } else { + // Get currently checked-in generate file + let old_codegen = std::fs::read_to_string(&test.destination).unwrap(); + // Generate new file + let new_codegen = cornucopia::generate_live( + client, + test.queries_path.to_str().unwrap(), // TODO: Update this once our API accepts paths + None, + CodegenSettings::from(&test), + ) + .map_err(Error::report)?; + // Format the generated code string by piping to rustfmt + let new_codegen_formatted = rustfmt_string(&new_codegen); + + // If the newly generated file differs from + // the currently checked in one, return an error. + if old_codegen != new_codegen_formatted { + Err(format!( + "\"{}\" is outdated", + test.destination.to_str().unwrap() + ))?; + } + } + println!("(generate) {} {}", test.name, "OK".green()); + + if test.run { + // Change current directory + std::env::set_current_dir(&original_pwd)?; + std::env::set_current_dir(&format!("../{}", test.base_path))?; + // Run + let result = Command::new("cargo").arg("run").output()?; + if result.status.success() { + println!("(run) {} {}", test.name, "OK".green()); + } else { + successful = false; + println!( + " {}\n{}", + "ERR".red(), + String::from_utf8_lossy(&result.stderr) + .as_ref() + .bright_black() + ); + } + } + + // Move back to original directory + std::env::set_current_dir(&original_pwd)?; + } + } + + Ok(successful) +} diff --git a/test_integration/src/errors.rs b/test_integration/src/errors.rs new file mode 100644 index 00000000..752e7831 --- /dev/null +++ b/test_integration/src/errors.rs @@ -0,0 +1,96 @@ +use cornucopia::{CodegenSettings, Error}; +use owo_colors::OwoColorize; + +use crate::{ + fixtures::{ErrorTest, TestSuite}, + utils::reset_db, +}; + +/// Run errors test, return true if all test are successful +pub(crate) fn run_errors_test( + client: &mut postgres::Client, + apply: bool, +) -> Result> { + let mut successful = true; + let original_pwd = std::env::current_dir().unwrap(); + let test_suites = TestSuite::::read("fixtures/errors"); + + for mut suite in test_suites { + println!("{} {}", "[error]".magenta(), suite.name.magenta()); + for test in suite.tests.iter_mut() { + // Generate file tree path + let temp_dir = tempfile::tempdir()?; + + // We need to change current dir for error path to always be the same + std::env::set_current_dir(&temp_dir)?; + + // Generate schema + std::fs::write( + "schema.sql", + [ + "CREATE TABLE author (id SERIAL, name TEXT);\n", + &test.schema, + ] + .concat(), + )?; + + // Generate queries files + std::fs::create_dir("queries")?; + std::fs::write("queries/test.sql", &test.query)?; + + // Reset db + reset_db(client)?; + + // Run codegen + let result = cornucopia::load_schema(client, vec!["schema.sql".into()]) + .map_err(Error::from) + .and_then(|_| { + cornucopia::generate_live( + client, + "queries", + None, + CodegenSettings::from(&*test), + ) + }); + + let err = result.unwrap_err().report(); + let err_trimmed = err.trim(); + if err_trimmed == test.error.trim() { + println!("{} {}", test.name, "OK".green()); + } else { + let got_msg = if apply { + "Apply:".bright_black() + } else { + "Got:".bright_black() + }; + let expected_msg = if apply { + "Previous:".bright_black() + } else { + "Expected:".bright_black() + }; + successful = false; + println!( + "{} {}\n{}\n{}\n{}\n{}\n", + test.name, + "ERR".red(), + expected_msg, + test.error, + got_msg, + err, + ); + } + if apply { + test.error = err_trimmed.into(); + } + std::env::set_current_dir(&original_pwd)?; + } + + if apply { + // Format test descriptor and update error message if needed + let edited = toml::to_string_pretty(&suite.tests)?; + std::fs::write(suite.path, edited)?; + } + } + + Ok(successful) +} diff --git a/test_integration/src/fixtures.rs b/test_integration/src/fixtures.rs new file mode 100644 index 00000000..aa7b91f6 --- /dev/null +++ b/test_integration/src/fixtures.rs @@ -0,0 +1,86 @@ +use std::path::{Path, PathBuf}; + +use cornucopia::CodegenSettings; +use serde::{de::DeserializeOwned, Deserialize}; + +#[derive(Deserialize)] +struct TestSuiteDeserializer { + test: Vec, +} + +pub struct TestSuite { + pub(crate) name: String, + pub(crate) path: PathBuf, + pub(crate) tests: Vec, +} + +impl TestSuite { + pub(crate) fn read>(fixtures_path: P) -> impl Iterator> { + std::fs::read_dir(fixtures_path).unwrap().map(|file| { + let file = file.unwrap(); + let name = file.file_name().to_string_lossy().to_string(); + let path = file.path(); + let content = std::fs::read_to_string(&path).unwrap(); + let tests: TestSuiteDeserializer = toml::from_str(&content).unwrap(); + TestSuite { + name, + tests: tests.test, + path, + } + }) + } +} + +/// Codegen test case +#[derive(Debug, serde::Deserialize)] +pub(crate) struct CodegenTest { + pub(crate) name: String, + pub(crate) base_path: String, + #[serde(default = "default_queries_path")] + pub(crate) queries_path: PathBuf, + #[serde(default = "default_destination_path")] + pub(crate) destination: PathBuf, + #[serde(default)] + pub(crate) sync: bool, + #[serde(default)] + pub(crate) derive_ser: bool, + #[serde(default)] + pub(crate) run: bool, +} + +fn default_queries_path() -> PathBuf { + PathBuf::from("queries") +} + +fn default_destination_path() -> PathBuf { + PathBuf::from("src/cornucopia.rs") +} + +impl From<&CodegenTest> for CodegenSettings { + fn from(codegen_test: &CodegenTest) -> Self { + Self { + is_async: !codegen_test.sync, + derive_ser: codegen_test.derive_ser, + } + } +} + +/// Error test case +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub(crate) struct ErrorTest { + pub(crate) name: String, + #[serde(default)] + pub(crate) query: String, + #[serde(default)] + pub(crate) schema: String, + pub(crate) error: String, +} + +impl From<&ErrorTest> for CodegenSettings { + fn from(_error_test: &ErrorTest) -> Self { + Self { + is_async: false, + derive_ser: false, + } + } +} diff --git a/test_integration/src/main.rs b/test_integration/src/main.rs index 0d07e574..0758ac8f 100644 --- a/test_integration/src/main.rs +++ b/test_integration/src/main.rs @@ -1,15 +1,15 @@ -use std::{ - borrow::Cow, - fmt::Display, - io::Write, - process::{Command, ExitCode, Stdio}, -}; +use std::{fmt::Display, process::ExitCode}; +use crate::{codegen::run_codegen_test, errors::run_errors_test}; use clap::Parser; -use cornucopia::{container, CodegenSettings, Error}; -use owo_colors::OwoColorize; +use cornucopia::container; -/// Start cornucopia test runner +mod codegen; +mod errors; +mod fixtures; +mod utils; + +/// Integration test CLI arguments #[derive(Parser, Debug)] #[clap(version)] struct Args { @@ -24,51 +24,10 @@ struct Args { podman: bool, } -#[derive(serde::Deserialize, serde::Serialize)] -struct ErrorTestSuite<'a> { - #[serde(borrow)] - test: Vec>, -} - -#[derive(serde::Deserialize, serde::Serialize)] -struct ErrorTest<'a> { - name: &'a str, - query: Option<&'a str>, - schema: Option<&'a str>, - query_name: Option<&'a str>, - error: Cow<'a, str>, -} - -#[derive(serde::Deserialize)] -struct CodegenTestSuite<'a> { - #[serde(borrow)] - codegen: Vec>, -} - -#[derive(serde::Deserialize)] -struct CodegenTest<'a> { - name: &'a str, - base_path: &'a str, - queries: Option<&'a str>, - destination: Option<&'a str>, - sync: Option, - derive_ser: Option, - run: Option, -} - -fn main() -> ExitCode { - let args = Args::parse(); - if test(args) { - ExitCode::SUCCESS - } else { - ExitCode::FAILURE - } -} - /// Print error to stderr fn display(result: Result) -> Result { if let Err(err) = &result { - eprintln!("{}", err); + eprintln!("{err}"); } result } @@ -93,220 +52,14 @@ fn test( successful.unwrap() } -/// Reset the current database -fn reset_db(client: &mut postgres::Client) -> Result<(), postgres::Error> { - client.batch_execute("DROP SCHEMA public CASCADE;CREATE SCHEMA public;") -} - -// Common schema to all error tests -const SCHEMA_BASE: &str = "CREATE TABLE author (id SERIAL, name TEXT);\n"; - -/// Run errors test, return true if all test are successful -fn run_errors_test( - client: &mut postgres::Client, - apply: bool, -) -> Result> { - let mut successful = true; - - let got_msg = if apply { - "Apply:".bright_black() - } else { - "Got:".bright_black() - }; - let expected_msg = if apply { - "Previous:".bright_black() +/// Main entry point +fn main() -> ExitCode { + let args = Args::parse(); + if test(args) { + ExitCode::SUCCESS } else { - "Expected:".bright_black() - }; - - let original_pwd = std::env::current_dir().unwrap(); - for file in std::fs::read_dir("fixtures/errors")? { - let file = file?; - let name = file.file_name().to_string_lossy().to_string(); - let content = std::fs::read_to_string(file.path())?; - let mut suite: ErrorTestSuite = toml::from_str(&content)?; - - println!("{} {}", "[error]".magenta(), name.magenta()); - for test in &mut suite.test { - // Generate file tree path - let temp_dir = tempfile::tempdir()?; - - // Reset db - reset_db(client)?; - - // We need to change current dir for error path to always be the same - std::env::set_current_dir(&temp_dir)?; - - // Generate schema - std::fs::write( - "schema.sql", - [SCHEMA_BASE, test.schema.unwrap_or_default()].concat(), - )?; - - // Generate queries files - std::fs::create_dir("queries")?; - let name = test.query_name.unwrap_or("test.sql"); - std::fs::write(&format!("queries/{name}"), test.query.unwrap_or_default())?; - - // Run codegen - let result: Result<(), cornucopia::Error> = (|| { - cornucopia::load_schema(client, vec!["schema.sql".into()])?; - cornucopia::generate_live( - client, - "queries", - None, - CodegenSettings { - is_async: false, - derive_ser: false, - }, - )?; - Ok(()) - })(); - - let err = result.err().map(Error::report).unwrap_or_default(); - if err.trim() == test.error.trim() { - println!("{} {}", test.name, "OK".green()); - } else { - successful = false; - println!( - "{} {}\n{}\n{}\n{}\n{}", - test.name, - "ERR".red(), - expected_msg, - test.error, - got_msg, - err, - ); - } - if apply { - test.error = Cow::Owned(err.trim().to_string()); - } - std::env::set_current_dir(&original_pwd)?; - } - - if apply { - // Format test descriptor and update error message if needed - let edited = toml::to_string_pretty(&suite)?; - std::fs::write(file.path(), edited)?; - } - } - Ok(successful) -} - -// Run codegen test, return true if all test are successful -fn run_codegen_test( - client: &mut postgres::Client, - apply: bool, -) -> Result> { - let mut successful = true; - let original_pwd = std::env::current_dir()?; - - for file in std::fs::read_dir("fixtures/codegen")? { - let file = file?; - let name = file.file_name().to_string_lossy().to_string(); - let content = std::fs::read_to_string(file.path())?; - println!("{} {}", "[codegen]".magenta(), name.magenta()); - - let suite: CodegenTestSuite = toml::from_str(&content)?; - - for codegen_test in suite.codegen { - std::env::set_current_dir(format!("../{}", codegen_test.base_path))?; - let queries_path = codegen_test.queries.unwrap_or("queries"); - let schema_path = "schema.sql"; - let destination = codegen_test.destination.unwrap_or("src/cornucopia.rs"); - let is_async = !codegen_test.sync.unwrap_or(false); - let derive_ser = codegen_test.derive_ser.unwrap_or(false); - - // Load schema - reset_db(client)?; - cornucopia::load_schema(client, vec![schema_path.to_string()])?; - - // If `--apply`, then the code will be regenerated. - // Otherwise, it is only checked. - if apply { - // Generate - cornucopia::generate_live( - client, - queries_path, - Some(destination), - CodegenSettings { - is_async, - derive_ser, - }, - ) - .map_err(Error::report)?; - // Format the generated file - Command::new("rustfmt") - .args(["--edition", "2021"]) - .arg(destination) - .output()?; - } else { - // Get currently checked-in generate file - let old_codegen = std::fs::read_to_string(destination).unwrap_or_default(); - // Generate new file - let new_codegen = cornucopia::generate_live( - client, - queries_path, - None, - CodegenSettings { - is_async, - derive_ser, - }, - ) - .map_err(Error::report)?; - // Format the generated code string by piping to rustfmt - let mut rustfmt = Command::new("rustfmt") - .args(["--edition", "2021"]) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn()?; - rustfmt - .stdin - .as_mut() - .unwrap() - .write_all(new_codegen.as_bytes())?; - let formated_new_codegen = - String::from_utf8(rustfmt.wait_with_output()?.stdout).unwrap(); - - // If the newly generated file differs from - // the currently checked in one, return an error. - if old_codegen != formated_new_codegen { - Err(format!("\"{destination}\" is outdated"))?; - } - } - println!("(generate) {} {}", codegen_test.name, "OK".green()); - - // Use the base path as run path if `run` `Some(true)`. - let run_path = match codegen_test.run { - Some(true) => Some(codegen_test.base_path), - _ => None, - }; - if let Some(path) = run_path { - // Change current directory - std::env::set_current_dir(&original_pwd)?; - std::env::set_current_dir(&format!("../{}", path))?; - // Run - let result = Command::new("cargo").arg("run").output()?; - if result.status.success() { - println!("(run) {} {}", codegen_test.name, "OK".green()); - } else { - successful = false; - println!( - " {}\n{}", - "ERR".red(), - String::from_utf8_lossy(&result.stderr) - .as_ref() - .bright_black() - ); - } - } - - // Move back to original directory - std::env::set_current_dir(&original_pwd)?; - } + ExitCode::FAILURE } - - Ok(successful) } #[cfg(test)] @@ -318,7 +71,7 @@ mod test { assert!(test(crate::Args { apply_errors: false, apply_codegen: false, - podman: false + podman: true })) } } diff --git a/test_integration/src/utils.rs b/test_integration/src/utils.rs new file mode 100644 index 00000000..1ba234b1 --- /dev/null +++ b/test_integration/src/utils.rs @@ -0,0 +1,35 @@ +use std::{ + io::Write, + path::Path, + process::{Command, Stdio}, +}; + +/// Reset the current database +pub(crate) fn reset_db(client: &mut postgres::Client) -> Result<(), postgres::Error> { + client.batch_execute("DROP SCHEMA public CASCADE;CREATE SCHEMA public;") +} + +pub(crate) fn rustfmt_file(path: &Path) { + Command::new("rustfmt") + .args(["--edition", "2021"]) + .arg(path) + .output() + .unwrap(); +} + +pub(crate) fn rustfmt_string(string: &str) -> String { + // Format the generated code string by piping to rustfmt + let mut rustfmt = Command::new("rustfmt") + .args(["--edition", "2021"]) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .unwrap(); + rustfmt + .stdin + .as_mut() + .unwrap() + .write_all(string.as_bytes()) + .unwrap(); + String::from_utf8(rustfmt.wait_with_output().unwrap().stdout).unwrap() +} From 7505a852bc837d3eb7af78d432a969bec36a52b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 30 Nov 2022 19:11:12 -0500 Subject: [PATCH 20/31] Upgrade dependencies. --- Cargo.lock | 201 +++++++++++++++++++++++++++----- benches/Cargo.toml | 2 +- crates/client_async/Cargo.toml | 2 +- crates/client_core/Cargo.toml | 4 +- crates/cornucopia/Cargo.toml | 6 +- examples/auto_build/Cargo.toml | 2 +- examples/basic_async/Cargo.toml | 2 +- test_codegen/Cargo.toml | 8 +- test_integration/Cargo.toml | 10 +- 9 files changed, 193 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7b58f3a3..a381bd41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,9 +60,9 @@ checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] name = "async-trait" -version = "0.1.58" +version = "0.1.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c" +checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364" dependencies = [ "proc-macro2", "quote", @@ -172,12 +172,78 @@ dependencies = [ "generic-array", ] +[[package]] +name = "borsh" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" +dependencies = [ + "borsh-derive", + "hashbrown 0.11.2", +] + +[[package]] +name = "borsh-derive" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate", + "proc-macro2", + "syn", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "bumpalo" version = "3.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +[[package]] +name = "bytecheck" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d11cac2c12b5adc6570dad2ee1b87eff4955dac476fe12d81e5fdd352e52406f" +dependencies = [ + "bytecheck_derive", + "ptr_meta", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e576ebe98e605500b3c8041bb888e966653577172df6dd97398714eb30b9bf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -258,9 +324,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.0.27" +version = "4.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0acbd8d28a0a60d7108d7ae850af6ba34cf2d1257fc646980e5f97ce14275966" +checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d" dependencies = [ "bitflags", "clap_derive", @@ -337,7 +403,7 @@ name = "cornucopia" version = "0.9.0" dependencies = [ "chumsky", - "clap 4.0.27", + "clap 4.0.29", "codegen_template", "heck", "indexmap", @@ -725,6 +791,15 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash 0.7.6", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -774,7 +849,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", ] [[package]] @@ -788,9 +863,9 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7d367024b3f3414d8e01f437f704f41a9f64ab36f9067fa73e526ad4c763c87" +checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" dependencies = [ "libc", "windows-sys", @@ -798,9 +873,9 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae5bc6e2eb41c9def29a3e0f1306382807764b9b53112030eff57435667352d" +checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330" dependencies = [ "hermit-abi 0.2.6", "io-lifetimes", @@ -1015,9 +1090,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" +checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" dependencies = [ "cfg-if", "libc", @@ -1165,6 +1240,15 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -1204,6 +1288,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "quote" version = "1.0.21" @@ -1301,24 +1405,63 @@ dependencies = [ "winapi", ] +[[package]] +name = "rend" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79af64b4b6362ffba04eef3a4e10829718a4896dac19daa741851c86781edf95" +dependencies = [ + "bytecheck", +] + [[package]] name = "retain_mut" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0" +[[package]] +name = "rkyv" +version = "0.7.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cec2b3485b07d96ddfd3134767b8a447b45ea4eb91448d0a35180ec0ffd5ed15" +dependencies = [ + "bytecheck", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eaedadc88b53e36dd32d940ed21ae4d850d5916f2581526921f553a72ac34c4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "rust_decimal" -version = "1.26.1" +version = "1.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee9164faf726e4f3ece4978b25ca877ddc6802fa77f38cdccb32c7f805ecd70c" +checksum = "33c321ee4e17d2b7abe12b5d20c1231db708dd36185c8a21e9de5fed6da4dbe9" dependencies = [ "arrayvec", + "borsh", + "bytecheck", "byteorder", "bytes", "num-traits", "postgres", + "rand", + "rkyv", "serde", + "serde_json", ] [[package]] @@ -1335,9 +1478,9 @@ checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" [[package]] name = "rustix" -version = "0.36.3" +version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b1fbb4dfc4eb1d390c02df47760bb19a84bb80b301ecc947ab5406394d8223e" +checksum = "cb93e85278e08bb5788653183213d3a60fc242b10cb9be96586f5a73dcb67c23" dependencies = [ "bitflags", "errno", @@ -1368,20 +1511,26 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "serde" -version = "1.0.147" +version = "1.0.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.147" +version = "1.0.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c" dependencies = [ "proc-macro2", "quote", @@ -1508,9 +1657,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +checksum = "4ae548ec36cf198c0ef7710d3c230987c2d6d7bd98ad6edc0274462724c585ce" dependencies = [ "proc-macro2", "quote", @@ -1572,7 +1721,7 @@ dependencies = [ name = "test_integration" version = "0.1.0" dependencies = [ - "clap 4.0.27", + "clap 4.0.29", "cornucopia", "cornucopia_async", "cornucopia_sync", @@ -1710,9 +1859,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.8.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ "proc-macro2", "quote", @@ -1810,7 +1959,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5faade31a542b8b35855fff6e8def199853b2da8da256da52f52f1316ee3137" dependencies = [ - "hashbrown", + "hashbrown 0.12.3", "regex", ] diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 0edcde8f..c25219ea 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -15,7 +15,7 @@ cornucopia_async = { path = "../crates/client_async" } criterion = { version = "0.4.0", features = ["html_reports"] } # async -tokio = { version = "1.21.2", features = ["full"] } +tokio = { version = "1.22.0", features = ["full"] } futures = "0.3.25" # rust-postgres interaction diff --git a/crates/client_async/Cargo.toml b/crates/client_async/Cargo.toml index 2364c01f..360b32c2 100644 --- a/crates/client_async/Cargo.toml +++ b/crates/client_async/Cargo.toml @@ -22,7 +22,7 @@ with-serde_json-1 = ["cornucopia_client_core/with-serde_json-1"] cornucopia_client_core = { path = "../client_core", version = "0.4.0" } # async -async-trait = "0.1.58" +async-trait = "0.1.59" # rust-postgres interaction tokio-postgres = "0.7.7" diff --git a/crates/client_core/Cargo.toml b/crates/client_core/Cargo.toml index 6e894c50..ccbe455a 100644 --- a/crates/client_core/Cargo.toml +++ b/crates/client_core/Cargo.toml @@ -25,6 +25,6 @@ fallible-iterator = "0.2.0" # json ## This crate implements the "ergonomic paramters" for ## `serde_json::Value` and `serde_json::raw::RawValue`. -serde_json = { version = "1.0.87", optional = true } +serde_json = { version = "1.0.89", optional = true } ## Used for `postgres_types::Json` `Serialize` trait bounds -serde = { version = "1.0.147", optional = true } +serde = { version = "1.0.148", optional = true } diff --git a/crates/cornucopia/Cargo.toml b/crates/cornucopia/Cargo.toml index 3a8310c7..a772fbd3 100644 --- a/crates/cornucopia/Cargo.toml +++ b/crates/cornucopia/Cargo.toml @@ -20,16 +20,16 @@ postgres-types = "0.2.4" # Error handling and reporting thiserror = "1.0.37" -miette = { version = "5.4.1", features = ["fancy"] } +miette = { version = "5.5.0", features = ["fancy"] } # Parser chumsky = "0.8.0" # CLI handling -clap = { version = "4.0.19", features = ["derive"] } +clap = { version = "4.0.29", features = ["derive"] } # Word case heck = "0.4.0" # Order-preserving map to work around borrowing issues -indexmap = "1.9.1" +indexmap = "1.9.2" diff --git a/examples/auto_build/Cargo.toml b/examples/auto_build/Cargo.toml index 850b5fe8..e73f629e 100644 --- a/examples/auto_build/Cargo.toml +++ b/examples/auto_build/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] # Async -tokio = { version = "1.21.2", features = ["full"] } +tokio = { version = "1.22.0", features = ["full"] } futures = "0.3.25" # Postgres interaction diff --git a/examples/basic_async/Cargo.toml b/examples/basic_async/Cargo.toml index 3fd6fa75..347cb49e 100644 --- a/examples/basic_async/Cargo.toml +++ b/examples/basic_async/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" [dependencies] # Async -tokio = { version = "1.21.2", features = ["full"] } +tokio = { version = "1.22.0", features = ["full"] } futures = "0.3.25" # Postgres interaction diff --git a/test_codegen/Cargo.toml b/test_codegen/Cargo.toml index 83bbc1db..0ffceba2 100644 --- a/test_codegen/Cargo.toml +++ b/test_codegen/Cargo.toml @@ -33,11 +33,11 @@ tokio-postgres = { version = "0.7.7", features = [ postgres-types = { version = "0.2.4", features = ["derive"] } # serde -serde = { version = "1.0.147", features = ["derive"] } +serde = { version = "1.0.148", features = ["derive"] } # extra types -serde_json = { version = "1.0.87", features = ["raw_value"] } +serde_json = { version = "1.0.89", features = ["raw_value"] } time = { version = "0.3.17", features = ["parsing", "serde"] } -uuid = { version = "1.2.1", features = ["serde"] } +uuid = { version = "1.2.2", features = ["serde"] } eui48 = { version = "1.1.0", features = ["serde"] } -rust_decimal = { version = "1.26.1", features = ["db-postgres"] } +rust_decimal = { version = "1.27.0", features = ["db-postgres"] } diff --git a/test_integration/Cargo.toml b/test_integration/Cargo.toml index 6765503e..ed2cbc31 100644 --- a/test_integration/Cargo.toml +++ b/test_integration/Cargo.toml @@ -22,7 +22,7 @@ tempfile = "3.3.0" owo-colors = "3.5.0" # CLI handling -clap = { version = "4.0.19", features = ["derive"] } +clap = { version = "4.0.29", features = ["derive"] } # async futures = "0.3.25" @@ -44,13 +44,13 @@ postgres-types = { version = "0.2.4", features = ["derive"] } # serde ## Both for test helpers ser/de and codegen `Serialize` derives -serde = { version = "1.0.147", features = ["derive"] } +serde = { version = "1.0.148", features = ["derive"] } ## Read fixture files toml = "0.5.9" # extra types -serde_json = { version = "1.0.87", features = ["raw_value"] } +serde_json = { version = "1.0.89", features = ["raw_value"] } time = { version = "0.3.17", features = ["parsing", "serde"] } -uuid = { version = "1.2.1", features = ["serde"] } +uuid = { version = "1.2.2", features = ["serde"] } eui48 = { version = "1.1.0", features = ["serde"] } -rust_decimal = { version = "1.26.1", features = ["db-postgres"] } +rust_decimal = { version = "1.27.0", features = ["db-postgres"] } From 1b6f01c9154e28944622568eaf809582fbdb6f43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Tue, 7 Feb 2023 21:09:25 -0500 Subject: [PATCH 21/31] Upgrade toml. --- test_integration/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_integration/Cargo.toml b/test_integration/Cargo.toml index ed2cbc31..8bf33c07 100644 --- a/test_integration/Cargo.toml +++ b/test_integration/Cargo.toml @@ -46,7 +46,7 @@ postgres-types = { version = "0.2.4", features = ["derive"] } ## Both for test helpers ser/de and codegen `Serialize` derives serde = { version = "1.0.148", features = ["derive"] } ## Read fixture files -toml = "0.5.9" +toml = "0.7.2" # extra types serde_json = { version = "1.0.89", features = ["raw_value"] } From eea13d8a5f82536f2ae9eddc5791f0185ae7aad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Tue, 7 Feb 2023 21:15:33 -0500 Subject: [PATCH 22/31] Docker should be the default for integration tests. --- test_integration/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_integration/src/main.rs b/test_integration/src/main.rs index 0758ac8f..cf21be31 100644 --- a/test_integration/src/main.rs +++ b/test_integration/src/main.rs @@ -71,7 +71,7 @@ mod test { assert!(test(crate::Args { apply_errors: false, apply_codegen: false, - podman: true + podman: false })) } } From 8ed8bfd4d85c53ae2dd1391221fd0d57a3f476d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 8 Feb 2023 00:31:28 -0500 Subject: [PATCH 23/31] Adapt `test_integration` to dual sync-async support. --- test_integration/src/fixtures.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test_integration/src/fixtures.rs b/test_integration/src/fixtures.rs index dceff525..d1d315f3 100644 --- a/test_integration/src/fixtures.rs +++ b/test_integration/src/fixtures.rs @@ -43,7 +43,7 @@ pub(crate) struct CodegenTest { #[serde(default)] pub(crate) sync: bool, #[serde(default)] - pub(crate) _async: bool, + pub(crate) r#async: bool, #[serde(default)] pub(crate) derive_ser: bool, #[serde(default)] @@ -61,8 +61,8 @@ fn default_destination_path() -> PathBuf { impl From<&CodegenTest> for CodegenSettings { fn from(codegen_test: &CodegenTest) -> Self { Self { - gen_async: codegen_test._async || !codegen_test.sync, - gen_sync: !codegen_test.sync, + gen_async: codegen_test.r#async || !codegen_test.sync, + gen_sync: codegen_test.sync, derive_ser: codegen_test.derive_ser, } } From 6db4fea1d51e5cfa61b04e400d5aab52631c87cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 8 Feb 2023 00:31:38 -0500 Subject: [PATCH 24/31] Fix merge typo. --- test_integration/fixtures/codegen/test_codegen.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_integration/fixtures/codegen/test_codegen.toml b/test_integration/fixtures/codegen/test_codegen.toml index 607524c2..9544709b 100644 --- a/test_integration/fixtures/codegen/test_codegen.toml +++ b/test_integration/fixtures/codegen/test_codegen.toml @@ -5,4 +5,4 @@ destination = "src/cornucopia.rs" sync = true async = true derive_ser = true -run = "codegen_test" +run = true From 2ec1ee9f5d9b3f3e4211a04027691aa926e5be97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 8 Feb 2023 00:31:59 -0500 Subject: [PATCH 25/31] Minor aesthetic change. --- test_integration/fixtures/codegen/examples.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_integration/fixtures/codegen/examples.toml b/test_integration/fixtures/codegen/examples.toml index f588d80e..aed93e25 100644 --- a/test_integration/fixtures/codegen/examples.toml +++ b/test_integration/fixtures/codegen/examples.toml @@ -13,5 +13,5 @@ run = true [[test]] name = "Basic async" base_path = "examples/basic_async" -run = true async = true +run = true From f9005a7348151cc79f6cefbeec1605b6db3cb835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 8 Feb 2023 03:28:36 -0500 Subject: [PATCH 26/31] Fix integration test. --- test_integration/src/errors.rs | 18 ++++++++++-------- test_integration/src/fixtures.rs | 31 +++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/test_integration/src/errors.rs b/test_integration/src/errors.rs index 34e901a4..d5531a1a 100644 --- a/test_integration/src/errors.rs +++ b/test_integration/src/errors.rs @@ -18,6 +18,9 @@ pub(crate) fn run_errors_test( for mut suite in test_suites { println!("{} {}", "[error]".magenta(), suite.name.magenta()); for test in suite.tests.iter_mut() { + // Reset db + reset_db(client)?; + // Generate file tree path let temp_dir = tempfile::tempdir()?; @@ -29,17 +32,17 @@ pub(crate) fn run_errors_test( "schema.sql", [ "CREATE TABLE author (id SERIAL, name TEXT);\n", - &test.schema, + test.schema.as_deref().unwrap_or_default(), ] .concat(), )?; // Generate queries files std::fs::create_dir("queries")?; - std::fs::write("queries/test.sql", &test.query)?; - - // Reset db - reset_db(client)?; + std::fs::write( + "queries/test.sql", + test.query.as_deref().unwrap_or_default(), + )?; // Run codegen let result = cornucopia::load_schema(client, &["schema.sql"]) @@ -85,10 +88,9 @@ pub(crate) fn run_errors_test( std::env::set_current_dir(&original_pwd)?; } + // Update error message if needed if apply { - // Format test descriptor and update error message if needed - let edited = toml::to_string_pretty(&suite.tests)?; - std::fs::write(suite.path, edited)?; + suite.write()?; } } diff --git a/test_integration/src/fixtures.rs b/test_integration/src/fixtures.rs index d1d315f3..308c81c2 100644 --- a/test_integration/src/fixtures.rs +++ b/test_integration/src/fixtures.rs @@ -1,11 +1,15 @@ -use std::path::{Path, PathBuf}; +use std::{ + error::Error, + path::{Path, PathBuf}, +}; use cornucopia::CodegenSettings; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -#[derive(Deserialize)] -struct TestSuiteDeserializer { - test: Vec, +#[derive(Serialize, Deserialize)] +struct TestSuiteSerde { + #[serde(rename = "test")] + tests: Vec, } pub struct TestSuite { @@ -21,16 +25,25 @@ impl TestSuite { let name = file.file_name().to_string_lossy().to_string(); let path = file.path(); let content = std::fs::read_to_string(&path).unwrap(); - let tests: TestSuiteDeserializer = toml::from_str(&content).unwrap(); + let test_suite: TestSuiteSerde = toml::from_str(&content).unwrap(); TestSuite { name, - tests: tests.test, + tests: test_suite.tests, path, } }) } } +impl TestSuite { + pub(crate) fn write(self) -> Result<(), Box> { + let suite = TestSuiteSerde { tests: self.tests }; + let edited = toml::to_string_pretty(&suite)?; + std::fs::write(self.path, edited)?; + Ok(()) + } +} + /// Codegen test case #[derive(Debug, Deserialize)] pub(crate) struct CodegenTest { @@ -72,10 +85,8 @@ impl From<&CodegenTest> for CodegenSettings { #[derive(Debug, Deserialize, Serialize)] pub(crate) struct ErrorTest { pub(crate) name: String, - #[serde(default)] - pub(crate) query: String, - #[serde(default)] - pub(crate) schema: String, + pub(crate) query: Option, + pub(crate) schema: Option, pub(crate) error: String, } From a95f2f5b1748a54953d3316a7c2490e9027398b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 8 Feb 2023 03:29:43 -0500 Subject: [PATCH 27/31] For some reason, `toml` now uses double quotes for multiline. --- test_integration/fixtures/errors/codegen.toml | 60 +++--- test_integration/fixtures/errors/prepare.toml | 20 +- test_integration/fixtures/errors/schema.toml | 12 +- .../fixtures/errors/validation.toml | 190 +++++++++--------- 4 files changed, 141 insertions(+), 141 deletions(-) diff --git a/test_integration/fixtures/errors/codegen.toml b/test_integration/fixtures/errors/codegen.toml index bcbf6dd3..1213663f 100644 --- a/test_integration/fixtures/errors/codegen.toml +++ b/test_integration/fixtures/errors/codegen.toml @@ -1,12 +1,12 @@ [[test]] -name = 'ClashBorrowed' -query = ''' +name = "ClashBorrowed" +query = """ --! select SELECT * FROM author; --! select_borrowed SELECT * FROM author; -''' -error = ''' +""" +error = """ × `SelectBorrowed` is used multiple time ╭─[queries/test.sql:1:1] 1 │ --! select @@ -18,17 +18,17 @@ error = ''' · ╰── redefined as row here 4 │ SELECT * FROM author; ╰──── - help: use a different name for one of those''' + help: use a different name for one of those""" [[test]] -name = 'ClashParams' -query = ''' +name = "ClashParams" +query = """ --! new_author INSERT INTO Author (id, name) VALUES (:id, :name); --! new_author_params SELECT * FROM author; -''' -error = ''' +""" +error = """ × `NewAuthorParams` is used multiple time ╭─[queries/test.sql:1:1] 1 │ --! new_author @@ -40,17 +40,17 @@ error = ''' · ╰── redefined as row here 4 │ SELECT * FROM author; ╰──── - help: use a different name for one of those''' + help: use a different name for one of those""" [[test]] -name = 'ClashQuery' -query = ''' +name = "ClashQuery" +query = """ --! select SELECT * FROM author; --! select_query SELECT * FROM author; -''' -error = ''' +""" +error = """ × `SelectQuery` is used multiple time ╭─[queries/test.sql:1:1] 1 │ --! select @@ -62,16 +62,16 @@ error = ''' · ╰── redefined as row here 4 │ SELECT * FROM author; ╰──── - help: use a different name for one of those''' + help: use a different name for one of those""" [[test]] -name = 'ClashTypeQuery' -query = ''' +name = "ClashTypeQuery" +query = """ --: AuthorParams() --! author: AuthorParams INSERT INTO Author (id, name) VALUES (:id, :name) RETURNING *; -''' -error = ''' +""" +error = """ × `AuthorParams` is used multiple time ╭─[queries/test.sql:1:1] 1 │ --: AuthorParams() @@ -81,15 +81,15 @@ error = ''' · ╰── previous definition as params here 3 │ INSERT INTO Author (id, name) VALUES (:id, :name) RETURNING *; ╰──── - help: use a different name for one of those''' + help: use a different name for one of those""" [[test]] -name = 'ClashTypeReuse' -query = ''' +name = "ClashTypeReuse" +query = """ --! author Author(): Author() INSERT INTO Author (id, name) VALUES (:id, :name) RETURNING *; -''' -error = ''' +""" +error = """ × `Author` is used multiple time ╭─[queries/test.sql:1:1] 1 │ --! author Author(): Author() @@ -98,15 +98,15 @@ error = ''' · ╰── previous definition as params here 2 │ INSERT INTO Author (id, name) VALUES (:id, :name) RETURNING *; ╰──── - help: use a different name for one of those''' + help: use a different name for one of those""" [[test]] -name = 'ClashInlineReuse' -query = ''' +name = "ClashInlineReuse" +query = """ --! author: AuthorParams(id?) INSERT INTO Author (id, name) VALUES (:id, :name) RETURNING *; -''' -error = ''' +""" +error = """ × `AuthorParams` is used multiple time ╭─[queries/test.sql:1:1] 1 │ --! author: AuthorParams(id?) @@ -115,4 +115,4 @@ error = ''' · ╰── previous definition as params here 2 │ INSERT INTO Author (id, name) VALUES (:id, :name) RETURNING *; ╰──── - help: use a different name for one of those''' + help: use a different name for one of those""" diff --git a/test_integration/fixtures/errors/prepare.toml b/test_integration/fixtures/errors/prepare.toml index 927b1c9b..4fe1a617 100644 --- a/test_integration/fixtures/errors/prepare.toml +++ b/test_integration/fixtures/errors/prepare.toml @@ -1,10 +1,10 @@ [[test]] -name = 'ColumnNameAlreadyTaken' -query = ''' +name = "ColumnNameAlreadyTaken" +query = """ --! authors SELECT id, name AS id FROM author; -''' -error = ''' +""" +error = """ × column `id` appear multiple time ╭─[queries/test.sql:1:1] 1 │ --! authors @@ -12,19 +12,19 @@ error = ''' · ╰── query returns one or more columns with the same name 2 │ SELECT id, name AS id FROM author; ╰──── - help: disambiguate column names in your SQL using an `AS` clause''' + help: disambiguate column names in your SQL using an `AS` clause""" [[test]] -name = 'InconsistentTypes' -query = ''' +name = "InconsistentTypes" +query = """ --! insert_author INSERT INTO Author (id, name) VALUES (:name, :name); -''' -error = ''' +""" +error = """ × Couldn't prepare query: inconsistent types deduced for parameter $1 ╭─[queries/test.sql:1:1] 1 │ --! insert_author 2 │ INSERT INTO Author (id, name) VALUES (:name, :name); · ▲ · ╰── error occurs near this location - ╰────''' + ╰────""" diff --git a/test_integration/fixtures/errors/schema.toml b/test_integration/fixtures/errors/schema.toml index 17a29ec9..e595ebc7 100644 --- a/test_integration/fixtures/errors/schema.toml +++ b/test_integration/fixtures/errors/schema.toml @@ -1,13 +1,13 @@ [[test]] -name = 'Syntax' -schema = ''' +name = "Syntax" +schema = """ CREATE TABLE syntax {}; -''' -error = ''' -× Could not execute schema: syntax error at or near "{" +""" +error = """ +× Could not execute schema: syntax error at or near \"{\" ╭─[schema.sql:1:1] 1 │ CREATE TABLE author (id SERIAL, name TEXT); 2 │ CREATE TABLE syntax {}; · ▲ · ╰── error occurs near this location - ╰────''' + ╰────""" diff --git a/test_integration/fixtures/errors/validation.toml b/test_integration/fixtures/errors/validation.toml index c318cbc7..8eaa72af 100644 --- a/test_integration/fixtures/errors/validation.toml +++ b/test_integration/fixtures/errors/validation.toml @@ -1,10 +1,10 @@ [[test]] -name = 'DuplicateCol' -query = ''' +name = "DuplicateCol" +query = """ --! new_author(id?, id?) INSERT INTO Author (id, name) VALUES (:id, :id); -''' -error = ''' +""" +error = """ × the field `id` is declared null multiple time ╭─[queries/test.sql:1:1] 1 │ --! new_author(id?, id?) @@ -13,15 +13,15 @@ error = ''' · ╰── previous nullity declaration 2 │ INSERT INTO Author (id, name) VALUES (:id, :id); ╰──── - help: remove one of the two declaration''' + help: remove one of the two declaration""" [[test]] -name = 'ColumnAlreadyNullable' -query = ''' +name = "ColumnAlreadyNullable" +query = """ --! author: (id?, name?, id?) SELECT * FROM author; -''' -error = ''' +""" +error = """ × the field `id` is declared null multiple time ╭─[queries/test.sql:1:1] 1 │ --! author: (id?, name?, id?) @@ -30,15 +30,15 @@ error = ''' · ╰── previous nullity declaration 2 │ SELECT * FROM author; ╰──── - help: remove one of the two declaration''' + help: remove one of the two declaration""" [[test]] -name = 'UnknownColumnName' -query = ''' +name = "UnknownColumnName" +query = """ --! author: (age?) SELECT * FROM author; -''' -error = ''' +""" +error = """ × unknown field ╭─[queries/test.sql:1:1] 1 │ --! author: (age?) @@ -46,15 +46,15 @@ error = ''' · ╰── no field with this name was found 2 │ SELECT * FROM author; ╰──── - help: use one of those names: id, name''' + help: use one of those names: id, name""" [[test]] -name = 'UnknownParamsName' -query = ''' +name = "UnknownParamsName" +query = """ --! new_author (age?) INSERT INTO Author (id, name) VALUES (:id, :name); -''' -error = ''' +""" +error = """ × unknown field ╭─[queries/test.sql:1:1] 1 │ --! new_author (age?) @@ -62,17 +62,17 @@ error = ''' · ╰── no field with this name was found 2 │ INSERT INTO Author (id, name) VALUES (:id, :name); ╰──── - help: use one of those names: id, name''' + help: use one of those names: id, name""" [[test]] -name = 'QueryAlreadyExists' -query = ''' +name = "QueryAlreadyExists" +query = """ --! author_id SELECT id FROM Author; --! author_id SELECT id FROM Author; -''' -error = ''' +""" +error = """ × the query `author_id` is defined multiple time ╭─[queries/test.sql:1:1] 1 │ --! author_id @@ -84,15 +84,15 @@ error = ''' · ╰── redefined here 4 │ SELECT id FROM Author; ╰──── - help: use a different name for one of those''' + help: use a different name for one of those""" [[test]] -name = 'TypeAlreadyExists' -query = ''' +name = "TypeAlreadyExists" +query = """ --: Row() --: Row(name?) -''' -error = ''' +""" +error = """ × the type `Row` is defined multiple time ╭─[queries/test.sql:1:1] 1 │ --: Row() @@ -102,18 +102,18 @@ error = ''' · ─┬─ · ╰── redefined here ╰──── - help: use a different name for one of those''' + help: use a different name for one of those""" [[test]] -name = 'InlineConflictDeclaredRow' -query = ''' +name = "InlineConflictDeclaredRow" +query = """ --: Row() --! author_name_inline: Row() SELECT name FROM Author; --! author_name_named: Row SELECT name FROM Author; -''' -error = ''' +""" +error = """ × the row `Row` is defined multiple time ╭─[queries/test.sql:1:1] 1 │ --: Row() @@ -124,18 +124,18 @@ error = ''' · ╰── redefined here 3 │ SELECT name FROM Author; ╰──── - help: use a different name for one of those''' + help: use a different name for one of those""" [[test]] -name = 'InlineConflictDeclaredParam' -query = ''' +name = "InlineConflictDeclaredParam" +query = """ --: Param() --! new_author_inline Param() INSERT INTO Author (id, name) VALUES (:id, :name); --! new_author_named Param INSERT INTO Author (id, name) VALUES (:id, :name); -''' -error = ''' +""" +error = """ × the param `Param` is defined multiple time ╭─[queries/test.sql:1:1] 1 │ --: Param() @@ -146,15 +146,15 @@ error = ''' · ╰── redefined here 3 │ INSERT INTO Author (id, name) VALUES (:id, :name); ╰──── - help: use a different name for one of those''' + help: use a different name for one of those""" [[test]] -name = 'UnknownNamedRow' -query = ''' +name = "UnknownNamedRow" +query = """ --! author: Author SELECT * FROM author; -''' -error = ''' +""" +error = """ × reference to an unknown named row `Author` ╭─[queries/test.sql:1:1] 1 │ --! author: Author @@ -162,15 +162,15 @@ error = ''' · ╰── unknown named row 2 │ SELECT * FROM author; ╰──── - help: declare an inline named type using `()`: Author()''' + help: declare an inline named type using `()`: Author()""" [[test]] -name = 'UnknownNamedParam' -query = ''' +name = "UnknownNamedParam" +query = """ --! new_author Param INSERT INTO Author (id, name) VALUES (:id, :name); -''' -error = ''' +""" +error = """ × reference to an unknown named param `Param` ╭─[queries/test.sql:1:1] 1 │ --! new_author Param @@ -178,18 +178,18 @@ error = ''' · ╰── unknown named param 2 │ INSERT INTO Author (id, name) VALUES (:id, :name); ╰──── - help: declare an inline named type using `()`: Param()''' + help: declare an inline named type using `()`: Param()""" [[test]] -name = 'NamedTypeMissingColumn' -query = ''' +name = "NamedTypeMissingColumn" +query = """ --: Row() --! author_names: Row SELECT name FROM Author; --! author_ids: Row SELECT id FROM Author; -''' -error = ''' +""" +error = """ × conflicting uses of named type `Row` ╭─[queries/test.sql:1:1] 1 │ --: Row() @@ -202,18 +202,18 @@ error = ''' · ╰── column `name` not found 5 │ SELECT id FROM Author; ╰──── - help: use a different named type for each query''' + help: use a different named type for each query""" [[test]] -name = 'NamedTypeMissingColumn2' -query = ''' +name = "NamedTypeMissingColumn2" +query = """ --: Row() --! author_names: Row SELECT name FROM Author; --! author: Row SELECT name, id FROM Author; -''' -error = ''' +""" +error = """ × conflicting uses of named type `Row` ╭─[queries/test.sql:1:1] 1 │ --: Row() @@ -226,18 +226,18 @@ error = ''' · ╰── column `id` expected here 5 │ SELECT name, id FROM Author; ╰──── - help: use a different named type for each query''' + help: use a different named type for each query""" [[test]] -name = 'NamedTypeWrongColumnType' -query = ''' +name = "NamedTypeWrongColumnType" +query = """ --: Row() --! author_names: Row SELECT name FROM Author; --! author_ids: Row SELECT id as name FROM Author; -''' -error = ''' +""" +error = """ × conflicting uses of named type `Row` ╭─[queries/test.sql:1:1] 1 │ --: Row() @@ -250,15 +250,15 @@ error = ''' · ╰── but here it has type `int4` 5 │ SELECT id as name FROM Author; ╰──── - help: use a different named type for each query''' + help: use a different named type for each query""" [[test]] -name = 'ImplicitExecuteRow' -query = ''' +name = "ImplicitExecuteRow" +query = """ --! delete: (name?) DELETE FROM author; -''' -error = ''' +""" +error = """ × the query `delete` declare a row but return nothing ╭─[queries/test.sql:1:1] 1 │ --! delete: (name?) @@ -268,15 +268,15 @@ error = ''' · ─────────┬───────── · ╰── but query return nothing ╰──── - help: remove row declaration''' + help: remove row declaration""" [[test]] -name = 'RowOnExecute' -query = ''' +name = "RowOnExecute" +query = """ --! delete: Row() DELETE FROM author; -''' -error = ''' +""" +error = """ × the query `delete` declare a row but return nothing ╭─[queries/test.sql:1:1] 1 │ --! delete: Row() @@ -286,15 +286,15 @@ error = ''' · ─────────┬───────── · ╰── but query return nothing ╰──── - help: remove row declaration''' + help: remove row declaration""" [[test]] -name = 'ParamOnSimpleQuery' -query = ''' +name = "ParamOnSimpleQuery" +query = """ --! delete Param() DELETE FROM author; -''' -error = ''' +""" +error = """ × the query `delete` declares a parameter but has no binding ╭─[queries/test.sql:1:1] 1 │ --! delete Param() @@ -304,15 +304,15 @@ error = ''' · ─────────┬───────── · ╰── but query has no binding ╰──── - help: remove parameter declaration''' + help: remove parameter declaration""" [[test]] -name = 'QueryReserved' -query = ''' +name = "QueryReserved" +query = """ --! crate SELECT * FROM author; -''' -error = ''' +""" +error = """ × `crate` is a reserved rust keyword that cannot be escaped ╭─[queries/test.sql:1:1] 1 │ --! crate @@ -320,15 +320,15 @@ error = ''' · ╰── reserved rust keyword 2 │ SELECT * FROM author; ╰──── - help: use a different name''' + help: use a different name""" [[test]] -name = 'TypeReserved' -query = ''' +name = "TypeReserved" +query = """ --! select: Self() SELECT * FROM author; -''' -error = ''' +""" +error = """ × `Self` is a reserved rust keyword that cannot be escaped ╭─[queries/test.sql:1:1] 1 │ --! select: Self() @@ -336,15 +336,15 @@ error = ''' · ╰── reserved rust keyword 2 │ SELECT * FROM author; ╰──── - help: use a different name''' + help: use a different name""" [[test]] -name = 'NameReserved' -query = ''' +name = "NameReserved" +query = """ --! query SELECT id, name as _ FROM author; -''' -error = ''' +""" +error = """ × `_` is a reserved rust keyword that cannot be escaped ╭─[queries/test.sql:1:1] 1 │ --! query @@ -352,4 +352,4 @@ error = ''' · ╰── from row declared here 2 │ SELECT id, name as _ FROM author; ╰──── - help: use a different name''' + help: use a different name""" From 20eb0b0a4a8157d748b8d58aee9f47a219090d56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 8 Feb 2023 03:30:05 -0500 Subject: [PATCH 28/31] Remove dependencies that were added by mistake. --- test_integration/Cargo.toml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test_integration/Cargo.toml b/test_integration/Cargo.toml index ea757d1f..1c6b6426 100644 --- a/test_integration/Cargo.toml +++ b/test_integration/Cargo.toml @@ -26,10 +26,3 @@ postgres = { version = "0.19.4" } serde = { version = "1.0.148", features = ["derive"] } ## Read/write fixture files toml = "0.7.2" - -# extra types -serde_json = { version = "1.0.89", features = ["raw_value"] } -time = { version = "0.3.17", features = ["parsing", "serde"] } -uuid = { version = "1.2.2", features = ["serde"] } -eui48 = { version = "1.1.0", features = ["serde"] } -rust_decimal = { version = "1.27.0", features = ["db-postgres"] } From e12543cebfd03526c776ddd806bdd7bfa8e4108b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 8 Feb 2023 03:30:13 -0500 Subject: [PATCH 29/31] Update lockfile. --- Cargo.lock | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 64cfc820..47f2d944 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1737,16 +1737,11 @@ version = "0.1.0" dependencies = [ "clap 4.1.4", "cornucopia", - "eui48", "owo-colors", "postgres", - "rust_decimal", "serde", - "serde_json", "tempfile", - "time", "toml 0.7.2", - "uuid", ] [[package]] From 34a9e55244138190d191203ace6abfb8d710fa63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 8 Feb 2023 03:38:39 -0500 Subject: [PATCH 30/31] Add minor comments. --- test_integration/src/codegen.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test_integration/src/codegen.rs b/test_integration/src/codegen.rs index 525669cd..9d465407 100644 --- a/test_integration/src/codegen.rs +++ b/test_integration/src/codegen.rs @@ -20,10 +20,13 @@ pub(crate) fn run_codegen_test( for suite in test_suites { println!("{}", format!("[codegen] {}", suite.name).magenta()); for test in suite.tests { + // Reset DB + reset_db(client)?; + + // Set current dir to test base path set_current_dir(format!("../{}", test.base_path))?; // Load schema - reset_db(client)?; cornucopia::load_schema(client, &["schema.sql"])?; // If `--apply`, then the code will be regenerated. From b5b3c9dde370d335bc13c946ed2d59316223ca53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Gari=C3=A9py?= Date: Wed, 8 Feb 2023 03:45:26 -0500 Subject: [PATCH 31/31] Change name of benchmark in fixture. --- test_integration/fixtures/codegen/benchmarks.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_integration/fixtures/codegen/benchmarks.toml b/test_integration/fixtures/codegen/benchmarks.toml index 828e3d21..d68f075d 100644 --- a/test_integration/fixtures/codegen/benchmarks.toml +++ b/test_integration/fixtures/codegen/benchmarks.toml @@ -1,5 +1,5 @@ [[test]] -name = "Bench" +name = "Execution benchmark" base_path = "benches/execution/cornucopia_benches" destination = "generated.rs" sync = true