diff --git a/Cargo.lock b/Cargo.lock index ed70e4f9..3d53c8ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -169,7 +169,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" dependencies = [ "concurrent-queue 2.4.0", - "event-listener 4.0.0", + "event-listener 4.0.1", "event-listener-strategy", "futures-core", "pin-project-lite", @@ -270,7 +270,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c" dependencies = [ - "event-listener 4.0.0", + "event-listener 4.0.1", "event-listener-strategy", "pin-project-lite", ] @@ -361,7 +361,7 @@ checksum = "fdf6721fb0140e4f897002dd086c06f6c27775df19cfe1fccb21181a48fd2c98" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -527,7 +527,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", "syn_derive", ] @@ -665,7 +665,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -764,9 +764,9 @@ checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" [[package]] name = "crossbeam" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" +checksum = "6eb9105919ca8e40d437fc9cbb8f1975d916f1bd28afe795a48aae32a2cc8920" dependencies = [ "cfg-if", "crossbeam-channel", @@ -778,9 +778,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c3242926edf34aec4ac3a77108ad4854bffaa2e4ddc1824124ce59231302d5" +checksum = "82a9b73a36529d9c47029b9fb3a6f0ea3cc916a261195352ba19e770fc1748b2" dependencies = [ "cfg-if", "crossbeam-utils", @@ -799,21 +799,20 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.16" +version = "0.9.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa" +checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset 0.9.0", ] [[package]] name = "crossbeam-queue" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9bcf5bdbfdd6030fb4a1c497b5d5fc5921aa2f60d359a17e249c0e6df3de153" +checksum = "adc6598521bb5a83d491e8c1fe51db7296019d2ca3cb93cc6c2a20369a4d78a2" dependencies = [ "cfg-if", "crossbeam-utils", @@ -821,9 +820,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.17" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f" +checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" dependencies = [ "cfg-if", ] @@ -895,7 +894,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -933,7 +932,7 @@ dependencies = [ "diesel_table_macro_syntax", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -942,7 +941,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" dependencies = [ - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -1154,9 +1153,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "4.0.0" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770d968249b5d99410d61f5bf89057f3199a077a04d087092f58e7d10692baae" +checksum = "84f2cdcf274580f2d63697192d744727b3198894b1bf02923643bf59e2c26712" dependencies = [ "concurrent-queue 2.4.0", "parking", @@ -1169,7 +1168,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" dependencies = [ - "event-listener 4.0.0", + "event-listener 4.0.1", "pin-project-lite", ] @@ -1347,7 +1346,7 @@ checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -2051,7 +2050,7 @@ checksum = "4bb752642ceeaad440a788ea0ee23c0317117068f49dc1f25fd23b26146a225d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -2180,9 +2179,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] @@ -2291,7 +2290,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -2805,9 +2804,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7673e0aa20ee4937c6aacfc12bb8341cfbf054cdd21df6bec5fd0629fe9339b" +checksum = "9e9d979b3ce68192e42760c7810125eb6cf2ea10efae545a156063e61f314e2a" [[package]] name = "rustls-webpki" @@ -2902,7 +2901,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -3269,7 +3268,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta-derive", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -3280,7 +3279,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -3302,9 +3301,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.41" +version = "2.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8" dependencies = [ "proc-macro2", "quote", @@ -3320,7 +3319,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -3381,7 +3380,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -3401,7 +3400,7 @@ checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -3454,7 +3453,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -3562,7 +3561,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] @@ -3778,7 +3777,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", "wasm-bindgen-shared", ] @@ -3812,7 +3811,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4162,22 +4161,22 @@ checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" [[package]] name = "zerocopy" -version = "0.7.31" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.31" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.42", ] [[package]] diff --git a/README.md b/README.md index 001350d4..146f759f 100644 --- a/README.md +++ b/README.md @@ -10,384 +10,20 @@ A collection of different transport implementations and related tools focused pr Embedded devices that have a heap allocator can use this `no_std` crate. -1. [Benchmarks](#benchmarks) -2. [Client API Framework](#client-api-framework) -3. [Database](#database) - - [Client Connection](#client-connection) - - [Object–Relational Mapping](#object–relational-mapping) - - [Schema Management](#schema-management) -4. [WebSocket](#websocket) +Documentation is available at . -# Benchmarks +## Benchmarks -If you disagree with any of the mentioned charts, feel free to checkout `wtx-bench` to point any misunderstandings or misconfigurations. +If you disagree with any of the mentioned charts, feel free to checkout [wtx-bench](https://github.com/c410-f3r/wtx/tree/main/wtx-bench) to point any misunderstandings or misconfigurations. There are mainly 2 things that impact performance, the chosen runtime and the number of pre-allocated bytes. Specially for servers that have to create a new instance for each handshake, pre-allocating a high number of bytes for short-lived or low-transfer connections can have a negative impact. -It is also possible to use libraries that manage pools of bytes to avoid having to heap-allocate all the time. +It is also possible to use libraries that manage pools of resources to avoid having to reconstruct expensive elements all the time. -# Client API Framework +### PostgreSQL client -A flexible client API framework for writing asynchronous, fast, organizable, scalable and maintainable applications. Supports several data formats, transports and custom parameters. +![PostgreSQL Benchmark](https://i.imgur.com/vf2tYxY.jpg) -Activation feature is called `client-api-framework`. Checkout the `wtx-apis` project to see a collection of APIs based on `wtx`. - -## Objective - -It is possible to directly decode responses using built-in methods provided by some transport implementations like `reqwest` or `surf` but as complexity grows, the cost of maintaining large sets of endpoints with ad-hoc solutions usually becomes unsustainable. Based on this scenario, `wtx` comes into play to organize and centralize data flow in a well-defined manner to increase productivity and maintainability. - -For API consumers, the calling convention of `wtx` endpoints is based on fluent interfaces which makes the usage more pleasant and intuitive. - -Moreover, the project may in the future create automatic bindings for other languages in order to avoid having duplicated API repositories. - -# Database - -## Client Connection - -PostgreSQL is currently the only supported database and more SQL or NoSQL variants shouldn't be too difficult to implement architecture-wise. - -Activation feature is called `postgres`. - -![PostgreSQL Benchmark](https://i.imgur.com/UNbwWmA.jpg) - -```rust -#[cfg(feature = "postgres")] -mod postgres { - use core::borrow::BorrowMut; - use wtx::{ - database::{client::postgres::{Executor, ExecutorBuffer}, Executor as _, Record, Records}, - misc::Stream, - }; - - async fn query_foo( - executor: &mut Executor, impl Stream>, - ) -> wtx::Result<(u32, String)> { - let record = executor.fetch_with_stmt::( - "SELECT bar,baz FROM foo WHERE bar = $1 AND baz = $2", - (1u32, "2") - ).await?; - Ok((record.decode("bar")?, record.decode("baz")?)) - } -} -``` - -## Object–Relational Mapping - -A very rudimentary ORM that currently supports very few operations that are not well tested. You probably should look for other similar projects. - -Activation feature is called `orm`. - -```rust -#[cfg(feature = "orm")] -mod orm { - use wtx::database::{ - orm::{Crud, FromSuffixRslt, NoTableAssociation, Table, TableField, TableParams}, - Database, FromRecords, Record, TableSuffix, - }; - - struct User<'conn> { - id: u32, - name: &'conn str, - password: &'conn str, - } - - impl<'conn> FromRecords for User<'conn> { - type Database = (); - type Error = wtx::Error; - - fn from_records( - _: &mut String, - curr_record: &::Record<'_>, - _: &::Records<'_>, - _: TableSuffix, - ) -> Result<(usize, Self), Self::Error> { - let id = curr_record.decode(0)?; - let name = curr_record.decode(1)?; - let password = curr_record.decode(2)?; - Ok((1, Self { id, name, password })) - } - } - - impl<'conn, 'entity> Table<'entity> for User<'conn> { - const PRIMARY_KEY_NAME: &'static str = "id"; - const TABLE_NAME: &'static str = "user"; - - type Associations = NoTableAssociation; - type Error = wtx::Error; - type Fields = (TableField<&'conn str>, TableField<&'conn str>); - type PrimaryKeyValue = &'entity u32; - - fn type_instances(_: TableSuffix) -> FromSuffixRslt<'entity, Self> { - (NoTableAssociation::new(), (TableField::new("name"), TableField::new("password"))) - } - - fn update_all_table_fields(entity: &'entity Self, table: &mut TableParams<'entity, Self>) { - *table.id_field_mut().value_mut() = Some((&entity.id).into()); - *table.fields_mut().0.value_mut() = Some((entity.name).into()); - *table.fields_mut().1.value_mut() = Some((entity.password).into()); - } - } - - async fn all_users<'conn>( - crud: &'conn mut impl Crud, - ) -> wtx::Result>> { - let mut buffer = String::new(); - let mut results = Vec::new(); - crud.read_all::>(&mut buffer, &mut results, &TableParams::default()).await?; - Ok(results) - } -} -``` - -## Schema Management - -Embedded and CLI workflows using raw SQL commands. - -Activation feature is called `sm`. - -### CLI - -```bash -# Example - -cargo install --git https://github.com/c410-f3r/wtx --features sm-dev wtx-ui -echo DATABASE_URI="postgres://USER:PASSWORD@localhost:5432/DATABASE" > .env -RUST_LOG=debug wtx-cli migrate -``` - -The CLI application expects a configuration file that contains a set of paths where each path is a directory with multiple migrations. - -```toml -# wtx.toml - -migration_groups = [ - "migrations/1__initial", - "migrations/2__fancy_stuff" -] -``` - -Each provided migration and group must contain an unique version and a name summarized by the following structure: - -```txt -// Execution order of migrations is dictated by their numeric declaration order. - -migrations -+-- 1__initial (Group) - +-- 1__create_author.sql (Migration) - +-- 2__create_post.sql (Migration) -+-- 2__fancy_stuff (Group) - +-- 1__something_fancy.sql (Migration) -wtx.toml -``` - -The SQL file itself is composed by two parts, one for migrations (`-- wtx IN` section) and another for rollbacks (`-- wtx OUT` section). - -```sql --- wtx IN - -CREATE TABLE author ( - id INT NOT NULL PRIMARY KEY, - added TIMESTAMP NOT NULL, - birthdate DATE NOT NULL, - email VARCHAR(100) NOT NULL, - first_name VARCHAR(50) NOT NULL, - last_name VARCHAR(50) NOT NULL -); - --- wtx OUT - -DROP TABLE author; -``` - -One cool thing about the expected file configuration is that it can also be divided into smaller pieces, for example, the above migration could be transformed into `1__author_up.sql` and `1__author_down.sql`. - -```sql --- 1__author_up.sql - -CREATE TABLE author ( - id INT NOT NULL PRIMARY KEY, - added TIMESTAMP NOT NULL, - birthdate DATE NOT NULL, - email VARCHAR(100) NOT NULL, - first_name VARCHAR(50) NOT NULL, - last_name VARCHAR(50) NOT NULL -); -``` - -```sql --- 1__author_down.sql - -DROP TABLE author; -``` - -```txt -migrations -+-- 1__some_group (Group) - +-- 1__author (Migration directory) - +-- 1__author_down.sql (Down migration) - +-- 1__author_up.sql (Up migration) - +-- 1__author.toml (Optional configuration) -wtx.toml -``` - -### Library - -The library gives freedom to arrange groups and uses some external crates, bringing ~10 additional dependencies into your application. If this overhead is not acceptable, then you probably should discard the library and use the CLI binary instead as part of a custom deployment strategy. - -```rust -#[cfg(all(feature = "sm", feature = "std"))] -mod sm { - use wtx::{ - database::{sm::Commands, DEFAULT_URI_VAR}, - misc::UriParts, - rng::StaticRng, - }; - use std::path::Path; - - #[tokio::main] - async fn main() { - let mut commands = Commands::with_executor(()); - commands - .migrate_from_dir( - (&mut <_>::default(), &mut <_>::default()), - Path::new("my_custom_migration_group_path"), - ) - .await - .unwrap(); - } -} -``` - -### Embedded migrations - -To make deployment easier, the final binary of your application can embed all necessary migrations through the binary that is available in the `wtx-ui` crate. - -```rust -#[cfg(feature = "sm")] -mod sm { - // This is an example! The actual contents are filled by the `wtx-ui embed-migrations` binary call. - mod embedded_migrations { - pub(crate) const GROUPS: wtx::database::sm::EmbeddedMigrationsTy = &[]; - } - - use wtx::database::sm::Commands; - - async fn migrate() -> wtx::Result<()> { - Commands::with_executor(()) - .migrate_from_groups((&mut String::new(), &mut Vec::new()), embedded_migrations::GROUPS) - .await - } -} -``` - -### Conditional migrations - -If one particular migration needs to be executed in a specific set of databases, then it is possible to use the `-- wtx dbs` parameter in a file. - -```sql --- wtx dbs mssql,postgres - --- wtx IN - -CREATE SCHEMA foo; - --- wtx OUT - -DROP SCHEMA foo; -``` - -### Repeatable migrations - -Repeatability can be specified with `-- wtx repeatability SOME_VALUE` where `SOME_VALUE` can be either `always` (regardless of the checksum) or `on-checksum-change` (runs only when the checksums changes). - -```sql --- wtx dbs postgres --- wtx repeatability always - --- wtx IN - -CREATE OR REPLACE PROCEDURE something() LANGUAGE SQL AS $$ $$ - --- wtx OUT - -DROP PROCEDURE something(); -``` - -Keep in mind that repeatable migrations might break subsequent operations, therefore, you must known what you are doing. If desirable, they can be separated into dedicated groups. - -```ini -migrations/1__initial_repeatable_migrations -migrations/2__normal_migrations -migrations/3__final_repeatable_migrations -``` - -### Namespaces/Schemas - -For supported databases, there is no direct user parameter that inserts migrations inside a single database schema but it is possible to specify the schema inside the SQL file and arrange the migration groups structure in a way that most suits you. - -```sql --- wtx IN - -CREATE TABLE cool_department_schema.author ( - id INT NOT NULL PRIMARY KEY, - full_name VARCHAR(50) NOT NULL -); - --- wtx OUT - -DROP TABLE cool_department_schema.author; -``` - -# WebSocket - -Provides low and high level abstractions to dispatch frames, as such, it is up to you to implement [Stream](https://docs.rs/wtx/latest/wtx/trait.Stream.html) with any desired logic or use any of the built-in strategies through the selection of features. - -Activation feature is called `web-socket`. +### WebSocket ![WebSocket Benchmark](https://i.imgur.com/Iv2WzJV.jpg) - -```rust -#[cfg(feature = "web-socket")] -mod web_socket { - use wtx::{ - misc::Stream, - rng::Rng, - web_socket::{ - FrameBufferVec, FrameMutVec, FrameVecMut, compression::NegotiatedCompression, OpCode, - WebSocketClientOwned - } - }; - - pub async fn handle_client_frames( - fb: &mut FrameBufferVec, - ws: &mut WebSocketClientOwned - ) -> wtx::Result<()> { - loop { - let frame = match ws.read_frame(fb).await { - Err(err) => { - println!("Error: {err}"); - ws.write_frame(&mut FrameMutVec::new_fin(fb, OpCode::Close, &[])?).await?; - break; - } - Ok(elem) => elem, - }; - match (frame.op_code(), frame.text_payload()) { - (_, Some(elem)) => println!("{elem}"), - (OpCode::Close, _) => break, - _ => {} - } - } - Ok(()) - } -} -``` - -See the `examples` directory for more suggestions. - -## Autobahn - -All the `fuzzingclient`/`fuzzingserver` tests provided by the Autobahn|Testsuite project are passing and the full reports can found at . - -## Compression - -The "permessage-deflate" extension, described in [RFC-7692](https://datatracker.ietf.org/doc/html/rfc7692), is the only supported compression format and is backed by the fastest compression library available at the current time, which is "zlib-ng". It also means that a C compiler is required to use such a feature. diff --git a/wtx-bench/src/main.rs b/wtx-bench/src/main.rs index 91735551..28f8e468 100644 --- a/wtx-bench/src/main.rs +++ b/wtx-bench/src/main.rs @@ -17,7 +17,7 @@ mod misc; mod postgres; mod web_socket; -use wtx::misc::UriPartsRef; +use wtx::misc::UriRef; #[tokio::main] async fn main() { @@ -25,13 +25,13 @@ async fn main() { match args.as_slice() { [first, second, rest @ ..] => match first.as_str() { "postgres" => { - let up = UriPartsRef::new(second.as_str()); + let uri = UriRef::new(second.as_str()); let mut diesel_async = misc::Agent { name: "diesel-async".to_owned(), result: 0 }; let mut sqlx_postgres = misc::Agent { name: "sqlx-postgres-tokio".to_owned(), result: 0 }; let mut tokio_postgres = misc::Agent { name: "tokio-postgres".to_owned(), result: 0 }; let mut wtx = misc::Agent { name: "wtx-tokio".to_owned(), result: 0 }; postgres::bench( - &up, + &uri, [&mut diesel_async, &mut sqlx_postgres, &mut tokio_postgres, &mut wtx], ) .await; @@ -43,10 +43,10 @@ async fn main() { } "web-socket" => { let mut agents = Vec::new(); - for uri in [second].into_iter().chain(rest) { - let up = UriPartsRef::new(uri.as_str()); - let mut agent = misc::Agent { name: up.href().to_owned(), result: 0 }; - web_socket::bench(up.authority(), &mut agent, uri).await; + for uri_string in [second].into_iter().chain(rest) { + let uri = UriRef::new(uri_string.as_str()); + let mut agent = misc::Agent { name: uri.href().to_owned(), result: 0 }; + web_socket::bench(&mut agent, &uri).await; agents.push(agent); } misc::plot(&agents, &web_socket::caption(), "/tmp/wtx-web-socket.png"); diff --git a/wtx-bench/src/postgres.rs b/wtx-bench/src/postgres.rs index c1b58014..c55b4340 100644 --- a/wtx-bench/src/postgres.rs +++ b/wtx-bench/src/postgres.rs @@ -1,7 +1,8 @@ use crate::misc::Agent; -use diesel_async::AsyncPgConnection; +use diesel::prelude::*; +use diesel_async::{AsyncPgConnection, RunQueryDsl}; use futures::stream::StreamExt; -use sqlx::{Connection, Either, Executor as _, Row}; +use sqlx::{Connection, Either, Executor as _, Row, Statement}; use std::time::Instant; use tokio::{net::TcpStream, task::JoinSet}; use tokio_postgres::NoTls; @@ -10,28 +11,28 @@ use wtx::{ client::postgres::{Executor, ExecutorBuffer}, Executor as _, Record as _, Records, }, - misc::UriPartsRef, + misc::UriRef, rng::{Rng, StdRng}, }; // Verifies the handling of concurrent calls. const CONNECTIONS: usize = 64; // Bytes to create and receive. -const DATA_LEN: usize = 1024; +const DATA_LEN: usize = 32 * 1024; // Number of sequential `SELECT` statements. -const QUERIES: usize = 1024; +const QUERIES: usize = 8 * 1024; const SELECT_QUERY: &str = "SELECT * FROM benchmark"; pub(crate) async fn bench( - up: &UriPartsRef<'_>, + uri: &UriRef<'_>, [diesel_async, sqlx_postgres, tokio_postgres, wtx]: [&mut Agent; 4], ) { - populate_db(&mut StdRng::default(), up).await; - bench_diesel_async(diesel_async, up).await; - bench_sqlx_postgres(sqlx_postgres, up).await; - bench_tokio_postgres(tokio_postgres, up).await; - bench_wtx(wtx, up).await; + populate_db(&mut StdRng::default(), uri).await; + bench_diesel_async(diesel_async, uri).await; + bench_sqlx_postgres(sqlx_postgres, uri).await; + bench_tokio_postgres(tokio_postgres, uri).await; + bench_wtx(wtx, uri).await; } pub(crate) fn caption() -> String { @@ -41,10 +42,7 @@ pub(crate) fn caption() -> String { } #[allow(clippy::single_char_lifetime_names, unused_qualifications, clippy::shadow_unrelated)] -async fn bench_diesel_async(agent: &mut Agent, up: &UriPartsRef<'_>) { - use diesel::prelude::*; - use diesel_async::RunQueryDsl; - +async fn bench_diesel_async(agent: &mut Agent, uri: &UriRef<'_>) { table! { benchmark(bar, baz) { bar -> Text, @@ -52,27 +50,27 @@ async fn bench_diesel_async(agent: &mut Agent, up: &UriPartsRef<'_>) { } } - let instant = Instant::now(); let mut set = JoinSet::new(); for _ in 0..CONNECTIONS { let _handle = set.spawn({ - let local_up = up.clone().into_string(); + let local_uri = uri.to_string(); async move { let (client, conn) = tokio_postgres::Config::new() - .dbname(local_up.path().get(1..).unwrap()) - .host(local_up.hostname()) - .password(local_up.password()) - .port(local_up.port().parse().unwrap()) - .user(local_up.user()) + .dbname(local_uri.path().get(1..).unwrap()) + .host(local_uri.hostname()) + .password(local_uri.password()) + .port(local_uri.port().parse().unwrap()) + .user(local_uri.user()) .connect(NoTls) .await .unwrap(); let _handle = tokio::spawn(async move { - if let Err(e) = conn.await { - println!("Error: {e}"); + if let Err(err) = conn.await { + println!("Error: {err}"); } }); let mut pg_conn = AsyncPgConnection::try_from(client).await.unwrap(); + let instant = Instant::now(); for _ in 0..QUERIES { let records = benchmark::table.load::<(String, String)>(&mut pg_conn).await.unwrap(); assert!(!records[0].0.is_empty()); @@ -80,27 +78,25 @@ async fn bench_diesel_async(agent: &mut Agent, up: &UriPartsRef<'_>) { assert!(!records[1].0.is_empty()); assert!(!records[1].1.is_empty()); } + instant.elapsed().as_millis() } }); } - while let Some(rslt) = set.join_next().await { - rslt.unwrap(); - } - agent.result = instant.elapsed().as_millis(); + exec(agent, &mut set).await; } -async fn bench_sqlx_postgres(agent: &mut Agent, up: &UriPartsRef<'_>) { - let instant = Instant::now(); +async fn bench_sqlx_postgres(agent: &mut Agent, uri: &UriRef<'_>) { let mut set = JoinSet::new(); for _ in 0..CONNECTIONS { let _handle = set.spawn({ - let local_uri = up.uri().to_owned(); + let local_uri = uri.uri().to_owned(); async move { let mut conn = sqlx::postgres::PgConnection::connect(&local_uri).await.unwrap(); - let _stmt = conn.prepare(SELECT_QUERY).await.unwrap(); + let stmt = conn.prepare(SELECT_QUERY).await.unwrap(); + let instant = Instant::now(); for _ in 0..QUERIES { let mut rows = Vec::new(); - let mut stream = conn.fetch_many(SELECT_QUERY); + let mut stream = stmt.query().fetch_many(&mut conn); while let Some(result) = stream.next().await { match result.unwrap() { Either::Left(_) => {} @@ -112,37 +108,35 @@ async fn bench_sqlx_postgres(agent: &mut Agent, up: &UriPartsRef<'_>) { assert!(!rows[1].get::<&str, _>(0).is_empty()); assert!(!rows[1].get::<&str, _>(1).is_empty()); } + instant.elapsed().as_millis() } }); } - while let Some(rslt) = set.join_next().await { - rslt.unwrap(); - } - agent.result = instant.elapsed().as_millis(); + exec(agent, &mut set).await; } -async fn bench_tokio_postgres(agent: &mut Agent, up: &UriPartsRef<'_>) { - let instant = Instant::now(); +async fn bench_tokio_postgres(agent: &mut Agent, uri: &UriRef<'_>) { let mut set = JoinSet::new(); for _ in 0..CONNECTIONS { let _handle = set.spawn({ - let local_up = up.clone().into_string(); + let local_uri = uri.to_string(); async move { let (client, conn) = tokio_postgres::Config::new() - .dbname(local_up.path().get(1..).unwrap()) - .host(local_up.hostname()) - .password(local_up.password()) - .port(local_up.port().parse().unwrap()) - .user(local_up.user()) + .dbname(local_uri.path().get(1..).unwrap()) + .host(local_uri.hostname()) + .password(local_uri.password()) + .port(local_uri.port().parse().unwrap()) + .user(local_uri.user()) .connect(NoTls) .await .unwrap(); let _handle = tokio::spawn(async move { - if let Err(e) = conn.await { - println!("Error: {e}"); + if let Err(err) = conn.await { + println!("Error: {err}"); } }); let stmt = client.prepare(SELECT_QUERY).await.unwrap(); + let instant = Instant::now(); for _ in 0..QUERIES { let rows = client.query(&stmt, &[]).await.unwrap(); assert!(!rows[0].get::<_, &str>(0).is_empty()); @@ -150,24 +144,22 @@ async fn bench_tokio_postgres(agent: &mut Agent, up: &UriPartsRef<'_>) { assert!(!rows[1].get::<_, &str>(0).is_empty()); assert!(!rows[1].get::<_, &str>(1).is_empty()); } + instant.elapsed().as_millis() } }); } - while let Some(rslt) = set.join_next().await { - rslt.unwrap(); - } - agent.result = instant.elapsed().as_millis(); + exec(agent, &mut set).await; } -async fn bench_wtx(agent: &mut Agent, up: &UriPartsRef<'_>) { - let instant = Instant::now(); +async fn bench_wtx(agent: &mut Agent, uri: &UriRef<'_>) { let mut set = JoinSet::new(); for _ in 0..CONNECTIONS { let _handle = set.spawn({ - let local_up = up.clone().into_string(); + let local_uri = uri.to_string(); async move { - let mut executor = wtx_executor(&mut StdRng::default(), &local_up.as_ref()).await; + let mut executor = wtx_executor(&mut StdRng::default(), &local_uri.to_ref()).await; let stmt = executor.prepare(SELECT_QUERY).await.unwrap(); + let instant = Instant::now(); for _ in 0..QUERIES { let records = executor.fetch_many_with_stmt(stmt, (), |_| Ok(())).await.unwrap(); assert!(!records.get(0).unwrap().decode::<_, &str>(0).unwrap().is_empty()); @@ -175,13 +167,11 @@ async fn bench_wtx(agent: &mut Agent, up: &UriPartsRef<'_>) { assert!(!records.get(1).unwrap().decode::<_, &str>(0).unwrap().is_empty()); assert!(!records.get(1).unwrap().decode::<_, &str>(1).unwrap().is_empty()); } + instant.elapsed().as_millis() } }); } - while let Some(rslt) = set.join_next().await { - rslt.unwrap(); - } - agent.result = instant.elapsed().as_millis(); + exec(agent, &mut set).await; } fn fill_and_split_data<'data>( @@ -199,8 +189,16 @@ fn fill_and_split_data<'data>( data.split_at(data.len() / 2) } -async fn populate_db(rng: &mut StdRng, up: &UriPartsRef<'_>) { - let mut executor = wtx_executor(rng, up).await; +async fn exec(agent: &mut Agent, set: &mut JoinSet) { + let mut sum = 0; + while let Some(rslt) = set.join_next().await { + sum += rslt.unwrap(); + } + agent.result = sum / u128::try_from(CONNECTIONS).unwrap(); +} + +async fn populate_db(rng: &mut StdRng, uri: &UriRef<'_>) { + let mut executor = wtx_executor(rng, uri).await; let mut data = String::new(); let _ = executor.execute_with_stmt("DROP TABLE IF EXISTS benchmark", ()).await.unwrap(); let _ = executor @@ -220,15 +218,12 @@ async fn populate_db(rng: &mut StdRng, up: &UriPartsRef<'_>) { .unwrap(); } -async fn wtx_executor( - rng: &mut StdRng, - up: &UriPartsRef<'_>, -) -> Executor { +async fn wtx_executor(rng: &mut StdRng, uri: &UriRef<'_>) -> Executor { Executor::connect( - &wtx::database::client::postgres::Config::from_uri_parts(up).unwrap(), + &wtx::database::client::postgres::Config::from_uri(uri).unwrap(), ExecutorBuffer::with_default_params(rng), rng, - TcpStream::connect(up.host()).await.unwrap(), + TcpStream::connect(uri.host()).await.unwrap(), ) .await .unwrap() diff --git a/wtx-bench/src/web_socket.rs b/wtx-bench/src/web_socket.rs index 5d4526f4..dc6cbaf3 100644 --- a/wtx-bench/src/web_socket.rs +++ b/wtx-bench/src/web_socket.rs @@ -4,6 +4,7 @@ use crate::misc::Agent; use std::time::Instant; use tokio::{net::TcpStream, task::JoinSet}; use wtx::{ + misc::UriRef, rng::StaticRng, web_socket::{ handshake::{WebSocketConnect, WebSocketConnectRaw}, @@ -34,13 +35,12 @@ const NUM_FRAMES: usize = { } }; -pub(crate) async fn bench(addr: &str, agent: &mut Agent, uri: &str) { +pub(crate) async fn bench(agent: &mut Agent, uri: &UriRef<'_>) { let instant = Instant::now(); let mut set = JoinSet::new(); for _ in 0..CONNECTIONS { let _handle = set.spawn({ - let local_addr: String = addr.to_owned(); - let local_uri = uri.to_owned(); + let local_uri = uri.to_string(); async move { let fb = &mut FrameBufferVec::default(); let (_, mut ws) = WebSocketConnectRaw { @@ -48,8 +48,8 @@ pub(crate) async fn bench(addr: &str, agent: &mut Agent, uri: &str) { fb, headers_buffer: &mut <_>::default(), rng: StaticRng::default(), - stream: TcpStream::connect(&local_addr).await.unwrap(), - uri: &local_uri, + stream: TcpStream::connect(local_uri.authority()).await.unwrap(), + uri: &local_uri.to_ref(), wsb: WebSocketBuffer::default(), } .connect() diff --git a/wtx-macros/src/api_types.rs b/wtx-macros/src/api_types.rs index 0a048d88..8dc1b7eb 100644 --- a/wtx-macros/src/api_types.rs +++ b/wtx-macros/src/api_types.rs @@ -35,9 +35,10 @@ pub(crate) fn api_types( Item::Type(container) => &mut container.ident, _ => return Err(crate::Error::NoEnumStructOrType(item.span())), }; + let api_string = api_ident.to_string(); let mut buffer = String::new(); - buffer.push_str(&api_ident.to_string()); + buffer.push_str(&api_string); let generic_pair_ident = create_ident(&mut buffer, ["Pair"]); let generic_pair_tt = quote::quote_spanned!(api_ident.span() => @@ -51,10 +52,16 @@ pub(crate) fn api_types( let generic_pkgs_aux_ident = create_ident(&mut buffer, ["PkgsAux"]); let generic_pkgs_aux_tt = quote::quote_spanned!(api_ident.span() => - #[doc = concat!("[", stringify!(#pkgs_aux_path), "] with [", stringify!(#api_ident), "] as the API.")] + #[doc = concat!("[", stringify!(#pkgs_aux_path), "] with an owned [", stringify!(#api_ident), "] as the API.")] pub type #generic_pkgs_aux_ident = #pkgs_aux_path<#api_ident, DRSR, TP>; ); + let generic_mut_pkgs_aux_ident = create_ident(&mut buffer, ["MutPkgsAux"]); + let generic_mut_pkgs_aux_tt = quote::quote_spanned!(api_ident.span() => + #[doc = concat!("[", stringify!(#pkgs_aux_path), "] with a mutable reference of [", stringify!(#api_ident), "] as the API.")] + pub type #generic_mut_pkgs_aux_ident<'api, DRSR, TP> = #pkgs_aux_path<&'api mut #api_ident, DRSR, TP>; + ); + let mut tys = Vec::new(); let mut custom_placeholder; @@ -71,17 +78,39 @@ pub(crate) fn api_types( TransportGroup::WebSocket => ["Ws", "WsParams"], }; let local_tp_ident = Ident::new(params, Span::mixed_site()); - let local_ty_ident = create_ident(&mut buffer, [camel_abbr, "PkgsAux"]); + let local_ty_ident_api_tp = create_ident(&mut buffer, [camel_abbr, "PkgsAux"]); + let local_ty_ident_api_mut_tp = create_ident(&mut buffer, ["Mut", camel_abbr, "PkgsAux"]); + buffer.clear(); + let local_ty_ident_tp = create_ident(&mut buffer, [camel_abbr, "PkgsAux"]); + buffer.push_str(&api_string); tys.push(quote::quote!( #[allow(unused_qualifications)] #[doc = concat!( - "[", stringify!(#pkgs_aux_path), "] with [", + "[", stringify!(#pkgs_aux_path), "] with an owned [", + stringify!(#api_ident), + "] as the API and [wtx::client_api_framework::network::", + stringify!(#local_tp_ident), + "] as the transport parameter." + )] + pub type #local_ty_ident_api_tp = #pkgs_aux_path<#api_ident, DRSR, wtx::client_api_framework::network::#local_tp_ident>; + + #[allow(unused_qualifications)] + #[doc = concat!( + "[", stringify!(#pkgs_aux_path), "] with a mutable reference of [", stringify!(#api_ident), "] as the API and [wtx::client_api_framework::network::", stringify!(#local_tp_ident), - "] as the transport parameters." + "] as the transport parameter." + )] + pub type #local_ty_ident_api_mut_tp<'api, DRSR> = #pkgs_aux_path<&'api mut #api_ident, DRSR, wtx::client_api_framework::network::#local_tp_ident>; + + #[allow(unused_qualifications)] + #[doc = concat!( + "[", stringify!(#pkgs_aux_path), "] with [wtx::client_api_framework::network::", + stringify!(#local_tp_ident), + "] as the transport parameter." )] - pub type #local_ty_ident = #pkgs_aux_path<#api_ident, DRSR, wtx::client_api_framework::network::#local_tp_ident>; + pub type #local_ty_ident_tp = #pkgs_aux_path; )); } @@ -90,6 +119,7 @@ pub(crate) fn api_types( #item #generic_pair_tt #generic_pkgs_aux_tt + #generic_mut_pkgs_aux_tt #(#tys)* ) .to_token_stream() diff --git a/wtx-macros/src/pkg/fir/fir_after_sending_item_values.rs b/wtx-macros/src/pkg/fir/fir_after_sending_item_values.rs index 42f2e07d..184837b0 100644 --- a/wtx-macros/src/pkg/fir/fir_after_sending_item_values.rs +++ b/wtx-macros/src/pkg/fir/fir_after_sending_item_values.rs @@ -5,7 +5,7 @@ create_fir_hook_item_values!( "after_sending", |arg| { Some(match arg { - "api" => quote::quote!(_api), + "api" => quote::quote!(_api.borrow_mut()), "params" => quote::quote!(&mut self.params), "res_params" => quote::quote!(_ext_res_params), _ => return None, diff --git a/wtx-macros/src/pkg/fir/fir_before_sending_item_values.rs b/wtx-macros/src/pkg/fir/fir_before_sending_item_values.rs index 50471255..1f2a79e6 100644 --- a/wtx-macros/src/pkg/fir/fir_before_sending_item_values.rs +++ b/wtx-macros/src/pkg/fir/fir_before_sending_item_values.rs @@ -5,7 +5,7 @@ create_fir_hook_item_values!( "before_sending", |arg| { Some(match arg { - "api" => quote::quote!(_api), + "api" => quote::quote!(_api.borrow_mut()), "params" => quote::quote!(&mut self.params), "req_bytes" => quote::quote!(_req_bytes), "req_params" => quote::quote!(_ext_req_params), diff --git a/wtx-macros/src/pkg/fir/fir_pkg_attr.rs b/wtx-macros/src/pkg/fir/fir_pkg_attr.rs index a5cd08be..4458a8c0 100644 --- a/wtx-macros/src/pkg/fir/fir_pkg_attr.rs +++ b/wtx-macros/src/pkg/fir/fir_pkg_attr.rs @@ -6,7 +6,6 @@ const EMPTY_NESTED_META: &Punctuated = &Punctuated::new() pub(crate) struct FirPkgAttr<'attrs> { pub(crate) api: &'attrs Path, pub(crate) data_formats: &'attrs Punctuated, - pub(crate) error: Option<&'attrs Path>, pub(crate) transports: &'attrs Punctuated, } @@ -16,7 +15,6 @@ impl<'attrs> TryFrom<&'attrs [NestedMeta]> for FirPkgAttr<'attrs> { fn try_from(from: &'attrs [NestedMeta]) -> Result { let mut api = None; let mut data_formats = None; - let mut error = None; let mut transports = None; for nested_meta in from { let NestedMeta::Meta(Meta::List(meta_list)) = nested_meta else { @@ -32,9 +30,6 @@ impl<'attrs> TryFrom<&'attrs [NestedMeta]> for FirPkgAttr<'attrs> { "data_format" => { data_formats = Some(&meta_list.nested); } - "error" => { - error = first_nested_meta_path(meta_list); - } "transport" => { transports = Some(&meta_list.nested); } @@ -44,7 +39,6 @@ impl<'attrs> TryFrom<&'attrs [NestedMeta]> for FirPkgAttr<'attrs> { Ok(Self { api: api.ok_or(crate::Error::MandatoryOuterAttrsAreNotPresent)?, data_formats: data_formats.unwrap_or(EMPTY_NESTED_META), - error, transports: transports.unwrap_or(EMPTY_NESTED_META), }) } diff --git a/wtx-macros/src/pkg/sir/sir_final_values.rs b/wtx-macros/src/pkg/sir/sir_final_values.rs index 26f80bf7..13f6cd29 100644 --- a/wtx-macros/src/pkg/sir/sir_final_values.rs +++ b/wtx-macros/src/pkg/sir/sir_final_values.rs @@ -74,7 +74,7 @@ impl<'attrs, 'module, 'others> let FirParamsItemValues { fpiv_ty, fpiv_params, fpiv_where_predicates, .. } = &fpiv; let FirReqItemValues { freqdiv_ident, freqdiv_params, freqdiv_where_predicates, .. } = freqdiv; let FirResItemValues { res_ident } = fresdiv; - let SirPkaAttr { api, data_formats, error, transport_groups } = &spa; + let SirPkaAttr { api, data_formats, transport_groups } = &spa; let camel_case_pkg_ident = &{ let idx = camel_case_id.len(); camel_case_id.push_str("Pkg"); @@ -120,8 +120,9 @@ impl<'attrs, 'module, 'others> impl< #(#lts,)* #(#tys,)* + A, DRSR - > wtx::client_api_framework::pkg::Package for #camel_case_pkg_ident< + > wtx::client_api_framework::pkg::Package for #camel_case_pkg_ident< #(#fpiv_params_iter,)* wtx::client_api_framework::data_format::#dfe_ext_req_ctnt_wrapper<#freqdiv_ident<#freqdiv_params>> > @@ -134,10 +135,9 @@ impl<'attrs, 'module, 'others> wtx::client_api_framework::data_format::#dfe_ext_res_ctnt_wrapper< #res_ident >: wtx::client_api_framework::dnsn::Deserialize, + A: wtx::client_api_framework::Api::Error> + core::borrow::BorrowMut<#api>, DRSR: wtx::misc::AsyncBounds, { - type Api = #api; - type Error = #error; type ExternalRequestContent = wtx::client_api_framework::data_format::#dfe_ext_req_ctnt_wrapper< #freqdiv_ident<#freqdiv_params> >; @@ -149,9 +149,9 @@ impl<'attrs, 'module, 'others> #[inline] async fn after_sending( &mut self, - _api: &mut Self::Api, + _api: &mut A, _ext_res_params: &mut <#tp as wtx::client_api_framework::network::transport::TransportParams>::ExternalResponseParams, - ) -> Result<(), Self::Error> { + ) -> Result<(), A::Error> { #( #fasiv_fn_name_ident_iter(#fasiv_fn_call_idents).await?; )* Ok(()) } @@ -159,10 +159,10 @@ impl<'attrs, 'module, 'others> #[inline] async fn before_sending( &mut self, - _api: &mut Self::Api, + _api: &mut A, _ext_req_params: &mut <#tp as wtx::client_api_framework::network::transport::TransportParams>::ExternalRequestParams, _req_bytes: &[u8], - ) -> Result<(), Self::Error> { + ) -> Result<(), A::Error> { #before_sending_defaults #( #fbsiv_fn_name_ident_iter(#fbsiv_fn_call_idents).await?; )* Ok(()) diff --git a/wtx-macros/src/pkg/sir/sir_pkg_attr.rs b/wtx-macros/src/pkg/sir/sir_pkg_attr.rs index 588bdd68..ab702f2a 100644 --- a/wtx-macros/src/pkg/sir/sir_pkg_attr.rs +++ b/wtx-macros/src/pkg/sir/sir_pkg_attr.rs @@ -1,16 +1,13 @@ use crate::{ - owned_or_ref::OwnedOrRef, pkg::{data_format::DataFormat, fir::fir_pkg_attr::FirPkgAttr}, transport_group::TransportGroup, }; -use proc_macro2::{Ident, Span}; -use syn::{punctuated::Punctuated, Path, PathArguments, PathSegment}; +use syn::Path; #[derive(Debug)] pub(crate) struct SirPkaAttr<'attrs> { pub(crate) api: &'attrs Path, pub(crate) data_formats: Vec, - pub(crate) error: OwnedOrRef<'attrs, Path>, pub(crate) transport_groups: Vec, } @@ -29,21 +26,6 @@ impl<'attrs> TryFrom> for SirPkaAttr<'attrs> { Ok(Self { api: fea.api, data_formats, - error: fea.error.map_or_else( - || { - let mut segments = Punctuated::new(); - segments.push(PathSegment { - ident: Ident::new("wtx", Span::mixed_site()), - arguments: PathArguments::None, - }); - segments.push(PathSegment { - ident: Ident::new("Error", Span::mixed_site()), - arguments: PathArguments::None, - }); - OwnedOrRef::Owned(Path { leading_colon: None, segments }) - }, - OwnedOrRef::Ref, - ), transport_groups: fea .transports .into_iter() diff --git a/wtx-macros/tests/ui/pass/custom_transport.rs b/wtx-macros/tests/ui/pass/custom_transport.rs index 7e310711..6f90d1a7 100644 --- a/wtx-macros/tests/ui/pass/custom_transport.rs +++ b/wtx-macros/tests/ui/pass/custom_transport.rs @@ -6,6 +6,7 @@ use wtx::client_api_framework::network::TransportGroup; use wtx::client_api_framework::network::transport::Transport; use wtx::client_api_framework::network::transport::TransportParams; use core::ops::Range; +use wtx::client_api_framework::Api; struct CustomTransport; @@ -13,24 +14,26 @@ impl Transport for CustomTransport { const GROUP: TransportGroup = TransportGroup::Custom("Custom"); type Params = CustomTransportParams; - async fn send

