From ab1f2d18666734ca17174ec8a3a26260ea90e5f9 Mon Sep 17 00:00:00 2001 From: Virgiel <> Date: Thu, 9 Jun 2022 03:56:32 +0200 Subject: [PATCH] better parser and parsing test --- Cargo.lock | 138 +------- Cargo.toml | 2 - codegen_test/queries/syntax.sql | 20 ++ codegen_test/src/cornucopia_async.rs | 456 +++++++++++++++++++++++++++ codegen_test/src/cornucopia_sync.rs | 435 +++++++++++++++++++++++++ grammar.pest | 105 ------ src/parser.rs | 8 +- 7 files changed, 924 insertions(+), 240 deletions(-) create mode 100644 codegen_test/queries/syntax.sql delete mode 100644 grammar.pest diff --git a/Cargo.lock b/Cargo.lock index 6664a9d7..44a14f94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,42 +69,15 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -dependencies = [ - "block-padding", - "byte-tools", - "byteorder", - "generic-array 0.12.4", -] - [[package]] name = "block-buffer" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" dependencies = [ - "generic-array 0.14.5", + "generic-array", ] -[[package]] -name = "block-padding" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -dependencies = [ - "byte-tools", -] - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - [[package]] name = "byteorder" version = "1.4.3" @@ -217,8 +190,6 @@ dependencies = [ "clap", "heck", "indexmap", - "pest", - "pest_derive", "postgres", "postgres-types", "prettyplease", @@ -285,7 +256,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" dependencies = [ - "generic-array 0.14.5", + "generic-array", "typenum", ] @@ -323,22 +294,13 @@ dependencies = [ "tokio", ] -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", -] - [[package]] name = "digest" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" dependencies = [ - "block-buffer 0.10.2", + "block-buffer", "crypto-common", "subtle", ] @@ -353,12 +315,6 @@ dependencies = [ "rustc-serialize", ] -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" - [[package]] name = "fallible-iterator" version = "0.2.0" @@ -463,15 +419,6 @@ dependencies = [ "slab", ] -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - [[package]] name = "generic-array" version = "0.14.5" @@ -520,7 +467,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.3", + "digest", ] [[package]] @@ -592,19 +539,13 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - [[package]] name = "md-5" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" dependencies = [ - "digest 0.10.3", + "digest", ] [[package]] @@ -650,12 +591,6 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" -[[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" - [[package]] name = "os_str_bytes" version = "6.1.0" @@ -697,49 +632,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" -[[package]] -name = "pest" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" -dependencies = [ - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pest_meta" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" -dependencies = [ - "maplit", - "pest", - "sha-1", -] - [[package]] name = "phf" version = "0.10.1" @@ -1014,18 +906,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha-1" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" -dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug", -] - [[package]] name = "sha2" version = "0.10.2" @@ -1034,7 +914,7 @@ checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.3", + "digest", ] [[package]] @@ -1305,12 +1185,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "ucd-trie" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" - [[package]] name = "unicode-bidi" version = "0.3.8" diff --git a/Cargo.toml b/Cargo.toml index 4412ee25..0ce6b1fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,8 +25,6 @@ time = { version = "0.3.9", features = ["parsing"] } thiserror = "1.0.31" clap = { version = "3.1.18", features = ["derive"] } -pest = "2.1.3" -pest_derive = "2.1.0" heck = "0.4.0" indexmap = "1.8.2" diff --git a/codegen_test/queries/syntax.sql b/codegen_test/queries/syntax.sql new file mode 100644 index 00000000..f64a348c --- /dev/null +++ b/codegen_test/queries/syntax.sql @@ -0,0 +1,20 @@ + +--:row CompactRow() + --: row SpaceRow () +--:param CompactField(a?,b?,c?) +--:param SpaceField ( a? , b? , c? ) + +--! select_compact +SELECT * FROM clone; + --! select_spaced + SELECT * FROM clone ; + +--!implicit_compact(name?,price?):(id?) +INSERT INTO item (name, price, show) VALUES (:name, :price, false) RETURNING id; + --! implicit_spaced ( name? , price? ) : ( id? ) +INSERT INTO item (name, price, show) VALUES (:name, :price, false) RETURNING id; + +--!named_compact Params:Row +INSERT INTO item (name, price, show) VALUES (:name, :price, false) RETURNING id; + --! named_spaced Params : Row +INSERT INTO item (name, price, show) VALUES (:name, :price, false) RETURNING id; diff --git a/codegen_test/src/cornucopia_async.rs b/codegen_test/src/cornucopia_async.rs index fe3934a3..5ec19506 100644 --- a/codegen_test/src/cornucopia_async.rs +++ b/codegen_test/src/cornucopia_async.rs @@ -2452,4 +2452,460 @@ pub mod queries { client.execute(&stmt, &[composite]).await } } + pub mod syntax { + use cornucopia_client::GenericClient; + use futures::{StreamExt, TryStreamExt}; + #[derive(Debug)] + pub struct ImplicitCompactParams<'a> { + pub name: Option<&'a str>, + pub price: Option, + } + impl<'a> ImplicitCompactParams<'a> { + pub fn implicit_compact( + &'a self, + client: &'a C, + ) -> ImplicitCompactQuery<'a, C, ImplicitCompact, 2> { + implicit_compact(client, &self.name, &self.price) + } + } + #[derive(Debug)] + pub struct ImplicitSpacedParams<'a> { + pub name: Option<&'a str>, + pub price: Option, + } + impl<'a> ImplicitSpacedParams<'a> { + pub fn implicit_spaced( + &'a self, + client: &'a C, + ) -> ImplicitSpacedQuery<'a, C, ImplicitSpaced, 2> { + implicit_spaced(client, &self.name, &self.price) + } + } + #[derive(Debug)] + pub struct Params<'a> { + pub name: &'a str, + pub price: f64, + } + impl<'a> Params<'a> { + pub fn named_compact( + &'a self, + client: &'a C, + ) -> RowQuery<'a, C, Row, 2> { + named_compact(client, &self.name, &self.price) + } + pub fn named_spaced( + &'a self, + client: &'a C, + ) -> RowQuery<'a, C, Row, 2> { + named_spaced(client, &self.name, &self.price) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct SelectCompact { + pub composite: super::super::types::public::CloneComposite, + } + pub struct SelectCompactBorrowed<'a> { + pub composite: super::super::types::public::CloneCompositeBorrowed<'a>, + } + impl<'a> From> for SelectCompact { + fn from(SelectCompactBorrowed { composite }: SelectCompactBorrowed<'a>) -> Self { + Self { + composite: composite.into(), + } + } + } + pub struct SelectCompactQuery<'a, C: GenericClient, T, const N: usize> { + client: &'a C, + params: [&'a (dyn postgres_types::ToSql + Sync); N], + query: &'static str, + extractor: fn(&tokio_postgres::Row) -> SelectCompactBorrowed, + mapper: fn(SelectCompactBorrowed) -> T, + } + impl<'a, C, T: 'a, const N: usize> SelectCompactQuery<'a, C, T, N> + where + C: GenericClient, + { + pub fn map( + self, + mapper: fn(SelectCompactBorrowed) -> R, + ) -> SelectCompactQuery<'a, C, R, N> { + SelectCompactQuery { + client: self.client, + params: self.params, + query: self.query, + extractor: self.extractor, + mapper, + } + } + pub async fn stmt(&self) -> Result { + self.client.prepare(self.query).await + } + pub async fn one(self) -> Result { + let stmt = self.stmt().await?; + let row = self.client.query_one(&stmt, &self.params).await?; + Ok((self.mapper)((self.extractor)(&row))) + } + pub async fn vec(self) -> Result, tokio_postgres::Error> { + self.stream().await?.try_collect().await + } + pub async fn opt(self) -> Result, tokio_postgres::Error> { + let stmt = self.stmt().await?; + Ok(self + .client + .query_opt(&stmt, &self.params) + .await? + .map(|row| (self.mapper)((self.extractor)(&row)))) + } + pub async fn stream( + self, + ) -> Result< + impl futures::Stream> + 'a, + tokio_postgres::Error, + > { + let stmt = self.stmt().await?; + let stream = self + .client + .query_raw(&stmt, cornucopia_client::slice_iter(&self.params)) + .await? + .map(move |res| res.map(|row| (self.mapper)((self.extractor)(&row)))) + .into_stream(); + Ok(stream) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct SelectSpaced { + pub composite: super::super::types::public::CloneComposite, + } + pub struct SelectSpacedBorrowed<'a> { + pub composite: super::super::types::public::CloneCompositeBorrowed<'a>, + } + impl<'a> From> for SelectSpaced { + fn from(SelectSpacedBorrowed { composite }: SelectSpacedBorrowed<'a>) -> Self { + Self { + composite: composite.into(), + } + } + } + pub struct SelectSpacedQuery<'a, C: GenericClient, T, const N: usize> { + client: &'a C, + params: [&'a (dyn postgres_types::ToSql + Sync); N], + query: &'static str, + extractor: fn(&tokio_postgres::Row) -> SelectSpacedBorrowed, + mapper: fn(SelectSpacedBorrowed) -> T, + } + impl<'a, C, T: 'a, const N: usize> SelectSpacedQuery<'a, C, T, N> + where + C: GenericClient, + { + pub fn map( + self, + mapper: fn(SelectSpacedBorrowed) -> R, + ) -> SelectSpacedQuery<'a, C, R, N> { + SelectSpacedQuery { + client: self.client, + params: self.params, + query: self.query, + extractor: self.extractor, + mapper, + } + } + pub async fn stmt(&self) -> Result { + self.client.prepare(self.query).await + } + pub async fn one(self) -> Result { + let stmt = self.stmt().await?; + let row = self.client.query_one(&stmt, &self.params).await?; + Ok((self.mapper)((self.extractor)(&row))) + } + pub async fn vec(self) -> Result, tokio_postgres::Error> { + self.stream().await?.try_collect().await + } + pub async fn opt(self) -> Result, tokio_postgres::Error> { + let stmt = self.stmt().await?; + Ok(self + .client + .query_opt(&stmt, &self.params) + .await? + .map(|row| (self.mapper)((self.extractor)(&row)))) + } + pub async fn stream( + self, + ) -> Result< + impl futures::Stream> + 'a, + tokio_postgres::Error, + > { + let stmt = self.stmt().await?; + let stream = self + .client + .query_raw(&stmt, cornucopia_client::slice_iter(&self.params)) + .await? + .map(move |res| res.map(|row| (self.mapper)((self.extractor)(&row)))) + .into_stream(); + Ok(stream) + } + } + #[derive(Debug, Clone, PartialEq, Copy)] + pub struct ImplicitCompact { + pub id: Option, + } + pub struct ImplicitCompactQuery<'a, C: GenericClient, T, const N: usize> { + client: &'a C, + params: [&'a (dyn postgres_types::ToSql + Sync); N], + query: &'static str, + extractor: fn(&tokio_postgres::Row) -> ImplicitCompact, + mapper: fn(ImplicitCompact) -> T, + } + impl<'a, C, T: 'a, const N: usize> ImplicitCompactQuery<'a, C, T, N> + where + C: GenericClient, + { + pub fn map( + self, + mapper: fn(ImplicitCompact) -> R, + ) -> ImplicitCompactQuery<'a, C, R, N> { + ImplicitCompactQuery { + client: self.client, + params: self.params, + query: self.query, + extractor: self.extractor, + mapper, + } + } + pub async fn stmt(&self) -> Result { + self.client.prepare(self.query).await + } + pub async fn one(self) -> Result { + let stmt = self.stmt().await?; + let row = self.client.query_one(&stmt, &self.params).await?; + Ok((self.mapper)((self.extractor)(&row))) + } + pub async fn vec(self) -> Result, tokio_postgres::Error> { + self.stream().await?.try_collect().await + } + pub async fn opt(self) -> Result, tokio_postgres::Error> { + let stmt = self.stmt().await?; + Ok(self + .client + .query_opt(&stmt, &self.params) + .await? + .map(|row| (self.mapper)((self.extractor)(&row)))) + } + pub async fn stream( + self, + ) -> Result< + impl futures::Stream> + 'a, + tokio_postgres::Error, + > { + let stmt = self.stmt().await?; + let stream = self + .client + .query_raw(&stmt, cornucopia_client::slice_iter(&self.params)) + .await? + .map(move |res| res.map(|row| (self.mapper)((self.extractor)(&row)))) + .into_stream(); + Ok(stream) + } + } + #[derive(Debug, Clone, PartialEq, Copy)] + pub struct ImplicitSpaced { + pub id: Option, + } + pub struct ImplicitSpacedQuery<'a, C: GenericClient, T, const N: usize> { + client: &'a C, + params: [&'a (dyn postgres_types::ToSql + Sync); N], + query: &'static str, + extractor: fn(&tokio_postgres::Row) -> ImplicitSpaced, + mapper: fn(ImplicitSpaced) -> T, + } + impl<'a, C, T: 'a, const N: usize> ImplicitSpacedQuery<'a, C, T, N> + where + C: GenericClient, + { + pub fn map( + self, + mapper: fn(ImplicitSpaced) -> R, + ) -> ImplicitSpacedQuery<'a, C, R, N> { + ImplicitSpacedQuery { + client: self.client, + params: self.params, + query: self.query, + extractor: self.extractor, + mapper, + } + } + pub async fn stmt(&self) -> Result { + self.client.prepare(self.query).await + } + pub async fn one(self) -> Result { + let stmt = self.stmt().await?; + let row = self.client.query_one(&stmt, &self.params).await?; + Ok((self.mapper)((self.extractor)(&row))) + } + pub async fn vec(self) -> Result, tokio_postgres::Error> { + self.stream().await?.try_collect().await + } + pub async fn opt(self) -> Result, tokio_postgres::Error> { + let stmt = self.stmt().await?; + Ok(self + .client + .query_opt(&stmt, &self.params) + .await? + .map(|row| (self.mapper)((self.extractor)(&row)))) + } + pub async fn stream( + self, + ) -> Result< + impl futures::Stream> + 'a, + tokio_postgres::Error, + > { + let stmt = self.stmt().await?; + let stream = self + .client + .query_raw(&stmt, cornucopia_client::slice_iter(&self.params)) + .await? + .map(move |res| res.map(|row| (self.mapper)((self.extractor)(&row)))) + .into_stream(); + Ok(stream) + } + } + #[derive(Debug, Clone, PartialEq, Copy)] + pub struct Row { + pub id: i32, + } + pub struct RowQuery<'a, C: GenericClient, T, const N: usize> { + client: &'a C, + params: [&'a (dyn postgres_types::ToSql + Sync); N], + query: &'static str, + extractor: fn(&tokio_postgres::Row) -> Row, + mapper: fn(Row) -> T, + } + impl<'a, C, T: 'a, const N: usize> RowQuery<'a, C, T, N> + where + C: GenericClient, + { + pub fn map(self, mapper: fn(Row) -> R) -> RowQuery<'a, C, R, N> { + RowQuery { + client: self.client, + params: self.params, + query: self.query, + extractor: self.extractor, + mapper, + } + } + pub async fn stmt(&self) -> Result { + self.client.prepare(self.query).await + } + pub async fn one(self) -> Result { + let stmt = self.stmt().await?; + let row = self.client.query_one(&stmt, &self.params).await?; + Ok((self.mapper)((self.extractor)(&row))) + } + pub async fn vec(self) -> Result, tokio_postgres::Error> { + self.stream().await?.try_collect().await + } + pub async fn opt(self) -> Result, tokio_postgres::Error> { + let stmt = self.stmt().await?; + Ok(self + .client + .query_opt(&stmt, &self.params) + .await? + .map(|row| (self.mapper)((self.extractor)(&row)))) + } + pub async fn stream( + self, + ) -> Result< + impl futures::Stream> + 'a, + tokio_postgres::Error, + > { + let stmt = self.stmt().await?; + let stream = self + .client + .query_raw(&stmt, cornucopia_client::slice_iter(&self.params)) + .await? + .map(move |res| res.map(|row| (self.mapper)((self.extractor)(&row)))) + .into_stream(); + Ok(stream) + } + } + pub fn select_compact<'a, C: GenericClient>( + client: &'a C, + ) -> SelectCompactQuery<'a, C, SelectCompact, 0> { + SelectCompactQuery { + client, + params: [], + query: "SELECT * FROM clone", + extractor: |row| SelectCompactBorrowed { + composite: row.get(0), + }, + mapper: |it| SelectCompact::from(it), + } + } + pub fn select_spaced<'a, C: GenericClient>( + client: &'a C, + ) -> SelectSpacedQuery<'a, C, SelectSpaced, 0> { + SelectSpacedQuery { + client, + params: [], + query: " SELECT * FROM clone ", + extractor: |row| SelectSpacedBorrowed { + composite: row.get(0), + }, + mapper: |it| SelectSpaced::from(it), + } + } + pub fn implicit_compact<'a, C: GenericClient>( + client: &'a C, + name: &'a Option<&'a str>, + price: &'a Option, + ) -> ImplicitCompactQuery<'a, C, ImplicitCompact, 2> { + ImplicitCompactQuery { + client, + params: [name, price], + query: "INSERT INTO item (name, price, show) VALUES ($1, $2, false) RETURNING id", + extractor: |row| ImplicitCompact { id: row.get(0) }, + mapper: |it| ImplicitCompact::from(it), + } + } + pub fn implicit_spaced<'a, C: GenericClient>( + client: &'a C, + name: &'a Option<&'a str>, + price: &'a Option, + ) -> ImplicitSpacedQuery<'a, C, ImplicitSpaced, 2> { + ImplicitSpacedQuery { + client, + params: [name, price], + query: " +INSERT INTO item (name, price, show) VALUES ($1, $2, false) RETURNING id", + extractor: |row| ImplicitSpaced { id: row.get(0) }, + mapper: |it| ImplicitSpaced::from(it), + } + } + pub fn named_compact<'a, C: GenericClient>( + client: &'a C, + name: &'a &'a str, + price: &'a f64, + ) -> RowQuery<'a, C, Row, 2> { + RowQuery { + client, + params: [name, price], + query: "INSERT INTO item (name, price, show) VALUES ($1, $2, false) RETURNING id", + extractor: |row| Row { id: row.get(0) }, + mapper: |it| Row::from(it), + } + } + pub fn named_spaced<'a, C: GenericClient>( + client: &'a C, + name: &'a &'a str, + price: &'a f64, + ) -> RowQuery<'a, C, Row, 2> { + RowQuery { + client, + params: [name, price], + query: " +INSERT INTO item (name, price, show) VALUES ($1, $2, false) RETURNING id", + extractor: |row| Row { id: row.get(0) }, + mapper: |it| Row::from(it), + } + } + } } diff --git a/codegen_test/src/cornucopia_sync.rs b/codegen_test/src/cornucopia_sync.rs index eaa912dd..6b894ab2 100644 --- a/codegen_test/src/cornucopia_sync.rs +++ b/codegen_test/src/cornucopia_sync.rs @@ -2389,4 +2389,439 @@ pub mod queries { client.execute(&stmt, &[composite]) } } + pub mod syntax { + use postgres::{fallible_iterator::FallibleIterator, GenericClient}; + #[derive(Debug)] + pub struct ImplicitCompactParams<'a> { + pub name: Option<&'a str>, + pub price: Option, + } + impl<'a> ImplicitCompactParams<'a> { + pub fn implicit_compact( + &'a self, + client: &'a mut C, + ) -> ImplicitCompactQuery<'a, C, ImplicitCompact, 2> { + implicit_compact(client, &self.name, &self.price) + } + } + #[derive(Debug)] + pub struct ImplicitSpacedParams<'a> { + pub name: Option<&'a str>, + pub price: Option, + } + impl<'a> ImplicitSpacedParams<'a> { + pub fn implicit_spaced( + &'a self, + client: &'a mut C, + ) -> ImplicitSpacedQuery<'a, C, ImplicitSpaced, 2> { + implicit_spaced(client, &self.name, &self.price) + } + } + #[derive(Debug)] + pub struct Params<'a> { + pub name: &'a str, + pub price: f64, + } + impl<'a> Params<'a> { + pub fn named_compact( + &'a self, + client: &'a mut C, + ) -> RowQuery<'a, C, Row, 2> { + named_compact(client, &self.name, &self.price) + } + pub fn named_spaced( + &'a self, + client: &'a mut C, + ) -> RowQuery<'a, C, Row, 2> { + named_spaced(client, &self.name, &self.price) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct SelectCompact { + pub composite: super::super::types::public::CloneComposite, + } + pub struct SelectCompactBorrowed<'a> { + pub composite: super::super::types::public::CloneCompositeBorrowed<'a>, + } + impl<'a> From> for SelectCompact { + fn from(SelectCompactBorrowed { composite }: SelectCompactBorrowed<'a>) -> Self { + Self { + composite: composite.into(), + } + } + } + pub struct SelectCompactQuery<'a, C: GenericClient, T, const N: usize> { + client: &'a mut C, + params: [&'a (dyn postgres_types::ToSql + Sync); N], + query: &'static str, + extractor: fn(&postgres::Row) -> SelectCompactBorrowed, + mapper: fn(SelectCompactBorrowed) -> T, + } + impl<'a, C, T: 'a, const N: usize> SelectCompactQuery<'a, C, T, N> + where + C: GenericClient, + { + pub fn map( + self, + mapper: fn(SelectCompactBorrowed) -> R, + ) -> SelectCompactQuery<'a, C, R, N> { + SelectCompactQuery { + client: self.client, + params: self.params, + query: self.query, + extractor: self.extractor, + mapper, + } + } + pub fn stmt(&mut self) -> Result { + self.client.prepare(self.query) + } + pub fn one(mut self) -> Result { + let stmt = self.stmt()?; + let row = self.client.query_one(&stmt, &self.params)?; + Ok((self.mapper)((self.extractor)(&row))) + } + pub fn vec(self) -> Result, postgres::Error> { + self.stream()?.collect() + } + pub fn opt(mut self) -> Result, postgres::Error> { + let stmt = self.stmt()?; + Ok(self + .client + .query_opt(&stmt, &self.params)? + .map(|row| (self.mapper)((self.extractor)(&row)))) + } + pub fn stream( + mut self, + ) -> Result> + 'a, postgres::Error> + { + let stmt = self.stmt()?; + let stream = self + .client + .query_raw(&stmt, cornucopia_client::slice_iter(&self.params))? + .iterator() + .map(move |res| res.map(|row| (self.mapper)((self.extractor)(&row)))); + Ok(stream) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct SelectSpaced { + pub composite: super::super::types::public::CloneComposite, + } + pub struct SelectSpacedBorrowed<'a> { + pub composite: super::super::types::public::CloneCompositeBorrowed<'a>, + } + impl<'a> From> for SelectSpaced { + fn from(SelectSpacedBorrowed { composite }: SelectSpacedBorrowed<'a>) -> Self { + Self { + composite: composite.into(), + } + } + } + pub struct SelectSpacedQuery<'a, C: GenericClient, T, const N: usize> { + client: &'a mut C, + params: [&'a (dyn postgres_types::ToSql + Sync); N], + query: &'static str, + extractor: fn(&postgres::Row) -> SelectSpacedBorrowed, + mapper: fn(SelectSpacedBorrowed) -> T, + } + impl<'a, C, T: 'a, const N: usize> SelectSpacedQuery<'a, C, T, N> + where + C: GenericClient, + { + pub fn map( + self, + mapper: fn(SelectSpacedBorrowed) -> R, + ) -> SelectSpacedQuery<'a, C, R, N> { + SelectSpacedQuery { + client: self.client, + params: self.params, + query: self.query, + extractor: self.extractor, + mapper, + } + } + pub fn stmt(&mut self) -> Result { + self.client.prepare(self.query) + } + pub fn one(mut self) -> Result { + let stmt = self.stmt()?; + let row = self.client.query_one(&stmt, &self.params)?; + Ok((self.mapper)((self.extractor)(&row))) + } + pub fn vec(self) -> Result, postgres::Error> { + self.stream()?.collect() + } + pub fn opt(mut self) -> Result, postgres::Error> { + let stmt = self.stmt()?; + Ok(self + .client + .query_opt(&stmt, &self.params)? + .map(|row| (self.mapper)((self.extractor)(&row)))) + } + pub fn stream( + mut self, + ) -> Result> + 'a, postgres::Error> + { + let stmt = self.stmt()?; + let stream = self + .client + .query_raw(&stmt, cornucopia_client::slice_iter(&self.params))? + .iterator() + .map(move |res| res.map(|row| (self.mapper)((self.extractor)(&row)))); + Ok(stream) + } + } + #[derive(Debug, Clone, PartialEq, Copy)] + pub struct ImplicitCompact { + pub id: Option, + } + pub struct ImplicitCompactQuery<'a, C: GenericClient, T, const N: usize> { + client: &'a mut C, + params: [&'a (dyn postgres_types::ToSql + Sync); N], + query: &'static str, + extractor: fn(&postgres::Row) -> ImplicitCompact, + mapper: fn(ImplicitCompact) -> T, + } + impl<'a, C, T: 'a, const N: usize> ImplicitCompactQuery<'a, C, T, N> + where + C: GenericClient, + { + pub fn map( + self, + mapper: fn(ImplicitCompact) -> R, + ) -> ImplicitCompactQuery<'a, C, R, N> { + ImplicitCompactQuery { + client: self.client, + params: self.params, + query: self.query, + extractor: self.extractor, + mapper, + } + } + pub fn stmt(&mut self) -> Result { + self.client.prepare(self.query) + } + pub fn one(mut self) -> Result { + let stmt = self.stmt()?; + let row = self.client.query_one(&stmt, &self.params)?; + Ok((self.mapper)((self.extractor)(&row))) + } + pub fn vec(self) -> Result, postgres::Error> { + self.stream()?.collect() + } + pub fn opt(mut self) -> Result, postgres::Error> { + let stmt = self.stmt()?; + Ok(self + .client + .query_opt(&stmt, &self.params)? + .map(|row| (self.mapper)((self.extractor)(&row)))) + } + pub fn stream( + mut self, + ) -> Result> + 'a, postgres::Error> + { + let stmt = self.stmt()?; + let stream = self + .client + .query_raw(&stmt, cornucopia_client::slice_iter(&self.params))? + .iterator() + .map(move |res| res.map(|row| (self.mapper)((self.extractor)(&row)))); + Ok(stream) + } + } + #[derive(Debug, Clone, PartialEq, Copy)] + pub struct ImplicitSpaced { + pub id: Option, + } + pub struct ImplicitSpacedQuery<'a, C: GenericClient, T, const N: usize> { + client: &'a mut C, + params: [&'a (dyn postgres_types::ToSql + Sync); N], + query: &'static str, + extractor: fn(&postgres::Row) -> ImplicitSpaced, + mapper: fn(ImplicitSpaced) -> T, + } + impl<'a, C, T: 'a, const N: usize> ImplicitSpacedQuery<'a, C, T, N> + where + C: GenericClient, + { + pub fn map( + self, + mapper: fn(ImplicitSpaced) -> R, + ) -> ImplicitSpacedQuery<'a, C, R, N> { + ImplicitSpacedQuery { + client: self.client, + params: self.params, + query: self.query, + extractor: self.extractor, + mapper, + } + } + pub fn stmt(&mut self) -> Result { + self.client.prepare(self.query) + } + pub fn one(mut self) -> Result { + let stmt = self.stmt()?; + let row = self.client.query_one(&stmt, &self.params)?; + Ok((self.mapper)((self.extractor)(&row))) + } + pub fn vec(self) -> Result, postgres::Error> { + self.stream()?.collect() + } + pub fn opt(mut self) -> Result, postgres::Error> { + let stmt = self.stmt()?; + Ok(self + .client + .query_opt(&stmt, &self.params)? + .map(|row| (self.mapper)((self.extractor)(&row)))) + } + pub fn stream( + mut self, + ) -> Result> + 'a, postgres::Error> + { + let stmt = self.stmt()?; + let stream = self + .client + .query_raw(&stmt, cornucopia_client::slice_iter(&self.params))? + .iterator() + .map(move |res| res.map(|row| (self.mapper)((self.extractor)(&row)))); + Ok(stream) + } + } + #[derive(Debug, Clone, PartialEq, Copy)] + pub struct Row { + pub id: i32, + } + pub struct RowQuery<'a, C: GenericClient, T, const N: usize> { + client: &'a mut C, + params: [&'a (dyn postgres_types::ToSql + Sync); N], + query: &'static str, + extractor: fn(&postgres::Row) -> Row, + mapper: fn(Row) -> T, + } + impl<'a, C, T: 'a, const N: usize> RowQuery<'a, C, T, N> + where + C: GenericClient, + { + pub fn map(self, mapper: fn(Row) -> R) -> RowQuery<'a, C, R, N> { + RowQuery { + client: self.client, + params: self.params, + query: self.query, + extractor: self.extractor, + mapper, + } + } + pub fn stmt(&mut self) -> Result { + self.client.prepare(self.query) + } + pub fn one(mut self) -> Result { + let stmt = self.stmt()?; + let row = self.client.query_one(&stmt, &self.params)?; + Ok((self.mapper)((self.extractor)(&row))) + } + pub fn vec(self) -> Result, postgres::Error> { + self.stream()?.collect() + } + pub fn opt(mut self) -> Result, postgres::Error> { + let stmt = self.stmt()?; + Ok(self + .client + .query_opt(&stmt, &self.params)? + .map(|row| (self.mapper)((self.extractor)(&row)))) + } + pub fn stream( + mut self, + ) -> Result> + 'a, postgres::Error> + { + let stmt = self.stmt()?; + let stream = self + .client + .query_raw(&stmt, cornucopia_client::slice_iter(&self.params))? + .iterator() + .map(move |res| res.map(|row| (self.mapper)((self.extractor)(&row)))); + Ok(stream) + } + } + pub fn select_compact<'a, C: GenericClient>( + client: &'a mut C, + ) -> SelectCompactQuery<'a, C, SelectCompact, 0> { + SelectCompactQuery { + client, + params: [], + query: "SELECT * FROM clone", + extractor: |row| SelectCompactBorrowed { + composite: row.get(0), + }, + mapper: |it| SelectCompact::from(it), + } + } + pub fn select_spaced<'a, C: GenericClient>( + client: &'a mut C, + ) -> SelectSpacedQuery<'a, C, SelectSpaced, 0> { + SelectSpacedQuery { + client, + params: [], + query: " SELECT * FROM clone ", + extractor: |row| SelectSpacedBorrowed { + composite: row.get(0), + }, + mapper: |it| SelectSpaced::from(it), + } + } + pub fn implicit_compact<'a, C: GenericClient>( + client: &'a mut C, + name: &'a Option<&'a str>, + price: &'a Option, + ) -> ImplicitCompactQuery<'a, C, ImplicitCompact, 2> { + ImplicitCompactQuery { + client, + params: [name, price], + query: "INSERT INTO item (name, price, show) VALUES ($1, $2, false) RETURNING id", + extractor: |row| ImplicitCompact { id: row.get(0) }, + mapper: |it| ImplicitCompact::from(it), + } + } + pub fn implicit_spaced<'a, C: GenericClient>( + client: &'a mut C, + name: &'a Option<&'a str>, + price: &'a Option, + ) -> ImplicitSpacedQuery<'a, C, ImplicitSpaced, 2> { + ImplicitSpacedQuery { + client, + params: [name, price], + query: " +INSERT INTO item (name, price, show) VALUES ($1, $2, false) RETURNING id", + extractor: |row| ImplicitSpaced { id: row.get(0) }, + mapper: |it| ImplicitSpaced::from(it), + } + } + pub fn named_compact<'a, C: GenericClient>( + client: &'a mut C, + name: &'a &'a str, + price: &'a f64, + ) -> RowQuery<'a, C, Row, 2> { + RowQuery { + client, + params: [name, price], + query: "INSERT INTO item (name, price, show) VALUES ($1, $2, false) RETURNING id", + extractor: |row| Row { id: row.get(0) }, + mapper: |it| Row::from(it), + } + } + pub fn named_spaced<'a, C: GenericClient>( + client: &'a mut C, + name: &'a &'a str, + price: &'a f64, + ) -> RowQuery<'a, C, Row, 2> { + RowQuery { + client, + params: [name, price], + query: " +INSERT INTO item (name, price, show) VALUES ($1, $2, false) RETURNING id", + extractor: |row| Row { id: row.get(0) }, + mapper: |it| Row::from(it), + } + } + } } diff --git a/grammar.pest b/grammar.pest deleted file mode 100644 index 936b33df..00000000 --- a/grammar.pest +++ /dev/null @@ -1,105 +0,0 @@ -// SQL -comment = _{ NEWLINE ~ (" " | "\t")* ~ "--" ~ !("!" | ":") ~ (!NEWLINE ~ ANY)*} -string_constant = _{ - "'" - ~ ("''" | (!"'" ~ ANY))* - ~ "'" -} -quoted_identifier = _{ - "\"" - ~ (!"\"" ~ ANY)* - ~ "\"" -} -c_style = _{ - "E" - ~ "'" - ~ ( "\\\\" | "''" | "\\'" | (!"'" ~ ANY))* - ~ "'" -} -dollar_quoted = _{ - "$" ~ PUSH((!"$" ~ ANY)*) ~ "$" - ~ (!("$" ~ POP ~ "$") ~ ANY)* - ~ ("$" ~ POP ~ "$") -} -sql_string = _{string_constant | quoted_identifier | c_style | dollar_quoted} - -// Whitespace -query_annotation_token = _{NEWLINE ~ (" " | "\t")* ~ "--!" | "--!" } -type_annotation_token = _{NEWLINE ~ (" " | "\t")* ~ "--:" | "--:" } -blank = _{ comment | " " | "\t" } -query_ws = _{ query_annotation_token | blank } -type_ws = _{ type_annotation_token | blank } - -// Numbers -number = { ASCII_DIGIT+ } - -// Postgres identifiers -ident = { - !"_"+ - ~ (("_" | LETTER) ~ (LETTER | ASCII_DIGIT | "_")*) -} -nullable = _{ident ~ "?"} -extended_bind_parameter = _{":" ~ ident} -pg_bind_param = _{"$" ~ number} - -// Field list -field_list = { - "(" ~ type_ws* - ~ (nullable ~ type_ws* - ~ ("," ~ type_ws* ~ nullable ~ type_ws*)*)? - ~ ","? ~ type_ws* - ~ ")" -} - -// Type annotation -row = {^"row"} -param = {^"param"} -db = {^"db"} -type_annotation_kind = _{row | param | db} -type_without_nullable_cols = {ident} -type_with_nullable_cols = {ident ~ type_ws* ~ field_list} -type_list = { - "(" ~ type_ws* - ~ ( (type_with_nullable_cols | type_without_nullable_cols) ~ type_ws* - ~ ("," ~ type_ws* ~ (type_with_nullable_cols | type_without_nullable_cols) ~ type_ws*)*)? - ~ ","? ~ type_ws* - ~ ")" -} -type_annotation = { - type_annotation_token - ~ type_ws* ~ type_annotation_kind - ~ type_ws* ~ (type_with_nullable_cols | type_without_nullable_cols) - ~ (NEWLINE*) -} - -// Query annotation -query_param = {(ident | field_list)} -query_row = {(ident | field_list)} - -query_annotation = { - query_annotation_token - ~ (query_ws* ~ ident) - ~ (query_ws* ~ query_param)? - ~ (query_ws* ~ ":" ~ query_ws* ~ query_row)? - ~ (query_ws* ~ NEWLINE+) -} - -// Query -query = { - query_annotation - ~ sql -} - -// SQL Block -sql = { - ( - sql_string - | "::" - | (extended_bind_parameter) - | (!(query_annotation_token | type_annotation_token | pg_bind_param) ~ ANY) - )+ -} - -// Parser -eoi = _{ !ANY } -parser = {SOI ~ (blank* ~ (type_annotation | query))* ~ eoi} \ No newline at end of file diff --git a/src/parser.rs b/src/parser.rs index 12d726f5..5c5312d3 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -66,10 +66,13 @@ fn ident() -> impl Parser, Error = Simple> { }) } fn ln() -> impl Parser> { + // TODO should allow a single new line ? one_of("\n\r").repeated().ignored() } fn space() -> impl Parser> { - one_of(" \t").repeated().ignored() + filter(|c: &char| c.is_whitespace() && *c != '\n') + .repeated() + .ignored() } fn blank() -> impl Parser> { whitespace().or(space() @@ -334,6 +337,9 @@ pub(crate) fn parse_query_module(path: &str, input: &str) -> Result