Compare commits

...

22 Commits
v0.2.0 ... main

Author SHA1 Message Date
62282eacd5
Fix compile error due to attempting to derive incorrect trait
All checks were successful
Build library & run tests / build (unix) (push) Successful in 1m27s
Build library & run tests / build (tcp) (push) Successful in 1m28s
Build library & run tests / docs (push) Successful in 1m28s
2024-07-05 13:13:23 +02:00
a5c975d113
Attempt to change the way errors are created
Some checks failed
Build library & run tests / docs (push) Failing after 2m31s
Build library & run tests / build (tcp) (push) Failing after 2m41s
Build library & run tests / build (unix) (push) Failing after 2m42s
2024-07-05 13:09:31 +02:00
ef1faf5bd9
Barely better error reporting 2024-06-26 11:28:14 +02:00
d47f62cdbb
Attempt to deal with task/memory leaks
All checks were successful
Build library & run tests / docs (push) Successful in 2m20s
Build library & run tests / build (unix) (push) Successful in 2m23s
Build library & run tests / build (tcp) (push) Successful in 2m27s
2024-06-26 11:16:20 +02:00
a1e10f93ce
bump
Some checks failed
Build library & run tests / build (unix) (push) Successful in 57s
Build library & run tests / build (tcp) (push) Successful in 54s
Publish library / publish (push) Failing after 21s
Build library & run tests / docs (push) Successful in 22s
2024-06-25 18:19:35 +02:00
2cf0b9abe4
Don't run doctests which would break on unix
All checks were successful
Build library & run tests / build (unix) (push) Successful in 28s
Build library & run tests / build (tcp) (push) Successful in 29s
Build library & run tests / docs (push) Successful in 30s
2024-06-25 11:48:47 +02:00
8b0f01e606
Add information about dependencies to README
Some checks failed
Build library & run tests / build (unix) (push) Failing after 48s
Build library & run tests / docs (push) Successful in 49s
Build library & run tests / build (tcp) (push) Successful in 53s
2024-06-25 11:44:25 +02:00
beda8c151d
Changes in dependencies 2024-06-25 11:43:04 +02:00
84f7009ad2
Add more proper cleanup for the server
Some checks failed
Build library & run tests / build (unix) (push) Failing after 34s
Build library & run tests / build (tcp) (push) Successful in 35s
Build library & run tests / docs (push) Successful in 37s
2024-06-25 11:39:07 +02:00
267b741ac4
implement std::error::Error for the error type
All checks were successful
Build library & run tests / build (unix) (push) Successful in 33s
Build library & run tests / build (tcp) (push) Successful in 34s
Build library & run tests / docs (push) Successful in 35s
2024-06-25 00:03:29 +02:00
bffb41e8a1
implement Drop for client struct
All checks were successful
Build library & run tests / build (unix) (push) Successful in 34s
Build library & run tests / build (tcp) (push) Successful in 35s
Build library & run tests / docs (push) Successful in 37s
2024-06-24 23:44:44 +02:00
b5870e62fe
Implement Display for the error enum
All checks were successful
Build library & run tests / build (unix) (push) Successful in 33s
Build library & run tests / build (tcp) (push) Successful in 35s
Build library & run tests / docs (push) Successful in 36s
2024-06-24 23:01:58 +02:00
f4d65a2c51
Sanitization improvements
Some checks failed
Build library & run tests / build (unix) (push) Successful in 19s
Build library & run tests / build (tcp) (push) Successful in 20s
Build library & run tests / docs (push) Successful in 23s
Publish library / publish (push) Failing after 26s
2024-06-24 22:32:03 +02:00
912b69ef93
Try to fix publish.yaml workflow
All checks were successful
Build library & run tests / build (unix) (push) Successful in 33s
Build library & run tests / build (tcp) (push) Successful in 34s
Build library & run tests / docs (push) Successful in 35s
2024-06-24 21:54:28 +02:00
2353c1648e
Bump for mistake in deploy workflow
Some checks failed
Build library & run tests / build (unix) (push) Successful in 20s
Build library & run tests / build (tcp) (push) Successful in 21s
Build library & run tests / docs (push) Successful in 24s
Publish library / publish (push) Failing after 26s
2024-06-24 21:34:30 +02:00
e1f453fa8b
Update publish.yaml to properly format token for gitea
All checks were successful
Build library & run tests / build (unix) (push) Successful in 34s
Build library & run tests / build (tcp) (push) Successful in 35s
Build library & run tests / docs (push) Successful in 36s
2024-06-24 21:33:34 +02:00
4dff84f4e7
Move rand to dev-dependencies
Some checks failed
Build library & run tests / build (tcp) (push) Successful in 20s
Build library & run tests / build (unix) (push) Successful in 19s
Build library & run tests / docs (push) Successful in 24s
Publish library / publish (push) Failing after 25s
2024-06-24 21:29:48 +02:00
4fc0359625
Lift dry run flag from publish.yaml
All checks were successful
Build library & run tests / build (tcp) (push) Successful in 34s
Build library & run tests / build (unix) (push) Successful in 33s
Build library & run tests / docs (push) Successful in 37s
2024-06-24 21:28:16 +02:00
8313209e0e
Bump for testing
All checks were successful
Build library & run tests / build (unix) (push) Successful in 1m7s
Build library & run tests / build (tcp) (push) Successful in 1m10s
Build library & run tests / docs (push) Successful in 22s
Publish library / publish (push) Successful in 32s
2024-06-24 21:20:40 +02:00
91deddc1d2
Add publishing to gitea releases for the release workflow
All checks were successful
Build library & run tests / build (unix) (push) Successful in 34s
Build library & run tests / build (tcp) (push) Successful in 35s
Build library & run tests / docs (push) Successful in 35s
2024-06-24 21:20:02 +02:00
0d018d0869
Attempt to add publish workflow
All checks were successful
Build library & run tests / build (unix) (push) Successful in 34s
Build library & run tests / build (tcp) (push) Successful in 34s
Build library & run tests / docs (push) Successful in 35s
2024-06-24 21:12:14 +02:00
71adf67727
Add description and repository to manifest
All checks were successful
Build library & run tests / build (unix) (push) Successful in 57s
Build library & run tests / docs (push) Successful in 58s
Build library & run tests / build (tcp) (push) Successful in 1m1s
2024-06-24 21:04:36 +02:00
9 changed files with 934 additions and 140 deletions