( + async fn send( &mut self, _: &mut P, - _: &mut PkgsAux, - ) -> Result<(), P::Error> + _: &mut PkgsAux, + ) -> Result<(), A::Error> where - P: Package, + A: Api, + P: Package, { Ok(()) } - async fn send_and_retrieve

( + async fn send_and_retrieve( &mut self, _: &mut P, - _: &mut PkgsAux, - ) -> Result, P::Error> + _: &mut PkgsAux, + ) -> Result, A::Error> where - P: Package, + A: Api, + P: Package, { Ok(0..0) } diff --git a/wtx-macros/tests/ui/pass/pkgs_aux_bounds.rs b/wtx-macros/tests/ui/pass/pkgs_aux_bounds.rs new file mode 100644 index 00000000..82c4787d --- /dev/null +++ b/wtx-macros/tests/ui/pass/pkgs_aux_bounds.rs @@ -0,0 +1,30 @@ +//! `#[pkg::aux]` accepts bounds + +wtx::create_packages_aux_wrapper!(); + +type Api = (); + +/// Generic API +pub trait GenericApi {} + +impl GenericApi for &mut Api {} +impl GenericApi for Api {} +impl GenericApi for Box {} + +#[wtx_macros::pkg(api(super::Api), data_format(json), transport(http))] +mod pkg { + #[pkg::aux] + impl super::PkgsAux + where + A: super::GenericApi + {} + + #[pkg::req_data] + pub(crate) type FooReq<'any> = &'any (); + + #[pkg::res_data] + pub(crate) type FooRes = (); +} + +fn main() { +} diff --git a/wtx-ui/src/sm.rs b/wtx-ui/src/sm.rs index b6021bd2..fc5624a3 100644 --- a/wtx-ui/src/sm.rs +++ b/wtx-ui/src/sm.rs @@ -5,7 +5,7 @@ use wtx::{ sm::{Commands, DbMigration, SchemaManagement, DEFAULT_CFG_FILE_NAME}, Identifier, DEFAULT_URI_VAR, }, - misc::UriPartsRef, + misc::UriRef, }; pub(crate) async fn sm(sm: &Sm) -> wtx::Result<()> { @@ -18,8 +18,8 @@ pub(crate) async fn sm(sm: &Sm) -> wtx::Result<()> { wtx::misc::tracing_subscriber_init()?; let var = std::env::var(DEFAULT_URI_VAR)?; - let up = UriPartsRef::new(&var); - match up.schema() { + let uri = UriRef::new(&var); + match uri.schema() { "postgres" | "postgresql" => { handle_commands((), sm).await?; } diff --git a/wtx-ui/src/web_socket.rs b/wtx-ui/src/web_socket.rs index 7e65b624..9a99f4ae 100644 --- a/wtx-ui/src/web_socket.rs +++ b/wtx-ui/src/web_socket.rs @@ -3,7 +3,7 @@ use tokio::{ net::{TcpListener, TcpStream}, }; use wtx::{ - misc::UriPartsRef, + misc::UriRef, rng::StdRng, web_socket::{ handshake::{WebSocketAccept, WebSocketAcceptRaw, WebSocketConnect, WebSocketConnectRaw}, @@ -12,7 +12,7 @@ use wtx::{ }; pub(crate) async fn _connect(uri: &str, cb: impl Fn(&str)) -> wtx::Result<()> { - let uri_parts = UriPartsRef::new(uri); + let uri = UriRef::new(uri); let fb = &mut FrameBufferVec::default(); let pb = &mut <_>::default(); let (_, mut ws) = WebSocketConnectRaw { @@ -20,8 +20,8 @@ pub(crate) async fn _connect(uri: &str, cb: impl Fn(&str)) -> wtx::Result<()> { headers_buffer: &mut <_>::default(), wsb: pb, rng: StdRng::default(), - stream: TcpStream::connect(uri_parts.host()).await?, - uri, + stream: TcpStream::connect(uri.host()).await?, + uri: &uri, compression: (), } .connect() @@ -53,8 +53,8 @@ pub(crate) async fn _serve( error: fn(wtx::Error), str: fn(&str), ) -> wtx::Result<()> { - let uri_parts = UriPartsRef::new(uri); - let listener = TcpListener::bind(uri_parts.host()).await?; + let uri = UriRef::new(uri); + let listener = TcpListener::bind(uri.host()).await?; loop { let (stream, _) = listener.accept().await?; let _jh = tokio::spawn(async move { diff --git a/wtx/Cargo.toml b/wtx/Cargo.toml index ba7978b0..2a0cecb2 100644 --- a/wtx/Cargo.toml +++ b/wtx/Cargo.toml @@ -1,41 +1,51 @@ [[bin]] name = "autobahn-client" +path = "src/bin/autobahn-client.rs" required-features = ["flate2", "tokio/rt-multi-thread", "web-socket-handshake"] [[bin]] name = "autobahn-server" +path = "src/bin/autobahn-server.rs" required-features = ["flate2", "tokio/rt-multi-thread", "web-socket-handshake"] [[example]] name = "database-client-postgres-tokio-rustls" +path = "examples/database-client-postgres-tokio-rustls.rs" required-features = ["_tokio-rustls-client", "postgres" ] [[example]] name = "web-socket-client-cli-raw-tokio-rustls" +path = "examples/web-socket-client-cli-raw-tokio-rustls.rs" required-features = ["_tokio-rustls-client", "tokio/io-std", "web-socket-handshake"] [[example]] name = "web-socket-server-echo-raw-async-std" +path = "examples/web-socket-server-echo-raw-async-std.rs" required-features = ["async-std/attributes", "web-socket-handshake"] [[example]] name = "web-socket-server-echo-raw-glommio" +path = "examples/web-socket-server-echo-raw-glommio.rs" required-features = ["glommio", "web-socket-handshake"] [[example]] name = "web-socket-server-echo-raw-smol" +path = "examples/web-socket-server-echo-raw-smol.rs" required-features = ["smol", "web-socket-handshake"] [[example]] name = "web-socket-server-echo-raw-tokio" +path = "examples/web-socket-server-echo-raw-tokio.rs" required-features = ["tokio", "web-socket-handshake"] [[example]] name = "web-socket-server-echo-raw-tokio-rustls" +path = "examples/web-socket-server-echo-raw-tokio-rustls.rs" required-features = ["_tokio-rustls-server", "web-socket-handshake"] [[example]] name = "web-socket-server-pool-raw-tokio" +path = "examples/web-socket-server-pool-raw-tokio.rs" required-features = ["deadpool", "web-socket-handshake"] [dependencies] @@ -138,13 +148,13 @@ _tracing-subscriber = ["tracing", "dep:tracing-subscriber", "dep:tracing-tree"] [package] authors = ["Caio Fernandes "] -categories = ["asynchronous", "database", "encoding", "network-programming", "no-std", "web-programming"] +categories = ["asynchronous", "database", "network-programming", "no-std", "web-programming"] description = "A collection of different transport implementations and related tools focused primarily on web technologies." documentation = "https://docs.rs/wtx" edition = "2021" -exclude = ["README.md", "examples", "src/bin", "tests"] +exclude = ["Cargo.lock", "examples", "README.md", "src/bin", "tests"] homepage = "https://c410-f3r.github.io/wtx" -keywords = ["api", "client", "database", "network", "orm", "serialization", "websocket"] +keywords = ["api", "database", "http", "network", "websocket"] license = "Apache-2.0" name = "wtx" readme = "README.md" diff --git a/wtx/examples/database-client-postgres-tokio-rustls.rs b/wtx/examples/database-client-postgres-tokio-rustls.rs index 49025984..1f261a5f 100644 --- a/wtx/examples/database-client-postgres-tokio-rustls.rs +++ b/wtx/examples/database-client-postgres-tokio-rustls.rs @@ -2,8 +2,6 @@ #[path = "./common/mod.rs"] mod common; -#[path = "./tls_stream/mod.rs"] -mod tls_stream; use tokio::net::TcpStream; use wtx::{ @@ -11,21 +9,29 @@ use wtx::{ client::postgres::{Config, Executor, ExecutorBuffer}, Executor as _, Record, Records, TransactionManager, }, - misc::UriPartsRef, + misc::{tls_stream_from_stream, UriRef}, rng::StdRng, }; #[tokio::main] async fn main() { let uri = common::_uri_from_args(); - let uri_parts = UriPartsRef::new(uri.as_str()); + let uri = UriRef::new(uri.as_str()); let mut rng = StdRng::default(); - let config = Config::from_uri_parts(&uri_parts).unwrap(); + let config = Config::from_uri(&uri).unwrap(); let eb = ExecutorBuffer::with_default_params(&mut rng); - let initial_stream = TcpStream::connect(uri_parts.host()).await.unwrap(); + let initial_stream = TcpStream::connect(uri.host()).await.unwrap(); let mut exec = Executor::connect_encrypted(&config, eb, initial_stream, &mut rng, |stream| async { - Ok(tls_stream::_tls_stream_stream(uri_parts.hostname(), stream).await) + Ok( + tls_stream_from_stream( + uri.hostname(), + Some(include_bytes!("../../.certs/root-ca.crt")), + stream, + ) + .await + .unwrap(), + ) }) .await .unwrap(); diff --git a/wtx/examples/tls_stream/mod.rs b/wtx/examples/tls_stream/mod.rs index 4d5cbbdc..7d7c0fb0 100644 --- a/wtx/examples/tls_stream/mod.rs +++ b/wtx/examples/tls_stream/mod.rs @@ -11,7 +11,7 @@ use webpki_roots::TLS_SERVER_ROOTS; static ROOT_CA: &[u8] = include_bytes!("../../../.certs/root-ca.crt"); -pub(crate) async fn _tls_stream_host(host: &str, hostname: &str) -> TlsStream { +pub(crate) async fn tls_stream_from_hosts(host: &str, hostname: &str) -> TlsStream { let stream = TcpStream::connect(host).await.unwrap(); _tls_connector() .connect(ServerName::try_from(hostname.to_string()).unwrap(), stream) @@ -19,7 +19,7 @@ pub(crate) async fn _tls_stream_host(host: &str, hostname: &str) -> TlsStream TlsStream { +pub(crate) async fn tls_stream_from_hostname_and_stream(hostname: &str, stream: TcpStream) -> TlsStream { _tls_connector() .connect(ServerName::try_from(hostname.to_string()).unwrap(), stream) .await diff --git a/wtx/examples/web-socket-client-cli-raw-tokio-rustls.rs b/wtx/examples/web-socket-client-cli-raw-tokio-rustls.rs index 07a2e424..07a9dcee 100644 --- a/wtx/examples/web-socket-client-cli-raw-tokio-rustls.rs +++ b/wtx/examples/web-socket-client-cli-raw-tokio-rustls.rs @@ -2,12 +2,10 @@ #[path = "./common/mod.rs"] mod common; -#[path = "./tls_stream/mod.rs"] -mod tls_stream; use tokio::io::{AsyncBufReadExt, BufReader}; use wtx::{ - misc::UriPartsRef, + misc::{tls_stream_from_host, UriRef}, rng::StdRng, web_socket::{ handshake::{WebSocketConnect, WebSocketConnectRaw}, @@ -19,13 +17,19 @@ use wtx::{ async fn main() { let fb = &mut FrameBufferVec::default(); let uri = common::_uri_from_args(); - let uri_parts = UriPartsRef::new(uri.as_str()); + let uri = UriRef::new(uri.as_str()); let (_, mut ws) = WebSocketConnectRaw { compression: (), fb, headers_buffer: &mut <_>::default(), rng: StdRng::default(), - stream: tls_stream::_tls_stream_host(uri_parts.host(), uri_parts.hostname()).await, + stream: tls_stream_from_host( + uri.host(), + uri.hostname(), + Some(include_bytes!("../../.certs/root-ca.crt")), + ) + .await + .unwrap(), uri: &uri, wsb: WebSocketBuffer::default(), } diff --git a/wtx/src/bin/autobahn-client.rs b/wtx/src/bin/autobahn-client.rs index 57fc96bc..f22436cc 100644 --- a/wtx/src/bin/autobahn-client.rs +++ b/wtx/src/bin/autobahn-client.rs @@ -2,6 +2,7 @@ use tokio::net::TcpStream; use wtx::{ + misc::UriRef, rng::StdRng, web_socket::{ compression::Flate2, @@ -22,7 +23,7 @@ async fn main() { headers_buffer: &mut <_>::default(), rng: StdRng::default(), stream: TcpStream::connect(host).await.unwrap(), - uri: &format!("http://{host}/runCase?case={case}&agent=wtx"), + uri: &UriRef::new(&format!("http://{host}/runCase?case={case}&agent=wtx")), wsb: &mut wsb, } .connect() @@ -50,7 +51,7 @@ async fn main() { headers_buffer: &mut <_>::default(), rng: StdRng::default(), stream: TcpStream::connect(host).await.unwrap(), - uri: &format!("http://{host}/updateReports?agent=wtx"), + uri: &UriRef::new(&format!("http://{host}/updateReports?agent=wtx")), wsb, } .connect() @@ -69,7 +70,7 @@ async fn get_case_count(fb: &mut FrameBufferVec, host: &str, wsb: &mut WebSocket headers_buffer: &mut <_>::default(), rng: StdRng::default(), stream: TcpStream::connect(host).await.unwrap(), - uri: &&format!("http://{host}/getCaseCount"), + uri: &UriRef::new(&format!("http://{host}/getCaseCount")), wsb, } .connect() diff --git a/wtx/src/client_api_framework/api.rs b/wtx/src/client_api_framework/api.rs index 641007fe..2152d149 100644 --- a/wtx/src/client_api_framework/api.rs +++ b/wtx/src/client_api_framework/api.rs @@ -1,5 +1,5 @@ use crate::misc::AsyncBounds; -use core::future::Future; +use core::{fmt::Display, future::Future}; /// Api definitions group different packages into a common namespace and define custom additional /// logical through hooks. @@ -9,7 +9,7 @@ use core::future::Future; )] pub trait Api: AsyncBounds { /// Any custom error structure that can be constructed from [crate::Error]. - type Error: From; + type Error: Display + From; /// Fallible hook that is automatically called after sending any related request. #[inline] diff --git a/wtx/src/client_api_framework/data_format/json/json_response.rs b/wtx/src/client_api_framework/data_format/json/json_response.rs index b9522c84..c3d37524 100644 --- a/wtx/src/client_api_framework/data_format/json/json_response.rs +++ b/wtx/src/client_api_framework/data_format/json/json_response.rs @@ -40,7 +40,7 @@ mod miniserde { data_format::JsonResponse, dnsn::{miniserde_serialize, Miniserde}, }, - misc::_from_utf8_basic_rslt, + misc::from_utf8_basic_rslt, }; use core::fmt::Display; @@ -49,7 +49,7 @@ mod miniserde { D: miniserde::Deserialize, { fn from_bytes(bytes: &[u8], _: &mut Miniserde) -> crate::Result { - Ok(Self { data: miniserde::json::from_str(_from_utf8_basic_rslt(bytes)?)? }) + Ok(Self { data: miniserde::json::from_str(from_utf8_basic_rslt(bytes)?)? }) } fn seq_from_bytes( @@ -60,7 +60,7 @@ mod miniserde { where E: Display + From, { - let data_fn = || crate::Result::Ok(miniserde::json::from_str(_from_utf8_basic_rslt(bytes)?)?); + let data_fn = || crate::Result::Ok(miniserde::json::from_str(from_utf8_basic_rslt(bytes)?)?); cb(Self { data: data_fn()? })?; Ok(()) } diff --git a/wtx/src/client_api_framework/dnsn/tests.rs b/wtx/src/client_api_framework/dnsn/tests.rs index b4272aec..bddcaff4 100644 --- a/wtx/src/client_api_framework/dnsn/tests.rs +++ b/wtx/src/client_api_framework/dnsn/tests.rs @@ -19,13 +19,11 @@ impl FooBar { } } -impl Package for FooBar +impl Package<(), DRSR, ()> for FooBar where EREQC: AsyncBounds + Serialize, ERESC: AsyncBounds + Deserialize, { - type Api = (); - type Error = crate::Error; type ExternalRequestContent = EREQC; type ExternalResponseContent = ERESC; type PackageParams = (); diff --git a/wtx/src/client_api_framework/macros.rs b/wtx/src/client_api_framework/macros.rs index e0c8dd22..3c2812fa 100644 --- a/wtx/src/client_api_framework/macros.rs +++ b/wtx/src/client_api_framework/macros.rs @@ -3,10 +3,10 @@ #[macro_export] macro_rules! create_packages_aux_wrapper { () => { - $crate::create_packages_aux_wrapper!(@PkgsAux); + $crate::create_packages_aux_wrapper!(@PkgsAux); }; ($name:ident) => { - $crate::create_packages_aux_wrapper!(@$name); + $crate::create_packages_aux_wrapper!(@$name); }; ($name:ident, $api_ty:ty) => { $crate::create_packages_aux_wrapper!(@$name); diff --git a/wtx/src/client_api_framework/misc.rs b/wtx/src/client_api_framework/misc.rs index 2a067ffa..451f513f 100644 --- a/wtx/src/client_api_framework/misc.rs +++ b/wtx/src/client_api_framework/misc.rs @@ -4,11 +4,9 @@ pub(crate) mod seq_visitor; mod from_bytes; mod pair; -mod query_writer; mod request_counter; mod request_limit; mod request_throttling; -mod url; use crate::client_api_framework::{ dnsn::Serialize, @@ -18,11 +16,9 @@ use crate::client_api_framework::{ }; pub use from_bytes::FromBytes; pub use pair::{Pair, PairMut}; -pub use query_writer::QueryWriter; pub use request_counter::RequestCounter; pub use request_limit::RequestLimit; pub use request_throttling::RequestThrottling; -pub use url::{Url, UrlString}; /// Used in all implementations of [crate::Transport::send] and/or /// [crate::Transport::send_and_receive`]. @@ -30,12 +26,13 @@ pub use url::{Url, UrlString}; // Borrow checker woes clippy::needless_pass_by_value, )] -pub(crate) fn log_req( +pub(crate) fn log_req( _pgk: &mut P, - _pkgs_aux: &mut PkgsAux, + _pkgs_aux: &mut PkgsAux, _trans: T, ) where - P: Package, + A: Api, + P: Package, T: Transport, { _debug!(trans_ty = display(_trans.ty()), "Request: {:?}", { @@ -44,7 +41,7 @@ pub(crate) fn log_req( _pgk .ext_req_content_mut() .to_bytes(&mut vec, &mut _pkgs_aux.drsr) - .and_then(|_| Ok(crate::misc::_from_utf8_basic_rslt(&vec)?.to_string())) + .and_then(|_| Ok(crate::misc::from_utf8_basic_rslt(&vec)?.to_string())) }); } @@ -57,12 +54,13 @@ pub(crate) fn log_res(_res: &[u8]) { _debug!("Response: {:?}", core::str::from_utf8(_res)); } -pub(crate) async fn manage_after_sending_related( +pub(crate) async fn manage_after_sending_related( pkg: &mut P, - pkgs_aux: &mut PkgsAux, -) -> Result<(), P::Error> + pkgs_aux: &mut PkgsAux, +) -> Result<(), A::Error> where - P: Package, + A: Api, + P: Package, TP: TransportParams, { pkgs_aux.api.after_sending().await?; @@ -70,13 +68,14 @@ where Ok(()) } -pub(crate) async fn manage_before_sending_related( +pub(crate) async fn manage_before_sending_related( pkg: &mut P, - pkgs_aux: &mut PkgsAux, + pkgs_aux: &mut PkgsAux, trans: T, -) -> Result<(), P::Error> +) -> Result<(), A::Error> where - P: Package, + A: Api, + P: Package, T: Transport, { log_req(pkg, pkgs_aux, trans); diff --git a/wtx/src/client_api_framework/misc/from_bytes.rs b/wtx/src/client_api_framework/misc/from_bytes.rs index 5553649a..45bb4984 100644 --- a/wtx/src/client_api_framework/misc/from_bytes.rs +++ b/wtx/src/client_api_framework/misc/from_bytes.rs @@ -1,4 +1,4 @@ -use crate::misc::_from_utf8_basic_rslt; +use crate::misc::from_utf8_basic_rslt; use alloc::{string::String, vec::Vec}; /// This trait only exists because of the lack of `impl TryFrom<&[u8]> for String` but such @@ -18,7 +18,7 @@ impl FromBytes for String { where Self: Sized, { - Ok(_from_utf8_basic_rslt(bytes)?.into()) + Ok(from_utf8_basic_rslt(bytes)?.into()) } } diff --git a/wtx/src/client_api_framework/misc/url.rs b/wtx/src/client_api_framework/misc/url.rs deleted file mode 100644 index 8e3034cb..00000000 --- a/wtx/src/client_api_framework/misc/url.rs +++ /dev/null @@ -1,230 +0,0 @@ -use crate::client_api_framework::misc::QueryWriter; -use alloc::string::String; -use cl_aux::DynString; -use core::fmt::Arguments; - -/// [Url] with a internal `String` storage. -pub type UrlString = Url; - -/// Some APIs must known certain parts of an URL in order to work. -/// -/// Constructors do not verify if an URL has actual valid content. -#[derive(Debug)] -pub struct Url { - initial_len: usize, - origin_end: usize, - path_end: usize, - url: S, -} - -impl Url -where - S: AsRef, -{ - /// Creates all inner parts from an URL. For example, https://localhost/api_version/endpoint?foo=bar. - #[inline] - pub fn from_url(url: S) -> crate::Result { - let [origin_end, path_end] = Self::instance_params_from_url(&url); - Ok(Self { initial_len: url.as_ref().len(), origin_end, path_end, url }) - } - - /// For example, /api_version/endpoint?foo=bar - #[inline] - pub fn href(&self) -> &str { - self.url.as_ref().get(self.origin_end..).unwrap_or_default() - } - - /// For example, https://localhost - #[inline] - pub fn origin(&self) -> &str { - self.url.as_ref().get(..self.origin_end).unwrap_or_default() - } - - /// For example, /api_version/endpoint - #[inline] - pub fn path(&self) -> &str { - self.url.as_ref().get(self.origin_end..self.path_end).unwrap_or_default() - } - - /// For example, ?foo=bar - #[inline] - pub fn query(&self) -> &str { - self.url.as_ref().get(self.path_end..).unwrap_or_default() - } - - /// For example, https://localhost/api_version/endpoint - #[inline] - pub fn url(&self) -> &str { - self.url.as_ref() - } - - fn instance_params_from_url(url: &S) -> [usize; 2] { - let mut slash_iter = url - .as_ref() - .as_bytes() - .iter() - .enumerate() - .filter_map(|(idx, elem)| (*elem == b'/').then_some(idx)); - let _ = slash_iter.next(); - let _ = slash_iter.next(); - let Some(origin_end) = slash_iter.next() else { - return [url.as_ref().len(); 2]; - }; - let after_origin = url.as_ref().get(origin_end..).unwrap_or_default(); - let path_end = if let Some(elem) = after_origin - .as_bytes() - .iter() - .enumerate() - .find_map(|(idx, elem)| (*elem == b'?').then_some(idx)) - { - elem.wrapping_add(origin_end) - } else { - url.as_ref().len() - }; - [origin_end, path_end] - } -} - -impl Url -where - S: DynString, -{ - /// Creates all inner parts from an origin (https://localhost). - #[inline] - pub fn from_origin(origin_str: &str) -> crate::Result { - Self::from_origin_path_and_query(origin_str, "", "") - } - - /// Creates all inner parts from an origin (https://localhost) and a path (/api_version/endpoint). - #[inline] - pub fn from_origin_and_path(origin_str: &str, path_str: &str) -> crate::Result { - Self::from_origin_path_and_query(origin_str, path_str, "") - } - - /// Creates all inner parts from an origin (https://localhost), a path (/api_version/endpoint) - /// and a query (?foo=bar) - #[inline] - pub fn from_origin_path_and_query( - origin_str: &str, - path_str: &str, - query_str: &str, - ) -> crate::Result { - let origin_end = origin_str.len(); - let path_end = origin_str.len().wrapping_add(path_str.len()); - let url = { - let mut s = S::default(); - s.push(origin_str)?; - s.push(path_str)?; - s.push(query_str)?; - s - }; - Ok(Self { initial_len: url.as_ref().len(), origin_end, path_end, url }) - } - - /// Clears the internal storage. - #[inline] - pub fn clear(&mut self) { - self.url.clear(); - self.origin_end = 0; - self.path_end = 0; - } - - /// Pushes an additional path paths erasing any subsequent content. - #[inline] - pub fn push_path(&mut self, args: Arguments<'_>) -> crate::Result<()> { - if self.url.as_ref().len() != self.path_end { - return Err(crate::Error::UrlCanNotOverwriteInitiallySetUrl); - } - self.url.truncate(self.path_end); - self.url.write_fmt(args)?; - self.path_end = self.url.len(); - Ok(()) - } - - /// See [`QueryWriter`]. - #[inline] - pub fn query_writer(&mut self) -> crate::Result> { - if self.url.as_ref().len() != self.path_end { - return Err(crate::Error::UrlCanNotOverwriteInitiallySetUrl); - } - Ok(QueryWriter::new(&mut self.url)) - } - - /// Erases any subsequent content after the origin. - #[inline] - pub fn retain_origin(&mut self) { - self.url.truncate(self.origin_end); - self.path_end = self.origin_end; - } - - /// Truncates the internal storage with the length of the URL initially created in this instance. - /// - /// If the current length is lesser than the original URL length, nothing will happen. - #[inline] - pub fn retain_with_initial_len(&mut self) { - if self.url.len() <= self.initial_len { - return; - } - self.url.truncate(self.initial_len); - let [origin_end, path_end] = Self::instance_params_from_url(&self.url); - self.origin_end = origin_end; - self.path_end = path_end; - } - - /// Sets the origin if, and only if the inner length is equal to zero. - #[inline] - pub fn set_origin(&mut self, args: Arguments<'_>) -> crate::Result<()> { - if !self.url.as_ref().is_empty() { - return Err(crate::Error::UrlCanNotOverwriteInitiallySetUrl); - } - self.url.clear(); - self.url.write_fmt(args)?; - self.origin_end = self.url.len(); - self.path_end = self.url.len(); - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use crate::client_api_framework::misc::UrlString; - - #[test] - fn from_origin_path_and_query_has_correct_behavior() { - let url = - UrlString::from_origin_path_and_query("http://ddas.com", "/wfdsq", "?fssqw=rq").unwrap(); - assert_eq!(url.origin(), "http://ddas.com"); - assert_eq!(url.path(), "/wfdsq"); - assert_eq!(url.query(), "?fssqw=rq"); - assert_eq!(url.url(), "http://ddas.com/wfdsq?fssqw=rq"); - } - - #[test] - fn from_url_has_correct_behavior() { - let url = UrlString::from_url("http://dasdas.com/rewqd?ter=w".into()).unwrap(); - assert_eq!(url.origin(), "http://dasdas.com"); - assert_eq!(url.path(), "/rewqd"); - assert_eq!(url.query(), "?ter=w"); - assert_eq!(url.url(), "http://dasdas.com/rewqd?ter=w"); - } - - #[test] - fn mutable_methods_have_correct_behavior() { - let mut url = UrlString::from_url("http://dasdas.com/rewqd".into()).unwrap(); - url.push_path(format_args!("/tretre")).unwrap(); - assert_eq!(url.origin(), "http://dasdas.com"); - assert_eq!(url.path(), "/rewqd/tretre"); - assert_eq!(url.query(), ""); - assert_eq!(url.url(), "http://dasdas.com/rewqd/tretre"); - - url.retain_origin(); - assert_eq!(url.origin(), "http://dasdas.com"); - - url.clear(); - url.set_origin(format_args!("http://tedfrwerew.com")).unwrap(); - assert_eq!(url.origin(), "http://tedfrwerew.com"); - assert_eq!(url.path(), ""); - assert_eq!(url.query(), ""); - assert_eq!(url.url(), "http://tedfrwerew.com"); - } -} diff --git a/wtx/src/client_api_framework/network/http.rs b/wtx/src/client_api_framework/network/http.rs index 84472191..69f865ee 100644 --- a/wtx/src/client_api_framework/network/http.rs +++ b/wtx/src/client_api_framework/network/http.rs @@ -4,7 +4,7 @@ mod status_code; use core::fmt::{Arguments, Write}; -use crate::client_api_framework::{misc::UrlString, network::transport::TransportParams}; +use crate::{client_api_framework::network::transport::TransportParams, misc::UriString}; use alloc::{string::String, vec::Vec}; pub use status_code::*; @@ -15,17 +15,17 @@ pub struct HttpParams(HttpReqParams, HttpResParams); impl HttpParams { /// For example, from `http://localhost`. #[inline] - pub fn from_url(url: &str) -> crate::Result { - Ok(Self( + pub fn from_uri(url: &str) -> Self { + Self( HttpReqParams { headers: HttpHeaders::default(), method: HttpMethod::Get, mime_type: None, - url: UrlString::from_url(url.into())?, + uri: UriString::new(url.into()), user_agent: None, }, HttpResParams { headers: <_>::default(), status_code: StatusCode::Forbidden }, - )) + ) } } @@ -58,7 +58,7 @@ impl TransportParams for HttpParams { self.0.headers.clear(); self.0.method = HttpMethod::Get; self.0.mime_type = None; - self.0.url.retain_with_initial_len(); + self.0.uri.retain_with_initial_len(); self.0.user_agent = None; self.1.headers.clear(); self.1.status_code = StatusCode::Forbidden; @@ -170,7 +170,7 @@ pub struct HttpReqParams { /// MIME type. pub mime_type: Option, /// URL. - pub url: UrlString, + pub uri: UriString, /// User agent. pub user_agent: Option, } diff --git a/wtx/src/client_api_framework/network/tcp.rs b/wtx/src/client_api_framework/network/tcp.rs index d991f432..1ca8ba52 100644 --- a/wtx/src/client_api_framework/network/tcp.rs +++ b/wtx/src/client_api_framework/network/tcp.rs @@ -1,4 +1,4 @@ -use crate::client_api_framework::{misc::UrlString, network::transport::TransportParams}; +use crate::{client_api_framework::network::transport::TransportParams, misc::UriString}; #[derive(Debug)] #[doc = generic_trans_params_doc!()] @@ -7,8 +7,8 @@ pub struct TcpParams(TcpReqParams, TcpResParams); impl TcpParams { /// For example, from `127.0.0.1:8090`. #[inline] - pub fn from_url(url: &str) -> crate::Result { - Ok(Self(TcpReqParams { url: UrlString::from_url(url.into())? }, TcpResParams)) + pub fn from_uri(url: &str) -> Self { + Self(TcpReqParams { url: UriString::new(url.into()) }, TcpResParams) } } @@ -46,7 +46,7 @@ impl TransportParams for TcpParams { #[doc = generic_trans_req_params_doc!("TCP")] pub struct TcpReqParams { /// Used every time a send-like function is called. - pub url: UrlString, + pub url: UriString, } #[derive(Debug)] diff --git a/wtx/src/client_api_framework/network/transport.rs b/wtx/src/client_api_framework/network/transport.rs index 30225a71..df5a172a 100644 --- a/wtx/src/client_api_framework/network/transport.rs +++ b/wtx/src/client_api_framework/network/transport.rs @@ -17,7 +17,7 @@ use crate::{ misc::log_res, network::TransportGroup, pkg::{BatchElems, BatchPkg, Package, PkgsAux}, - Id, + Api, Id, }, misc::AsyncBounds, }; @@ -42,39 +42,42 @@ pub trait Transport { type Params: TransportParams; /// Sends a request without trying to retrieve any counterpart data. - fn send

