├── .gitignore ├── examples ├── asgi_only │ ├── Cargo.toml │ ├── README.md │ ├── .gitignore │ ├── demo.py │ ├── src │ │ └── lib.rs │ └── Cargo.lock └── mixed_routes │ ├── Cargo.toml │ ├── README.md │ ├── .gitignore │ ├── src │ └── lib.rs │ ├── demo.py │ └── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── lib.rs └── asgi.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock -------------------------------------------------------------------------------- /examples/asgi_only/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "asgi_only" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | [lib] 8 | name = "asgi_only" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | axum = "0.8.1" 13 | parviocula = { version = "0.0.4", path = "../..", features = ["extension-module"] } 14 | pyo3 = { version = "0.24.0", features = ["extension-module"] } 15 | tokio = "1.44.1" 16 | -------------------------------------------------------------------------------- /examples/mixed_routes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mixed_routes" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | [lib] 8 | name = "mixed_routes" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | axum = "0.8.1" 13 | parviocula = { version = "0.0.4", path = "../..", features = ["extension-module"] } 14 | pyo3 = { version = "0.24.0", features = ["extension-module"] } 15 | serde = { version = "1.0.219", features = ["derive"] } 16 | tokio = { version = "1.44.1", features = ["full"] } 17 | -------------------------------------------------------------------------------- /examples/asgi_only/README.md: -------------------------------------------------------------------------------- 1 | ## Asgi Only 2 | 3 | Demos simply running a python asgi app with an axum based asgi server. 4 | 5 | ## Project setup 6 | 7 | 8 | Requires [Maturin](https://github.com/PyO3/maturin) 9 | 10 | ``` 11 | pip install maturin 12 | ``` 13 | 14 | ``` 15 | maturin new -b pyo3 asgi_only 16 | cd asgi_only 17 | cargo add --path ../../ -F extension-module 18 | cargo add pyo3 -F extension-module 19 | cargo add axum 20 | 21 | < write code > 22 | ``` 23 | 24 | ## Running using venv 25 | 26 | ``` 27 | python -m venv .venv 28 | source .venv/bin/activate 29 | pip install maturin 30 | pip install starlette 31 | maturin develop 32 | python demo.py 33 | 34 | ... 35 | 36 | $ curl http://localhost:3000/simple 37 | HELLO WORLD! 38 | ``` 39 | -------------------------------------------------------------------------------- /examples/mixed_routes/README.md: -------------------------------------------------------------------------------- 1 | ## Mixed Routes 2 | 3 | Demos handling the same uri path with both python and rust. 4 | 5 | ## Project setup 6 | 7 | Requires [Maturin](https://github.com/PyO3/maturin) 8 | 9 | ``` 10 | pip install maturin 11 | ``` 12 | 13 | ``` 14 | maturin new -b pyo3 mixed_routes 15 | cd mixed_routes 16 | cargo add --path ../../ -F extension-module 17 | cargo add pyo3 -F extension-module 18 | cargo add tokio -F full 19 | cargo add axum 20 | cargo add serde -F derive 21 | 22 | < write code > 23 | ``` 24 | 25 | ## Running using venv 26 | 27 | ``` 28 | python -m venv .venv 29 | source .venv/bin/activate 30 | pip install maturin 31 | pip install starlette 32 | maturin develop 33 | python demo.py 34 | 35 | ... 36 | 37 | $ curl http://localhost:3000/post_or_get?name=bob 38 | Hello bob, from rust! 39 | $ curl -H 'Content-Type: application/json' -d '{"name": "bob"}' http://localhost:3000/post_or_get 40 | Hello bob, from python! 41 | ``` 42 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "parviocula" 3 | version = "0.0.4" 4 | edition = "2021" 5 | keywords = ["asgi", "axum"] 6 | description = "A simple ASGI server aimed at helping the transition from python ASGI applications to an Axum application" 7 | homepage = "https://github.com/tristan/parviocula" 8 | repository = "https://github.com/tristan/parviocula" 9 | authors = ["Tristan King "] 10 | license = "MIT" 11 | readme = "README.md" 12 | 13 | [dependencies] 14 | axum = "0.8.1" 15 | axum-extra = "0.10.0" 16 | futures = "0.3.31" 17 | http-body-util = "0.1.3" 18 | hyper = "1.6.0" 19 | percent-encoding = "2.3.1" 20 | pyo3 = { version = "0.24.0" } 21 | pyo3-async-runtimes = { version = "0.24.0", features = ["tokio-runtime"] } 22 | tokio = { version = "1.44.1", features = ["sync"] } 23 | tracing = { version = "0.1.41", optional = true } 24 | 25 | [features] 26 | tracing = ["dep:tracing"] 27 | extension-module = ["pyo3/extension-module"] 28 | auto-initialize = ["pyo3/auto-initialize"] 29 | -------------------------------------------------------------------------------- /examples/asgi_only/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | .pytest_cache/ 6 | *.py[cod] 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | .venv/ 14 | env/ 15 | bin/ 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | include/ 26 | man/ 27 | venv/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | pip-selfcheck.json 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | 45 | # Translations 46 | *.mo 47 | 48 | # Mr Developer 49 | .mr.developer.cfg 50 | .project 51 | .pydevproject 52 | 53 | # Rope 54 | .ropeproject 55 | 56 | # Django stuff: 57 | *.log 58 | *.pot 59 | 60 | .DS_Store 61 | 62 | # Sphinx documentation 63 | docs/_build/ 64 | 65 | # PyCharm 66 | .idea/ 67 | 68 | # VSCode 69 | .vscode/ 70 | 71 | # Pyenv 72 | .python-version -------------------------------------------------------------------------------- /examples/mixed_routes/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | .pytest_cache/ 6 | *.py[cod] 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | .venv/ 14 | env/ 15 | bin/ 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | include/ 26 | man/ 27 | venv/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | pip-selfcheck.json 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | 45 | # Translations 46 | *.mo 47 | 48 | # Mr Developer 49 | .mr.developer.cfg 50 | .project 51 | .pydevproject 52 | 53 | # Rope 54 | .ropeproject 55 | 56 | # Django stuff: 57 | *.log 58 | *.pot 59 | 60 | .DS_Store 61 | 62 | # Sphinx documentation 63 | docs/_build/ 64 | 65 | # PyCharm 66 | .idea/ 67 | 68 | # VSCode 69 | .vscode/ 70 | 71 | # Pyenv 72 | .python-version -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Tristan King 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /examples/asgi_only/demo.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from starlette.applications import Starlette 3 | from starlette.requests import Request 4 | from starlette.responses import JSONResponse, Response 5 | from starlette.routing import Route 6 | from asgi_only import create_server 7 | 8 | async def simple(_request: Request): 9 | return Response(content="HELLO WORLD!") 10 | 11 | async def json(_request: Request): 12 | return JSONResponse({"hello": "world!"}) 13 | 14 | async def echo(request: Request): 15 | agent = request.headers.get("User-Agent", "UNKNOWN") 16 | body = (await request.body()).decode("utf-8") 17 | return JSONResponse({"hello": agent, "body": body}) 18 | 19 | async def startup(): 20 | print(">>>>> STARTUP CALLED!") 21 | 22 | async def shutdown(): 23 | print(">>>>> SHUTDOWN CALLED!") 24 | 25 | app = Starlette(debug=True, routes=[ 26 | Route("/simple", simple), 27 | Route("/json", json), 28 | Route("/echo", json), 29 | ], on_startup=[startup], on_shutdown=[shutdown]) 30 | 31 | async def main(): 32 | context = create_server(app, port=3000) 33 | 34 | try: 35 | task = context.start() 36 | # shield the task so we can avoid cancelling it on ctrl+c 37 | await asyncio.shield(task) 38 | finally: 39 | # trigger a graceful shutdown and wait for everything to close 40 | # (ctrl+c will most of the time be captured in this finally but it's not perfect) 41 | await context.shutdown() 42 | await task 43 | 44 | if __name__ == "__main__": 45 | asyncio.run(main()) 46 | -------------------------------------------------------------------------------- /examples/mixed_routes/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::net::SocketAddr; 2 | 3 | use axum::{extract::Query, response::IntoResponse, routing::get, Router}; 4 | use parviocula::{AsgiHandler, ServerContext}; 5 | use pyo3::prelude::*; 6 | use serde::Deserialize; 7 | 8 | #[derive(Deserialize)] 9 | struct RootQuery { 10 | name: String, 11 | } 12 | 13 | async fn get_root(Query(RootQuery { name }): Query) -> impl IntoResponse { 14 | format!("Hello {name}, from rust!") 15 | } 16 | 17 | async fn start(port: u16, shutdown_signal: tokio::sync::oneshot::Receiver<()>, asgi: AsgiHandler) { 18 | let app = Router::new() 19 | .route("/post_or_get", get(get_root).post(asgi.clone())) 20 | .fallback(asgi); 21 | let addr = SocketAddr::new([127, 0, 0, 1].into(), port); 22 | let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); 23 | 24 | if let Err(err) = axum::serve(listener, app) 25 | .with_graceful_shutdown(async move { 26 | if let Err(err) = shutdown_signal.await { 27 | eprintln!("failed to send shutdown signal: {err}"); 28 | } 29 | }) 30 | .await 31 | { 32 | eprintln!("error running server: {err}"); 33 | }; 34 | } 35 | 36 | #[pyfunction] 37 | #[pyo3(signature = (app, port=None))] 38 | fn create_server(app: PyObject, port: Option) -> PyResult> { 39 | let port = port.unwrap_or(3000); 40 | let ctx = parviocula::create_server_context( 41 | app, 42 | Box::new(move |asgi, rx| async move { 43 | start(port, rx, asgi).await; 44 | }), 45 | ); 46 | Ok(ctx) 47 | } 48 | 49 | #[pymodule] 50 | fn mixed_routes(m: &Bound) -> PyResult<()> { 51 | m.add_function(wrap_pyfunction!(create_server, m.clone())?)?; 52 | Ok(()) 53 | } 54 | -------------------------------------------------------------------------------- /examples/mixed_routes/demo.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | from starlette.applications import Starlette 4 | from starlette.requests import Request 5 | from starlette.responses import JSONResponse, Response 6 | from starlette.status import HTTP_204_NO_CONTENT 7 | from starlette.routing import Route 8 | from mixed_routes import create_server 9 | 10 | async def simple(request): 11 | return Response(content="HELLO WORLD!") 12 | 13 | async def empty(request): 14 | return Response(status_code=HTTP_204_NO_CONTENT) 15 | 16 | async def mixed(request: Request): 17 | data = await request.json() 18 | name = data["name"] 19 | return Response(content=f"Hello {name}, from python!") 20 | 21 | async def headers(request: Request): 22 | return Response(content="check headers!", headers={"SOME":"DATA"}) 23 | 24 | async def json(request: Request): 25 | return JSONResponse({"hello": "world!"}) 26 | 27 | async def startup(): 28 | print(">>>>> STARTUP CALLED!") 29 | 30 | async def shutdown(): 31 | print(">>>>> SHUTDOWN CALLED!") 32 | 33 | async def main(): 34 | app = Starlette(debug=True, routes=[ 35 | Route("/simple", simple), 36 | Route("/empty", empty), 37 | Route("/json", json), 38 | Route("/post_or_get", mixed, methods=["POST"]), 39 | Route("/headers", headers), 40 | ], on_startup=[startup], on_shutdown=[shutdown]) 41 | 42 | context = create_server(app, port=3000) 43 | 44 | try: 45 | task = context.start() 46 | # shield the task so we can avoid cancelling it on ctrl+c 47 | await asyncio.shield(task) 48 | finally: 49 | # trigger a graceful shutdown and wait for everything to close 50 | # (ctrl+c will most of the time be captured in this finally but it's not perfect) 51 | await context.shutdown() 52 | await task 53 | 54 | asyncio.run(main()) 55 | -------------------------------------------------------------------------------- /examples/asgi_only/src/lib.rs: -------------------------------------------------------------------------------- 1 | use axum::{serve, Router}; 2 | use parviocula::{AsgiHandler, ServerContext}; 3 | use pyo3::exceptions::PyValueError; 4 | use pyo3::prelude::*; 5 | use pyo3::types::PyString; 6 | use pyo3::PyResult; 7 | use std::net::{IpAddr, Ipv4Addr, SocketAddr}; 8 | use tokio::net::TcpListener; 9 | 10 | #[pyfunction] 11 | #[pyo3(signature = (app, host=None, port=None))] 12 | fn create_server( 13 | app: PyObject, 14 | host: Option<&Bound<'_, PyString>>, 15 | port: Option, 16 | ) -> PyResult> { 17 | let host = match host { 18 | Some(host) => IpAddr::V4( 19 | host.to_string() 20 | .parse() 21 | .map_err(|_| PyErr::new::("Invalid host"))?, 22 | ), 23 | None => IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 24 | }; 25 | let port = port.unwrap_or(3000); 26 | 27 | let ctx = parviocula::create_server_context( 28 | app, 29 | Box::new(move |asgi: AsgiHandler, rx| async move { 30 | let app = Router::new().fallback(asgi); 31 | let addr = SocketAddr::new(host, port); 32 | let listener = match TcpListener::bind(addr).await { 33 | Ok(listener) => listener, 34 | Err(err) => { 35 | eprintln!("Failed to bind to address: {err}"); 36 | return; 37 | } 38 | }; 39 | let res = serve(listener, app) 40 | .with_graceful_shutdown(async move { 41 | if let Err(e) = rx.await { 42 | eprintln!("{e}"); 43 | } 44 | }) 45 | .await; 46 | if let Err(err) = res { 47 | eprintln!("{err}"); 48 | } 49 | }), 50 | ); 51 | Ok(ctx) 52 | } 53 | 54 | #[pymodule] 55 | fn asgi_only(m: &Bound) -> PyResult<()> { 56 | m.add_function(wrap_pyfunction!(create_server, m)?)?; 57 | Ok(()) 58 | } 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Parviocula 2 | 3 | A simple [ASGI](https://asgi.readthedocs.io/en/latest/introduction.html) server aimed at helping the transition from python ASGI applications to an [Axum](https://github.com/tokio-rs/axum) application (or maybe in the future, any [Tower](https://github.com/tower-rs/tower) based web framework). 4 | 5 | The goal is to allow writing an Axum based application in rust, with the option of falling back onto the python ASGI application to handle requests not yet implemented in rust, allowing a slow transition from an ASGI based python service to an Axum based rust service without having to do it all in one go. 6 | 7 | This takes on the role of the ASGI server you would currently use, using Hyper to handle all the http protocol stuff and converting the requests into ASGI messages using [pyo3](https://github.com/PyO3/pyo3/) and passing them onto the python ASGI application. 8 | 9 | This requires writing a small pyo3 based wrapper for your rust server that allows launching it from python. See the [asgi_only](./examples/asgi_only) example for a minimal starting example. The [`README.md`](./examples/asgi_only/README.md) in the examples details the setup for the project as well. 10 | 11 | ## Limitations 12 | 13 | While in most cases you can simply use the `fallback` on the Axum router to forward things not implemented in rust onto the python code, if you have some methods on the same path implemented in both rust and python (e.g. a GET handled by rust, and the POST still handled by python) you need to specificly tell the router to forward the python methods onto the ASGI router. See the [mixed_routes](./examples/mixed_routes) example. 14 | 15 | Forwarding routes from nested Axum routers onto the ASGI application will lose the path information from the parent router, and as a result the ASGI app will only have the path information from the current router passed to it. For now it's recommended to only use a single flat router, and wait until you've replaced all the ASGI parts to split the router up if you wish. 16 | 17 | ## FAQ 18 | 19 | ### Is it Blazingly Fast? 20 | 21 | 🤷. Testing on my machine with [oha](https://github.com/hatoo/oha), comparing against [uvicorn](https://github.com/encode/uvicorn), using the [`asgi_example`](./examples/asgi_only), building with `maturin develop --release`, running with `oha http://localhost:3000/echo -z 10s -c 1 -H "user-agent: oha" -d "hello"`, it currently only gets ~80-90% of the requests per second of uvicorn with a single oha worker. But with 50 oha workers (`oha ... -c 50`) it gets ~200% the requests per second of uvicorn. When replacing `uvicorn` in a large suite of integration tests I have that take upwards of 10 minutes to complete, I see no significant difference in the completion speed of the test suite. 22 | 23 | ### Which ASGI application frameworks does it support? 24 | 25 | I've used it with [Starlette](https://github.com/encode/starlette) directly and [FastAPI](https://github.com/tiangolo/fastapi/). I suspect others should work as well, but I've not tested any. 26 | 27 | ### Can I replace my python ASGI server with this? 28 | 29 | Maybe? But unless you're using it to transition your code base to rust it's not worth it. It's probably slower (at least if you use uvicorn like I do) and you'll likely lose a lot of the niceties you get from your current mature ASGI server. 30 | 31 | ### The `ServerContext` API is terrible! Can you change it? 32 | 33 | Feel free to open an Issue with suggestions or a Pull Request with changes and I'll consider it. 34 | 35 | ### You're doing X wrong, it should be done like this ...! 36 | 37 | Again, feel free to open an Issue with suggestions or a Pull Request with changes and I'll look at changing it. 38 | 39 | ## TODOs 40 | 41 | - Make it faster? 42 | - Some tests for the ASGI server to make sure it conforms to the specification. 43 | - Implement as a Tower Service, rather than an Axum Handler 44 | - Figure out if the `ServerContext::start` function can `await` until the server actually starts listening for requests 45 | - Starting the server from rust. i.e. being able to more easily replace a python ASGI server as the entry point for starting the application. (this has proven to be tricky due to linking to python directly, and I had troubles starting the asyncio event loop from rust. In the end it's much easier to launch from python and I don't think there's really any benefit to doing it from rust anyway) 46 | - pyo3_asyncio sometimes generates `InvalidStateError`s due to using `call_soon_threadsafe` to `set_result` on it's futures, and in some cases (that I haven't been able to make a minimal example for yet) the futures are beging cancelled after the `call_soon_threadsafe` call but before the actual `set_result` call it made. It doesn't effect anything (as the futures were cancelled), but is annoying to see the errors in the logs. 47 | - python typing helpers 48 | - More tracing support? 49 | - Websockets? 50 | - Figure out the OpenAPI story 51 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod asgi; 2 | 3 | use std::future::Future; 4 | use std::sync::Arc; 5 | 6 | use futures::future::BoxFuture; 7 | use pyo3::exceptions::PyRuntimeError; 8 | use pyo3::prelude::*; 9 | use pyo3::types::{PyDict, PyString}; 10 | use tokio::sync::{mpsc, oneshot, Mutex}; 11 | 12 | pub use crate::asgi::AsgiHandler; 13 | 14 | #[pyclass] 15 | struct Receiver { 16 | rx: Arc>>>, 17 | } 18 | 19 | impl Receiver { 20 | pub fn new() -> (Receiver, mpsc::UnboundedSender>) { 21 | let (tx, rx) = mpsc::unbounded_channel::>(); 22 | ( 23 | Receiver { 24 | rx: Arc::new(Mutex::new(rx)), 25 | }, 26 | tx, 27 | ) 28 | } 29 | } 30 | 31 | #[pymethods] 32 | impl Receiver { 33 | fn __call__<'a>(&'a mut self, py: Python<'a>) -> PyResult> { 34 | let rx = self.rx.clone(); 35 | pyo3_async_runtimes::tokio::future_into_py(py, async move { 36 | let next = rx 37 | .lock() 38 | .await 39 | .recv() 40 | .await 41 | .ok_or_else(|| PyErr::new::("connection closed"))?; 42 | Ok(next) 43 | }) 44 | } 45 | } 46 | 47 | #[pyclass] 48 | pub struct Sender { 49 | tx: mpsc::UnboundedSender>, 50 | locals: Arc, 51 | } 52 | 53 | impl Sender { 54 | pub fn new( 55 | locals: Arc, 56 | ) -> (Sender, mpsc::UnboundedReceiver>) { 57 | let (tx, rx) = mpsc::unbounded_channel::>(); 58 | (Sender { tx, locals }, rx) 59 | } 60 | } 61 | 62 | pub trait AsyncFn { 63 | fn call(&self, handler: AsgiHandler, rx: oneshot::Receiver<()>) -> BoxFuture<'static, ()>; 64 | } 65 | 66 | impl AsyncFn for T 67 | where 68 | T: Fn(AsgiHandler, oneshot::Receiver<()>) -> F, 69 | F: Future + Send + 'static, 70 | { 71 | fn call(&self, handler: AsgiHandler, rx: oneshot::Receiver<()>) -> BoxFuture<'static, ()> { 72 | Box::pin(self(handler, rx)) 73 | } 74 | } 75 | 76 | #[pymethods] 77 | impl Sender { 78 | fn __call__<'a>(&'a mut self, py: Python<'a>, args: Py) -> PyResult> { 79 | let fut = self.locals.event_loop(py).call_method0("create_future")?; 80 | if self.tx.is_closed() { 81 | fut.call_method1("set_result", (py.None(),))?; 82 | } else { 83 | match self.tx.send(args) { 84 | Ok(_) => fut.call_method1("set_result", (py.None(),))?, 85 | Err(_) => fut.call_method1( 86 | "set_exception", 87 | (PyErr::new::("connection closed"),), 88 | )?, 89 | }; 90 | } 91 | Ok(fut) 92 | } 93 | } 94 | 95 | #[pyclass] 96 | pub struct ServerContext { 97 | trigger_shutdown_tx: Option>, 98 | trigger_shutdown_rx: Option>, 99 | wait_shutdown_tx: Option>, 100 | wait_shutdown_rx: Option>, 101 | app: Option, 102 | server: Option>, 103 | } 104 | 105 | #[pymethods] 106 | impl ServerContext { 107 | fn shutdown<'a>(&'a mut self, py: Python<'a>) -> PyResult> { 108 | if let (Some(tx), Some(rx)) = ( 109 | self.trigger_shutdown_tx.take(), 110 | self.wait_shutdown_rx.take(), 111 | ) { 112 | if let Err(_e) = tx.send(()) { 113 | #[cfg(feature = "tracing")] 114 | tracing::warn!("failed to send shutdown notification: {:?}", _e); 115 | } 116 | pyo3_async_runtimes::tokio::future_into_py(py, async move { 117 | if let Err(_e) = rx.await { 118 | #[cfg(feature = "tracing")] 119 | tracing::warn!("failed waiting for shutdown: {:?}", _e); 120 | } 121 | Ok::<_, PyErr>(Python::with_gil(|py| py.None())) 122 | }) 123 | } else { 124 | pyo3_async_runtimes::tokio::future_into_py(py, async move { 125 | Ok::<_, PyErr>(Python::with_gil(|py| py.None())) 126 | }) 127 | } 128 | } 129 | 130 | fn start<'a>(&'a mut self, py: Python<'a>) -> PyResult> { 131 | match ( 132 | self.trigger_shutdown_rx.take(), 133 | self.app.take(), 134 | self.server.take(), 135 | self.wait_shutdown_tx.take(), 136 | ) { 137 | (Some(rx), Some(app), Some(server), Some(tx)) => { 138 | let locals = Arc::new( 139 | pyo3_async_runtimes::TaskLocals::with_running_loop(py)?.copy_context(py)?, 140 | ); 141 | let (lifespan_receiver, lifespan_receiver_tx) = Receiver::new(); 142 | let (lifespan_sender, mut lifespan_sender_rx) = Sender::new(locals.clone()); 143 | //let (ready_tx, ready_rx) = oneshot::channel::<()>(); 144 | 145 | pyo3_async_runtimes::tokio::future_into_py(py, async move { 146 | // https://asgi.readthedocs.io/en/latest/specs/lifespan.html 147 | let lifespan = Python::with_gil(|py| { 148 | let asgi = PyDict::new(py); 149 | asgi.set_item("spec_version", "2.0")?; 150 | asgi.set_item("version", "2.0")?; 151 | let scope = PyDict::new(py); 152 | scope.set_item("type", "lifespan")?; 153 | scope.set_item("asgi", asgi)?; 154 | 155 | let sender = Py::new(py, lifespan_sender)?; 156 | let receiver = Py::new(py, lifespan_receiver)?; 157 | let args = (scope, receiver, sender); 158 | let res = app.call_method1(py, "__call__", args)?; 159 | let fut = res.extract(py)?; 160 | pyo3_async_runtimes::into_future_with_locals(&locals, fut) 161 | })?; 162 | 163 | let lifespan_startup = Python::with_gil(|py| { 164 | let scope = PyDict::new(py); 165 | scope.set_item("type", "lifespan.startup")?; 166 | let scope: Py = scope.into(); 167 | Ok::, PyErr>(scope) 168 | })?; 169 | if lifespan_receiver_tx.send(lifespan_startup).is_err() { 170 | return Err(PyErr::new::( 171 | "Failed to send lifespan startup", 172 | )); 173 | } 174 | 175 | // will continue running until the server sends lifespan.shutdown 176 | tokio::spawn(async move { 177 | if let Err(_e) = lifespan.await { 178 | #[cfg(feature = "tracing")] 179 | tracing::error!("Error processing lifespan: {_e}"); 180 | } 181 | }); 182 | 183 | if let Some(resp) = lifespan_sender_rx.recv().await { 184 | Python::with_gil(|py| { 185 | let dict: Bound<'_, PyDict> = resp.into_bound(py); 186 | if let Ok(Some(value)) = dict.get_item("type") { 187 | let value: Bound<'_, PyString> = value.downcast_into()?; 188 | let value = value.to_str()?; 189 | if value == "lifespan.startup.complete" { 190 | return Ok(()); 191 | } 192 | } 193 | Err(PyErr::new::( 194 | "Failed during asgi startup", 195 | )) 196 | })?; 197 | } 198 | 199 | // create asgi service 200 | let asgi_handler = AsgiHandler::new_with_locals(Arc::new(app), locals.clone()); 201 | 202 | server.call(asgi_handler, rx).await; 203 | 204 | // shutdown 205 | let lifespan_shutdown = Python::with_gil(|py| { 206 | let scope = PyDict::new(py); 207 | scope.set_item("type", "lifespan.shutdown")?; 208 | let scope: Py = scope.into(); 209 | Ok::, PyErr>(scope) 210 | })?; 211 | if lifespan_receiver_tx.send(lifespan_shutdown).is_err() { 212 | return Err(PyErr::new::( 213 | "Failed to send lifespan shutdown", 214 | )); 215 | } 216 | 217 | // receive the shutdown success, event though we don't care about it. without this the sender_rx gets dropped too early and the shutdown fails. 218 | lifespan_sender_rx.recv().await; 219 | 220 | if let Err(_e) = tx.send(()) { 221 | #[cfg(feature = "tracing")] 222 | tracing::error!("Failed to send shutdown completion"); 223 | } 224 | 225 | Ok(Python::with_gil(|py| py.None())) 226 | }) 227 | } 228 | (_, _, _, _) => Err(PyErr::new::("Already started")), 229 | } 230 | } 231 | } 232 | 233 | /// Create a server context wrapping the main server method 234 | pub fn create_server_context( 235 | app: PyObject, 236 | server: Box, 237 | ) -> Py { 238 | let (tx, rx) = tokio::sync::oneshot::channel(); 239 | let (wait_shutdown_tx, wait_shutdown_rx) = tokio::sync::oneshot::channel(); 240 | let ctx = ServerContext { 241 | trigger_shutdown_tx: Some(tx), 242 | trigger_shutdown_rx: Some(rx), 243 | wait_shutdown_tx: Some(wait_shutdown_tx), 244 | wait_shutdown_rx: Some(wait_shutdown_rx), 245 | app: Some(app), 246 | server: Some(server), 247 | }; 248 | Python::with_gil(|py| Py::new(py, ctx).expect("failed to create context")) 249 | } 250 | -------------------------------------------------------------------------------- /src/asgi.rs: -------------------------------------------------------------------------------- 1 | use crate::Sender; 2 | use axum::{ 3 | body::{to_bytes, Body, Bytes}, 4 | handler::Handler, 5 | http::{HeaderName, HeaderValue, Request, StatusCode, Version}, 6 | response::{IntoResponse, Response}, 7 | }; 8 | 9 | use pyo3::types::{PyBytes, PyDict, PyInt, PyString}; 10 | use pyo3::{ 11 | exceptions::PyRuntimeError, 12 | prelude::*, 13 | types::{PyList, PySequence}, 14 | DowncastError, DowncastIntoError, 15 | }; 16 | use std::{ 17 | future::Future, 18 | pin::Pin, 19 | sync::{ 20 | atomic::{AtomicBool, Ordering}, 21 | Arc, 22 | }, 23 | }; 24 | use tokio::sync::{ 25 | mpsc::{self, UnboundedReceiver}, 26 | Mutex, 27 | }; 28 | 29 | #[derive(Clone)] 30 | pub struct AsgiHandler { 31 | app: Arc, 32 | locals: Arc, 33 | } 34 | 35 | impl AsgiHandler { 36 | pub fn new_with_locals( 37 | app: Arc, 38 | locals: Arc, 39 | ) -> AsgiHandler { 40 | AsgiHandler { app, locals } 41 | } 42 | } 43 | 44 | #[derive(Debug)] 45 | enum AsgiError { 46 | PyErr(PyErr), 47 | InvalidHttpVersion, 48 | ExpectedResponseStart, 49 | MissingResponse, 50 | ExpectedResponseBody, 51 | FailedToCreateResponse, 52 | InvalidHeader, 53 | InvalidUtf8InPath, 54 | } 55 | 56 | impl From for AsgiError { 57 | fn from(e: PyErr) -> Self { 58 | AsgiError::PyErr(e) 59 | } 60 | } 61 | 62 | impl<'a, 'b> From> for AsgiError { 63 | fn from(_e: DowncastError<'a, 'b>) -> Self { 64 | AsgiError::PyErr(PyErr::new::("failed to downcast type")) 65 | } 66 | } 67 | 68 | impl<'a> From> for AsgiError { 69 | fn from(_e: DowncastIntoError<'a>) -> Self { 70 | AsgiError::PyErr(PyErr::new::("failed to downcast type")) 71 | } 72 | } 73 | 74 | impl IntoResponse for AsgiError { 75 | fn into_response(self) -> Response { 76 | match self { 77 | AsgiError::InvalidHttpVersion => (StatusCode::BAD_REQUEST, "Unsupported HTTP version"), 78 | AsgiError::InvalidUtf8InPath => (StatusCode::BAD_REQUEST, "Invalid Utf8 in path"), 79 | AsgiError::PyErr(_) 80 | | AsgiError::ExpectedResponseStart 81 | | AsgiError::MissingResponse 82 | | AsgiError::ExpectedResponseBody 83 | | AsgiError::FailedToCreateResponse 84 | | AsgiError::InvalidHeader => { 85 | (StatusCode::INTERNAL_SERVER_ERROR, "Internal Server Error") 86 | } 87 | } 88 | .into_response() 89 | } 90 | } 91 | 92 | /// Used to set the HttpReceiver's disconnected flag when the connection is closed 93 | struct SetTrueOnDrop(Arc); 94 | 95 | impl Drop for SetTrueOnDrop { 96 | fn drop(&mut self) { 97 | self.0.store(true, Ordering::SeqCst); 98 | } 99 | } 100 | 101 | #[pyclass] 102 | struct HttpReceiver { 103 | disconnected: Arc, 104 | rx: Arc>>>, 105 | locals: Arc, 106 | } 107 | 108 | #[pymethods] 109 | impl HttpReceiver { 110 | fn __call__<'a>(&'a self, py: Python<'a>) -> PyResult> { 111 | let rx = self.rx.clone(); 112 | let disconnected = self.disconnected.clone(); 113 | pyo3_async_runtimes::tokio::future_into_py_with_locals( 114 | py, 115 | self.locals.clone_ref(py), 116 | async move { 117 | let next = rx.lock().await.recv().await; 118 | 119 | if next.is_none() || disconnected.load(Ordering::SeqCst) { 120 | Python::with_gil(|py| { 121 | let scope = PyDict::new(py); 122 | scope.set_item("type", "http.disconnect")?; 123 | Ok::<_, PyErr>(scope.into()) 124 | }) 125 | } else if let Some(Some(body)) = next { 126 | const MAX_BODY_SIZE: usize = 4 * 1024 * 1024; // 4MB 127 | let bytes = to_bytes(body, MAX_BODY_SIZE) 128 | .await 129 | .map_err(|_e| PyErr::new::("failed to fetch data"))?; 130 | Python::with_gil(|py| { 131 | let bytes = PyBytes::new(py, &bytes[..]); 132 | let scope = PyDict::new(py); 133 | scope.set_item("type", "http.request")?; 134 | scope.set_item("body", bytes)?; 135 | let scope: Py = scope.into(); 136 | Ok::<_, PyErr>(scope) 137 | }) 138 | } else { 139 | Python::with_gil(|py| { 140 | let scope = PyDict::new(py); 141 | scope.set_item("type", "http.request")?; 142 | Ok::<_, PyErr>(scope.into()) 143 | }) 144 | } 145 | }, 146 | ) 147 | } 148 | } 149 | 150 | impl Handler for AsgiHandler { 151 | type Future = Pin> + Send>>; 152 | 153 | fn call(self, req: Request, _state: S) -> Self::Future { 154 | let app = self.app.clone(); 155 | let (http_sender, mut http_sender_rx) = Sender::new(self.locals.clone()); 156 | let disconnected = Arc::new(AtomicBool::new(false)); 157 | let (receiver_tx, receiver_rx) = mpsc::unbounded_channel(); 158 | let receiver = HttpReceiver { 159 | rx: Arc::new(Mutex::new(receiver_rx)), 160 | disconnected: disconnected.clone(), 161 | locals: self.locals.clone(), 162 | }; 163 | let (req, body): (_, Body) = req.into_parts(); 164 | Box::pin(async move { 165 | receiver_tx.send(Some(body)).unwrap(); 166 | let _disconnected = SetTrueOnDrop(disconnected); 167 | match Python::with_gil(|py| { 168 | let asgi = PyDict::new(py); 169 | asgi.set_item("spec_version", "2.0")?; 170 | asgi.set_item("version", "2.0")?; 171 | let scope = PyDict::new(py); 172 | scope.set_item("type", "http")?; 173 | scope.set_item("asgi", asgi)?; 174 | scope.set_item( 175 | "http_version", 176 | match req.version { 177 | Version::HTTP_10 => "1.0", 178 | Version::HTTP_11 => "1.1", 179 | Version::HTTP_2 => "2", 180 | _ => return Err(AsgiError::InvalidHttpVersion), 181 | }, 182 | )?; 183 | scope.set_item("method", req.method.as_str())?; 184 | scope.set_item("scheme", req.uri.scheme_str().unwrap_or("http"))?; 185 | if let Some(path_and_query) = req.uri.path_and_query() { 186 | let path = path_and_query.path(); 187 | let raw_path = path.as_bytes(); 188 | // the spec requires this to be percent decoded at this point 189 | // https://asgi.readthedocs.io/en/latest/specs/www.html#http-connection-scope 190 | let path = percent_encoding::percent_decode(raw_path) 191 | .decode_utf8() 192 | .map_err(|_| AsgiError::InvalidUtf8InPath)?; 193 | scope.set_item("path", path)?; 194 | let raw_path_bytes = PyBytes::new(py, path_and_query.path().as_bytes()); 195 | scope.set_item("raw_path", raw_path_bytes)?; 196 | if let Some(query) = path_and_query.query() { 197 | let qs_bytes = PyBytes::new(py, query.as_bytes()); 198 | scope.set_item("query_string", qs_bytes)?; 199 | } else { 200 | let qs_bytes = PyBytes::new(py, "".as_bytes()); 201 | scope.set_item("query_string", qs_bytes)?; 202 | } 203 | } else { 204 | // TODO: is it even possible to get here? 205 | // we have to set these to something as they're not optional in the spec 206 | scope.set_item("path", "")?; 207 | let raw_path_bytes = PyBytes::new(py, "".as_bytes()); 208 | scope.set_item("raw_path", raw_path_bytes)?; 209 | let qs_bytes = PyBytes::new(py, "".as_bytes()); 210 | scope.set_item("query_string", qs_bytes)?; 211 | } 212 | scope.set_item("root_path", "")?; 213 | 214 | let headers = req 215 | .headers 216 | .iter() 217 | .map(|(name, value)| { 218 | let name_bytes = PyBytes::new(py, name.as_str().as_bytes()); 219 | let value_bytes = PyBytes::new(py, value.as_bytes()); 220 | // This unwrap() is safe because PyList::new only fails if there's a Python 221 | // exception during list creation, which won't happen for a simple list of 222 | // two PyBytes objects that were just successfully created 223 | PyList::new(py, [name_bytes, value_bytes]).unwrap() 224 | }) 225 | .collect::>(); 226 | // This unwrap() is safe because PyList::new only fails if there's a Python 227 | // exception during list creation, which won't happen for a simple list of 228 | // PyList objects that were already successfully created above 229 | let headers = PyList::new(py, headers).unwrap(); 230 | scope.set_item("headers", headers)?; 231 | // TODO: client/server args 232 | let sender = Py::new(py, http_sender)?; 233 | let receiver = Py::new(py, receiver)?; 234 | let args = (scope, receiver, sender); 235 | let res = app.call_method1(py, "__call__", args)?; 236 | let fut = res.extract(py)?; 237 | let coro = pyo3_async_runtimes::into_future_with_locals(&self.locals, fut)?; 238 | Ok::<_, AsgiError>(coro) 239 | }) { 240 | Ok(http_coro) => { 241 | tokio::spawn(async move { 242 | if let Err(_e) = http_coro.await { 243 | #[cfg(feature = "tracing")] 244 | tracing::error!("error handling request: {_e}"); 245 | } 246 | }); 247 | 248 | let mut response = Response::builder(); 249 | 250 | if let Some(resp) = http_sender_rx.recv().await { 251 | let (status, headers) = match Python::with_gil(|py| { 252 | let dict: Bound<'_, PyDict> = resp.into_bound(py); 253 | if let Ok(Some(value)) = dict.get_item("type") { 254 | let value: Bound<'_, PyString> = value.downcast_into()?; 255 | let value = value.to_str()?; 256 | if value == "http.response.start" { 257 | let value: Bound<'_, PyInt> = dict 258 | .get_item("status") 259 | .and_then(|opt| { 260 | opt.ok_or_else(|| { 261 | PyErr::new::( 262 | "Missing status in http.response.start", 263 | ) 264 | }) 265 | })? 266 | .downcast_into()?; 267 | let status: u16 = value.extract()?; 268 | 269 | let headers = if let Ok(Some(raw)) = dict.get_item("headers") { 270 | let outer: Bound<'_, PySequence> = raw.downcast_into()?; 271 | Some( 272 | outer 273 | .try_iter()? 274 | .map(|item| { 275 | item.and_then(|item| { 276 | let seq: Bound<'_, PySequence> = 277 | item.downcast_into()?; 278 | let header: Vec = 279 | seq.get_item(0)?.extract()?; 280 | let value: Vec = 281 | seq.get_item(1)?.extract()?; 282 | Ok((header, value)) 283 | }) 284 | }) 285 | .collect::>>()?, 286 | ) 287 | } else { 288 | None 289 | }; 290 | Ok((status, headers)) 291 | } else { 292 | Err(AsgiError::ExpectedResponseStart) 293 | } 294 | } else { 295 | Err(AsgiError::ExpectedResponseStart) 296 | } 297 | }) { 298 | Ok((status, headers)) => (status, headers), 299 | Err(e) => { 300 | return e.into_response(); 301 | } 302 | }; 303 | response = response.status(status); 304 | if let Some(pyheaders) = headers { 305 | let headers = response.headers_mut().unwrap(); 306 | for (name, value) in pyheaders { 307 | let name = match HeaderName::from_bytes(&name) { 308 | Ok(name) => name, 309 | Err(_e) => { 310 | return AsgiError::InvalidHeader.into_response(); 311 | } 312 | }; 313 | let value = match HeaderValue::from_bytes(&value) { 314 | Ok(value) => value, 315 | Err(_e) => { 316 | return AsgiError::InvalidHeader.into_response(); 317 | } 318 | }; 319 | headers.append(name, value); 320 | } 321 | } 322 | } else { 323 | return AsgiError::MissingResponse.into_response(); 324 | } 325 | 326 | let mut body = Vec::new(); 327 | while let Some(resp) = http_sender_rx.recv().await { 328 | let (bytes, more_body) = match Python::with_gil(|py| { 329 | let dict: Bound<'_, PyDict> = resp.into_bound(py); 330 | if let Ok(Some(value)) = dict.get_item("type") { 331 | let value: Bound<'_, PyString> = 332 | value.downcast_into().map_err(|_| { 333 | AsgiError::PyErr(PyErr::new::( 334 | "failed to downcast type", 335 | )) 336 | })?; 337 | let value = value.to_str()?; 338 | if value == "http.response.body" { 339 | let more_body = 340 | if let Ok(Some(raw)) = dict.get_item("more_body") { 341 | raw.extract::()? 342 | } else { 343 | false 344 | }; 345 | if let Ok(Some(raw)) = dict.get_item("body") { 346 | Ok((raw.extract::>()?, more_body)) 347 | } else { 348 | Ok((Vec::new(), more_body)) 349 | } 350 | } else { 351 | Err(AsgiError::ExpectedResponseBody) 352 | } 353 | } else { 354 | Err(AsgiError::ExpectedResponseBody) 355 | } 356 | }) { 357 | Ok((bytes, more_body)) => (bytes, more_body), 358 | Err(e) => { 359 | return e.into_response(); 360 | } 361 | }; 362 | body.extend(bytes); 363 | if !more_body { 364 | break; 365 | } 366 | } 367 | 368 | let body = Body::from(Bytes::from(body)); 369 | match response.body(body) { 370 | Ok(response) => response.into_response(), 371 | Err(_e) => { 372 | #[cfg(feature = "tracing")] 373 | tracing::error!("Failed to create response: {_e}"); 374 | AsgiError::FailedToCreateResponse.into_response() 375 | } 376 | } 377 | } 378 | Err(e) => { 379 | #[cfg(feature = "tracing")] 380 | tracing::error!("Error preparing request scope: {e:?}"); 381 | e.into_response() 382 | } 383 | } 384 | }) 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /examples/asgi_only/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "asgi_only" 22 | version = "0.1.0" 23 | dependencies = [ 24 | "axum", 25 | "parviocula", 26 | "pyo3", 27 | "tokio", 28 | ] 29 | 30 | [[package]] 31 | name = "autocfg" 32 | version = "1.1.0" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 35 | 36 | [[package]] 37 | name = "axum" 38 | version = "0.8.1" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8" 41 | dependencies = [ 42 | "axum-core", 43 | "bytes", 44 | "form_urlencoded", 45 | "futures-util", 46 | "http", 47 | "http-body", 48 | "http-body-util", 49 | "hyper", 50 | "hyper-util", 51 | "itoa", 52 | "matchit", 53 | "memchr", 54 | "mime", 55 | "percent-encoding", 56 | "pin-project-lite", 57 | "rustversion", 58 | "serde", 59 | "serde_json", 60 | "serde_path_to_error", 61 | "serde_urlencoded", 62 | "sync_wrapper", 63 | "tokio", 64 | "tower", 65 | "tower-layer", 66 | "tower-service", 67 | "tracing", 68 | ] 69 | 70 | [[package]] 71 | name = "axum-core" 72 | version = "0.5.0" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733" 75 | dependencies = [ 76 | "bytes", 77 | "futures-util", 78 | "http", 79 | "http-body", 80 | "http-body-util", 81 | "mime", 82 | "pin-project-lite", 83 | "rustversion", 84 | "sync_wrapper", 85 | "tower-layer", 86 | "tower-service", 87 | "tracing", 88 | ] 89 | 90 | [[package]] 91 | name = "axum-extra" 92 | version = "0.10.0" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "460fc6f625a1f7705c6cf62d0d070794e94668988b1c38111baeec177c715f7b" 95 | dependencies = [ 96 | "axum", 97 | "axum-core", 98 | "bytes", 99 | "futures-util", 100 | "http", 101 | "http-body", 102 | "http-body-util", 103 | "mime", 104 | "pin-project-lite", 105 | "serde", 106 | "tower", 107 | "tower-layer", 108 | "tower-service", 109 | ] 110 | 111 | [[package]] 112 | name = "backtrace" 113 | version = "0.3.74" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 116 | dependencies = [ 117 | "addr2line", 118 | "cfg-if", 119 | "libc", 120 | "miniz_oxide", 121 | "object", 122 | "rustc-demangle", 123 | "windows-targets", 124 | ] 125 | 126 | [[package]] 127 | name = "bytes" 128 | version = "1.10.1" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 131 | 132 | [[package]] 133 | name = "cfg-if" 134 | version = "1.0.0" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 137 | 138 | [[package]] 139 | name = "fnv" 140 | version = "1.0.7" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 143 | 144 | [[package]] 145 | name = "form_urlencoded" 146 | version = "1.2.1" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 149 | dependencies = [ 150 | "percent-encoding", 151 | ] 152 | 153 | [[package]] 154 | name = "futures" 155 | version = "0.3.31" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 158 | dependencies = [ 159 | "futures-channel", 160 | "futures-core", 161 | "futures-executor", 162 | "futures-io", 163 | "futures-sink", 164 | "futures-task", 165 | "futures-util", 166 | ] 167 | 168 | [[package]] 169 | name = "futures-channel" 170 | version = "0.3.31" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 173 | dependencies = [ 174 | "futures-core", 175 | "futures-sink", 176 | ] 177 | 178 | [[package]] 179 | name = "futures-core" 180 | version = "0.3.31" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 183 | 184 | [[package]] 185 | name = "futures-executor" 186 | version = "0.3.31" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 189 | dependencies = [ 190 | "futures-core", 191 | "futures-task", 192 | "futures-util", 193 | ] 194 | 195 | [[package]] 196 | name = "futures-io" 197 | version = "0.3.31" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 200 | 201 | [[package]] 202 | name = "futures-macro" 203 | version = "0.3.31" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 206 | dependencies = [ 207 | "proc-macro2", 208 | "quote", 209 | "syn", 210 | ] 211 | 212 | [[package]] 213 | name = "futures-sink" 214 | version = "0.3.31" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 217 | 218 | [[package]] 219 | name = "futures-task" 220 | version = "0.3.31" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 223 | 224 | [[package]] 225 | name = "futures-util" 226 | version = "0.3.31" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 229 | dependencies = [ 230 | "futures-channel", 231 | "futures-core", 232 | "futures-io", 233 | "futures-macro", 234 | "futures-sink", 235 | "futures-task", 236 | "memchr", 237 | "pin-project-lite", 238 | "pin-utils", 239 | "slab", 240 | ] 241 | 242 | [[package]] 243 | name = "gimli" 244 | version = "0.31.1" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 247 | 248 | [[package]] 249 | name = "heck" 250 | version = "0.5.0" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 253 | 254 | [[package]] 255 | name = "http" 256 | version = "1.3.1" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 259 | dependencies = [ 260 | "bytes", 261 | "fnv", 262 | "itoa", 263 | ] 264 | 265 | [[package]] 266 | name = "http-body" 267 | version = "1.0.1" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 270 | dependencies = [ 271 | "bytes", 272 | "http", 273 | ] 274 | 275 | [[package]] 276 | name = "http-body-util" 277 | version = "0.1.3" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" 280 | dependencies = [ 281 | "bytes", 282 | "futures-core", 283 | "http", 284 | "http-body", 285 | "pin-project-lite", 286 | ] 287 | 288 | [[package]] 289 | name = "httparse" 290 | version = "1.10.1" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 293 | 294 | [[package]] 295 | name = "httpdate" 296 | version = "1.0.2" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 299 | 300 | [[package]] 301 | name = "hyper" 302 | version = "1.6.0" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" 305 | dependencies = [ 306 | "bytes", 307 | "futures-channel", 308 | "futures-util", 309 | "http", 310 | "http-body", 311 | "httparse", 312 | "httpdate", 313 | "itoa", 314 | "pin-project-lite", 315 | "smallvec", 316 | "tokio", 317 | ] 318 | 319 | [[package]] 320 | name = "hyper-util" 321 | version = "0.1.10" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" 324 | dependencies = [ 325 | "bytes", 326 | "futures-util", 327 | "http", 328 | "http-body", 329 | "hyper", 330 | "pin-project-lite", 331 | "tokio", 332 | "tower-service", 333 | ] 334 | 335 | [[package]] 336 | name = "indoc" 337 | version = "2.0.6" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" 340 | 341 | [[package]] 342 | name = "itoa" 343 | version = "1.0.15" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 346 | 347 | [[package]] 348 | name = "libc" 349 | version = "0.2.171" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" 352 | 353 | [[package]] 354 | name = "log" 355 | version = "0.4.17" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 358 | dependencies = [ 359 | "cfg-if", 360 | ] 361 | 362 | [[package]] 363 | name = "matchit" 364 | version = "0.8.4" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" 367 | 368 | [[package]] 369 | name = "memchr" 370 | version = "2.5.0" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 373 | 374 | [[package]] 375 | name = "memoffset" 376 | version = "0.9.1" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" 379 | dependencies = [ 380 | "autocfg", 381 | ] 382 | 383 | [[package]] 384 | name = "mime" 385 | version = "0.3.16" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 388 | 389 | [[package]] 390 | name = "miniz_oxide" 391 | version = "0.8.5" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" 394 | dependencies = [ 395 | "adler2", 396 | ] 397 | 398 | [[package]] 399 | name = "mio" 400 | version = "1.0.3" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 403 | dependencies = [ 404 | "libc", 405 | "wasi", 406 | "windows-sys", 407 | ] 408 | 409 | [[package]] 410 | name = "object" 411 | version = "0.36.7" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 414 | dependencies = [ 415 | "memchr", 416 | ] 417 | 418 | [[package]] 419 | name = "once_cell" 420 | version = "1.15.0" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" 423 | 424 | [[package]] 425 | name = "parviocula" 426 | version = "0.0.4" 427 | dependencies = [ 428 | "axum", 429 | "axum-extra", 430 | "futures", 431 | "http-body-util", 432 | "hyper", 433 | "percent-encoding", 434 | "pyo3", 435 | "pyo3-async-runtimes", 436 | "tokio", 437 | ] 438 | 439 | [[package]] 440 | name = "percent-encoding" 441 | version = "2.3.1" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 444 | 445 | [[package]] 446 | name = "pin-project-lite" 447 | version = "0.2.16" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 450 | 451 | [[package]] 452 | name = "pin-utils" 453 | version = "0.1.0" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 456 | 457 | [[package]] 458 | name = "portable-atomic" 459 | version = "1.11.0" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" 462 | 463 | [[package]] 464 | name = "proc-macro2" 465 | version = "1.0.94" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" 468 | dependencies = [ 469 | "unicode-ident", 470 | ] 471 | 472 | [[package]] 473 | name = "pyo3" 474 | version = "0.24.0" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "7f1c6c3591120564d64db2261bec5f910ae454f01def849b9c22835a84695e86" 477 | dependencies = [ 478 | "cfg-if", 479 | "indoc", 480 | "libc", 481 | "memoffset", 482 | "once_cell", 483 | "portable-atomic", 484 | "pyo3-build-config", 485 | "pyo3-ffi", 486 | "pyo3-macros", 487 | "unindent", 488 | ] 489 | 490 | [[package]] 491 | name = "pyo3-async-runtimes" 492 | version = "0.24.0" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "dd0b83dc42f9d41f50d38180dad65f0c99763b65a3ff2a81bf351dd35a1df8bf" 495 | dependencies = [ 496 | "futures", 497 | "once_cell", 498 | "pin-project-lite", 499 | "pyo3", 500 | "tokio", 501 | ] 502 | 503 | [[package]] 504 | name = "pyo3-build-config" 505 | version = "0.24.0" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "e9b6c2b34cf71427ea37c7001aefbaeb85886a074795e35f161f5aecc7620a7a" 508 | dependencies = [ 509 | "once_cell", 510 | "target-lexicon", 511 | ] 512 | 513 | [[package]] 514 | name = "pyo3-ffi" 515 | version = "0.24.0" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "5507651906a46432cdda02cd02dd0319f6064f1374c9147c45b978621d2c3a9c" 518 | dependencies = [ 519 | "libc", 520 | "pyo3-build-config", 521 | ] 522 | 523 | [[package]] 524 | name = "pyo3-macros" 525 | version = "0.24.0" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "b0d394b5b4fd8d97d48336bb0dd2aebabad39f1d294edd6bcd2cccf2eefe6f42" 528 | dependencies = [ 529 | "proc-macro2", 530 | "pyo3-macros-backend", 531 | "quote", 532 | "syn", 533 | ] 534 | 535 | [[package]] 536 | name = "pyo3-macros-backend" 537 | version = "0.24.0" 538 | source = "registry+https://github.com/rust-lang/crates.io-index" 539 | checksum = "fd72da09cfa943b1080f621f024d2ef7e2773df7badd51aa30a2be1f8caa7c8e" 540 | dependencies = [ 541 | "heck", 542 | "proc-macro2", 543 | "pyo3-build-config", 544 | "quote", 545 | "syn", 546 | ] 547 | 548 | [[package]] 549 | name = "quote" 550 | version = "1.0.40" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 553 | dependencies = [ 554 | "proc-macro2", 555 | ] 556 | 557 | [[package]] 558 | name = "rustc-demangle" 559 | version = "0.1.24" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 562 | 563 | [[package]] 564 | name = "rustversion" 565 | version = "1.0.20" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" 568 | 569 | [[package]] 570 | name = "ryu" 571 | version = "1.0.11" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" 574 | 575 | [[package]] 576 | name = "serde" 577 | version = "1.0.219" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 580 | dependencies = [ 581 | "serde_derive", 582 | ] 583 | 584 | [[package]] 585 | name = "serde_derive" 586 | version = "1.0.219" 587 | source = "registry+https://github.com/rust-lang/crates.io-index" 588 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 589 | dependencies = [ 590 | "proc-macro2", 591 | "quote", 592 | "syn", 593 | ] 594 | 595 | [[package]] 596 | name = "serde_json" 597 | version = "1.0.83" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" 600 | dependencies = [ 601 | "itoa", 602 | "ryu", 603 | "serde", 604 | ] 605 | 606 | [[package]] 607 | name = "serde_path_to_error" 608 | version = "0.1.17" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" 611 | dependencies = [ 612 | "itoa", 613 | "serde", 614 | ] 615 | 616 | [[package]] 617 | name = "serde_urlencoded" 618 | version = "0.7.1" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 621 | dependencies = [ 622 | "form_urlencoded", 623 | "itoa", 624 | "ryu", 625 | "serde", 626 | ] 627 | 628 | [[package]] 629 | name = "slab" 630 | version = "0.4.7" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" 633 | dependencies = [ 634 | "autocfg", 635 | ] 636 | 637 | [[package]] 638 | name = "smallvec" 639 | version = "1.14.0" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" 642 | 643 | [[package]] 644 | name = "socket2" 645 | version = "0.5.8" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" 648 | dependencies = [ 649 | "libc", 650 | "windows-sys", 651 | ] 652 | 653 | [[package]] 654 | name = "syn" 655 | version = "2.0.100" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" 658 | dependencies = [ 659 | "proc-macro2", 660 | "quote", 661 | "unicode-ident", 662 | ] 663 | 664 | [[package]] 665 | name = "sync_wrapper" 666 | version = "1.0.2" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 669 | 670 | [[package]] 671 | name = "target-lexicon" 672 | version = "0.13.2" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" 675 | 676 | [[package]] 677 | name = "tokio" 678 | version = "1.44.1" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" 681 | dependencies = [ 682 | "backtrace", 683 | "libc", 684 | "mio", 685 | "pin-project-lite", 686 | "socket2", 687 | "tokio-macros", 688 | "windows-sys", 689 | ] 690 | 691 | [[package]] 692 | name = "tokio-macros" 693 | version = "2.5.0" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 696 | dependencies = [ 697 | "proc-macro2", 698 | "quote", 699 | "syn", 700 | ] 701 | 702 | [[package]] 703 | name = "tower" 704 | version = "0.5.2" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" 707 | dependencies = [ 708 | "futures-core", 709 | "futures-util", 710 | "pin-project-lite", 711 | "sync_wrapper", 712 | "tokio", 713 | "tower-layer", 714 | "tower-service", 715 | "tracing", 716 | ] 717 | 718 | [[package]] 719 | name = "tower-layer" 720 | version = "0.3.3" 721 | source = "registry+https://github.com/rust-lang/crates.io-index" 722 | checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 723 | 724 | [[package]] 725 | name = "tower-service" 726 | version = "0.3.3" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 729 | 730 | [[package]] 731 | name = "tracing" 732 | version = "0.1.41" 733 | source = "registry+https://github.com/rust-lang/crates.io-index" 734 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 735 | dependencies = [ 736 | "log", 737 | "pin-project-lite", 738 | "tracing-core", 739 | ] 740 | 741 | [[package]] 742 | name = "tracing-core" 743 | version = "0.1.33" 744 | source = "registry+https://github.com/rust-lang/crates.io-index" 745 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 746 | dependencies = [ 747 | "once_cell", 748 | ] 749 | 750 | [[package]] 751 | name = "unicode-ident" 752 | version = "1.0.3" 753 | source = "registry+https://github.com/rust-lang/crates.io-index" 754 | checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" 755 | 756 | [[package]] 757 | name = "unindent" 758 | version = "0.2.4" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" 761 | 762 | [[package]] 763 | name = "wasi" 764 | version = "0.11.0+wasi-snapshot-preview1" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 767 | 768 | [[package]] 769 | name = "windows-sys" 770 | version = "0.52.0" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 773 | dependencies = [ 774 | "windows-targets", 775 | ] 776 | 777 | [[package]] 778 | name = "windows-targets" 779 | version = "0.52.6" 780 | source = "registry+https://github.com/rust-lang/crates.io-index" 781 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 782 | dependencies = [ 783 | "windows_aarch64_gnullvm", 784 | "windows_aarch64_msvc", 785 | "windows_i686_gnu", 786 | "windows_i686_gnullvm", 787 | "windows_i686_msvc", 788 | "windows_x86_64_gnu", 789 | "windows_x86_64_gnullvm", 790 | "windows_x86_64_msvc", 791 | ] 792 | 793 | [[package]] 794 | name = "windows_aarch64_gnullvm" 795 | version = "0.52.6" 796 | source = "registry+https://github.com/rust-lang/crates.io-index" 797 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 798 | 799 | [[package]] 800 | name = "windows_aarch64_msvc" 801 | version = "0.52.6" 802 | source = "registry+https://github.com/rust-lang/crates.io-index" 803 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 804 | 805 | [[package]] 806 | name = "windows_i686_gnu" 807 | version = "0.52.6" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 810 | 811 | [[package]] 812 | name = "windows_i686_gnullvm" 813 | version = "0.52.6" 814 | source = "registry+https://github.com/rust-lang/crates.io-index" 815 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 816 | 817 | [[package]] 818 | name = "windows_i686_msvc" 819 | version = "0.52.6" 820 | source = "registry+https://github.com/rust-lang/crates.io-index" 821 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 822 | 823 | [[package]] 824 | name = "windows_x86_64_gnu" 825 | version = "0.52.6" 826 | source = "registry+https://github.com/rust-lang/crates.io-index" 827 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 828 | 829 | [[package]] 830 | name = "windows_x86_64_gnullvm" 831 | version = "0.52.6" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 834 | 835 | [[package]] 836 | name = "windows_x86_64_msvc" 837 | version = "0.52.6" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 840 | -------------------------------------------------------------------------------- /examples/mixed_routes/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "autocfg" 22 | version = "1.1.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 25 | 26 | [[package]] 27 | name = "axum" 28 | version = "0.8.1" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8" 31 | dependencies = [ 32 | "axum-core", 33 | "bytes", 34 | "form_urlencoded", 35 | "futures-util", 36 | "http", 37 | "http-body", 38 | "http-body-util", 39 | "hyper", 40 | "hyper-util", 41 | "itoa", 42 | "matchit", 43 | "memchr", 44 | "mime", 45 | "percent-encoding", 46 | "pin-project-lite", 47 | "rustversion", 48 | "serde", 49 | "serde_json", 50 | "serde_path_to_error", 51 | "serde_urlencoded", 52 | "sync_wrapper", 53 | "tokio", 54 | "tower", 55 | "tower-layer", 56 | "tower-service", 57 | "tracing", 58 | ] 59 | 60 | [[package]] 61 | name = "axum-core" 62 | version = "0.5.0" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733" 65 | dependencies = [ 66 | "bytes", 67 | "futures-util", 68 | "http", 69 | "http-body", 70 | "http-body-util", 71 | "mime", 72 | "pin-project-lite", 73 | "rustversion", 74 | "sync_wrapper", 75 | "tower-layer", 76 | "tower-service", 77 | "tracing", 78 | ] 79 | 80 | [[package]] 81 | name = "axum-extra" 82 | version = "0.10.0" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "460fc6f625a1f7705c6cf62d0d070794e94668988b1c38111baeec177c715f7b" 85 | dependencies = [ 86 | "axum", 87 | "axum-core", 88 | "bytes", 89 | "futures-util", 90 | "http", 91 | "http-body", 92 | "http-body-util", 93 | "mime", 94 | "pin-project-lite", 95 | "serde", 96 | "tower", 97 | "tower-layer", 98 | "tower-service", 99 | ] 100 | 101 | [[package]] 102 | name = "backtrace" 103 | version = "0.3.74" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 106 | dependencies = [ 107 | "addr2line", 108 | "cfg-if", 109 | "libc", 110 | "miniz_oxide", 111 | "object", 112 | "rustc-demangle", 113 | "windows-targets", 114 | ] 115 | 116 | [[package]] 117 | name = "bitflags" 118 | version = "1.3.2" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 121 | 122 | [[package]] 123 | name = "bytes" 124 | version = "1.10.1" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 127 | 128 | [[package]] 129 | name = "cfg-if" 130 | version = "1.0.0" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 133 | 134 | [[package]] 135 | name = "fnv" 136 | version = "1.0.7" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 139 | 140 | [[package]] 141 | name = "form_urlencoded" 142 | version = "1.1.0" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" 145 | dependencies = [ 146 | "percent-encoding", 147 | ] 148 | 149 | [[package]] 150 | name = "futures" 151 | version = "0.3.31" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 154 | dependencies = [ 155 | "futures-channel", 156 | "futures-core", 157 | "futures-executor", 158 | "futures-io", 159 | "futures-sink", 160 | "futures-task", 161 | "futures-util", 162 | ] 163 | 164 | [[package]] 165 | name = "futures-channel" 166 | version = "0.3.31" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 169 | dependencies = [ 170 | "futures-core", 171 | "futures-sink", 172 | ] 173 | 174 | [[package]] 175 | name = "futures-core" 176 | version = "0.3.31" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 179 | 180 | [[package]] 181 | name = "futures-executor" 182 | version = "0.3.31" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 185 | dependencies = [ 186 | "futures-core", 187 | "futures-task", 188 | "futures-util", 189 | ] 190 | 191 | [[package]] 192 | name = "futures-io" 193 | version = "0.3.31" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 196 | 197 | [[package]] 198 | name = "futures-macro" 199 | version = "0.3.31" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 202 | dependencies = [ 203 | "proc-macro2", 204 | "quote", 205 | "syn", 206 | ] 207 | 208 | [[package]] 209 | name = "futures-sink" 210 | version = "0.3.31" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 213 | 214 | [[package]] 215 | name = "futures-task" 216 | version = "0.3.31" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 219 | 220 | [[package]] 221 | name = "futures-util" 222 | version = "0.3.31" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 225 | dependencies = [ 226 | "futures-channel", 227 | "futures-core", 228 | "futures-io", 229 | "futures-macro", 230 | "futures-sink", 231 | "futures-task", 232 | "memchr", 233 | "pin-project-lite", 234 | "pin-utils", 235 | "slab", 236 | ] 237 | 238 | [[package]] 239 | name = "gimli" 240 | version = "0.31.1" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 243 | 244 | [[package]] 245 | name = "heck" 246 | version = "0.5.0" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 249 | 250 | [[package]] 251 | name = "http" 252 | version = "1.3.1" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 255 | dependencies = [ 256 | "bytes", 257 | "fnv", 258 | "itoa", 259 | ] 260 | 261 | [[package]] 262 | name = "http-body" 263 | version = "1.0.1" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 266 | dependencies = [ 267 | "bytes", 268 | "http", 269 | ] 270 | 271 | [[package]] 272 | name = "http-body-util" 273 | version = "0.1.3" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" 276 | dependencies = [ 277 | "bytes", 278 | "futures-core", 279 | "http", 280 | "http-body", 281 | "pin-project-lite", 282 | ] 283 | 284 | [[package]] 285 | name = "httparse" 286 | version = "1.10.1" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 289 | 290 | [[package]] 291 | name = "httpdate" 292 | version = "1.0.2" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 295 | 296 | [[package]] 297 | name = "hyper" 298 | version = "1.6.0" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" 301 | dependencies = [ 302 | "bytes", 303 | "futures-channel", 304 | "futures-util", 305 | "http", 306 | "http-body", 307 | "httparse", 308 | "httpdate", 309 | "itoa", 310 | "pin-project-lite", 311 | "smallvec", 312 | "tokio", 313 | ] 314 | 315 | [[package]] 316 | name = "hyper-util" 317 | version = "0.1.10" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" 320 | dependencies = [ 321 | "bytes", 322 | "futures-util", 323 | "http", 324 | "http-body", 325 | "hyper", 326 | "pin-project-lite", 327 | "tokio", 328 | "tower-service", 329 | ] 330 | 331 | [[package]] 332 | name = "indoc" 333 | version = "2.0.6" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" 336 | 337 | [[package]] 338 | name = "itoa" 339 | version = "1.0.15" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 342 | 343 | [[package]] 344 | name = "libc" 345 | version = "0.2.171" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" 348 | 349 | [[package]] 350 | name = "lock_api" 351 | version = "0.4.9" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 354 | dependencies = [ 355 | "autocfg", 356 | "scopeguard", 357 | ] 358 | 359 | [[package]] 360 | name = "log" 361 | version = "0.4.17" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 364 | dependencies = [ 365 | "cfg-if", 366 | ] 367 | 368 | [[package]] 369 | name = "matchit" 370 | version = "0.8.4" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" 373 | 374 | [[package]] 375 | name = "memchr" 376 | version = "2.5.0" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 379 | 380 | [[package]] 381 | name = "memoffset" 382 | version = "0.9.1" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" 385 | dependencies = [ 386 | "autocfg", 387 | ] 388 | 389 | [[package]] 390 | name = "mime" 391 | version = "0.3.16" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 394 | 395 | [[package]] 396 | name = "miniz_oxide" 397 | version = "0.8.5" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" 400 | dependencies = [ 401 | "adler2", 402 | ] 403 | 404 | [[package]] 405 | name = "mio" 406 | version = "1.0.3" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 409 | dependencies = [ 410 | "libc", 411 | "wasi", 412 | "windows-sys 0.52.0", 413 | ] 414 | 415 | [[package]] 416 | name = "mixed_routes" 417 | version = "0.1.0" 418 | dependencies = [ 419 | "axum", 420 | "parviocula", 421 | "pyo3", 422 | "serde", 423 | "tokio", 424 | ] 425 | 426 | [[package]] 427 | name = "object" 428 | version = "0.36.7" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 431 | dependencies = [ 432 | "memchr", 433 | ] 434 | 435 | [[package]] 436 | name = "once_cell" 437 | version = "1.15.0" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" 440 | 441 | [[package]] 442 | name = "parking_lot" 443 | version = "0.12.1" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 446 | dependencies = [ 447 | "lock_api", 448 | "parking_lot_core", 449 | ] 450 | 451 | [[package]] 452 | name = "parking_lot_core" 453 | version = "0.9.3" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" 456 | dependencies = [ 457 | "cfg-if", 458 | "libc", 459 | "redox_syscall", 460 | "smallvec", 461 | "windows-sys 0.36.1", 462 | ] 463 | 464 | [[package]] 465 | name = "parviocula" 466 | version = "0.0.4" 467 | dependencies = [ 468 | "axum", 469 | "axum-extra", 470 | "futures", 471 | "http-body-util", 472 | "hyper", 473 | "percent-encoding", 474 | "pyo3", 475 | "pyo3-async-runtimes", 476 | "tokio", 477 | ] 478 | 479 | [[package]] 480 | name = "percent-encoding" 481 | version = "2.3.1" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 484 | 485 | [[package]] 486 | name = "pin-project-lite" 487 | version = "0.2.16" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 490 | 491 | [[package]] 492 | name = "pin-utils" 493 | version = "0.1.0" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 496 | 497 | [[package]] 498 | name = "portable-atomic" 499 | version = "1.11.0" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" 502 | 503 | [[package]] 504 | name = "proc-macro2" 505 | version = "1.0.94" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" 508 | dependencies = [ 509 | "unicode-ident", 510 | ] 511 | 512 | [[package]] 513 | name = "pyo3" 514 | version = "0.24.0" 515 | source = "registry+https://github.com/rust-lang/crates.io-index" 516 | checksum = "7f1c6c3591120564d64db2261bec5f910ae454f01def849b9c22835a84695e86" 517 | dependencies = [ 518 | "cfg-if", 519 | "indoc", 520 | "libc", 521 | "memoffset", 522 | "once_cell", 523 | "portable-atomic", 524 | "pyo3-build-config", 525 | "pyo3-ffi", 526 | "pyo3-macros", 527 | "unindent", 528 | ] 529 | 530 | [[package]] 531 | name = "pyo3-async-runtimes" 532 | version = "0.24.0" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "dd0b83dc42f9d41f50d38180dad65f0c99763b65a3ff2a81bf351dd35a1df8bf" 535 | dependencies = [ 536 | "futures", 537 | "once_cell", 538 | "pin-project-lite", 539 | "pyo3", 540 | "tokio", 541 | ] 542 | 543 | [[package]] 544 | name = "pyo3-build-config" 545 | version = "0.24.0" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "e9b6c2b34cf71427ea37c7001aefbaeb85886a074795e35f161f5aecc7620a7a" 548 | dependencies = [ 549 | "once_cell", 550 | "target-lexicon", 551 | ] 552 | 553 | [[package]] 554 | name = "pyo3-ffi" 555 | version = "0.24.0" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "5507651906a46432cdda02cd02dd0319f6064f1374c9147c45b978621d2c3a9c" 558 | dependencies = [ 559 | "libc", 560 | "pyo3-build-config", 561 | ] 562 | 563 | [[package]] 564 | name = "pyo3-macros" 565 | version = "0.24.0" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "b0d394b5b4fd8d97d48336bb0dd2aebabad39f1d294edd6bcd2cccf2eefe6f42" 568 | dependencies = [ 569 | "proc-macro2", 570 | "pyo3-macros-backend", 571 | "quote", 572 | "syn", 573 | ] 574 | 575 | [[package]] 576 | name = "pyo3-macros-backend" 577 | version = "0.24.0" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "fd72da09cfa943b1080f621f024d2ef7e2773df7badd51aa30a2be1f8caa7c8e" 580 | dependencies = [ 581 | "heck", 582 | "proc-macro2", 583 | "pyo3-build-config", 584 | "quote", 585 | "syn", 586 | ] 587 | 588 | [[package]] 589 | name = "quote" 590 | version = "1.0.40" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 593 | dependencies = [ 594 | "proc-macro2", 595 | ] 596 | 597 | [[package]] 598 | name = "redox_syscall" 599 | version = "0.2.16" 600 | source = "registry+https://github.com/rust-lang/crates.io-index" 601 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 602 | dependencies = [ 603 | "bitflags", 604 | ] 605 | 606 | [[package]] 607 | name = "rustc-demangle" 608 | version = "0.1.24" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 611 | 612 | [[package]] 613 | name = "rustversion" 614 | version = "1.0.20" 615 | source = "registry+https://github.com/rust-lang/crates.io-index" 616 | checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" 617 | 618 | [[package]] 619 | name = "ryu" 620 | version = "1.0.11" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" 623 | 624 | [[package]] 625 | name = "scopeguard" 626 | version = "1.1.0" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 629 | 630 | [[package]] 631 | name = "serde" 632 | version = "1.0.219" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 635 | dependencies = [ 636 | "serde_derive", 637 | ] 638 | 639 | [[package]] 640 | name = "serde_derive" 641 | version = "1.0.219" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 644 | dependencies = [ 645 | "proc-macro2", 646 | "quote", 647 | "syn", 648 | ] 649 | 650 | [[package]] 651 | name = "serde_json" 652 | version = "1.0.86" 653 | source = "registry+https://github.com/rust-lang/crates.io-index" 654 | checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074" 655 | dependencies = [ 656 | "itoa", 657 | "ryu", 658 | "serde", 659 | ] 660 | 661 | [[package]] 662 | name = "serde_path_to_error" 663 | version = "0.1.17" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" 666 | dependencies = [ 667 | "itoa", 668 | "serde", 669 | ] 670 | 671 | [[package]] 672 | name = "serde_urlencoded" 673 | version = "0.7.1" 674 | source = "registry+https://github.com/rust-lang/crates.io-index" 675 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 676 | dependencies = [ 677 | "form_urlencoded", 678 | "itoa", 679 | "ryu", 680 | "serde", 681 | ] 682 | 683 | [[package]] 684 | name = "signal-hook-registry" 685 | version = "1.4.0" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 688 | dependencies = [ 689 | "libc", 690 | ] 691 | 692 | [[package]] 693 | name = "slab" 694 | version = "0.4.7" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" 697 | dependencies = [ 698 | "autocfg", 699 | ] 700 | 701 | [[package]] 702 | name = "smallvec" 703 | version = "1.14.0" 704 | source = "registry+https://github.com/rust-lang/crates.io-index" 705 | checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" 706 | 707 | [[package]] 708 | name = "socket2" 709 | version = "0.5.8" 710 | source = "registry+https://github.com/rust-lang/crates.io-index" 711 | checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" 712 | dependencies = [ 713 | "libc", 714 | "windows-sys 0.52.0", 715 | ] 716 | 717 | [[package]] 718 | name = "syn" 719 | version = "2.0.100" 720 | source = "registry+https://github.com/rust-lang/crates.io-index" 721 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" 722 | dependencies = [ 723 | "proc-macro2", 724 | "quote", 725 | "unicode-ident", 726 | ] 727 | 728 | [[package]] 729 | name = "sync_wrapper" 730 | version = "1.0.2" 731 | source = "registry+https://github.com/rust-lang/crates.io-index" 732 | checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 733 | 734 | [[package]] 735 | name = "target-lexicon" 736 | version = "0.13.2" 737 | source = "registry+https://github.com/rust-lang/crates.io-index" 738 | checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" 739 | 740 | [[package]] 741 | name = "tokio" 742 | version = "1.44.1" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" 745 | dependencies = [ 746 | "backtrace", 747 | "bytes", 748 | "libc", 749 | "mio", 750 | "parking_lot", 751 | "pin-project-lite", 752 | "signal-hook-registry", 753 | "socket2", 754 | "tokio-macros", 755 | "windows-sys 0.52.0", 756 | ] 757 | 758 | [[package]] 759 | name = "tokio-macros" 760 | version = "2.5.0" 761 | source = "registry+https://github.com/rust-lang/crates.io-index" 762 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 763 | dependencies = [ 764 | "proc-macro2", 765 | "quote", 766 | "syn", 767 | ] 768 | 769 | [[package]] 770 | name = "tower" 771 | version = "0.5.2" 772 | source = "registry+https://github.com/rust-lang/crates.io-index" 773 | checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" 774 | dependencies = [ 775 | "futures-core", 776 | "futures-util", 777 | "pin-project-lite", 778 | "sync_wrapper", 779 | "tokio", 780 | "tower-layer", 781 | "tower-service", 782 | "tracing", 783 | ] 784 | 785 | [[package]] 786 | name = "tower-layer" 787 | version = "0.3.3" 788 | source = "registry+https://github.com/rust-lang/crates.io-index" 789 | checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 790 | 791 | [[package]] 792 | name = "tower-service" 793 | version = "0.3.3" 794 | source = "registry+https://github.com/rust-lang/crates.io-index" 795 | checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 796 | 797 | [[package]] 798 | name = "tracing" 799 | version = "0.1.37" 800 | source = "registry+https://github.com/rust-lang/crates.io-index" 801 | checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" 802 | dependencies = [ 803 | "cfg-if", 804 | "log", 805 | "pin-project-lite", 806 | "tracing-core", 807 | ] 808 | 809 | [[package]] 810 | name = "tracing-core" 811 | version = "0.1.30" 812 | source = "registry+https://github.com/rust-lang/crates.io-index" 813 | checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" 814 | dependencies = [ 815 | "once_cell", 816 | ] 817 | 818 | [[package]] 819 | name = "unicode-ident" 820 | version = "1.0.5" 821 | source = "registry+https://github.com/rust-lang/crates.io-index" 822 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 823 | 824 | [[package]] 825 | name = "unindent" 826 | version = "0.2.4" 827 | source = "registry+https://github.com/rust-lang/crates.io-index" 828 | checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" 829 | 830 | [[package]] 831 | name = "wasi" 832 | version = "0.11.0+wasi-snapshot-preview1" 833 | source = "registry+https://github.com/rust-lang/crates.io-index" 834 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 835 | 836 | [[package]] 837 | name = "windows-sys" 838 | version = "0.36.1" 839 | source = "registry+https://github.com/rust-lang/crates.io-index" 840 | checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" 841 | dependencies = [ 842 | "windows_aarch64_msvc 0.36.1", 843 | "windows_i686_gnu 0.36.1", 844 | "windows_i686_msvc 0.36.1", 845 | "windows_x86_64_gnu 0.36.1", 846 | "windows_x86_64_msvc 0.36.1", 847 | ] 848 | 849 | [[package]] 850 | name = "windows-sys" 851 | version = "0.52.0" 852 | source = "registry+https://github.com/rust-lang/crates.io-index" 853 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 854 | dependencies = [ 855 | "windows-targets", 856 | ] 857 | 858 | [[package]] 859 | name = "windows-targets" 860 | version = "0.52.6" 861 | source = "registry+https://github.com/rust-lang/crates.io-index" 862 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 863 | dependencies = [ 864 | "windows_aarch64_gnullvm", 865 | "windows_aarch64_msvc 0.52.6", 866 | "windows_i686_gnu 0.52.6", 867 | "windows_i686_gnullvm", 868 | "windows_i686_msvc 0.52.6", 869 | "windows_x86_64_gnu 0.52.6", 870 | "windows_x86_64_gnullvm", 871 | "windows_x86_64_msvc 0.52.6", 872 | ] 873 | 874 | [[package]] 875 | name = "windows_aarch64_gnullvm" 876 | version = "0.52.6" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 879 | 880 | [[package]] 881 | name = "windows_aarch64_msvc" 882 | version = "0.36.1" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" 885 | 886 | [[package]] 887 | name = "windows_aarch64_msvc" 888 | version = "0.52.6" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 891 | 892 | [[package]] 893 | name = "windows_i686_gnu" 894 | version = "0.36.1" 895 | source = "registry+https://github.com/rust-lang/crates.io-index" 896 | checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" 897 | 898 | [[package]] 899 | name = "windows_i686_gnu" 900 | version = "0.52.6" 901 | source = "registry+https://github.com/rust-lang/crates.io-index" 902 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 903 | 904 | [[package]] 905 | name = "windows_i686_gnullvm" 906 | version = "0.52.6" 907 | source = "registry+https://github.com/rust-lang/crates.io-index" 908 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 909 | 910 | [[package]] 911 | name = "windows_i686_msvc" 912 | version = "0.36.1" 913 | source = "registry+https://github.com/rust-lang/crates.io-index" 914 | checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" 915 | 916 | [[package]] 917 | name = "windows_i686_msvc" 918 | version = "0.52.6" 919 | source = "registry+https://github.com/rust-lang/crates.io-index" 920 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 921 | 922 | [[package]] 923 | name = "windows_x86_64_gnu" 924 | version = "0.36.1" 925 | source = "registry+https://github.com/rust-lang/crates.io-index" 926 | checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" 927 | 928 | [[package]] 929 | name = "windows_x86_64_gnu" 930 | version = "0.52.6" 931 | source = "registry+https://github.com/rust-lang/crates.io-index" 932 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 933 | 934 | [[package]] 935 | name = "windows_x86_64_gnullvm" 936 | version = "0.52.6" 937 | source = "registry+https://github.com/rust-lang/crates.io-index" 938 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 939 | 940 | [[package]] 941 | name = "windows_x86_64_msvc" 942 | version = "0.36.1" 943 | source = "registry+https://github.com/rust-lang/crates.io-index" 944 | checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" 945 | 946 | [[package]] 947 | name = "windows_x86_64_msvc" 948 | version = "0.52.6" 949 | source = "registry+https://github.com/rust-lang/crates.io-index" 950 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 951 | --------------------------------------------------------------------------------