2
.cargo/config.toml Normal file
View File

@ -0,0 +1,2 @@
[build]
rustflags = ["--cfg", "tokio_unstable"]

View File

@ -0,0 +1,23 @@
name: Publish library
on:
push:
branches:
- master
tags:
- v*
workflow_dispatch:
jobs:
publish:
runs-on: nix
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Publish to Gitea Cargo registry
run: nix develop -c cargo publish --token "Bearer ${{ secrets.GITHUB_TOKEN }}" --index sparse+https://git.colon-three.com/api/packages/kodi/cargo/
- name: Publish to crates.io
run: nix develop -c cargo publish --token ${{ secrets.CRATESIO_TOKEN }}
- name: Publish to Gitea Releases
uses: akkuman/gitea-release-action@v1
with:
draft: true

731
Cargo.lock generated
View File

@ -75,6 +75,12 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "anyhow"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "async-stream"
version = "0.3.5"
@ -97,6 +103,68 @@ dependencies = [
"syn",
]
[[package]]
name = "async-trait"
version = "0.1.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "autocfg"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "axum"
version = "0.6.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
dependencies = [
"async-trait",
"axum-core",
"bitflags 1.3.2",
"bytes",
"futures-util",
"http",
"http-body",
"hyper",
"itoa",
"matchit",
"memchr",
"mime",
"percent-encoding",
"pin-project-lite",
"rustversion",
"serde",
"sync_wrapper",
"tower",
"tower-layer",
"tower-service",
]
[[package]]
name = "axum-core"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c"
dependencies = [
"async-trait",
"bytes",
"futures-util",
"http",
"http-body",
"mime",
"rustversion",
"tower-layer",
"tower-service",
]
[[package]]
name = "backtrace"
version = "0.3.73"
@ -118,6 +186,12 @@ version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
@ -127,6 +201,12 @@ dependencies = [
"serde",
]
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "1.6.0"
@ -152,9 +232,72 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
[[package]]
name = "eagle"
version = "0.2.0"
name = "console-api"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a257c22cd7e487dd4a13d413beabc512c5052f0bc048db0da6a84c3d8a6142fd"
dependencies = [
"futures-core",
"prost",
"prost-types",
"tonic",
"tracing-core",
]
[[package]]
name = "console-subscriber"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31c4cc54bae66f7d9188996404abdf7fdfa23034ef8e43478c8810828abad758"
dependencies = [
"console-api",
"crossbeam-channel",
"crossbeam-utils",
"futures-task",
"hdrhistogram",
"humantime",
"prost",
"prost-types",
"serde",
"serde_json",
"thread_local",
"tokio",
"tokio-stream",
"tonic",
"tracing",
"tracing-core",
"tracing-subscriber",
]
[[package]]
name = "crc32fast"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]]
name = "eagle"
version = "0.3.0"
dependencies = [
"console-subscriber",
"env_logger",
"log",
"proc-macro2",
@ -163,10 +306,17 @@ dependencies = [
"ron",
"serde",
"syn",
"thiserror",
"tokio",
"tokio-test",
]
[[package]]
name = "either"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "env_filter"
version = "0.1.0"
@ -190,12 +340,67 @@ dependencies = [
"log",
]
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "flate2"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "futures-channel"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
dependencies = [
"futures-core",
]
[[package]]
name = "futures-core"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
name = "futures-sink"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
[[package]]
name = "futures-task"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
[[package]]
name = "futures-util"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"pin-utils",
]
[[package]]
name = "getrandom"
version = "0.2.15"
@ -213,24 +418,179 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
[[package]]
name = "h2"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
dependencies = [
"bytes",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
"http",
"indexmap 2.2.6",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
name = "hdrhistogram"
version = "7.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d"
dependencies = [
"base64",
"byteorder",
"flate2",
"nom",
"num-traits",
]
[[package]]
name = "hermit-abi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "http"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "http-body"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
dependencies = [
"bytes",
"http",
"pin-project-lite",
]
[[package]]
name = "httparse"
version = "1.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
[[package]]
name = "httpdate"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "hyper"
version = "0.14.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33"
dependencies = [
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
"socket2",
"tokio",
"tower-service",
"tracing",
"want",
]
[[package]]
name = "hyper-timeout"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
dependencies = [
"hyper",
"pin-project-lite",
"tokio",
"tokio-io-timeout",
]
[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown 0.12.3",
]
[[package]]
name = "indexmap"
version = "2.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [
"equivalent",
"hashbrown 0.14.5",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
[[package]]
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.155"
@ -243,12 +603,39 @@ version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "matchers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
dependencies = [
"regex-automata 0.1.10",
]
[[package]]
name = "matchit"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "mime"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.7.4"
@ -269,6 +656,25 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.16.0"
@ -288,12 +694,50 @@ dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "percent-encoding"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pin-project"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "pin-project-lite"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
@ -309,6 +753,38 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "prost"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29"
dependencies = [
"bytes",
"prost-derive",
]
[[package]]
name = "prost-derive"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1"
dependencies = [
"anyhow",
"itertools",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "prost-types"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0"
dependencies = [
"prost",
]
[[package]]
name = "quote"
version = "1.0.36"
@ -356,8 +832,17 @@ checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
"regex-automata 0.4.7",
"regex-syntax 0.8.4",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
dependencies = [
"regex-syntax 0.6.29",
]
[[package]]
@ -368,9 +853,15 @@ checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
"regex-syntax 0.8.4",
]
[[package]]
name = "regex-syntax"
version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
version = "0.8.4"
@ -384,7 +875,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
dependencies = [
"base64",
"bitflags",
"bitflags 2.5.0",
"serde",
"serde_derive",
]
@ -395,6 +886,18 @@ version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustversion"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "serde"
version = "1.0.203"
@ -415,6 +918,35 @@ dependencies = [
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[package]]
name = "socket2"
version = "0.5.7"
@ -436,6 +968,42 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "sync_wrapper"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]]
name = "thiserror"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]]
name = "tokio"
version = "1.38.0"
@ -450,9 +1018,20 @@ dependencies = [
"pin-project-lite",
"socket2",
"tokio-macros",
"tracing",
"windows-sys 0.48.0",
]
[[package]]
name = "tokio-io-timeout"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf"
dependencies = [
"pin-project-lite",
"tokio",
]
[[package]]
name = "tokio-macros"
version = "2.3.0"
@ -488,6 +1067,131 @@ dependencies = [
"tokio-stream",
]
[[package]]
name = "tokio-util"
version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
]
[[package]]
name = "tonic"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13"
dependencies = [
"async-stream",
"async-trait",
"axum",
"base64",
"bytes",
"h2",
"http",
"http-body",
"hyper",
"hyper-timeout",
"percent-encoding",
"pin-project",
"prost",
"tokio",
"tokio-stream",
"tower",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
dependencies = [
"futures-core",
"futures-util",
"indexmap 1.9.3",
"pin-project",
"pin-project-lite",
"rand",
"slab",
"tokio",
"tokio-util",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower-layer"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
[[package]]
name = "tower-service"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
[[package]]
name = "tracing"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
dependencies = [
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
dependencies = [
"matchers",
"once_cell",
"regex",
"sharded-slab",
"thread_local",
"tracing",
"tracing-core",
]
[[package]]
name = "try-lock"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "unicode-ident"
version = "1.0.12"
@ -500,6 +1204,21 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "want"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
dependencies = [
"try-lock",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"

View File

@ -1,33 +1,35 @@
[package]
name = "eagle"
version = "0.2.0"
edition = "2021"
version = "0.3.0"
description = "A simple library for creating RPC protocols."
repository = "https://git.colon-three.com/kodi/eagle"
authors = ["KodiCraft <kodi@kdcf.me>"]
license = "AGPL-3.0"
publish = ["gitea"]
edition = "2021"
resolver = "2"
[features]
default = ["tcp", "log"]
tcp = ["tokio/net"]
unix = ["tokio/net"]
log = ["dep:log", "dep:env_logger"]
tcp = []
unix = []
log = []
[dependencies]
proc-macro2 = "1.0.85"
quote = "1.0.36"
rand = "0.8.5"
ron = "0.8.1"
serde = { version = "1.0.203", features = ["serde_derive"] }
syn = "2.0.66"
tokio = { version = "1.38.0", features = ["sync", "io-util"] }
env_logger = { version = "0.11.3", optional = true }
log = { version = "0.4.21", optional = true }
[dev-dependencies]
# tokio = { version = "1.38.0", features = ["sync", "io-util"] }
ron = "0.8.1"
serde = { version = "1.0.203", features = ["serde_derive"] }
rand = "0.8.5"
tokio = { version = "1.38.0", features = ["sync", "rt-multi-thread", "macros", "time", "io-util", "net"] }
env_logger = "0.11.3"
log = "0.4.21"
tokio-test = "0.4.4"
env_logger = { version = "0.11.3" }
log = { version = "0.4.21" }
console-subscriber = "0.3.0"
thiserror = "1.0.61"
[lib]
proc-macro = true

View File

@ -9,6 +9,8 @@ Eagle is still in early development. Performance is not ideal, the interface is
Eagle is a library which allows you to easily build an [RPC](https://en.wikipedia.org/wiki/Remote_procedure_call) protocol.
It uses a macro to generate the required communication code and makes adding new functions easy and quick. Eagle is designed to work specifically with [`tokio`](https://crates.io/crates/tokio) and uses [`serde`](https://crates.io/crates/serde) for formatting data.
Please note that since `eagle` is a pure proc-macro library, you must manually add compatible versions of `tokio`, `serde`, `ron` and optionally `log` to your dependencies.
## Using Eagle
The way that `eagle` is designed to be used is inside a shared dependency between your "server" and your "client". Both of these should be in a workspace. Create a `shared` crate which both components should depend on, this crate should have `eagle` as a dependency. By default `eagle` uses TCP for communication, but you may disable default features and enable the `unix` feature on `eagle` to use unix sockets instead.
@ -67,9 +69,9 @@ Your handler can now be used by the server. You can easily bind your server to a
use shared::ExampleServer;
let handler = ExampleHandler { state: 0 };
let server_task = tokio::spawn(ExampleServer::bind(handler, "127.0.0.1:1234"));
let server_task = ExampleServer::bind(handler, "127.0.0.1:1234").await;
// Or, if you're using the 'unix' feature...
let server_task = tokio::spawn(ExampleServer::bind(handler, "/tmp/sock"));
let server_task = ExampleServer::bind(handler, "/tmp/sock").await;
```

View File

@ -14,7 +14,18 @@
naersk-lib = pkgs.callPackage naersk {};
in
{
packages = {
packages = rec {
all = pkgs.stdenv.mkDerivation rec {
name = "all";
buildInputs = [
default
doc
unix
tcp
clippy_unix
clippy_tcp
];
};
default = naersk-lib.buildPackage {
src = ./.;
doCheck = true;

View File

@ -102,13 +102,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
//! # tokio_test::block_on(async {
//! let handler = Handler;
//! let address = "127.0.0.1:12345"; // Or, if using the 'unix' feature, "/tmp/eagle.sock"
//! let server_task = tokio::spawn(ExampleServer::bind(handler, address));
//! let server = ExampleServer::bind(handler, address).await;
//! # });
//! ```
//!
//! Please note the usage of `tokio::spawn`. This is because the `bind` function
//! will not return until the server is closed. You can use the `abort` method
//! on the task to close the server.
//! Once bound, the server will begin listening for incoming connections and
//! queries.
//!
//! On the client side, you can simply use the generated client struct to connect
//! to the server and begin sending queries.
@ -136,16 +134,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
//! # tokio_test::block_on(async {
//! # let handler = Handler;
//! let address = "127.0.0.1:12345"; // Or, if using the 'unix' feature, "/tmp/eagle.sock"
//! # let server_task = tokio::spawn(ExampleServer::bind(handler, address));
//! # let server = ExampleServer::bind(handler, address).await;
//! # tokio::time::sleep(tokio::time::Duration::from_millis(10)).await; // Wait for the server to start
//! let client = ExampleClient::connect(address).await.unwrap();
//! # // Wait for the server to start, the developer is responsible for this in production
//! # tokio::time::sleep(std::time::Duration::from_millis(10)).await;
//! assert_eq!(client.add(2, 5).await.unwrap(), 7);
//! # });
//! ```
//!
//! The client can be closed by calling the `close` method on the client struct.
//! This will abort the connection.
#![warn(missing_docs)]
use proc_macro::TokenStream;
@ -198,28 +192,28 @@ fn derive_protocol(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
// TODO: These logs should be filterable in some way
#[cfg(feature = "log")]
#[allow(unused_variables)]
let debug = quote! { log::debug! };
let debug = quote! { ::log::debug! };
#[cfg(feature = "log")]
#[allow(unused_variables)]
let info = quote! { log::info! };
let info = quote! { ::log::info! };
#[cfg(feature = "log")]
#[allow(unused_variables)]
let warn = quote! { log::warn! };
let warn = quote! { ::log::warn! };
#[cfg(feature = "log")]
#[allow(unused_variables)]
let error = quote! { log::error! };
let error = quote! { ::log::error! };
#[cfg(not(feature = "log"))]
#[allow(unused_variables)]
let debug = quote! { eprintln! };
let debug = quote! { ::std::eprintln! };
#[cfg(not(feature = "log"))]
#[allow(unused_variables)]
let info = quote! { eprintln! };
let info = quote! { ::std::eprintln! };
#[cfg(not(feature = "log"))]
#[allow(unused_variables)]
let warn = quote! { eprintln! };
let warn = quote! { ::std::eprintln! };
#[cfg(not(feature = "log"))]
#[allow(unused_variables)]
let error = quote! { eprintln! };
let error = quote! { ::std::eprintln! };
// Must be on an enum
let enum_ = match &input.data {
@ -255,6 +249,29 @@ fn derive_protocol(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
let mut query_set_answer = Vec::new();
let mut query_get_answer = Vec::new();
// TODO: This should just be regular code!
let join_handle_guard_name = format_ident!("__{}JoinHandleGuard", name);
let join_handle_guard = quote! {
struct #join_handle_guard_name<T: ::std::fmt::Debug>(::tokio::task::JoinHandle<T>);
impl<T: ::std::fmt::Debug> From<::tokio::task::JoinHandle<T>> for #join_handle_guard_name<T> {
fn from(handle: ::tokio::task::JoinHandle<T>) -> Self {
#debug("Creating join handle guard for task {:?}", handle);
Self(handle)
}
}
impl #join_handle_guard_name<()> {
pub fn abort(self) {
self.0.abort();
}
}
impl<T: ::std::fmt::Debug> Drop for #join_handle_guard_name<T> {
fn drop(&mut self) {
#debug("Dropping join handle guard for task {:?}", self.0);
self.0.abort();
}
}
};
for variant in &enum_.variants {
// Every variant must have 2 fields
// The first field is the question (serverbound), the second field is the answer (clientbound)
@ -299,8 +316,8 @@ fn derive_protocol(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
// There is a function that must be implemented to get the answer from the query enum
query_get_answer.push(quote! {
#query_enum_name::#var_name(_, answer) => match answer {
Some(answer) => Some(#answer_enum_name::#var_name(answer.clone())),
None => None
::std::option::Option::Some(answer) => ::std::option::Option::Some(#answer_enum_name::#var_name(answer.clone())),
::std::option::Option::None => ::std::option::Option::None
},
});
// There is a function that the server uses to call the appropriate function when receiving a query
@ -313,7 +330,7 @@ fn derive_protocol(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
});
// The function that the server needs to implement
server_trait.push(quote! {
fn #var_name(&mut self, #question_args) -> impl std::future::Future<Output = #answer_type> + Send;
fn #var_name(&mut self, #question_args) -> impl ::std::future::Future<Output = #answer_type> + Send;
});
// The function that the client uses to communicate
client_impl.push(quote! {
@ -323,42 +340,44 @@ fn derive_protocol(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
let answer = self.recv_until(nonce).await?;
match answer {
#answer_enum_name::#var_name(answer) => Ok(answer),
_ => panic!("The answer for this query is not the correct type."),
_ => ::std::panic!("The answer for this query is not the correct type."),
}
}
});
// The query enum is the same as the source enum, but the second field is always wrapped in a Option<>
query_enum.push(quote! {
#var_name(#question_field, Option<#answer_type>)
#var_name(#question_field, ::std::option::Option<#answer_type>)
});
}
// Create an error and result type for sending messages
let error_enum = quote! {
#[derive(Debug)]
#[derive(::std::fmt::Debug, ::std::clone::Clone, ::thiserror::Error)]
#vis enum #error_enum_name {
SendError(tokio::sync::mpsc::error::SendError<(u64, #question_enum_name)>),
#[error("Failed to send query: {0}")]
SendError(::tokio::sync::mpsc::error::SendError<(u64, #question_enum_name)>),
#[error("Connection to sender thread closed")]
Closed,
}
};
// Create enums for the types of messages the server and client will use
let answer_enum = quote! {
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
#[derive(::serde::Serialize, ::serde::Deserialize, ::std::clone::Clone, ::std::fmt::Debug)]
#vis enum #answer_enum_name {
#(#server_enum), *,
Ready
}
};
let question_enum = quote! {
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
#[derive(::serde::Serialize, ::serde::Deserialize, ::std::clone::Clone, ::std::fmt::Debug)]
#vis enum #question_enum_name {
#(#client_enum), *
}
};
// Create an enum to represent the queries the client has sent
let query_enum = quote! {
#[derive(Clone, Debug)]
#[derive(::std::clone::Clone, ::std::fmt::Debug)]
#vis enum #query_enum_name {
#(#query_enum), *
}
@ -368,7 +387,7 @@ fn derive_protocol(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
#(#query_set_answer)*
};
}
pub fn get_answer(&self) -> Option<#answer_enum_name> {
pub fn get_answer(&self) -> ::std::option::Option<#answer_enum_name> {
match self {
#(#query_get_answer)*
}
@ -384,17 +403,17 @@ fn derive_protocol(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
};
#[cfg(feature = "tcp")]
let stream_type = quote! { tokio::net::TcpStream };
let stream_type = quote! { ::tokio::net::TcpStream };
#[cfg(feature = "tcp")]
let stream_addr_trait = quote! { tokio::net::ToSocketAddrs };
let stream_addr_trait = quote! { ::tokio::net::ToSocketAddrs };
#[cfg(feature = "tcp")]
let listener_type = quote! { tokio::net::TcpListener };
let listener_type = quote! { ::tokio::net::TcpListener };
#[cfg(feature = "unix")]
let stream_type = quote! { tokio::net::UnixStream };
let stream_type = quote! { ::tokio::net::UnixStream };
#[cfg(feature = "unix")]
let stream_addr_trait = quote! { std::convert::AsRef<std::path::Path> };
let stream_addr_trait = quote! { ::std::convert::AsRef<std::path::Path> };
#[cfg(feature = "unix")]
let listener_type = quote! { tokio::net::UnixListener };
let listener_type = quote! { ::tokio::net::UnixListener };
// Create a trait which the server will have to implement
let server_trait = quote! {
@ -414,40 +433,41 @@ fn derive_protocol(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
};
let sc_struct = quote! {
#[derive(Clone)]
#vis struct #server_connection_struct_name<H: #server_trait_name + ::std::marker::Send + Clone + 'static> {
handler: ::std::sync::Arc<tokio::sync::Mutex<H>>,
tasks: ::std::sync::Arc<tokio::sync::Mutex<Vec<tokio::task::JoinHandle<()>>>>,
#vis struct #server_connection_struct_name<H: #server_trait_name + ::std::marker::Send + ::std::clone::Clone + 'static> {
handler: ::std::sync::Arc<::tokio::sync::Mutex<H>>,
tasks: ::std::sync::Arc<::tokio::sync::Mutex<::std::vec::Vec<#join_handle_guard_name<()>>>>,
}
impl<H: #server_trait_name + ::std::marker::Send + Clone + 'static> #server_connection_struct_name<H> {
pub async fn bind<A: #stream_addr_trait + ::std::marker::Send + std::fmt::Display + 'static>(handler: H, addr: A) -> Self {
impl<H: #server_trait_name + ::std::marker::Send + std::clone::Clone + 'static> #server_connection_struct_name<H> {
pub async fn bind<A: #stream_addr_trait + ::std::marker::Send + ::std::fmt::Display + 'static>(handler: H, addr: A) -> Self {
#info("Binding server to address {}", addr);
let handler = ::std::sync::Arc::new(tokio::sync::Mutex::new(handler));
let tasks = ::std::sync::Arc::new(tokio::sync::Mutex::new(Vec::new()));
let handler = ::std::sync::Arc::new(::tokio::sync::Mutex::new(handler));
let tasks = ::std::sync::Arc::new(::tokio::sync::Mutex::new(::std::vec::Vec::new()));
let sc = Self {
handler,
tasks,
};
let sc_clone = sc.clone();
let acc_task = tokio::spawn(async move {
let acc_task = ::tokio::spawn(async move {
sc_clone.accept_connections(addr).await.expect("Failed to accept connections!");
});
sc.tasks.lock().await.push(acc_task);
sc.tasks.lock().await.push(acc_task.into());
sc
}
pub async fn accept_connections<A: #stream_addr_trait>(
&self,
addr: A,
) -> Result<(), std::io::Error> {
) -> ::std::result::Result<(), ::std::io::Error> {
#listener_statement
#info("Listening for clients on {:?}", listener.local_addr()?);
loop {
let (stream, _) = listener.accept().await?;
#info("Accepted connection from {:?}", stream.peer_addr()?);
let self_clone = self.clone();
let run_task = tokio::spawn(async move {
let run_task = ::tokio::spawn(async move {
self_clone.run(stream).await;
});
self.tasks.lock().await.push(run_task);
self.tasks.lock().await.push(run_task.into());
}
}
@ -458,7 +478,7 @@ fn derive_protocol(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
}
async fn send(&self, stream: &mut #stream_type, nonce: u64, answer: #answer_enum_name) {
use tokio::io::AsyncWriteExt;
use ::tokio::io::AsyncWriteExt;
let serialized = ron::ser::to_string(&(nonce, answer)).expect("Failed to serialize response!");
let len = serialized.len() as u32;
#debug("Sending `{}`", serialized);
@ -467,24 +487,24 @@ fn derive_protocol(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
}
async fn run(&self, mut stream: #stream_type) {
use tokio::io::AsyncWriteExt;
use tokio::io::AsyncReadExt;
let mut buf = Vec::with_capacity(1024);
use ::tokio::io::AsyncWriteExt;
use ::tokio::io::AsyncReadExt;
let mut buf = ::std::vec::Vec::with_capacity(1024);
self.send(&mut stream, 0, #answer_enum_name::Ready).await;
loop {
tokio::select! {
Ok(_) = stream.readable() => {
::tokio::select! {
::std::result::Result::Ok(_) = stream.readable() => {
let mut read_buf = [0; 1024];
match stream.try_read(&mut read_buf) {
Ok(0) => break, // Stream closed
Ok(n) => {
::std::result::Result::Ok(0) => break, // Stream closed
::std::result::Result::Ok(n) => {
#debug("Received {} bytes (server)", n);
buf.extend_from_slice(&read_buf[..n]);
while buf.len() >= 4 {
let len = u32::from_le_bytes(buf[..4].try_into().expect("Failed to convert bytes to u32"));
if buf.len() >= (4 + len as usize) {
let serialized = std::str::from_utf8(&buf[4..(4 + len as usize)]).expect("Failed to convert bytes to string");
let (nonce, question): (u64, #question_enum_name) = ron::de::from_str(serialized).expect("Failed to deserialize query!");
let serialized = ::std::str::from_utf8(&buf[4..(4 + len as usize)]).expect("Failed to convert bytes to string");
let (nonce, question): (u64, #question_enum_name) = ::ron::de::from_str(serialized).expect("Failed to deserialize query!");
let answer = self.handle(question).await;
self.send(&mut stream, nonce, answer).await;
buf.drain(0..(4 + len as usize));
@ -493,8 +513,8 @@ fn derive_protocol(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
}
}
},
Err(ref e) if e.kind() == ::std::io::ErrorKind::WouldBlock => { continue; },
Err(e) => eprintln!("Error reading from stream: {:?}", e),
::std::result::Result::Err(ref e) if e.kind() == ::std::io::ErrorKind::WouldBlock => { continue; },
::std::result::Result::Err(e) => ::std::eprintln!("Error reading from stream: {:?}", e),
}
}
}
@ -519,21 +539,21 @@ fn derive_protocol(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
}
pub fn insert(&self, nonce: u64, query: #query_enum_name) {
self.queries.lock().unwrap().insert(nonce, query);
self.queries.lock().expect("Could not insert new query; Mutex has been poisoned.").insert(nonce, query);
}
pub fn get(&self, nonce: &u64) -> Option<#query_enum_name> {
self.queries.lock().unwrap().get(nonce).cloned()
pub fn get(&self, nonce: &u64) -> ::std::option::Option<#query_enum_name> {
self.queries.lock().expect("Could not get query; Mutex has been poisoned.").get(nonce).cloned()
}
pub fn set_answer(&self, nonce: u64, answer: #answer_enum_name) {
if let Some(query) = self.queries.lock().unwrap().get_mut(&nonce) {
if let ::std::option::Option::Some(query) = self.queries.lock().expect("Could not set answer to query; Mutex has been poisoned.").get_mut(&nonce) {
query.set_answer(answer);
}
}
pub fn len(&self) -> usize {
self.queries.lock().unwrap().len()
self.queries.lock().expect("Could not get query count; Mutex has been poisoned.").len()
}
}
};
@ -541,16 +561,16 @@ fn derive_protocol(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
// Create a struct to handle the connection from the client to the server
let cc_struct = quote! {
struct #client_connection_struct_name {
to_send: tokio::sync::mpsc::Receiver<(u64, #question_enum_name)>,
received: tokio::sync::mpsc::Sender<(u64, #answer_enum_name)>,
ready: std::sync::Arc<tokio::sync::Notify>,
to_send: ::tokio::sync::mpsc::Receiver<(u64, #question_enum_name)>,
received: ::tokio::sync::mpsc::Sender<(u64, #answer_enum_name)>,
ready: ::std::sync::Arc<tokio::sync::Notify>,
stream: #stream_type,
}
impl #client_connection_struct_name {
pub fn new(
to_send: tokio::sync::mpsc::Receiver<(u64, #question_enum_name)>,
received: tokio::sync::mpsc::Sender<(u64, #answer_enum_name)>,
ready: std::sync::Arc<tokio::sync::Notify>,
to_send: ::tokio::sync::mpsc::Receiver<(u64, #question_enum_name)>,
received: ::tokio::sync::mpsc::Sender<(u64, #answer_enum_name)>,
ready: ::std::sync::Arc<::tokio::sync::Notify>,
stream: #stream_type,
) -> Self {
Self {
@ -562,23 +582,23 @@ fn derive_protocol(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
}
pub async fn run(mut self) {
use tokio::io::AsyncWriteExt;
use tokio::io::AsyncReadExt;
let mut buf = Vec::with_capacity(1024);
use ::tokio::io::AsyncWriteExt;
use ::tokio::io::AsyncReadExt;
let mut buf = ::std::vec::Vec::with_capacity(1024);
loop {
tokio::select! {
Some(msg) = self.to_send.recv() => {
::tokio::select! {
::std::option::Option::Some(msg) = self.to_send.recv() => {
let serialized = ron::ser::to_string(&msg).expect("Failed to serialize query!");
let len = serialized.len() as u32;
#debug("Sending `{}`", serialized);
self.stream.write_all(&len.to_le_bytes()).await.expect("Failed to write length!");
self.stream.write_all(serialized.as_bytes()).await.expect("Failed to write query!");
},
Ok(_) = self.stream.readable() => {
::std::result::Result::Ok(_) = self.stream.readable() => {
let mut read_buf = [0; 1024];
match self.stream.try_read(&mut read_buf) {
Ok(0) => { break; },
Ok(n) => {
::std::result::Result::Ok(0) => { break; },
::std::result::Result::Ok(n) => {
#debug("Received {} bytes (client)", n);
buf.extend_from_slice(&read_buf[..n]);
while buf.len() >= 4 {
@ -590,7 +610,12 @@ fn derive_protocol(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
#debug("Received ready signal");
self.ready.notify_one();
} else {
self.received.send(response).await.expect("Failed to send response!");
match self.received.send(response).await {
::std::result::Result::Ok(_) => {},
::std::result::Result::Err(e) => {
#error("Failed to send received answer to Client: {:?}", e);
},
};
}
buf.drain(0..(4 + len as usize));
} else {
@ -598,8 +623,8 @@ fn derive_protocol(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
}
}
},
Err(ref e) if e.kind() == ::std::io::ErrorKind::WouldBlock => { continue; },
Err(e) => eprintln!("Error reading from stream: {:?}", e),
::std::result::Result::Err(ref e) if e.kind() == ::std::io::ErrorKind::WouldBlock => { continue; },
::std::result::Result::Err(e) => eprintln!("Error reading from stream: {:?}", e),
}
}
}
@ -610,33 +635,33 @@ fn derive_protocol(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
// Create a struct which the client will use to communicate
let client_recv_queue_wrapper = format_ident!("__{}RecvQueueWrapper", name);
let client_struct = quote! {
#[derive(Clone)]
#[derive(::std::clone::Clone)]
struct #client_recv_queue_wrapper {
recv_queue: ::std::sync::Arc<::tokio::sync::Mutex<tokio::sync::mpsc::Receiver<(u64, #answer_enum_name)>>>,
recv_queue: ::std::sync::Arc<::tokio::sync::Mutex<::tokio::sync::mpsc::Receiver<(u64, #answer_enum_name)>>>,
}
impl #client_recv_queue_wrapper {
fn new(recv_queue: tokio::sync::mpsc::Receiver<(u64, #answer_enum_name)>) -> Self {
fn new(recv_queue: ::tokio::sync::mpsc::Receiver<(u64, #answer_enum_name)>) -> Self {
Self {
recv_queue: ::std::sync::Arc::new(::tokio::sync::Mutex::new(recv_queue)),
}
}
async fn recv(&self) -> Option<(u64, #answer_enum_name)> {
async fn recv(&self) -> ::std::option::Option<(u64, #answer_enum_name)> {
self.recv_queue.lock().await.recv().await
}
}
#[derive(Clone)]
#vis struct #client_struct_name {
queries: #queries_struct_name,
send_queue: tokio::sync::mpsc::Sender<(u64, #question_enum_name)>,
send_queue: ::tokio::sync::mpsc::Sender<(u64, #question_enum_name)>,
recv_queue: #client_recv_queue_wrapper,
ready: ::std::sync::Arc<tokio::sync::Mutex<bool>>,
ready_notify: ::std::sync::Arc<tokio::sync::Notify>,
connection_task: Option<::std::sync::Arc<tokio::task::JoinHandle<()>>>,
connection_task: ::std::option::Option<::std::sync::Arc<#join_handle_guard_name<()>>>,
}
impl #client_struct_name {
pub fn new(send_queue: tokio::sync::mpsc::Sender<(u64, #question_enum_name)>,
recv_queue: tokio::sync::mpsc::Receiver<(u64, #answer_enum_name)>,
connection_task: Option<::std::sync::Arc<tokio::task::JoinHandle<()>>>,
pub fn new(send_queue: ::tokio::sync::mpsc::Sender<(u64, #question_enum_name)>,
recv_queue: ::tokio::sync::mpsc::Receiver<(u64, #answer_enum_name)>,
connection_task: ::std::option::Option<::std::sync::Arc<#join_handle_guard_name<()>>>,
ready_notify: ::std::sync::Arc<tokio::sync::Notify>) -> Self {
Self {
queries: #queries_struct_name::new(),
@ -650,19 +675,14 @@ fn derive_protocol(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
pub async fn connect<A: #stream_addr_trait + ::std::fmt::Display>(addr: A) -> Result<Self, std::io::Error> {
#info("Connecting to server at address {}", addr);
let stream = #stream_type::connect(addr).await?;
let (send_queue, to_send) = tokio::sync::mpsc::channel(16);
let (to_recv, recv_queue) = tokio::sync::mpsc::channel(16);
let (send_queue, to_send) = ::tokio::sync::mpsc::channel(16);
let (to_recv, recv_queue) = ::tokio::sync::mpsc::channel(16);
let ready_notify = ::std::sync::Arc::new(tokio::sync::Notify::new());
let connection = #client_connection_struct_name::new(to_send, to_recv, ready_notify.clone(), stream);
let connection_task = tokio::spawn(connection.run());
Ok(Self::new(send_queue, recv_queue, Some(::std::sync::Arc::new(connection_task)), ready_notify))
let connection_task = ::tokio::spawn(connection.run());
Ok(Self::new(send_queue, recv_queue, ::std::option::Option::Some(::std::sync::Arc::new(connection_task.into())), ready_notify))
}
pub fn close(self) {
if let Some(task) = self.connection_task {
task.abort();
}
}
async fn send(&self, query: #question_enum_name) -> Result<u64, #error_enum_name> {
async fn send(&self, query: #question_enum_name) -> ::std::result::Result<u64, #error_enum_name> {
// Wait until the connection is ready
if !*self.ready.lock().await {
self.ready_notify.notified().await;
@ -672,28 +692,28 @@ fn derive_protocol(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
let nonce = self.queries.len() as u64;
let res = self.send_queue.send((nonce, query.clone())).await;
match res {
Ok(_) => {
::std::result::Result::Ok(_) => {
self.queries.insert(nonce, query.into());
Ok(nonce)
::std::result::Result::Ok(nonce)
}
Err(e) => Err(#error_enum_name::SendError(e)),
::std::result::Result::Err(e) => ::std::result::Result::Err(#error_enum_name::SendError(e)),
}
}
async fn recv_until(&self, id: u64) -> Result<#answer_enum_name, #error_enum_name> {
async fn recv_until(&self, id: u64) -> ::std::result::Result<#answer_enum_name, #error_enum_name> {
loop {
// Check if we've received the answer for the query we're looking for
if let Some(query) = self.queries.get(&id) {
if let Some(answer) = query.get_answer() {
if let ::std::option::Option::Some(query) = self.queries.get(&id) {
if let ::std::option::Option::Some(answer) = query.get_answer() {
#info("Found answer for query {}", id);
return Ok(answer);
}
}
match self.recv_queue.recv().await {
Some((nonce, answer)) => {
::std::option::Option::Some((nonce, answer)) => {
#info("Received answer for query {}", nonce);
self.queries.set_answer(nonce, answer.clone());
}
None => return Err(#error_enum_name::Closed),
::std::option::Option::None => return ::std::result::Result::Err(#error_enum_name::Closed),
};
}
}
@ -702,6 +722,7 @@ fn derive_protocol(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
};
let expanded = quote! {
#join_handle_guard // TODO: This should just be regular code and not in the macro!
#error_enum
#answer_enum
#question_enum

View File

@ -24,9 +24,10 @@ use tokio::sync::{
Notify,
};
static INIT: Once = Once::new();
static LOG_INIT: Once = Once::new();
static CONSOLE_INIT: Once = Once::new();
pub fn init_logger() {
INIT.call_once(|| {
LOG_INIT.call_once(|| {
let env = Env::default()
.filter_or("RUST_LOG", "info")
.write_style_or("LOG_STYLE", "always");
@ -34,6 +35,11 @@ pub fn init_logger() {
Builder::from_env(env).format_timestamp_nanos().init();
});
}
pub fn init_console() {
CONSOLE_INIT.call_once(|| {
console_subscriber::init();
});
}
#[derive(Protocol)]
enum TestProtocol {
@ -46,6 +52,7 @@ enum TestProtocol {
#[tokio::test]
async fn client() {
init_logger();
init_console();
let (qtx, qrx) = mpsc::channel(16);
let (atx, arx) = mpsc::channel(16);
let ready_notify = Arc::new(Notify::new());
@ -110,6 +117,7 @@ async fn server_loop(
#[tokio::test]
async fn heavy_async() {
init_logger();
init_console();
let (qtx, qrx) = mpsc::channel(16);
let (atx, arx) = mpsc::channel(16);
let ready_notify = Arc::new(Notify::new());

View File

@ -18,10 +18,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
use eagle::Protocol;
use env_logger::{Builder, Env};
use std::sync::Once;
use tokio::net::TcpStream;
static INIT: Once = Once::new();
static LOG_INIT: Once = Once::new();
static CONSOLE_INIT: Once = Once::new();
pub fn init_logger() {
INIT.call_once(|| {
LOG_INIT.call_once(|| {
let env = Env::default()
.filter_or("RUST_LOG", "info")
.write_style_or("LOG_STYLE", "always");
@ -29,6 +31,11 @@ pub fn init_logger() {
Builder::from_env(env).format_timestamp_nanos().init();
});
}
pub fn init_console() {
CONSOLE_INIT.call_once(|| {
console_subscriber::init();
});
}
#[derive(Protocol)]
enum TestProtocol {
@ -65,6 +72,7 @@ impl Drop for Cleanup {
#[tokio::test]
async fn e2e() {
init_logger();
init_console();
#[cfg(feature = "unix")]
let address = format!("/tmp/eagle-test-{}.sock", rand::random::<u64>());
#[cfg(feature = "unix")]
@ -73,9 +81,8 @@ async fn e2e() {
};
#[cfg(feature = "tcp")]
let address = format!("127.0.0.1:{}", 10000 + rand::random::<u64>() % 1000);
let server_task = tokio::spawn(TestProtocolServer::bind(TrivialServer, address.clone()));
// Wait for the server to start, the developer is responsible for this in production
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
let _server = TestProtocolServer::bind(TrivialServer, address.clone()).await;
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await; // Wait for the server to start
let client = TestProtocolClient::connect(address).await.unwrap();
assert_eq!(client.addition(2, 5).await.unwrap(), 7);
assert_eq!(
@ -90,5 +97,4 @@ async fn e2e() {
"The number is 42"
);
client.void().await.unwrap();
server_task.abort();
}