( + fn send( &mut self, pkg: &mut P, - pkgs_aux: &mut PkgsAux, - ) -> impl AsyncBounds + Future> + pkgs_aux: &mut PkgsAux, + ) -> impl AsyncBounds + Future> where - P: AsyncBounds + Package; + A: Api, + P: AsyncBounds + Package; /// Sends a request and then awaits its counterpart data response. /// /// The returned bytes are stored in `pkgs_aux` and its length is returned by this method. - fn send_and_retrieve

( + fn send_and_retrieve( &mut self, pkg: &mut P, - pkgs_aux: &mut PkgsAux, - ) -> impl AsyncBounds + Future, P::Error>> + pkgs_aux: &mut PkgsAux, + ) -> impl AsyncBounds + Future, A::Error>> where - P: AsyncBounds + Package; + A: Api, + P: AsyncBounds + Package; /// Convenient method similar to [Self::send_retrieve_and_decode_contained] but used for batch /// requests. /// /// All the expected data must be available in a single response. #[inline] - fn send_retrieve_and_decode_batch( + fn send_retrieve_and_decode_batch( &mut self, pkgs: &mut [P], - pkgs_aux: &mut PkgsAux, + pkgs_aux: &mut PkgsAux, ress: &mut RESS, - ) -> impl AsyncBounds + Future> + ) -> impl AsyncBounds + Future> where + A: Api, DRSR: AsyncBounds, - P: AsyncBounds + Package, + P: AsyncBounds + Package, P::ExternalRequestContent: AsyncBounds + Borrow + Ord, P::ExternalResponseContent: AsyncBounds + Borrow + Ord, RESS: AsyncBounds + DynContigColl, @@ -82,7 +85,7 @@ pub trait Transport { Self::Params: AsyncBounds, ::ExternalRequestParams: AsyncBounds, ::ExternalResponseParams: AsyncBounds, - for<'any> BatchElems<'any, DRSR, P, Self::Params>: Serialize, + for<'any> BatchElems<'any, A, DRSR, P, Self::Params>: Serialize, { async { let batch_package = &mut BatchPkg::new(pkgs); @@ -100,14 +103,15 @@ pub trait Transport { /// Internally calls [Self::send_and_retrieve] and then tries to decode the defined response specified /// in [Package::ExternalResponseContent]. #[inline] - fn send_retrieve_and_decode_contained

( + fn send_retrieve_and_decode_contained( &mut self, pkg: &mut P, - pkgs_aux: &mut PkgsAux, - ) -> impl AsyncBounds + Future> + pkgs_aux: &mut PkgsAux, + ) -> impl AsyncBounds + Future> where + A: Api, DRSR: AsyncBounds, - P: AsyncBounds + Package, + P: AsyncBounds + Package, Self: AsyncBounds, Self::Params: AsyncBounds, ::ExternalRequestParams: AsyncBounds, @@ -141,25 +145,27 @@ where type Params = T::Params; #[inline] - async fn send

( + async fn send( &mut self, pkg: &mut P, - pkgs_aux: &mut PkgsAux, - ) -> Result<(), P::Error> + pkgs_aux: &mut PkgsAux, + ) -> Result<(), A::Error> where - P: AsyncBounds + Package, + A: Api, + P: AsyncBounds + Package, { (**self).send(pkg, pkgs_aux).await } #[inline] - async fn send_and_retrieve

( + async fn send_and_retrieve( &mut self, pkg: &mut P, - pkgs_aux: &mut PkgsAux, - ) -> Result, P::Error> + pkgs_aux: &mut PkgsAux, + ) -> Result, A::Error> where - P: AsyncBounds + Package, + A: Api, + P: AsyncBounds + Package, { (**self).send_and_retrieve(pkg, pkgs_aux).await } @@ -177,12 +183,10 @@ mod tests { #[derive(Debug, Eq, PartialEq)] pub(crate) struct PingPong(pub(crate) Ping, pub(crate) ()); - impl Package for PingPong + impl Package<(), DRSR, TP> for PingPong where TP: TransportParams, { - type Api = (); - type Error = crate::Error; type ExternalRequestContent = Ping; type ExternalResponseContent = Pong; type PackageParams = (); diff --git a/wtx/src/client_api_framework/network/transport/bi_transport.rs b/wtx/src/client_api_framework/network/transport/bi_transport.rs index 2477df3a..7345b11d 100644 --- a/wtx/src/client_api_framework/network/transport/bi_transport.rs +++ b/wtx/src/client_api_framework/network/transport/bi_transport.rs @@ -4,6 +4,7 @@ use crate::{ misc::log_res, network::transport::Transport, pkg::{Package, PkgsAux}, + Api, }, misc::AsyncBounds, }; @@ -19,23 +20,25 @@ use core::{future::Future, ops::Range}; pub trait BiTransport: Transport { /// Retrieves data from the server filling the internal buffer and returning the amount of /// bytes written. - fn retrieve( + fn retrieve( &mut self, - pkgs_aux: &mut PkgsAux, + pkgs_aux: &mut PkgsAux, ) -> impl AsyncBounds + Future>> where - API: AsyncBounds; + A: Api, + A: AsyncBounds; /// Internally calls [Self::retrieve] and then tries to decode the defined response specified /// in [Package::ExternalResponseContent]. #[inline] - fn retrieve_and_decode_contained

( + fn retrieve_and_decode_contained( &mut self, - pkgs_aux: &mut PkgsAux, - ) -> impl AsyncBounds + Future> + pkgs_aux: &mut PkgsAux, + ) -> impl AsyncBounds + Future> where + A: Api, DRSR: AsyncBounds, - P: Package, + P: Package, Self: AsyncBounds, Self::Params: AsyncBounds, { @@ -60,12 +63,12 @@ where Self: AsyncBounds, { #[inline] - async fn retrieve( + async fn retrieve( &mut self, - pkgs_aux: &mut PkgsAux, + pkgs_aux: &mut PkgsAux, ) -> crate::Result> where - API: AsyncBounds, + A: Api, { (**self).retrieve(pkgs_aux).await } diff --git a/wtx/src/client_api_framework/network/transport/mock.rs b/wtx/src/client_api_framework/network/transport/mock.rs index 23c861bb..2b0de441 100644 --- a/wtx/src/client_api_framework/network/transport/mock.rs +++ b/wtx/src/client_api_framework/network/transport/mock.rs @@ -8,6 +8,7 @@ use crate::{ misc::{manage_after_sending_related, manage_before_sending_related, FromBytes}, network::{transport::Transport, TransportGroup}, pkg::{Package, PkgsAux}, + Api, }, misc::AsyncBounds, }; @@ -100,13 +101,14 @@ where type Params = (); #[inline] - async fn send

( + async fn send( &mut self, pkg: &mut P, - pkgs_aux: &mut PkgsAux, - ) -> Result<(), P::Error> + pkgs_aux: &mut PkgsAux, + ) -> Result<(), A::Error> where - P: AsyncBounds + Package, + A: Api, + P: AsyncBounds + Package, { manage_before_sending_related(pkg, pkgs_aux, &mut *self).await?; self.requests.push(Cow::Owned(FromBytes::from_bytes(&pkgs_aux.byte_buffer)?)); @@ -116,13 +118,14 @@ where } #[inline] - async fn send_and_retrieve

( + async fn send_and_retrieve( &mut self, pkg: &mut P, - pkgs_aux: &mut PkgsAux, - ) -> Result, P::Error> + pkgs_aux: &mut PkgsAux, + ) -> Result, A::Error> where - P: AsyncBounds + Package, + A: Api, + P: AsyncBounds + Package, { >::send(self, pkg, pkgs_aux).await?; let response = self.pop_response()?; diff --git a/wtx/src/client_api_framework/network/transport/reqwest.rs b/wtx/src/client_api_framework/network/transport/reqwest.rs index a47c5d9e..3a9e06f5 100644 --- a/wtx/src/client_api_framework/network/transport/reqwest.rs +++ b/wtx/src/client_api_framework/network/transport/reqwest.rs @@ -7,8 +7,9 @@ use crate::{ HttpParams, TransportGroup, }, pkg::{Package, PkgsAux}, + Api, }, - misc::{AsyncBounds, _from_utf8_basic_rslt}, + misc::{from_utf8_basic_rslt, AsyncBounds}, }; use core::ops::Range; use reqwest::{ @@ -25,7 +26,7 @@ use reqwest::{ /// let _ = reqwest::Client::new() /// .send_retrieve_and_decode_contained( /// &mut (), -/// &mut PkgsAux::from_minimum((), (), HttpParams::from_url("URL")?.into()), +/// &mut PkgsAux::from_minimum((), (), HttpParams::from_uri("URI").into()), /// ) /// .await?; /// # Ok(()) } @@ -38,26 +39,28 @@ where type Params = HttpParams; #[inline] - async fn send

( + async fn send( &mut self, pkg: &mut P, - pkgs_aux: &mut PkgsAux, - ) -> Result<(), P::Error> + pkgs_aux: &mut PkgsAux, + ) -> Result<(), A::Error> where - P: AsyncBounds + Package, + A: Api, + P: AsyncBounds + Package, { let _res = response(self, pkg, pkgs_aux).await?; Ok(()) } #[inline] - async fn send_and_retrieve

( + async fn send_and_retrieve( &mut self, pkg: &mut P, - pkgs_aux: &mut PkgsAux, - ) -> Result, P::Error> + pkgs_aux: &mut PkgsAux, + ) -> Result, A::Error> where - P: AsyncBounds + Package, + A: Api, + P: AsyncBounds + Package, { let res = response(self, pkg, pkgs_aux).await?; let received_bytes = res.bytes().await.map_err(Into::into)?; @@ -66,18 +69,19 @@ where } } -async fn response( +async fn response( client: &mut Client, pkg: &mut P, - pkgs_aux: &mut PkgsAux, -) -> Result + pkgs_aux: &mut PkgsAux, +) -> Result where + A: Api, DRSR: AsyncBounds, - P: Package, + P: Package, { - fn manage_data( + fn manage_data( mut rb: RequestBuilder, - pkgs_aux: &mut PkgsAux, + pkgs_aux: &mut PkgsAux, ) -> RequestBuilder { if let Some(data_format) = &pkgs_aux.tp.ext_req_params().mime_type { rb = rb.header(CONTENT_TYPE, HeaderValue::from_static(data_format._as_str())); @@ -88,13 +92,13 @@ where pkgs_aux.byte_buffer.clear(); manage_before_sending_related(pkg, pkgs_aux, &mut *client).await?; let mut rb = match pkgs_aux.tp.ext_req_params().method { - HttpMethod::Delete => client.delete(pkgs_aux.tp.ext_req_params().url.url()), - HttpMethod::Get => client.get(pkgs_aux.tp.ext_req_params().url.url()), + HttpMethod::Delete => client.delete(pkgs_aux.tp.ext_req_params().uri.uri()), + HttpMethod::Get => client.get(pkgs_aux.tp.ext_req_params().uri.uri()), HttpMethod::Patch => { - manage_data(client.patch(pkgs_aux.tp.ext_req_params().url.url()), pkgs_aux) + manage_data(client.patch(pkgs_aux.tp.ext_req_params().uri.uri()), pkgs_aux) } - HttpMethod::Post => manage_data(client.post(pkgs_aux.tp.ext_req_params().url.url()), pkgs_aux), - HttpMethod::Put => manage_data(client.put(pkgs_aux.tp.ext_req_params().url.url()), pkgs_aux), + HttpMethod::Post => manage_data(client.post(pkgs_aux.tp.ext_req_params().uri.uri()), pkgs_aux), + HttpMethod::Put => manage_data(client.put(pkgs_aux.tp.ext_req_params().uri.uri()), pkgs_aux), }; pkgs_aux.byte_buffer.clear(); for (key, value) in pkgs_aux.tp.ext_req_params().headers.iter() { @@ -109,7 +113,7 @@ where .tp .ext_res_params_mut() .headers - .push_str(key.as_str(), _from_utf8_basic_rslt(value.as_bytes()).map_err(Into::into)?)?; + .push_str(key.as_str(), from_utf8_basic_rslt(value.as_bytes()).map_err(Into::into)?)?; } pkgs_aux.tp.ext_res_params_mut().status_code = <_>::try_from(Into::::into(res.status()))?; manage_after_sending_related(pkg, pkgs_aux).await?; diff --git a/wtx/src/client_api_framework/network/transport/std.rs b/wtx/src/client_api_framework/network/transport/std.rs index 54c54521..4e727540 100644 --- a/wtx/src/client_api_framework/network/transport/std.rs +++ b/wtx/src/client_api_framework/network/transport/std.rs @@ -6,6 +6,7 @@ use crate::{ TcpParams, TransportGroup, UdpParams, }, pkg::{Package, PkgsAux}, + Api, }, misc::AsyncBounds, }; @@ -23,25 +24,27 @@ where type Params = TcpParams; #[inline] - async fn send

( + async fn send( &mut self, pkg: &mut P, - pkgs_aux: &mut PkgsAux, - ) -> Result<(), P::Error> + pkgs_aux: &mut PkgsAux, + ) -> Result<(), A::Error> where - P: AsyncBounds + Package, + A: Api, + P: AsyncBounds + Package, { send(pkg, pkgs_aux, self, |bytes, _, trans| Ok(trans.write(bytes)?)).await } #[inline] - async fn send_and_retrieve

( + async fn send_and_retrieve( &mut self, pkg: &mut P, - pkgs_aux: &mut PkgsAux, - ) -> Result, P::Error> + pkgs_aux: &mut PkgsAux, + ) -> Result, A::Error> where - P: AsyncBounds + Package, + A: Api, + P: AsyncBounds + Package, { send_and_retrieve(pkg, pkgs_aux, self, |bytes, _, trans| Ok(trans.read(bytes)?)).await } @@ -55,46 +58,49 @@ where type Params = UdpParams; #[inline] - async fn send

( + async fn send( &mut self, pkg: &mut P, - pkgs_aux: &mut PkgsAux, - ) -> Result<(), P::Error> + pkgs_aux: &mut PkgsAux, + ) -> Result<(), A::Error> where - P: AsyncBounds + Package, + A: Api, + P: AsyncBounds + Package, { send(pkg, pkgs_aux, self, |bytes, ext_req_params, trans| { - Ok(trans.send_to(bytes, ext_req_params.url.url())?) + Ok(trans.send_to(bytes, ext_req_params.url.uri())?) }) .await } #[inline] - async fn send_and_retrieve

( + async fn send_and_retrieve( &mut self, pkg: &mut P, - pkgs_aux: &mut PkgsAux, - ) -> Result, P::Error> + pkgs_aux: &mut PkgsAux, + ) -> Result, A::Error> where - P: AsyncBounds + Package, + A: Api, + P: AsyncBounds + Package, { send_and_retrieve(pkg, pkgs_aux, self, |bytes, _, trans| Ok(trans.recv(bytes)?)).await } } -async fn send( +async fn send( pkg: &mut P, - pkgs_aux: &mut PkgsAux, + pkgs_aux: &mut PkgsAux, trans: &mut T, cb: impl Fn( &[u8], &::ExternalRequestParams, &mut T, ) -> crate::Result, -) -> Result<(), P::Error> +) -> Result<(), A::Error> where + A: Api, DRSR: AsyncBounds, - P: Package, + P: Package, T: AsyncBounds + Transport, T::Params: AsyncBounds, { @@ -120,18 +126,19 @@ where } } -async fn send_and_retrieve( +async fn send_and_retrieve( pkg: &mut P, - pkgs_aux: &mut PkgsAux, + pkgs_aux: &mut PkgsAux, trans: &mut T, cb: impl Fn( &mut [u8], &::ExternalRequestParams, &mut T, ) -> crate::Result, -) -> Result, P::Error> +) -> Result, A::Error> where - P: AsyncBounds + Package, + A: Api, + P: AsyncBounds + Package, T: Transport, { trans.send(pkg, pkgs_aux).await?; @@ -164,7 +171,7 @@ mod tests { #[tokio::test(flavor = "multi_thread")] async fn tcp() { let uri_client = _uri(); - let uri_server = uri_client.clone(); + let uri_server = uri_client.to_string(); let _server = tokio::spawn(async move { let tcp_listener = TcpListener::bind(uri_server.host()).unwrap(); let mut buffer = [0; 8]; @@ -173,7 +180,7 @@ mod tests { stream.write_all(&buffer[..idx]).unwrap(); }); sleep(Duration::from_millis(100)).await.unwrap(); - let mut pa = PkgsAux::from_minimum((), (), TcpParams::from_url(uri_client.uri()).unwrap()); + let mut pa = PkgsAux::from_minimum((), (), TcpParams::from_uri(uri_client.uri())); let mut trans = TcpStream::connect(uri_client.host()).unwrap(); let res = trans.send_retrieve_and_decode_contained(&mut PingPong(Ping, ()), &mut pa).await.unwrap(); @@ -183,7 +190,7 @@ mod tests { #[tokio::test] async fn udp() { let addr = "127.0.0.1:12346"; - let mut pa = PkgsAux::from_minimum((), (), UdpParams::from_url(addr).unwrap()); + let mut pa = PkgsAux::from_minimum((), (), UdpParams::from_uri(addr)); let mut trans = UdpSocket::bind(addr).unwrap(); let res = trans.send_retrieve_and_decode_contained(&mut PingPong(Ping, ()), &mut pa).await.unwrap(); diff --git a/wtx/src/client_api_framework/network/transport/unit.rs b/wtx/src/client_api_framework/network/transport/unit.rs index e21074be..0ff2c47c 100644 --- a/wtx/src/client_api_framework/network/transport/unit.rs +++ b/wtx/src/client_api_framework/network/transport/unit.rs @@ -3,6 +3,7 @@ use crate::{ misc::{manage_after_sending_related, manage_before_sending_related}, network::{transport::Transport, TransportGroup}, pkg::{Package, PkgsAux}, + Api, }, misc::AsyncBounds, }; @@ -25,13 +26,14 @@ where type Params = (); #[inline] - async fn send

( + async fn send( &mut self, pkg: &mut P, - pkgs_aux: &mut PkgsAux, - ) -> Result<(), P::Error> + pkgs_aux: &mut PkgsAux, + ) -> Result<(), A::Error> where - P: AsyncBounds + Package, + A: Api, + P: AsyncBounds + Package, { manage_before_sending_related(pkg, pkgs_aux, self).await?; manage_after_sending_related(pkg, pkgs_aux).await?; @@ -39,13 +41,14 @@ where } #[inline] - async fn send_and_retrieve

( + async fn send_and_retrieve( &mut self, pkg: &mut P, - pkgs_aux: &mut PkgsAux, - ) -> Result, P::Error> + pkgs_aux: &mut PkgsAux, + ) -> Result, A::Error> where - P: AsyncBounds + Package, + A: Api, + P: AsyncBounds + Package, { self.send(pkg, pkgs_aux).await?; Ok(0..0) diff --git a/wtx/src/client_api_framework/network/transport/wtx.rs b/wtx/src/client_api_framework/network/transport/wtx.rs index ee430538..d2178fdf 100644 --- a/wtx/src/client_api_framework/network/transport/wtx.rs +++ b/wtx/src/client_api_framework/network/transport/wtx.rs @@ -6,6 +6,7 @@ use crate::{ TransportGroup, WsParams, WsReqParamsTy, }, pkg::{Package, PkgsAux}, + Api, }, misc::{AsyncBounds, Stream}, rng::Rng, @@ -33,25 +34,27 @@ where type Params = WsParams; #[inline] - async fn send

( + async fn send( &mut self, pkg: &mut P, - pkgs_aux: &mut PkgsAux, - ) -> Result<(), P::Error> + pkgs_aux: &mut PkgsAux, + ) -> Result<(), A::Error> where - P: AsyncBounds + Package, + A: Api, + P: AsyncBounds + Package, { send(&mut self.0, pkg, pkgs_aux, &mut self.1).await } #[inline] - async fn send_and_retrieve

( + async fn send_and_retrieve( &mut self, pkg: &mut P, - pkgs_aux: &mut PkgsAux, - ) -> Result, P::Error> + pkgs_aux: &mut PkgsAux, + ) -> Result, A::Error> where - P: AsyncBounds + Package, + A: Api, + P: AsyncBounds + Package, { send_and_retrieve(&mut self.0, pkg, pkgs_aux, &mut self.1).await } @@ -71,12 +74,12 @@ where for<'ty> &'ty WSB: AsyncBounds, { #[inline] - async fn retrieve( + async fn retrieve( &mut self, - pkgs_aux: &mut PkgsAux, + pkgs_aux: &mut PkgsAux, ) -> crate::Result> where - API: AsyncBounds, + A: Api, { retrieve(pkgs_aux, &mut self.1).await } @@ -100,25 +103,27 @@ where type Params = WsParams; #[inline] - async fn send

( + async fn send( &mut self, pkg: &mut P, - pkgs_aux: &mut PkgsAux, - ) -> Result<(), P::Error> + pkgs_aux: &mut PkgsAux, + ) -> Result<(), A::Error> where - P: AsyncBounds + Package, + A: Api, + P: AsyncBounds + Package, { send(self.0, pkg, pkgs_aux, self.1).await } #[inline] - async fn send_and_retrieve

( + async fn send_and_retrieve( &mut self, pkg: &mut P, - pkgs_aux: &mut PkgsAux, - ) -> Result, P::Error> + pkgs_aux: &mut PkgsAux, + ) -> Result, A::Error> where - P: AsyncBounds + Package, + A: Api, + P: AsyncBounds + Package, { send_and_retrieve(self.0, pkg, pkgs_aux, self.1).await } @@ -139,19 +144,19 @@ where for<'ty> &'ty WSB: AsyncBounds, { #[inline] - async fn retrieve( + async fn retrieve( &mut self, - pkgs_aux: &mut PkgsAux, + pkgs_aux: &mut PkgsAux, ) -> crate::Result> where - API: AsyncBounds, + A: Api, { retrieve(pkgs_aux, self.1).await } } -async fn retrieve( - pkgs_aux: &mut PkgsAux, +async fn retrieve( + pkgs_aux: &mut PkgsAux, ws: &mut WebSocketClient, ) -> crate::Result> where @@ -176,16 +181,17 @@ where Ok(indcs.1.into()..indcs.2) } -async fn send( +async fn send( fb: &mut FrameBufferVec, pkg: &mut P, - pkgs_aux: &mut PkgsAux, + pkgs_aux: &mut PkgsAux, ws: &mut WebSocketClient, -) -> Result<(), P::Error> +) -> Result<(), A::Error> where + A: Api, DRSR: AsyncBounds, NC: AsyncBounds + NegotiatedCompression, - P: AsyncBounds + Package, + P: AsyncBounds + Package, RNG: AsyncBounds + Rng, S: AsyncBounds + Stream, WSB: AsyncBounds + BorrowMut, @@ -210,16 +216,17 @@ where Ok(()) } -async fn send_and_retrieve( +async fn send_and_retrieve( fb: &mut FrameBufferVec, pkg: &mut P, - pkgs_aux: &mut PkgsAux, + pkgs_aux: &mut PkgsAux, ws: &mut WebSocketClient, -) -> Result, P::Error> +) -> Result, A::Error> where + A: Api, DRSR: AsyncBounds, NC: AsyncBounds + NegotiatedCompression, - P: AsyncBounds + Package, + P: AsyncBounds + Package, RNG: AsyncBounds + Rng, S: AsyncBounds + Stream, WSB: AsyncBounds + BorrowMut, diff --git a/wtx/src/client_api_framework/network/udp.rs b/wtx/src/client_api_framework/network/udp.rs index 49113620..0a7d369a 100644 --- a/wtx/src/client_api_framework/network/udp.rs +++ b/wtx/src/client_api_framework/network/udp.rs @@ -1,4 +1,4 @@ -use crate::client_api_framework::{misc::UrlString, network::transport::TransportParams}; +use crate::{client_api_framework::network::transport::TransportParams, misc::UriString}; #[derive(Debug)] #[doc = generic_trans_params_doc!()] @@ -7,8 +7,8 @@ pub struct UdpParams(UdpReqParams, UdpResParams); impl UdpParams { /// For example, from `127.0.0.1:8090`. #[inline] - pub fn from_url(url: &str) -> crate::Result { - Ok(Self(UdpReqParams { url: UrlString::from_url(url.into())? }, UdpResParams)) + pub fn from_uri(url: &str) -> Self { + Self(UdpReqParams { url: UriString::new(url.into()) }, UdpResParams) } } @@ -46,7 +46,7 @@ impl TransportParams for UdpParams { #[doc = generic_trans_req_params_doc!("UDP")] pub struct UdpReqParams { /// Used every time a send-like function is called. - pub url: UrlString, + pub url: UriString, } #[derive(Debug)] diff --git a/wtx/src/client_api_framework/pkg.rs b/wtx/src/client_api_framework/pkg.rs index 5b2b67b4..213d3ee8 100644 --- a/wtx/src/client_api_framework/pkg.rs +++ b/wtx/src/client_api_framework/pkg.rs @@ -15,7 +15,7 @@ use crate::{ misc::AsyncBounds, }; pub use batch_pkg::{BatchElems, BatchPkg}; -use core::{fmt::Display, future::Future}; +use core::future::Future; pub use pkg_with_helper::*; pub use pkgs_aux::*; @@ -24,20 +24,18 @@ pub use pkgs_aux::*; /// /// # Types /// +/// `A`: Associated API. /// `DRSR`: DeserializeR/SerializeR /// `TP`: Transport Parameters #[allow( // Downstream make use of async functionalities clippy::unused_async )] -pub trait Package +pub trait Package where + A: Api, TP: TransportParams, { - /// Which API this package is attached to. - type Api: Api; - /// Any custom error structure that can be constructed from [crate::Error]. - type Error: Display + From; /// The expected data format that is going to be sent to an external actor. type ExternalRequestContent: Serialize; /// The expected data format returned by an external actor. @@ -50,9 +48,9 @@ where #[inline] fn after_sending( &mut self, - _: &mut Self::Api, + _: &mut A, _: &mut TP::ExternalResponseParams, - ) -> impl AsyncBounds + Future> { + ) -> impl AsyncBounds + Future> { async { Ok(()) } } @@ -61,10 +59,10 @@ where #[inline] fn before_sending( &mut self, - _: &mut Self::Api, + _: &mut A, _: &mut TP::ExternalRequestParams, _: &[u8], - ) -> impl AsyncBounds + Future> { + ) -> impl AsyncBounds + Future> { async { Ok(()) } } @@ -85,12 +83,10 @@ where fn pkg_params_mut(&mut self) -> &mut Self::PackageParams; } -impl Package for () +impl Package<(), DRSR, TP> for () where TP: TransportParams, { - type Api = (); - type Error = crate::Error; type ExternalRequestContent = (); type ExternalResponseContent = (); type PackageParams = (); @@ -116,16 +112,15 @@ where } } -impl Package for &mut P +impl Package for &mut P where - P: Package, + A: Api, + P: Package, TP: AsyncBounds + TransportParams, TP::ExternalRequestParams: AsyncBounds, TP::ExternalResponseParams: AsyncBounds, Self: AsyncBounds, { - type Api = P::Api; - type Error = P::Error; type ExternalRequestContent = P::ExternalRequestContent; type ExternalResponseContent = P::ExternalResponseContent; type PackageParams = P::PackageParams; @@ -133,19 +128,19 @@ where #[inline] async fn after_sending( &mut self, - api: &mut Self::Api, + api: &mut A, ext_res_params: &mut TP::ExternalResponseParams, - ) -> Result<(), Self::Error> { + ) -> Result<(), A::Error> { (**self).after_sending(api, ext_res_params).await } #[inline] async fn before_sending( &mut self, - api: &mut Self::Api, + api: &mut A, ext_req_params: &mut TP::ExternalRequestParams, req_bytes: &[u8], - ) -> Result<(), Self::Error> { + ) -> Result<(), A::Error> { (**self).before_sending(api, ext_req_params, req_bytes).await } diff --git a/wtx/src/client_api_framework/pkg/batch_pkg.rs b/wtx/src/client_api_framework/pkg/batch_pkg.rs index 0ffe6527..e96277c2 100644 --- a/wtx/src/client_api_framework/pkg/batch_pkg.rs +++ b/wtx/src/client_api_framework/pkg/batch_pkg.rs @@ -3,7 +3,7 @@ use crate::{ dnsn::{Deserialize, Serialize}, network::transport::TransportParams, pkg::Package, - Id, + Api, Id, }, misc::AsyncBounds, }; @@ -12,9 +12,9 @@ use core::{borrow::Borrow, marker::PhantomData}; /// Used to perform batch requests with multiple packages. #[derive(Debug)] -pub struct BatchPkg<'slice, DRSR, P, TP>(BatchElems<'slice, DRSR, P, TP>, ()); +pub struct BatchPkg<'slice, A, DRSR, P, TP>(BatchElems<'slice, A, DRSR, P, TP>, ()); -impl<'slice, DRSR, P, TP> BatchPkg<'slice, DRSR, P, TP> { +impl<'slice, A, DRSR, P, TP> BatchPkg<'slice, A, DRSR, P, TP> { /// Currently, only slices of packages are allowed to perform batch requests. #[inline] pub fn new(slice: &'slice mut [P]) -> Self { @@ -22,9 +22,10 @@ impl<'slice, DRSR, P, TP> BatchPkg<'slice, DRSR, P, TP> { } } -impl BatchPkg<'_, DRSR, P, TP> +impl BatchPkg<'_, A, DRSR, P, TP> where - P: Package, + A: Api, + P: Package, P::ExternalRequestContent: Borrow + Ord, P::ExternalResponseContent: Borrow + Ord, TP: TransportParams, @@ -36,7 +37,7 @@ where buffer: &mut B, bytes: &[u8], drsr: &mut DRSR, - ) -> Result<(), P::Error> + ) -> Result<(), A::Error> where B: DynContigColl, { @@ -57,7 +58,7 @@ where } buffer.push(eresc).map_err(Into::into)?; pkgs_idx = pkgs_idx.wrapping_add(1); - Ok::<_, P::Error>(()) + Ok::<_, A::Error>(()) }, )?; if responses_are_not_sorted { @@ -100,27 +101,26 @@ where } } -impl<'slice, DRSR, P, TP> Package for BatchPkg<'slice, DRSR, P, TP> +impl<'slice, A, DRSR, P, TP> Package for BatchPkg<'slice, A, DRSR, P, TP> where - BatchElems<'slice, DRSR, P, TP>: Serialize, + A: Api, + BatchElems<'slice, A, DRSR, P, TP>: Serialize, DRSR: AsyncBounds, - P: AsyncBounds + Package, + P: AsyncBounds + Package, TP: AsyncBounds + TransportParams, TP::ExternalRequestParams: AsyncBounds, TP::ExternalResponseParams: AsyncBounds, { - type Api = P::Api; - type Error = P::Error; - type ExternalRequestContent = BatchElems<'slice, DRSR, P, TP>; + type ExternalRequestContent = BatchElems<'slice, A, DRSR, P, TP>; type ExternalResponseContent = (); type PackageParams = (); #[inline] async fn after_sending( &mut self, - api: &mut Self::Api, + api: &mut A, ext_res_params: &mut TP::ExternalResponseParams, - ) -> Result<(), Self::Error> { + ) -> Result<(), A::Error> { for elem in &mut *self.0 .0 { elem.after_sending(api, ext_res_params).await?; } @@ -130,10 +130,10 @@ where #[inline] async fn before_sending( &mut self, - api: &mut Self::Api, + api: &mut A, ext_req_params: &mut TP::ExternalRequestParams, req_bytes: &[u8], - ) -> Result<(), Self::Error> { + ) -> Result<(), A::Error> { for elem in &mut *self.0 .0 { elem.before_sending(api, ext_req_params, req_bytes).await?; } @@ -163,7 +163,7 @@ where /// Used internally and exclusively by [BatchPkg]. Not intended for public usage. #[derive(Debug)] -pub struct BatchElems<'slice, DRSR, P, T>(&'slice mut [P], PhantomData<(DRSR, T)>); +pub struct BatchElems<'slice, A, DRSR, P, T>(&'slice mut [P], PhantomData<(A, DRSR, T)>); #[cfg(feature = "serde_json")] mod serde_json { @@ -171,13 +171,15 @@ mod serde_json { dnsn::SerdeJson, network::transport::TransportParams, pkg::{BatchElems, Package}, + Api, }; use serde::Serializer; - impl crate::client_api_framework::dnsn::Serialize - for BatchElems<'_, DRSR, P, TP> + impl crate::client_api_framework::dnsn::Serialize + for BatchElems<'_, A, DRSR, P, TP> where - P: Package, + A: Api, + P: Package, P::ExternalRequestContent: serde::Serialize, TP: TransportParams, { diff --git a/wtx/src/client_api_framework/pkg/pkg_with_helper.rs b/wtx/src/client_api_framework/pkg/pkg_with_helper.rs index a0070241..9b6769de 100644 --- a/wtx/src/client_api_framework/pkg/pkg_with_helper.rs +++ b/wtx/src/client_api_framework/pkg/pkg_with_helper.rs @@ -1,6 +1,6 @@ use crate::{ client_api_framework::{ - data_format::JsonRpcRequest, network::transport::TransportParams, pkg::Package, Id, + data_format::JsonRpcRequest, network::transport::TransportParams, pkg::Package, Api, Id, }, misc::AsyncBounds, }; @@ -32,16 +32,15 @@ impl PkgWithHelper { } } -impl Package for PkgWithHelper +impl Package for PkgWithHelper where + A: Api, H: AsyncBounds, - P: AsyncBounds + Package, + P: AsyncBounds + Package, TP: AsyncBounds + TransportParams, TP::ExternalRequestParams: AsyncBounds, TP::ExternalResponseParams: AsyncBounds, { - type Api = P::Api; - type Error = P::Error; type ExternalRequestContent = P::ExternalRequestContent; type ExternalResponseContent = P::ExternalResponseContent; type PackageParams = P::PackageParams; @@ -49,19 +48,19 @@ where #[inline] async fn after_sending( &mut self, - api: &mut Self::Api, + api: &mut A, ext_res_params: &mut TP::ExternalResponseParams, - ) -> Result<(), Self::Error> { + ) -> Result<(), A::Error> { self.pkg.after_sending(api, ext_res_params).await } #[inline] async fn before_sending( &mut self, - api: &mut Self::Api, + api: &mut A, ext_req_params: &mut TP::ExternalRequestParams, req_bytes: &[u8], - ) -> Result<(), Self::Error> { + ) -> Result<(), A::Error> { self.pkg.before_sending(api, ext_req_params, req_bytes).await } diff --git a/wtx/src/client_api_framework/pkg/pkgs_aux.rs b/wtx/src/client_api_framework/pkg/pkgs_aux.rs index d2871070..57acad01 100644 --- a/wtx/src/client_api_framework/pkg/pkgs_aux.rs +++ b/wtx/src/client_api_framework/pkg/pkgs_aux.rs @@ -17,12 +17,12 @@ use alloc::vec::Vec; /// * `DRSR`: DeserializeR/SerializeR /// * `TP`: Transport Parameters #[derive(Debug)] -pub struct PkgsAux +pub struct PkgsAux where TP: TransportParams, { /// API instance. - pub api: API, + pub api: A, /// Used by practically all transports to serialize or receive data in any desired operation. /// /// Some transports require a pre-filled buffer so it is important to not modify indiscriminately. @@ -34,13 +34,13 @@ where built_requests: Id, } -impl PkgsAux +impl PkgsAux where TP: TransportParams, { /// Creates an instance with the minimum amount of mandatory parameters. #[inline] - pub fn from_minimum(api: API, drsr: DRSR, tp: TP) -> Self { + pub fn from_minimum(api: A, drsr: DRSR, tp: TP) -> Self { Self { api, byte_buffer: Vec::new(), drsr, tp, built_requests: 0 } } diff --git a/wtx/src/database/client/postgres/config.rs b/wtx/src/database/client/postgres/config.rs index a4815445..5e32c987 100644 --- a/wtx/src/database/client/postgres/config.rs +++ b/wtx/src/database/client/postgres/config.rs @@ -1,4 +1,4 @@ -use crate::misc::UriPartsRef; +use crate::misc::UriRef; use core::time::Duration; /// Configuration @@ -20,21 +20,21 @@ pub struct Config<'data> { impl<'data> Config<'data> { /// Unwraps the elements of an URI. #[inline] - pub fn from_uri_parts(up: &'data UriPartsRef<'_>) -> crate::Result> { + pub fn from_uri(uri: &'data UriRef<'_>) -> crate::Result> { let mut this = Self { app_name: "", connect_timeout: Duration::ZERO, - db: up.path().get(1..).unwrap_or_default(), - host: up.host(), + db: uri.path().get(1..).unwrap_or_default(), + host: uri.host(), keepalives: true, load_balance_hosts: LoadBalanceHosts::Disable, - password: up.password(), - port: up.port().parse()?, + password: uri.password(), + port: uri.port().parse()?, target_session_attrs: TargetSessionAttrs::Any, tcp_user_timeout: Duration::ZERO, - user: up.user(), + user: uri.user(), }; - for key_value in up.query().split('&') { + for key_value in uri.query().split('&') { let mut iter = key_value.split(':'); if let [Some(key), Some(value)] = [iter.next(), iter.next()] { this.set_param(key, value)?; diff --git a/wtx/src/database/client/postgres/executor/authentication.rs b/wtx/src/database/client/postgres/executor/authentication.rs index cb5511a8..e082cfde 100644 --- a/wtx/src/database/client/postgres/executor/authentication.rs +++ b/wtx/src/database/client/postgres/executor/authentication.rs @@ -6,7 +6,7 @@ use crate::{ }, Identifier, }, - misc::{FilledBufferWriter, PartitionedFilledBuffer, Stream, _from_utf8_basic_rslt}, + misc::{from_utf8_basic_rslt, FilledBufferWriter, PartitionedFilledBuffer, Stream}, rng::Rng, }; use alloc::vec::Vec; @@ -98,7 +98,7 @@ where MessageTy::ParameterStatus(name, value) => { params.insert( params.partition_point(|(local_name, _)| local_name.as_bytes() < name), - (_from_utf8_basic_rslt(name)?.try_into()?, _from_utf8_basic_rslt(value)?.try_into()?), + (from_utf8_basic_rslt(name)?.try_into()?, from_utf8_basic_rslt(value)?.try_into()?), ); } MessageTy::ReadyForQuery => return Ok(()), diff --git a/wtx/src/database/client/postgres/field.rs b/wtx/src/database/client/postgres/field.rs index d6ed7acb..e82cd0ab 100644 --- a/wtx/src/database/client/postgres/field.rs +++ b/wtx/src/database/client/postgres/field.rs @@ -1,4 +1,4 @@ -use crate::{database::client::postgres::Oid, misc::_from_utf8_basic_rslt}; +use crate::{database::client::postgres::Oid, misc::from_utf8_basic_rslt}; #[derive(Debug)] pub(crate) struct MsgField<'bytes> { @@ -17,7 +17,7 @@ impl<'bytes> MsgField<'bytes> { let &[_, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, ..] = rest_bytes else { return Err(crate::Error::UnexpectedDatabaseMessageBytes); }; - let name = _from_utf8_basic_rslt(name_bytes)?; + let name = from_utf8_basic_rslt(name_bytes)?; let _table_oid = u32::from_be_bytes([a, b, c, d]); let _column_id = i16::from_be_bytes([e, f]); let type_oid = u32::from_be_bytes([g, h, i, j]); diff --git a/wtx/src/database/client/postgres/integration_tests.rs b/wtx/src/database/client/postgres/integration_tests.rs index f42b57c1..51ad489e 100644 --- a/wtx/src/database/client/postgres/integration_tests.rs +++ b/wtx/src/database/client/postgres/integration_tests.rs @@ -1,13 +1,9 @@ -#[cfg(feature = "_tokio-rustls-client")] -#[path = "../../../../examples/tls_stream/mod.rs"] -mod tls_stream; - use crate::{ database::{ client::postgres::{Config, Executor, ExecutorBuffer}, Executor as _, Record, Records as _, }, - misc::UriPartsRef, + misc::{tls_stream_from_stream, UriRef}, rng::StaticRng, }; use tokio::net::TcpStream; @@ -23,14 +19,24 @@ async fn conn_md5() { #[tokio::test] async fn conn_scram() { let uri = "postgres://wtx_scram:wtx@localhost:5433/wtx"; - let up = UriPartsRef::new(&uri); + let uri = UriRef::new(&uri); let mut rng = StaticRng::default(); let _executor = Executor::connect_encrypted( - &Config::from_uri_parts(&up).unwrap(), + &Config::from_uri(&uri).unwrap(), ExecutorBuffer::with_default_params(&mut rng), - TcpStream::connect(up.host()).await.unwrap(), + TcpStream::connect(uri.host()).await.unwrap(), &mut rng, - |stream| async { Ok(tls_stream::_tls_stream_stream(up.hostname(), stream).await) }, + |stream| async { + Ok( + tls_stream_from_stream( + uri.hostname(), + Some(include_bytes!("../../../../../.certs/root-ca.crt")), + stream, + ) + .await + .unwrap(), + ) + }, ) .await .unwrap(); @@ -259,14 +265,13 @@ async fn reuses_cached_statement() { } async fn executor() -> Executor { - let uri = "postgres://wtx_md5:wtx@localhost:5432/wtx"; - let up = UriPartsRef::new(&uri); + let uri = UriRef::new("postgres://wtx_md5:wtx@localhost:5432/wtx"); let mut rng = StaticRng::default(); Executor::connect( - &Config::from_uri_parts(&up).unwrap(), + &Config::from_uri(&uri).unwrap(), ExecutorBuffer::with_default_params(&mut rng), &mut rng, - TcpStream::connect(up.host()).await.unwrap(), + TcpStream::connect(uri.host()).await.unwrap(), ) .await .unwrap() diff --git a/wtx/src/database/client/postgres/message.rs b/wtx/src/database/client/postgres/message.rs index 2394e3f5..4f7b179c 100644 --- a/wtx/src/database/client/postgres/message.rs +++ b/wtx/src/database/client/postgres/message.rs @@ -1,6 +1,6 @@ use crate::{ database::client::postgres::{Authentication, DbError}, - misc::{_atoi, _from_utf8_basic_rslt}, + misc::{_atoi, from_utf8_basic_rslt}, }; use core::any::type_name; @@ -87,7 +87,7 @@ impl<'bytes> TryFrom<&'bytes [u8]> for MessageTy<'bytes> { } [b'D', _, _, _, _, a, b, rest @ ..] => Self::DataRow(u16::from_be_bytes([*a, *b]), rest), [b'E', _, _, _, _, rest @ ..] => { - return Err(DbError::try_from(_from_utf8_basic_rslt(rest)?)?.into()) + return Err(DbError::try_from(from_utf8_basic_rslt(rest)?)?.into()) } [b'G', ..] => Self::CopyInResponse, [b'H', ..] => Self::CopyOutResponse, diff --git a/wtx/src/database/client/postgres/record.rs b/wtx/src/database/client/postgres/record.rs index 945b1c8d..4e35fac5 100644 --- a/wtx/src/database/client/postgres/record.rs +++ b/wtx/src/database/client/postgres/record.rs @@ -102,7 +102,7 @@ impl<'exec> ValueIdent> for str { mod arrayvec { use crate::{ database::{FromRecord, Record}, - misc::_from_utf8_basic_rslt, + misc::from_utf8_basic_rslt, }; use arrayvec::ArrayString; @@ -114,7 +114,7 @@ mod arrayvec { #[inline] fn from_record(record: crate::database::client::postgres::Record<'exec>) -> Result { Ok( - _from_utf8_basic_rslt(record.value(0).ok_or(crate::Error::NoInnerValue("Record"))?.bytes()) + from_utf8_basic_rslt(record.value(0).ok_or(crate::Error::NoInnerValue("Record"))?.bytes()) .map_err(From::from)? .try_into() .map_err(From::from)?, diff --git a/wtx/src/database/client/postgres/tys.rs b/wtx/src/database/client/postgres/tys.rs index 278eea69..7335deaf 100644 --- a/wtx/src/database/client/postgres/tys.rs +++ b/wtx/src/database/client/postgres/tys.rs @@ -21,7 +21,7 @@ mod arrayvec { client::postgres::{Postgres, Value}, Decode, Encode, }, - misc::{FilledBufferWriter, _from_utf8_basic_rslt}, + misc::{from_utf8_basic_rslt, FilledBufferWriter}, }; use arrayvec::ArrayString; @@ -33,7 +33,7 @@ mod arrayvec { #[inline] fn decode(input: Self::Value<'_>) -> Result { - Ok(_from_utf8_basic_rslt(input.bytes()).map_err(Into::into)?.try_into().map_err(Into::into)?) + Ok(from_utf8_basic_rslt(input.bytes()).map_err(Into::into)?.try_into().map_err(Into::into)?) } } @@ -112,7 +112,7 @@ mod collections { client::postgres::{Postgres, Value}, Decode, Encode, }, - misc::{FilledBufferWriter, _from_utf8_basic_rslt}, + misc::{from_utf8_basic_rslt, FilledBufferWriter}, }; use alloc::string::String; @@ -149,7 +149,7 @@ mod collections { #[inline] fn decode(input: Self::Value<'_>) -> Result { - Ok(_from_utf8_basic_rslt(input.bytes()).map_err(crate::Error::from)?) + Ok(from_utf8_basic_rslt(input.bytes()).map_err(crate::Error::from)?) } } @@ -174,7 +174,7 @@ mod collections { #[inline] fn decode(input: Self::Value<'_>) -> Result { - Ok(_from_utf8_basic_rslt(input.bytes()).map_err(crate::Error::from)?.into()) + Ok(from_utf8_basic_rslt(input.bytes()).map_err(crate::Error::from)?.into()) } } diff --git a/wtx/src/database/sm/integration_tests.rs b/wtx/src/database/sm/integration_tests.rs index 592c9a27..9933fd2e 100644 --- a/wtx/src/database/sm/integration_tests.rs +++ b/wtx/src/database/sm/integration_tests.rs @@ -42,9 +42,9 @@ macro_rules! create_integration_tests { create_integration_test!( { let uri = std::env::var(DEFAULT_URI_VAR).unwrap(); - let uri_parts = crate::misc::UriPartsRef::new(&uri); - let config = crate::database::client::postgres::Config::from_uri_parts(&uri_parts).unwrap(); - let stream = TcpStream::connect(uri_parts.host()).await.unwrap(); + let uri = crate::misc::UriRef::new(&uri); + let config = crate::database::client::postgres::Config::from_uri(&uri).unwrap(); + let stream = TcpStream::connect(uri.host()).await.unwrap(); let mut rng = StaticRng::default(); crate::database::client::postgres::Executor::connect( &config, diff --git a/wtx/src/error.rs b/wtx/src/error.rs index 2c75b4f2..330f0288 100644 --- a/wtx/src/error.rs +++ b/wtx/src/error.rs @@ -35,6 +35,8 @@ pub enum Error { #[cfg(feature = "crypto-common")] CryptoCommonInvalidLength(crypto_common::InvalidLength), #[cfg(feature = "base64")] + DecodeError(base64::DecodeError), + #[cfg(feature = "base64")] DecodeSliceError(base64::DecodeSliceError), #[cfg(feature = "deadpool")] DeadPoolManagedPoolError(Box>), @@ -122,8 +124,8 @@ pub enum Error { UnknownHttpStatusCode(u16), /// `wtx` can not perform this operation due to known limitations. UnsupportedOperation, - /// Only append is possible but overwritten is still viable through resetting. - UrlCanNotOverwriteInitiallySetUrl, + /// Only appending is possible but overwritten is still viable through resetting. + UriCanNotBeOverwritten, // ***** Internal - Database client ***** // @@ -329,6 +331,15 @@ impl From for Error { } } +#[cfg(feature = "base64")] +impl From for Error { + #[inline] + #[track_caller] + fn from(from: base64::DecodeError) -> Self { + Self::DecodeError(from) + } +} + #[cfg(feature = "base64")] impl From for Error { #[inline] diff --git a/wtx/src/misc.rs b/wtx/src/misc.rs index 5d96ff9c..9e6aa480 100644 --- a/wtx/src/misc.rs +++ b/wtx/src/misc.rs @@ -13,27 +13,42 @@ mod filled_buffer_writer; mod fn_mut_fut; mod generic_time; mod partitioned_filled_buffer; +mod query_writer; mod stream; +#[cfg(feature = "_tokio-rustls-client")] +mod tokio_rustls; mod traits; -mod uri_parts; +mod uri; mod wrapper; +#[cfg(feature = "_tokio-rustls-client")] +pub use self::tokio_rustls::*; #[cfg(test)] use alloc::string::String; pub(crate) use array_chunks::ArrayChunksMut; pub use async_bounds::AsyncBounds; -pub(crate) use basic_utf8_error::BasicUtf8Error; +pub use basic_utf8_error::BasicUtf8Error; use core::{any::type_name, time::Duration}; pub use enum_var_strings::EnumVarStrings; pub use filled_buffer_writer::FilledBufferWriter; pub use fn_mut_fut::FnMutFut; pub use generic_time::GenericTime; pub(crate) use partitioned_filled_buffer::PartitionedFilledBuffer; +pub use query_writer::QueryWriter; pub use stream::{BytesStream, Stream, TlsStream}; pub use traits::SingleTypeStorage; -pub use uri_parts::{UriParts, UriPartsRef, UriPartsString}; +pub use uri::{Uri, UriRef, UriString}; pub use wrapper::Wrapper; +/// Internally uses `simdutf8` if the feature was activated. +#[inline] +pub fn from_utf8_basic_rslt(bytes: &[u8]) -> Result<&str, BasicUtf8Error> { + #[cfg(feature = "simdutf8")] + return simdutf8::basic::from_utf8(bytes).ok().ok_or(BasicUtf8Error {}); + #[cfg(not(feature = "simdutf8"))] + return core::str::from_utf8(bytes).ok().ok_or(BasicUtf8Error {}); +} + /// Useful when a request returns an optional field but the actual usage is within a /// [core::result::Result] context. #[inline] @@ -106,7 +121,7 @@ where T: core::str::FromStr, T::Err: Into, { - Ok(_from_utf8_basic_rslt(bytes)?.parse().map_err(Into::into)?) + Ok(from_utf8_basic_rslt(bytes)?.parse().map_err(Into::into)?) } #[cfg(feature = "atoi")] pub(crate) fn _atoi(bytes: &[u8]) -> crate::Result @@ -116,19 +131,12 @@ where atoi::atoi(bytes).ok_or(crate::Error::AtoiInvalidBytes) } -pub(crate) fn _from_utf8_basic_rslt(bytes: &[u8]) -> Result<&str, BasicUtf8Error> { - #[cfg(feature = "simdutf8")] - return simdutf8::basic::from_utf8(bytes).ok().ok_or(BasicUtf8Error {}); - #[cfg(not(feature = "simdutf8"))] - return core::str::from_utf8(bytes).ok().ok_or(BasicUtf8Error {}); -} - #[cfg(test)] -pub(crate) fn _uri() -> UriPartsString { +pub(crate) fn _uri() -> UriString { use core::sync::atomic::{AtomicU32, Ordering}; static PORT: AtomicU32 = AtomicU32::new(7000); let uri = alloc::format!("http://127.0.0.1:{}", PORT.fetch_add(1, Ordering::Relaxed)); - UriPartsString::new(uri) + UriString::new(uri) } pub(crate) async fn _read_until( diff --git a/wtx/src/misc/basic_utf8_error.rs b/wtx/src/misc/basic_utf8_error.rs index 8f0b0490..2c58e29f 100644 --- a/wtx/src/misc/basic_utf8_error.rs +++ b/wtx/src/misc/basic_utf8_error.rs @@ -1,5 +1,6 @@ /// Basic string error that doesn't contain any information. -pub(crate) struct BasicUtf8Error; +#[derive(Debug)] +pub struct BasicUtf8Error; impl From for crate::Error { #[inline] diff --git a/wtx/src/misc/filled_buffer_writer.rs b/wtx/src/misc/filled_buffer_writer.rs index fecd674b..55a0b134 100644 --- a/wtx/src/misc/filled_buffer_writer.rs +++ b/wtx/src/misc/filled_buffer_writer.rs @@ -60,6 +60,12 @@ impl<'vec> FilledBufferWriter<'vec> { self._extend_from_slice_generic(slice, []); } + pub(crate) fn _extend_from_slices(&mut self, slices: &[&[u8]]) { + for slice in slices { + self._extend_from_slice(slice); + } + } + /// The `c` suffix means that `slice` is copied as a C string. pub(crate) fn _extend_from_slice_c(&mut self, slice: &[u8]) { self._extend_from_slice_generic(slice, [0]); diff --git a/wtx/src/client_api_framework/misc/query_writer.rs b/wtx/src/misc/query_writer.rs similarity index 82% rename from wtx/src/client_api_framework/misc/query_writer.rs rename to wtx/src/misc/query_writer.rs index 2572abc3..20729a99 100644 --- a/wtx/src/client_api_framework/misc/query_writer.rs +++ b/wtx/src/misc/query_writer.rs @@ -1,5 +1,7 @@ -use cl_aux::DynString; -use core::{borrow::Borrow, fmt::Display}; +use core::{ + borrow::Borrow, + fmt::{Display, Write}, +}; /// Query parameters need special handling because of the initial `?`. #[derive(Debug)] @@ -10,10 +12,10 @@ pub struct QueryWriter<'str, S> { impl<'str, S> QueryWriter<'str, S> where - S: DynString, + S: AsRef + Write, { pub(crate) fn new(s: &'str mut S) -> Self { - Self { initial_len: s.len(), s } + Self { initial_len: s.as_ref().len(), s } } /// Writes `?param=value` or `¶m=value`. @@ -22,7 +24,7 @@ where where T: Display, { - if self.s.len() == self.initial_len { + if self.s.as_ref().len() == self.initial_len { self.s.write_fmt(format_args!("?{param}={value}"))?; } else { self.s.write_fmt(format_args!("&{param}={value}"))?; diff --git a/wtx/src/misc/tokio_rustls.rs b/wtx/src/misc/tokio_rustls.rs new file mode 100644 index 00000000..578175df --- /dev/null +++ b/wtx/src/misc/tokio_rustls.rs @@ -0,0 +1,47 @@ +use rustls_pemfile::certs; +use rustls_pki_types::ServerName; +use std::sync::Arc; +use tokio::net::TcpStream; +use tokio_rustls::{ + client::TlsStream, + rustls::{ClientConfig, RootCertStore}, + TlsConnector, +}; +use webpki_roots::TLS_SERVER_ROOTS; + +/// Private method. +pub async fn tls_stream_from_host( + host: &str, + hostname: &str, + root_ca: Option<&[u8]>, +) -> crate::Result> { + let stream = TcpStream::connect(host).await?; + Ok(tls_connector(root_ca)?.connect(server_name(hostname)?, stream).await?) +} + +/// Private method. +pub async fn tls_stream_from_stream( + hostname: &str, + root_ca: Option<&[u8]>, + stream: TcpStream, +) -> crate::Result> { + Ok(tls_connector(root_ca)?.connect(server_name(hostname)?, stream).await?) +} + +fn server_name(hostname: &str) -> crate::Result> { + Ok( + ServerName::try_from(hostname.to_string()) + .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidInput, err))?, + ) +} + +fn tls_connector(root_ca: Option<&[u8]>) -> crate::Result { + let mut root_store = RootCertStore::empty(); + root_store.extend(TLS_SERVER_ROOTS.iter().cloned()); + if let Some(elem) = root_ca { + let certs: Vec<_> = certs(&mut &*elem).collect::>()?; + let _ = root_store.add_parsable_certificates(certs); + } + let config = ClientConfig::builder().with_root_certificates(root_store).with_no_client_auth(); + Ok(TlsConnector::from(Arc::new(config))) +} diff --git a/wtx/src/misc/uri.rs b/wtx/src/misc/uri.rs new file mode 100644 index 00000000..d82d8c2f --- /dev/null +++ b/wtx/src/misc/uri.rs @@ -0,0 +1,306 @@ +use crate::misc::QueryWriter; +use alloc::string::String; +use core::fmt::{Arguments, Debug, Formatter, Write}; + +/// [Uri] with a string reference. +pub type UriRef<'uri> = Uri<&'uri str>; +/// [Uri] with an owned string. +pub type UriString = Uri; + +/// Elements that compose an URI. +/// +/// ```txt +/// foo://user:password@hostname:80/path?query=value#hash +/// ``` +#[derive(Eq, Ord, PartialEq, PartialOrd)] +pub struct Uri { + authority_start_idx: u16, + href_start_idx: u16, + initial_len: u16, + uri: S, +} + +impl Uri +where + S: AsRef, +{ + /// Analyzes the provided `uri` to create a new instance. + #[inline] + pub fn new(uri: S) -> Self { + let initial_len = uri.as_ref().len().try_into().unwrap_or(u16::MAX); + let valid_uri = uri.as_ref().get(..initial_len.into()).unwrap_or_default(); + let authority_start_idx: u16 = valid_uri + .match_indices("://") + .next() + .and_then(|(element, _)| element.wrapping_add(3).try_into().ok()) + .unwrap_or_default(); + let href_start_idx = valid_uri + .as_bytes() + .iter() + .copied() + .enumerate() + .skip(authority_start_idx.into()) + .find_map(|(idx, el)| (el == b'/').then_some(idx).and_then(|_usisze| _usisze.try_into().ok())) + .unwrap_or(initial_len); + Self { authority_start_idx, href_start_idx, initial_len, uri } + } + + /// ```rust + /// let uri = wtx::misc::Uri::new("foo://user:password@hostname:80/path?query=value#hash"); + /// assert_eq!(uri.authority(), "user:password@hostname:80"); + /// ``` + #[inline] + pub fn authority(&self) -> &str { + self + .uri + .as_ref() + .get(self.authority_start_idx.into()..self.href_start_idx.into()) + .unwrap_or_default() + } + + /// ```rust + /// let uri = wtx::misc::Uri::new("foo://user:password@hostname:80/path?query=value#hash"); + /// assert_eq!(uri.fragment(), "hash"); + /// ``` + #[inline] + pub fn fragment(&self) -> &str { + let href = self.href(); + let maybe_rslt = href.rsplit_once('?').map_or(href, |el| el.1); + if let Some((_, rslt)) = maybe_rslt.rsplit_once('#') { + rslt + } else { + maybe_rslt + } + } + + /// ```rust + /// let uri = wtx::misc::Uri::new("foo://user:password@hostname:80/path?query=value#hash"); + /// assert_eq!(uri.host(), "hostname:80"); + /// ``` + #[inline] + pub fn host(&self) -> &str { + let authority = self.authority(); + if let Some(elem) = authority.split_once('@') { + elem.1 + } else { + authority + } + } + + /// ```rust + /// let uri = wtx::misc::Uri::new("foo://user:password@hostname:80/path?query=value#hash"); + /// assert_eq!(uri.hostname(), "hostname"); + /// ``` + #[inline] + pub fn hostname(&self) -> &str { + let host = self.host(); + host.split_once(':').map_or(host, |el| el.0) + } + + /// ```rust + /// let uri = wtx::misc::Uri::new("foo://user:password@hostname:80/path?query=value#hash"); + /// assert_eq!(uri.href(), "/path?query=value#hash"); + /// ``` + #[inline] + pub fn href(&self) -> &str { + if let Some(elem) = self.uri.as_ref().get(self.href_start_idx.into()..) { + if !elem.is_empty() { + return elem; + } + } + "/" + } + + /// ```rust + /// let uri = wtx::misc::Uri::new("foo://user:password@hostname:80/path?query=value#hash"); + /// assert_eq!(uri.password(), "password"); + /// ``` + #[inline] + pub fn password(&self) -> &str { + if let Some(elem) = self.userinfo().split_once(':') { + elem.1 + } else { + "" + } + } + + /// ```rust + /// let uri = wtx::misc::Uri::new("foo://user:password@hostname:80/path?query=value#hash"); + /// assert_eq!(uri.path(), "/path"); + /// ``` + #[inline] + pub fn path(&self) -> &str { + let href = self.href(); + href.rsplit_once('?').map_or(href, |el| el.0) + } + + /// ```rust + /// let uri = wtx::misc::Uri::new("foo://user:password@hostname:80/path?query=value#hash"); + /// assert_eq!(uri.port(), "80"); + /// ``` + #[inline] + pub fn port(&self) -> &str { + let host = self.host(); + host.split_once(':').map_or(host, |el| el.1) + } + + /// ```rust + /// let uri = wtx::misc::Uri::new("foo://user:password@hostname:80/path?query=value#hash"); + /// assert_eq!(uri.query(), "query=value"); + /// ``` + #[inline] + pub fn query(&self) -> &str { + let href = self.href(); + let before_hash = if let Some((elem, _)) = href.rsplit_once('#') { elem } else { href }; + if let Some((_, elem)) = before_hash.rsplit_once('?') { + elem + } else { + "" + } + } + + /// ```rust + /// let uri = wtx::misc::Uri::new("foo://user:password@hostname:80/path?query=value#hash"); + /// assert_eq!(uri.schema(), "foo"); + /// ``` + #[inline] + pub fn schema(&self) -> &str { + let mut iter = self.uri.as_ref().split("://"); + let first_opt = iter.next(); + if iter.next().is_some() { + first_opt.unwrap_or_default() + } else { + "" + } + } + + /// See [UriPartsRef]. + #[inline] + pub fn to_ref(&self) -> UriRef<'_> { + UriRef { + authority_start_idx: self.authority_start_idx, + href_start_idx: self.href_start_idx, + initial_len: self.initial_len, + uri: self.uri.as_ref(), + } + } + + /// See [UriPartsString]. + #[inline] + pub fn to_string(&self) -> UriString { + UriString { + authority_start_idx: self.authority_start_idx, + href_start_idx: self.href_start_idx, + initial_len: self.initial_len, + uri: self.uri.as_ref().into(), + } + } + + /// Full URI. + #[inline] + pub fn uri(&self) -> &str { + self.uri.as_ref() + } + + /// ```rust + /// let uri = wtx::misc::Uri::new("foo://user:password@hostname:80/path?query=value#hash"); + /// assert_eq!(uri.user(), "user"); + /// ``` + #[inline] + pub fn user(&self) -> &str { + if let Some(elem) = self.userinfo().split_once(':') { + elem.0 + } else { + "" + } + } + + /// ```rust + /// let uri = wtx::misc::Uri::new("foo://user:password@hostname:80/path?query=value#hash"); + /// assert_eq!(uri.userinfo(), "user:password"); + /// ``` + #[inline] + pub fn userinfo(&self) -> &str { + if let Some(elem) = self.authority().split_once('@') { + elem.0 + } else { + "" + } + } +} + +impl UriString { + /// Clears the internal storage. + #[inline] + pub fn clear(&mut self) { + self.authority_start_idx = 0; + self.href_start_idx = 0; + self.uri.clear(); + } + + /// Pushes an additional path erasing any subsequent content. + #[inline] + pub fn push_path(&mut self, args: Arguments<'_>) -> crate::Result<()> { + if !self.query().is_empty() { + return Err(crate::Error::UriCanNotBeOverwritten); + } + self.uri.write_fmt(args)?; + Ok(()) + } + + /// See [`QueryWriter`]. + #[inline] + pub fn query_writer(&mut self) -> crate::Result> { + if !self.query().is_empty() { + return Err(crate::Error::UriCanNotBeOverwritten); + } + Ok(QueryWriter::new(&mut self.uri)) + } + + /// Truncates the internal storage with the length of the URL initially created in this instance. + /// + /// If the current length is lesser than the original URL length, nothing will happen. + #[inline] + pub fn retain_with_initial_len(&mut self) { + self.uri.truncate(self.initial_len.into()); + } +} + +impl Debug for Uri { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Uri").finish() + } +} + +impl From for Uri +where + S: AsRef, +{ + #[inline] + fn from(value: S) -> Self { + Self::new(value) + } +} + +#[cfg(test)] +mod tests { + use crate::misc::UriString; + + #[test] + fn mutable_methods_have_correct_behavior() { + let mut uri = UriString::new("http://dasdas.com/rewqd".into()); + uri.push_path(format_args!("/tretre")).unwrap(); + assert_eq!(uri.path(), "/rewqd/tretre"); + assert_eq!(uri.query(), ""); + assert_eq!(uri.uri(), "http://dasdas.com/rewqd/tretre"); + uri.retain_with_initial_len(); + assert_eq!(uri.path(), "/rewqd"); + assert_eq!(uri.query(), ""); + assert_eq!(uri.uri(), "http://dasdas.com/rewqd"); + uri.clear(); + assert_eq!(uri.path(), "/"); + assert_eq!(uri.query(), ""); + assert_eq!(uri.uri(), ""); + } +} diff --git a/wtx/src/misc/uri_parts.rs b/wtx/src/misc/uri_parts.rs deleted file mode 100644 index 5fef1ce5..00000000 --- a/wtx/src/misc/uri_parts.rs +++ /dev/null @@ -1,231 +0,0 @@ -use alloc::string::String; - -/// [UriParts] with an owned string. -pub type UriPartsString = UriParts; -/// [UriParts] with a string reference. -pub type UriPartsRef<'uri> = UriParts<&'uri str>; - -/// Elements that compose an URI. -/// -/// ```txt -/// foo://user:password@hostname:80/path?query=value#hash -/// ``` -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct UriParts { - authority_start_idx: usize, - href_start_idx: usize, - uri: S, -} - -impl UriParts -where - S: AsRef, -{ - /// Analyzes the provided `uri` to create a new instance. - #[inline] - pub fn new(uri: S) -> Self { - let authority_start_idx = uri - .as_ref() - .match_indices("://") - .next() - .map(|(element, _)| element.wrapping_add(3)) - .unwrap_or_default(); - let href_start_idx = uri - .as_ref() - .as_bytes() - .iter() - .copied() - .enumerate() - .skip(authority_start_idx) - .find_map(|(idx, el)| (el == b'/').then_some(idx)) - .unwrap_or(uri.as_ref().len()); - Self { authority_start_idx, href_start_idx, uri } - } - - /// ```rust - /// let up = wtx::misc::UriParts::new("foo://user:password@hostname:80/path?query=value#hash"); - /// assert_eq!(up.authority(), "user:password@hostname:80"); - /// ``` - #[inline] - pub fn authority(&self) -> &str { - self.uri.as_ref().get(self.authority_start_idx..self.href_start_idx).unwrap_or_default() - } - - /// See [UriPartsRef]. - #[inline] - pub fn as_ref(&self) -> UriPartsRef<'_> { - UriPartsRef { - authority_start_idx: self.authority_start_idx, - href_start_idx: self.href_start_idx, - uri: self.uri.as_ref(), - } - } - - /// ```rust - /// let up = wtx::misc::UriParts::new("foo://user:password@hostname:80/path?query=value#hash"); - /// assert_eq!(up.fragment(), "hash"); - /// ``` - #[inline] - pub fn fragment(&self) -> &str { - let href = self.href(); - let maybe_rslt = href.rsplit_once('?').map_or(href, |el| el.1); - if let Some((_, rslt)) = maybe_rslt.rsplit_once('#') { - rslt - } else { - maybe_rslt - } - } - - /// ```rust - /// let up = wtx::misc::UriParts::new("foo://user:password@hostname:80/path?query=value#hash"); - /// assert_eq!(up.host(), "hostname:80"); - /// ``` - #[inline] - pub fn host(&self) -> &str { - let authority = self.authority(); - if let Some(elem) = authority.split_once('@') { - elem.1 - } else { - authority - } - } - - /// ```rust - /// let up = wtx::misc::UriParts::new("foo://user:password@hostname:80/path?query=value#hash"); - /// assert_eq!(up.hostname(), "hostname"); - /// ``` - #[inline] - pub fn hostname(&self) -> &str { - let host = self.host(); - host.split_once(':').map_or(host, |el| el.0) - } - - /// ```rust - /// let up = wtx::misc::UriParts::new("foo://user:password@hostname:80/path?query=value#hash"); - /// assert_eq!(up.href(), "/path?query=value#hash"); - /// ``` - #[inline] - pub fn href(&self) -> &str { - if let Some(elem) = self.uri.as_ref().get(self.href_start_idx..) { - if !elem.is_empty() { - return elem; - } - } - "/" - } - - /// See [UriPartsString]. - #[inline] - pub fn into_string(self) -> UriPartsString { - UriPartsString { - authority_start_idx: self.authority_start_idx, - href_start_idx: self.href_start_idx, - uri: self.uri.as_ref().into(), - } - } - - /// ```rust - /// let up = wtx::misc::UriParts::new("foo://user:password@hostname:80/path?query=value#hash"); - /// assert_eq!(up.password(), "password"); - /// ``` - #[inline] - pub fn password(&self) -> &str { - if let Some(elem) = self.userinfo().split_once(':') { - elem.1 - } else { - "" - } - } - - /// ```rust - /// let up = wtx::misc::UriParts::new("foo://user:password@hostname:80/path?query=value#hash"); - /// assert_eq!(up.path(), "/path"); - /// ``` - #[inline] - pub fn path(&self) -> &str { - let href = self.href(); - href.rsplit_once('?').map_or(href, |el| el.0) - } - - /// ```rust - /// let up = wtx::misc::UriParts::new("foo://user:password@hostname:80/path?query=value#hash"); - /// assert_eq!(up.port(), "80"); - /// ``` - #[inline] - pub fn port(&self) -> &str { - let host = self.host(); - host.split_once(':').map_or(host, |el| el.1) - } - - /// ```rust - /// let up = wtx::misc::UriParts::new("foo://user:password@hostname:80/path?query=value#hash"); - /// assert_eq!(up.query(), "query=value"); - /// ``` - #[inline] - pub fn query(&self) -> &str { - let href = self.href(); - let maybe_rslt = href.rsplit_once('?').map_or(href, |el| el.1); - if let Some((rslt, _)) = maybe_rslt.rsplit_once('#') { - rslt - } else { - maybe_rslt - } - } - - /// ```rust - /// let up = wtx::misc::UriParts::new("foo://user:password@hostname:80/path?query=value#hash"); - /// assert_eq!(up.schema(), "foo"); - /// ``` - #[inline] - pub fn schema(&self) -> &str { - let mut iter = self.uri.as_ref().split("://"); - let first_opt = iter.next(); - if iter.next().is_some() { - first_opt.unwrap_or_default() - } else { - "" - } - } - - /// Full URI. - #[inline] - pub fn uri(&self) -> &str { - self.uri.as_ref() - } - - /// ```rust - /// let up = wtx::misc::UriParts::new("foo://user:password@hostname:80/path?query=value#hash"); - /// assert_eq!(up.user(), "user"); - /// ``` - #[inline] - pub fn user(&self) -> &str { - if let Some(elem) = self.userinfo().split_once(':') { - elem.0 - } else { - "" - } - } - - /// ```rust - /// let up = wtx::misc::UriParts::new("foo://user:password@hostname:80/path?query=value#hash"); - /// assert_eq!(up.userinfo(), "user:password"); - /// ``` - #[inline] - pub fn userinfo(&self) -> &str { - if let Some(elem) = self.authority().split_once('@') { - elem.0 - } else { - "" - } - } -} - -impl From for UriParts -where - S: AsRef, -{ - #[inline] - fn from(value: S) -> Self { - Self::new(value) - } -} diff --git a/wtx/src/web_socket.rs b/wtx/src/web_socket.rs index b50da79a..aea0f057 100644 --- a/wtx/src/web_socket.rs +++ b/wtx/src/web_socket.rs @@ -21,7 +21,7 @@ mod web_socket_buffer; #[cfg(feature = "tracing")] use crate::web_socket::misc::Role; use crate::{ - misc::{PartitionedFilledBuffer, Stream, _from_utf8_basic_rslt, _read_until}, + misc::{from_utf8_basic_rslt, PartitionedFilledBuffer, Stream, _read_until}, rng::Rng, web_socket::{ compression::NegotiatedCompression, @@ -165,7 +165,7 @@ where .as_ref() .get(payload_start_idx..payload_start_idx.wrapping_add(payload_len)) .unwrap_or_default(); - if matches!(first_rfi.op_code, OpCode::Text) && _from_utf8_basic_rslt(payload).is_err() { + if matches!(first_rfi.op_code, OpCode::Text) && from_utf8_basic_rslt(payload).is_err() { return Err(crate::Error::InvalidUTF8); } payload_len @@ -695,7 +695,7 @@ where [] => {} [_] => return Err(crate::Error::InvalidCloseFrame), [a, b, rest @ ..] => { - let _ = _from_utf8_basic_rslt(rest)?; + let _ = from_utf8_basic_rslt(rest)?; let is_not_allowed = !CloseCode::try_from(u16::from_be_bytes([*a, *b]))?.is_allowed(); if is_not_allowed || rest.len() > MAX_CONTROL_FRAME_PAYLOAD_LEN - 2 { Self::write_control_frame( @@ -910,7 +910,7 @@ where return Err(crate::Error::UnexpectedMessageFrame); } OpCode::Text => { - let _ = _from_utf8_basic_rslt(fb.payload())?; + let _ = from_utf8_basic_rslt(fb.payload())?; } OpCode::Binary | OpCode::Close | OpCode::Ping | OpCode::Pong => {} } diff --git a/wtx/src/web_socket/compression/flate2.rs b/wtx/src/web_socket/compression/flate2.rs index 8f76e77f..da680bd3 100644 --- a/wtx/src/web_socket/compression/flate2.rs +++ b/wtx/src/web_socket/compression/flate2.rs @@ -1,6 +1,6 @@ use crate::{ http::Header, - misc::{FilledBufferWriter, _from_utf8_basic_rslt}, + misc::{from_utf8_basic_rslt, FilledBufferWriter}, web_socket::{compression::NegotiatedCompression, misc::_trim_bytes, Compression, DeflateConfig}, }; use core::str::FromStr; @@ -234,7 +234,7 @@ where T: FromStr, { let after_equals = bytes.split(|byte| byte == &b'=').nth(1)?; - _from_utf8_basic_rslt(after_equals).ok()?.parse::().ok() + from_utf8_basic_rslt(after_equals).ok()?.parse::().ok() } #[inline] diff --git a/wtx/src/web_socket/handshake/raw.rs b/wtx/src/web_socket/handshake/raw.rs index 4c592d64..3d19a5ec 100644 --- a/wtx/src/web_socket/handshake/raw.rs +++ b/wtx/src/web_socket/handshake/raw.rs @@ -1,4 +1,7 @@ -use crate::web_socket::{handshake::HeadersBuffer, FrameBuffer}; +use crate::{ + misc::UriRef, + web_socket::{handshake::HeadersBuffer, FrameBuffer}, +}; const MAX_READ_HEADER_LEN: usize = 64; @@ -29,7 +32,7 @@ pub struct WebSocketConnectRaw<'fb, 'hb, 'uri, B, C, H, RNG, S, WSB> { /// Stream pub stream: S, /// Uri - pub uri: &'uri str, + pub uri: &'uri UriRef<'uri>, /// WebSocket Buffer pub wsb: WSB, } @@ -38,7 +41,7 @@ pub struct WebSocketConnectRaw<'fb, 'hb, 'uri, B, C, H, RNG, S, WSB> { mod httparse_impls { use crate::{ http::{ExpectedHeader, Header as _, Request as _}, - misc::{AsyncBounds, FilledBufferWriter, Stream, UriPartsRef}, + misc::{AsyncBounds, FilledBufferWriter, Stream, UriRef}, rng::Rng, web_socket::{ compression::NegotiatedCompression, @@ -52,7 +55,7 @@ mod httparse_impls { }, }; use alloc::vec::Vec; - use core::{borrow::BorrowMut, str}; + use core::borrow::BorrowMut; use httparse::{Header, Request, Response, Status, EMPTY_HEADER}; const MAX_READ_LEN: usize = 2 * 1024; @@ -148,8 +151,8 @@ mod httparse_impls { let nb = &mut self.wsb.borrow_mut().nb; nb._clear(); let mut fbw = nb.into(); - let (key, req) = build_req(&self.compression, &mut fbw, key_buffer, &mut self.rng, self.uri); - self.stream.write_all(req).await?; + let key = build_req(&self.compression, &mut fbw, key_buffer, &mut self.rng, self.uri); + self.stream.write_all(fbw._curr_bytes()).await?; let mut read = 0; self.fb._set_indices_through_expansion(0, 0, MAX_READ_LEN); let len = loop { @@ -188,27 +191,31 @@ mod httparse_impls { } /// Client request - fn build_req<'fpb, 'kb, C>( + fn build_req<'kb, C>( compression: &C, - fbw: &'fpb mut FilledBufferWriter<'_>, + fbw: &mut FilledBufferWriter<'_>, key_buffer: &'kb mut [u8; 26], rng: &mut impl Rng, - uri: &str, - ) -> (&'kb [u8], &'fpb [u8]) + uri: &UriRef<'_>, + ) -> &'kb [u8] where C: Compression, { - let uri_parts = UriPartsRef::new(uri); let key = gen_key(key_buffer, rng); - fbw._extend_from_slices_group_rn(&[b"GET ", uri_parts.href().as_bytes(), b" HTTP/1.1"]); + fbw._extend_from_slices_group_rn(&[b"GET ", uri.href().as_bytes(), b" HTTP/1.1"]); fbw._extend_from_slice_rn(b"Connection: Upgrade"); - fbw._extend_from_slices_group_rn(&[b"Host: ", uri_parts.host().as_bytes()]); + match (uri.schema(), uri.port()) { + ("http" | "ws", "80") | ("https" | "wss", "443") => { + fbw._extend_from_slices_group_rn(&[b"Host: ", uri.hostname().as_bytes()]); + } + _ => fbw._extend_from_slices_group_rn(&[b"Host: ", uri.host().as_bytes()]), + } fbw._extend_from_slices_group_rn(&[b"Sec-WebSocket-Key: ", key]); fbw._extend_from_slice_rn(b"Sec-WebSocket-Version: 13"); fbw._extend_from_slice_rn(b"Upgrade: websocket"); compression.write_req_headers(fbw); fbw._extend_from_slice_rn(b""); - (key, fbw._curr_bytes()) + key } /// Server response diff --git a/wtx/src/web_socket/handshake/tests.rs b/wtx/src/web_socket/handshake/tests.rs index 328350fd..59d5d34e 100644 --- a/wtx/src/web_socket/handshake/tests.rs +++ b/wtx/src/web_socket/handshake/tests.rs @@ -91,7 +91,7 @@ where headers_buffer: &mut <_>::default(), rng: StaticRng::default(), stream: TcpStream::connect(uri.host()).await.unwrap(), - uri: uri.uri(), + uri: &uri.to_ref(), wsb: WebSocketBuffer::with_capacity(0, 0), } .connect()