├── demo ├── .gitignore ├── start.bat ├── start.sh ├── README.md ├── Cargo.toml ├── src │ └── main.rs └── Cargo.lock ├── .github ├── FUNDING.yml └── workflows │ ├── clippy.yml │ ├── rustfmt.yml │ ├── tests.yml │ └── release.yml ├── .gitignore ├── .vscode └── settings.json ├── src ├── utils.rs ├── main.rs ├── spawn.rs ├── cli.rs └── fd.rs ├── Makefile ├── dist-workspace.toml ├── Cargo.toml ├── README.md ├── LICENSE └── Cargo.lock /demo/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [mitsuhiko] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target 3 | **/*.rs.bk 4 | -------------------------------------------------------------------------------- /demo/start.bat: -------------------------------------------------------------------------------- 1 | systemfd -s http::3001 -- cargo watch -x run 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.checkOnSave.command": "clippy" 3 | } -------------------------------------------------------------------------------- /demo/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | systemfd --no-pid -s http::3001 -- cargo watch -x run 3 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | # Demo 2 | 3 | This is a small demo that shows how to use systemfd/listenfd 4 | with the axum framework. 5 | -------------------------------------------------------------------------------- /demo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "demo" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | axum = "0.8.1" 8 | listenfd = "1.0.2" 9 | tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread"] } 10 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// Helper to quit with a status code and no message. 4 | #[derive(Debug)] 5 | pub struct QuietExit(pub i32); 6 | 7 | impl fmt::Display for QuietExit { 8 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 9 | write!(f, "exit with status code {}", self.0) 10 | } 11 | } 12 | 13 | impl std::error::Error for QuietExit {} 14 | -------------------------------------------------------------------------------- /.github/workflows/clippy.yml: -------------------------------------------------------------------------------- 1 | name: Clippy 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: dtolnay/rust-toolchain@master 12 | with: 13 | toolchain: stable 14 | components: clippy, rustfmt 15 | - name: Run clippy 16 | run: make lint 17 | -------------------------------------------------------------------------------- /.github/workflows/rustfmt.yml: -------------------------------------------------------------------------------- 1 | name: Rustfmt 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: dtolnay/rust-toolchain@master 12 | with: 13 | toolchain: stable 14 | components: clippy, rustfmt 15 | - name: Run rustfmt 16 | run: make format-check 17 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: test 2 | 3 | build: 4 | @cargo build --all-features 5 | 6 | doc: 7 | @cargo doc --all-features 8 | 9 | test: 10 | @cargo test 11 | @cd demo; cargo check 12 | 13 | format: 14 | @rustup component add rustfmt 2> /dev/null 15 | @cargo fmt --all 16 | 17 | format-check: 18 | @rustup component add rustfmt 2> /dev/null 19 | @cargo fmt --all -- --check 20 | 21 | lint: 22 | @rustup component add clippy 2> /dev/null 23 | @cargo clippy 24 | 25 | .PHONY: all doc test format format-check lint 26 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod cli; 2 | mod fd; 3 | mod spawn; 4 | mod utils; 5 | 6 | use std::process; 7 | 8 | fn main() { 9 | match cli::execute() { 10 | Ok(()) => {} 11 | Err(err) => { 12 | if let Some(&utils::QuietExit(code)) = err.downcast_ref() { 13 | process::exit(code); 14 | } 15 | println!("error: {}", err); 16 | for cause in err.chain().skip(1) { 17 | println!(" caused by: {}", cause); 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /dist-workspace.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["cargo:."] 3 | 4 | # Config for 'dist' 5 | [dist] 6 | # The preferred dist version to use in CI (Cargo.toml SemVer syntax) 7 | cargo-dist-version = "0.28.0" 8 | # CI backends to support 9 | ci = "github" 10 | # The installers to generate for each app 11 | installers = ["shell", "powershell"] 12 | # Target platforms to build apps for (Rust target-triple syntax) 13 | targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc"] 14 | # Which actions to run on pull requests 15 | pr-run-mode = "plan" 16 | # Whether to install an updater program 17 | install-updater = false 18 | # Path that installers should place binaries in 19 | install-path = "CARGO_HOME" 20 | -------------------------------------------------------------------------------- /demo/src/main.rs: -------------------------------------------------------------------------------- 1 | use axum::{routing::get, Router}; 2 | use tokio::net::TcpListener; 3 | 4 | async fn root() -> &'static str { 5 | "Hello, World!" 6 | } 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<(), Box> { 10 | let app = Router::new().route("/", get(root)); 11 | 12 | // try to first get a socket from listenfd, if that does not give us 13 | // one (eg: no systemd or systemfd), open on port 3000 instead. 14 | let mut listenfd = listenfd::ListenFd::from_env(); 15 | let listener = match listenfd.take_tcp_listener(0).unwrap() { 16 | Some(listener) => TcpListener::from_std(listener), 17 | None => TcpListener::bind("0.0.0.0:3000").await, 18 | }?; 19 | axum::serve(listener, app).await?; 20 | Ok(()) 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test-latest: 7 | name: Test on Latest 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v3 12 | - uses: dtolnay/rust-toolchain@master 13 | with: 14 | toolchain: stable 15 | - name: Test 16 | run: make test 17 | 18 | build-stable: 19 | name: Build on 1.64.0 20 | runs-on: ubuntu-latest 21 | 22 | steps: 23 | - uses: actions/checkout@v3 24 | - uses: dtolnay/rust-toolchain@master 25 | with: 26 | toolchain: 1.64.0 27 | - name: Test 28 | run: cargo check 29 | 30 | build-windows: 31 | name: Build on Windows 32 | runs-on: windows-2019 33 | 34 | steps: 35 | - uses: actions/checkout@v3 36 | - uses: dtolnay/rust-toolchain@master 37 | with: 38 | toolchain: stable 39 | - name: Test 40 | run: cargo check 41 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "systemfd" 3 | version = "0.4.6" 4 | authors = ["Armin Ronacher "] 5 | description = "A convenient helper for passing sockets into another process. Best to be combined with listenfd and cargo-watch." 6 | keywords = ["socket", "listenfd", "systemd", "socketactivation", "cargo-watch"] 7 | homepage = "https://github.com/mitsuhiko/systemfd" 8 | repository = "https://github.com/mitsuhiko/systemfd" 9 | readme = "README.md" 10 | license = "Apache-2.0" 11 | edition = "2018" 12 | rust-version = "1.64.0" 13 | 14 | [dependencies] 15 | clap = { version = "4.0.0", features = ["std", "help", "wrap_help"], default-features = false } 16 | regex = { version = "1.9.0", features = ["std"], default-features = false } 17 | lazy_static = "1.4.0" 18 | console = "0.15.8" 19 | anyhow = "1.0.52" 20 | 21 | [target."cfg(unix)".dependencies] 22 | nix = { version = "0.26.0", features = ["net", "process"], default-features = false } 23 | libc = "0.2.74" 24 | 25 | [target."cfg(windows)".dependencies] 26 | socket2 = "0.5.7" 27 | uuid = { version = "1.1.0", features = ["v4"] } 28 | windows-sys = { version = "0.52.0", features = ["Win32_Networking_WinSock"] } 29 | 30 | # The profile that 'cargo dist' will build with 31 | [profile.dist] 32 | inherits = "release" 33 | codegen-units = 1 34 | lto = true 35 | -------------------------------------------------------------------------------- /src/spawn.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::OsString; 2 | use std::process::Command; 3 | 4 | use anyhow::Error; 5 | 6 | use crate::fd::{Fd, RawFd}; 7 | 8 | #[cfg(unix)] 9 | mod imp { 10 | use super::*; 11 | use nix::unistd::getpid; 12 | use std::os::unix::process::CommandExt; 13 | 14 | pub fn spawn( 15 | raw_fds: Vec<(Fd, RawFd)>, 16 | cmdline: &[&OsString], 17 | no_pid: bool, 18 | ) -> Result<(), Error> { 19 | let mut cmd = Command::new(cmdline[0]); 20 | cmd.args(&cmdline[1..]); 21 | 22 | if !raw_fds.is_empty() { 23 | cmd.env("LISTEN_FDS", raw_fds.len().to_string()); 24 | let (_, rawfd) = raw_fds.first().unwrap(); 25 | cmd.env("LISTEN_FDS_FIRST_FD", rawfd.to_string()); 26 | if !no_pid { 27 | cmd.env("LISTEN_PID", getpid().to_string()); 28 | } 29 | } 30 | anyhow::bail!("failed to spawn: {}", cmd.exec()); 31 | } 32 | } 33 | 34 | #[cfg(windows)] 35 | mod imp { 36 | use super::*; 37 | use std::io::{Read, Write}; 38 | use std::mem; 39 | use std::net::{TcpListener, TcpStream}; 40 | use std::slice; 41 | use std::thread; 42 | 43 | use anyhow::anyhow; 44 | use uuid::Uuid; 45 | use windows_sys::Win32::Networking::WinSock::{WSADuplicateSocketW, SOCKET, WSAPROTOCOL_INFOW}; 46 | 47 | use crate::utils::QuietExit; 48 | 49 | fn share_sockets( 50 | mut sock: TcpStream, 51 | ref_secret: &Uuid, 52 | raw_fds: &[(Fd, RawFd)], 53 | ) -> Result<(), Error> { 54 | let mut data = Vec::new(); 55 | sock.read_to_end(&mut data)?; 56 | let out = String::from_utf8(data)?; 57 | let mut pieces = out.split('|'); 58 | 59 | let secret: Uuid = pieces 60 | .next() 61 | .and_then(|x| x.parse().ok()) 62 | .ok_or_else(|| anyhow!("invalid secret"))?; 63 | if &secret != ref_secret { 64 | return Err(anyhow!("invalid secret")); 65 | } 66 | let pid = pieces 67 | .next() 68 | .and_then(|x| x.parse().ok()) 69 | .ok_or_else(|| anyhow!("invalid or missing pid"))?; 70 | 71 | for &(_, raw_fd) in raw_fds { 72 | let mut proto_info: WSAPROTOCOL_INFOW = unsafe { mem::zeroed() }; 73 | unsafe { 74 | let rv = WSADuplicateSocketW(raw_fd as SOCKET, pid, &mut proto_info); 75 | if rv != 0 { 76 | return Err(anyhow!("socket duplicate failed with {}", rv)); 77 | } 78 | } 79 | let bytes: *const u8 = &proto_info as *const WSAPROTOCOL_INFOW as *const _; 80 | sock.write_all(unsafe { 81 | slice::from_raw_parts(bytes, mem::size_of::()) 82 | })?; 83 | } 84 | 85 | Ok(()) 86 | } 87 | 88 | pub fn spawn( 89 | raw_fds: Vec<(Fd, RawFd)>, 90 | cmdline: &[&OsString], 91 | _no_pid: bool, 92 | ) -> Result<(), Error> { 93 | let mut cmd = Command::new(cmdline[0]); 94 | cmd.args(&cmdline[1..]); 95 | 96 | let secret: Uuid = Uuid::new_v4(); 97 | let listener = TcpListener::bind("127.0.0.1:0")?; 98 | let sockserver_addr = listener.local_addr()?; 99 | 100 | cmd.env("SYSTEMFD_SOCKET_SERVER", sockserver_addr.to_string()); 101 | cmd.env("SYSTEMFD_SOCKET_SECRET", secret.to_string()); 102 | 103 | thread::spawn(move || { 104 | for stream in listener.incoming().flatten() { 105 | share_sockets(stream, &secret, &raw_fds).unwrap(); 106 | } 107 | }); 108 | 109 | let mut child = cmd.spawn()?; 110 | let status = child.wait()?; 111 | 112 | Err(QuietExit(status.code().unwrap()).into()) 113 | } 114 | } 115 | 116 | pub use self::imp::*; 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # systemfd 2 | 3 | [![Crates.io](https://img.shields.io/crates/d/systemfd.svg)](https://crates.io/crates/systemfd) 4 | [![License](https://img.shields.io/github/license/mitsuhiko/systemfd)](https://github.com/mitsuhiko/systemfd/blob/master/LICENSE) 5 | [![rustc 1.64.0](https://img.shields.io/badge/rust-1.64%2B-orange.svg)](https://img.shields.io/badge/rust-1.64%2B-orange.svg) 6 | 7 | `systemfd` is the 1% of systemd that's useful for development. It's a tiny process that 8 | opens a bunch of sockets and passes them to another process so that that process can 9 | then restart itself without dropping connections. For that it uses the systemd socket 10 | passing protocol (`LISTEN_FDS` + `LISTEN_PID`) environment variables on macOS and Linux 11 | and a custom protocol on Windows. Both are supported by the 12 | [listenfd](https://github.com/mitsuhiko/listenfd) crate. 13 | 14 | When combined with [cargo-watch](https://github.com/watchexec/cargo-watch) or 15 | [watchexec](https://github.com/watchexec/watchexec) you can get automatically 16 | reloading development servers: 17 | 18 | ``` 19 | $ systemfd --no-pid -s http::5000 -- cargo watch -x run 20 | $ systemfd --no-pid -s http::5000 -- watchexec -r -- cargo run 21 | ``` 22 | 23 | The `--no-pid` flag disables passing the `LISTEN_PID` variable on unix (it has no effect 24 | on Windows). This makes `listenfd` skip the pid check which would fail with 25 | `cargo watch` otherwise. To see how to implement a server ready for systemfd 26 | see below. 27 | 28 | ## Installation 29 | 30 | You can install a precompiled version of systemfd with curl to bash: 31 | 32 | ``` 33 | curl -sSfL https://github.com/mitsuhiko/systemfd/releases/latest/download/systemfd-installer.sh | sh 34 | ``` 35 | 36 | On windows you can use powershell to install it: 37 | 38 | ``` 39 | powershell -c "irm https://github.com/mitsuhiko/systemfd/releases/latest/download/systemfd-installer.ps1 | iex" 40 | ``` 41 | 42 | Alternatively you can get systemfd by installing it with cargo: 43 | 44 | ``` 45 | cargo install systemfd 46 | ``` 47 | 48 | ## Usage 49 | 50 | `systemfd` creates one or more sockets as specified on the command line and then 51 | invokes another application. Each time you pass the `-s` (or `--socket`) 52 | parameter a new socket is created. The value for the parameter is a socket 53 | specification in the format `[TYPE::]VALUE` where `TYPE` defaults to `tcp` or 54 | `unix` depending on the value. The following types exist: 55 | 56 | * `tcp`: creates a tcp listener 57 | * `http`: creates a tcp listener for http usage (prints a http url in the info output) 58 | * `https`: creates a tcp listener for https usage (prints a https url in the info output) 59 | * `unix`: creates a unix listener socket 60 | * `udp`: creates a udp socket 61 | 62 | `VALUE` depends on the socket type. The following formats are supported: 63 | 64 | * ``: an integer port value, assumes `127.0.0.1` as host 65 | * `:`: binds to a specific network interface and port 66 | * ``: requests a specific unix socket 67 | 68 | ## Examples 69 | 70 | ``` 71 | $ systemfd -s http::5000 -- my-server-executable 72 | $ systemfd -s http::5000 -s https::5443 -- my-server-executable 73 | $ systemfd -s 5000 -- my-server-executable 74 | $ systemfd -s udp::1567 -- my-game-server-executable 75 | ``` 76 | 77 | When `systemfd` starts it will print out the socket it created. This can be disabled 78 | by passing `-q`. Additionally if a port is set to `0` a random port is picked. 79 | 80 | ## Windows Protocol 81 | 82 | On Windows, passing of sockets is significantly more complex than on Unix. To 83 | achieve this, this utility implements a custom socket passing system that is also 84 | implemented by the listenfd crate. When the sockets are created, an additional 85 | local RPC server is spawned that gives out duplicates of the sockets to other 86 | processes. The RPC server uses TCP and is communicated to the child with the 87 | `SYSTEMFD_SOCKET_SERVER` environment variable. The RPC calls themselves are 88 | protected with a `SYSTEMFD_SOCKET_SECRET` secret key. 89 | 90 | The only understood command is `SECRET|PID` with secret and the processes' PID 91 | inserted. The server replies with N `WSAPROTOCOL_INFOW` structures. The client 92 | is expected to count the number of bytes and act accordingly. 93 | 94 | This protocol is currently somewhat of a hack and might change. It only 95 | exists to support the `listenfd` crate. 96 | 97 | ## License and Links 98 | 99 | - [Issue Tracker](https://github.com/mitsuhiko/systemfd/issues) 100 | - [Example](demo) 101 | - License: [Apache-2.0](LICENSE) 102 | -------------------------------------------------------------------------------- /src/cli.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::OsString; 2 | use std::io::{self, Write}; 3 | 4 | use anyhow::Error; 5 | use clap::{builder::Command, Arg}; 6 | use clap::{value_parser, ArgAction}; 7 | use console::{set_colors_enabled, Style}; 8 | 9 | use crate::fd::Fd; 10 | use crate::spawn; 11 | 12 | fn make_app() -> Command { 13 | Command::new("systemfd") 14 | .version(env!("CARGO_PKG_VERSION")) 15 | .max_term_width(79) 16 | .about( 17 | "\nsystemfd is a helper application that is particularly useful for \ 18 | Rust developers during development. It implements the systemd \ 19 | socket passing protocol which permits a socket to be opened from a \ 20 | processed and then passed to others. On windows a custom protocol \ 21 | is used. When paired with cargo-watch and the listenfd crate, \ 22 | automatic reloading servers can be used during development.", 23 | ) 24 | .arg( 25 | Arg::new("color") 26 | .long("color") 27 | .value_name("WHEN") 28 | .default_value("auto") 29 | .value_parser(["auto", "always", "never"]) 30 | .help("Controls the color output"), 31 | ) 32 | .arg( 33 | Arg::new("socket") 34 | .short('s') 35 | .long("socket") 36 | .value_name("TYPE::SPEC") 37 | .action(ArgAction::Append) 38 | .help( 39 | "This parameter can be supplied multiple times. Each time it's \ 40 | specified a new socket of a certain specification is created.\n\ 41 | In the simplest situation just a port number is given in which \ 42 | case a TCP listening socket at that port is created. To also \ 43 | force a certain network interface the format can be given as \ 44 | HOST:PORT. Additionally a type prefix in the form TYPE::SPEC \ 45 | can be given which picks a different socket type.\n\n\ 46 | The following socket types exist: tcp, http, https, unix, udp.\n\n\ 47 | The http/https sockets are just aliases to tcp that render \ 48 | different help output.", 49 | ), 50 | ) 51 | .arg( 52 | Arg::new("backlog") 53 | .short('b') 54 | .long("backlog") 55 | .default_value("1") 56 | .value_parser(value_parser!(i32).range(1..)) 57 | .value_name("LISTEN-QUEUE") 58 | .help( 59 | "The length of the socket backlog queue in any listen call which \ 60 | must be a positive integer greater than or equal to 1. The OS may \ 61 | silently cap this value to a lower setting.", 62 | ), 63 | ) 64 | .arg( 65 | Arg::new("no_reuse") 66 | .long("no-reuse") 67 | .action(ArgAction::SetTrue) 68 | .help("When disabled SO_REUSEADDR/SO_REUSEPORT are not set on the sockets."), 69 | ) 70 | .arg( 71 | Arg::new("no_pid") 72 | .long("no-pid") 73 | .action(ArgAction::SetTrue) 74 | .help( 75 | "When this is set the LISTEN_PID environment variable is not \ 76 | emitted. This is supported by some systems such as the listenfd \ 77 | crate to skip the pid check. This is necessary for proxying \ 78 | through to other processes like cargo-watch which would break \ 79 | the pid check. This has no effect on windows.", 80 | ), 81 | ) 82 | .arg( 83 | Arg::new("quiet") 84 | .short('q') 85 | .long("quiet") 86 | .action(ArgAction::SetTrue) 87 | .help("Suppress all systemfd output."), 88 | ) 89 | .arg( 90 | Arg::new("command") 91 | .last(true) 92 | .num_args(1..) 93 | .value_parser(value_parser!(OsString)) 94 | .required(true) 95 | .help("The command that should be run"), 96 | ) 97 | } 98 | 99 | pub fn execute() -> Result<(), Error> { 100 | let app = make_app(); 101 | let matches = app.get_matches(); 102 | let quiet = matches.get_flag("quiet"); 103 | 104 | let prefix_style = Style::new().dim().bold(); 105 | let log_style = Style::new().cyan(); 106 | match matches 107 | .get_one::("color") 108 | .as_ref() 109 | .map(|x| x.as_str()) 110 | { 111 | Some("always") => set_colors_enabled(true), 112 | Some("never") => set_colors_enabled(false), 113 | _ => {} 114 | } 115 | 116 | macro_rules! log { 117 | ($($arg:expr),*) => { 118 | if !quiet { 119 | writeln!( 120 | &mut io::stderr(), 121 | "{} {}", 122 | prefix_style.apply_to("~>"), 123 | log_style.apply_to(format_args!($($arg),*)) 124 | ).ok(); 125 | } 126 | } 127 | } 128 | 129 | let mut fds: Vec = Vec::new(); 130 | if let Some(values) = matches.get_many::("socket") { 131 | for socket in values { 132 | fds.push(socket.parse()?); 133 | } 134 | } 135 | 136 | let reuse = !matches.get_flag("no_reuse"); 137 | 138 | let mut raw_fds = vec![]; 139 | if fds.is_empty() { 140 | log!("warning: no sockets created"); 141 | } else { 142 | for fd in fds { 143 | let raw_fd = 144 | fd.create_raw_fd(*matches.get_one("backlog").expect("default value"), reuse)?; 145 | raw_fds.push((fd, raw_fd)); 146 | } 147 | } 148 | 149 | if !quiet { 150 | for &(ref fd, raw_fd) in &raw_fds { 151 | log!("socket {} -> fd #{}", fd.describe_raw_fd(raw_fd)?, raw_fd); 152 | } 153 | } 154 | 155 | let cmdline: Vec<_> = matches.get_many::("command").unwrap().collect(); 156 | spawn::spawn(raw_fds, &cmdline, matches.get_flag("no_pid")) 157 | } 158 | -------------------------------------------------------------------------------- /src/fd.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Display; 2 | use std::net::{Ipv4Addr, SocketAddr}; 3 | use std::path::PathBuf; 4 | use std::str::FromStr; 5 | 6 | use anyhow::bail; 7 | use lazy_static::lazy_static; 8 | use regex::Regex; 9 | 10 | #[cfg(unix)] 11 | pub use std::os::unix::io::RawFd; 12 | #[cfg(windows)] 13 | pub use std::os::windows::io::RawSocket as RawFd; 14 | 15 | lazy_static! { 16 | static ref SPLIT_PREFIX: Regex = Regex::new(r"^([a-zA-Z]+)::(.+)$").unwrap(); 17 | } 18 | 19 | #[derive(Debug)] 20 | pub enum Fd { 21 | HttpListener(SocketAddr, bool), 22 | TcpListener(SocketAddr), 23 | UnixListener(PathBuf), 24 | UdpSocket(SocketAddr), 25 | } 26 | 27 | impl Fd { 28 | /// Creates a new listener from a string. 29 | pub fn new_listener(s: &str) -> Result { 30 | if let Ok(port) = s.parse() { 31 | Ok(Fd::TcpListener(SocketAddr::new( 32 | Ipv4Addr::new(127, 0, 0, 1).into(), 33 | port, 34 | ))) 35 | } else if let Ok(socket_addr) = s.parse() { 36 | Ok(Fd::TcpListener(socket_addr)) 37 | } else if s.contains('/') { 38 | Fd::new_unix_listener(s) 39 | } else { 40 | bail!( 41 | "unsupported specification '{}'. Please provide \ 42 | an explicit socket type", 43 | s 44 | ) 45 | } 46 | } 47 | 48 | /// Creates a new tcp listener from a string. 49 | pub fn new_tcp_listener(s: &str) -> Result { 50 | if let Ok(port) = s.parse() { 51 | Ok(Fd::TcpListener(SocketAddr::new( 52 | Ipv4Addr::new(127, 0, 0, 1).into(), 53 | port, 54 | ))) 55 | } else { 56 | Ok(Fd::TcpListener(s.parse()?)) 57 | } 58 | } 59 | 60 | /// Creates a new http listener from a string. 61 | pub fn new_http_listener(s: &str, secure: bool) -> Result { 62 | if let Ok(port) = s.parse() { 63 | Ok(Fd::HttpListener( 64 | SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), port), 65 | secure, 66 | )) 67 | } else { 68 | Ok(Fd::HttpListener(s.parse()?, secure)) 69 | } 70 | } 71 | 72 | /// Creates a new unix listener from a string. 73 | pub fn new_unix_listener(s: &str) -> Result { 74 | Ok(Fd::UnixListener(PathBuf::from(s))) 75 | } 76 | 77 | /// Creates a new udp socket from a string. 78 | pub fn new_udp_socket(s: &str) -> Result { 79 | if let Ok(port) = s.parse() { 80 | Ok(Fd::UdpSocket(SocketAddr::new( 81 | Ipv4Addr::new(127, 0, 0, 1).into(), 82 | port, 83 | ))) 84 | } else { 85 | Ok(Fd::UdpSocket(s.parse()?)) 86 | } 87 | } 88 | 89 | fn should_listen(&self) -> bool { 90 | match self { 91 | Fd::TcpListener(..) => true, 92 | Fd::UnixListener(..) => true, 93 | Fd::HttpListener(..) => true, 94 | Fd::UdpSocket(..) => false, 95 | } 96 | } 97 | 98 | /// Creates a raw fd from the fd spec. 99 | pub fn create_raw_fd(&self, listen_backlog: i32, reuse: bool) -> Result { 100 | create_raw_fd(self, listen_backlog, reuse) 101 | } 102 | 103 | pub fn describe_raw_fd(&self, raw_fd: RawFd) -> Result { 104 | let addr = describe_addr(raw_fd)?; 105 | Ok(match self { 106 | Fd::TcpListener(..) => format!("{} (tcp listener)", addr), 107 | Fd::HttpListener(_addr, secure) => { 108 | format!("{}://{}/", if *secure { "https" } else { "http" }, addr) 109 | } 110 | Fd::UnixListener(..) => format!("{} (unix listener)", addr), 111 | Fd::UdpSocket(..) => format!("{} (udp)", addr), 112 | }) 113 | } 114 | } 115 | 116 | impl FromStr for Fd { 117 | type Err = anyhow::Error; 118 | 119 | fn from_str(s: &str) -> Result { 120 | let (ty, val) = if let Some(caps) = SPLIT_PREFIX.captures(s) { 121 | ( 122 | Some(caps.get(1).unwrap().as_str()), 123 | caps.get(2).unwrap().as_str(), 124 | ) 125 | } else { 126 | (None, s) 127 | }; 128 | 129 | match ty { 130 | Some("tcp") => Fd::new_tcp_listener(val), 131 | Some("http") => Fd::new_http_listener(val, false), 132 | Some("https") => Fd::new_http_listener(val, true), 133 | Some("unix") => Fd::new_unix_listener(val), 134 | Some("udp") => Fd::new_udp_socket(val), 135 | Some(ty) => bail!("unknown socket type '{}'", ty), 136 | None => Fd::new_listener(val), 137 | } 138 | } 139 | } 140 | 141 | #[cfg(unix)] 142 | mod imp { 143 | use super::*; 144 | use anyhow::Error; 145 | use libc::close; 146 | use nix::sys::socket; 147 | use nix::sys::socket::setsockopt; 148 | use nix::sys::socket::sockopt::ReuseAddr; 149 | use nix::sys::socket::sockopt::ReusePort; 150 | use nix::sys::socket::AddressFamily; 151 | 152 | pub fn create_raw_fd(fd: &Fd, listen_backlog: i32, reuse: bool) -> Result { 153 | let (addr, fam, ty) = sock_info(fd)?; 154 | let sock = socket::socket(fam, ty, socket::SockFlag::empty(), None)?; 155 | 156 | if reuse { 157 | setsockopt(sock, ReuseAddr, &true)?; 158 | 159 | // port reuse will only work on inet sockets. On new linux kernels 160 | // in particular it will fail with an error if attempted on unix sockets 161 | // or others. 162 | if matches!(fam, AddressFamily::Inet | AddressFamily::Inet6) { 163 | setsockopt(sock, ReusePort, &true)?; 164 | } 165 | } 166 | 167 | // kill stale unix sockets if they are there 168 | if let Fd::UnixListener(ref path) = fd { 169 | std::fs::remove_file(path).ok(); 170 | } 171 | 172 | let rv = socket::bind(sock, &*addr) 173 | .map_err(From::from) 174 | .and_then(|_| { 175 | if fd.should_listen() { 176 | socket::listen(sock, listen_backlog as usize)?; 177 | } 178 | Ok(()) 179 | }); 180 | 181 | if rv.is_err() { 182 | unsafe { close(sock) }; 183 | } 184 | 185 | rv.map(|_| sock) 186 | } 187 | 188 | pub fn describe_addr(raw_fd: RawFd) -> Result { 189 | Ok(socket::getsockname::(raw_fd)?) 190 | } 191 | 192 | fn sock_info( 193 | fd: &Fd, 194 | ) -> Result< 195 | ( 196 | Box, 197 | socket::AddressFamily, 198 | socket::SockType, 199 | ), 200 | Error, 201 | > { 202 | Ok(match *fd { 203 | Fd::TcpListener(addr) => ( 204 | Box::new(socket::SockaddrStorage::from(addr)), 205 | if addr.is_ipv4() { 206 | socket::AddressFamily::Inet 207 | } else { 208 | socket::AddressFamily::Inet6 209 | }, 210 | socket::SockType::Stream, 211 | ), 212 | Fd::HttpListener(addr, _secure) => ( 213 | Box::new(socket::SockaddrStorage::from(addr)), 214 | if addr.is_ipv4() { 215 | socket::AddressFamily::Inet 216 | } else { 217 | socket::AddressFamily::Inet6 218 | }, 219 | socket::SockType::Stream, 220 | ), 221 | Fd::UdpSocket(addr) => ( 222 | Box::new(socket::SockaddrStorage::from(addr)), 223 | if addr.is_ipv4() { 224 | socket::AddressFamily::Inet 225 | } else { 226 | socket::AddressFamily::Inet6 227 | }, 228 | socket::SockType::Datagram, 229 | ), 230 | Fd::UnixListener(ref path) => ( 231 | Box::new(socket::UnixAddr::new(path)?), 232 | socket::AddressFamily::Unix, 233 | socket::SockType::Stream, 234 | ), 235 | }) 236 | } 237 | } 238 | 239 | #[cfg(windows)] 240 | mod imp { 241 | use super::*; 242 | use std::mem::ManuallyDrop; 243 | use std::os::windows::io::{FromRawSocket, IntoRawSocket}; 244 | 245 | use anyhow::{bail, Error}; 246 | 247 | pub fn create_raw_fd(fd: &Fd, listen_backlog: i32, reuse: bool) -> Result { 248 | let (addr, dom, ty) = sock_info(fd)?; 249 | let sock = socket2::Socket::new(dom, ty, None)?; 250 | 251 | if reuse { 252 | sock.set_reuse_address(true); 253 | } 254 | 255 | sock.bind(&addr)?; 256 | if fd.should_listen() { 257 | sock.listen(listen_backlog as _)?; 258 | } 259 | 260 | Ok(sock.into_raw_socket()) 261 | } 262 | 263 | pub fn describe_addr(raw_fd: RawFd) -> Result { 264 | let sock = ManuallyDrop::new(unsafe { socket2::Socket::from_raw_socket(raw_fd) }); 265 | let local_addr = sock.local_addr()?; 266 | let rv = local_addr.as_socket().unwrap(); 267 | Ok(rv) 268 | } 269 | 270 | fn sock_info(fd: &Fd) -> Result<(socket2::SockAddr, socket2::Domain, socket2::Type), Error> { 271 | Ok(match *fd { 272 | Fd::TcpListener(addr) => ( 273 | addr.into(), 274 | if addr.is_ipv4() { 275 | socket2::Domain::IPV4 276 | } else { 277 | socket2::Domain::IPV6 278 | }, 279 | socket2::Type::STREAM, 280 | ), 281 | Fd::HttpListener(addr, _secure) => ( 282 | addr.into(), 283 | if addr.is_ipv4() { 284 | socket2::Domain::IPV4 285 | } else { 286 | socket2::Domain::IPV6 287 | }, 288 | socket2::Type::STREAM, 289 | ), 290 | Fd::UdpSocket(addr) => ( 291 | addr.into(), 292 | if addr.is_ipv4() { 293 | socket2::Domain::IPV4 294 | } else { 295 | socket2::Domain::IPV6 296 | }, 297 | socket2::Type::DGRAM, 298 | ), 299 | Fd::UnixListener(..) => { 300 | bail!("Cannot use unix sockets on windows"); 301 | } 302 | }) 303 | } 304 | } 305 | 306 | use self::imp::*; 307 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # This file was autogenerated by dist: https://opensource.axo.dev/cargo-dist/ 2 | # 3 | # Copyright 2022-2024, axodotdev 4 | # SPDX-License-Identifier: MIT or Apache-2.0 5 | # 6 | # CI that: 7 | # 8 | # * checks for a Git Tag that looks like a release 9 | # * builds artifacts with dist (archives, installers, hashes) 10 | # * uploads those artifacts to temporary workflow zip 11 | # * on success, uploads the artifacts to a GitHub Release 12 | # 13 | # Note that the GitHub Release will be created with a generated 14 | # title/body based on your changelogs. 15 | 16 | name: Release 17 | permissions: 18 | "contents": "write" 19 | 20 | # This task will run whenever you push a git tag that looks like a version 21 | # like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc. 22 | # Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where 23 | # PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION 24 | # must be a Cargo-style SemVer Version (must have at least major.minor.patch). 25 | # 26 | # If PACKAGE_NAME is specified, then the announcement will be for that 27 | # package (erroring out if it doesn't have the given version or isn't dist-able). 28 | # 29 | # If PACKAGE_NAME isn't specified, then the announcement will be for all 30 | # (dist-able) packages in the workspace with that version (this mode is 31 | # intended for workspaces with only one dist-able package, or with all dist-able 32 | # packages versioned/released in lockstep). 33 | # 34 | # If you push multiple tags at once, separate instances of this workflow will 35 | # spin up, creating an independent announcement for each one. However, GitHub 36 | # will hard limit this to 3 tags per commit, as it will assume more tags is a 37 | # mistake. 38 | # 39 | # If there's a prerelease-style suffix to the version, then the release(s) 40 | # will be marked as a prerelease. 41 | on: 42 | pull_request: 43 | push: 44 | tags: 45 | - '**[0-9]+.[0-9]+.[0-9]+*' 46 | 47 | jobs: 48 | # Run 'dist plan' (or host) to determine what tasks we need to do 49 | plan: 50 | runs-on: "ubuntu-20.04" 51 | outputs: 52 | val: ${{ steps.plan.outputs.manifest }} 53 | tag: ${{ !github.event.pull_request && github.ref_name || '' }} 54 | tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }} 55 | publishing: ${{ !github.event.pull_request }} 56 | env: 57 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 58 | steps: 59 | - uses: actions/checkout@v4 60 | with: 61 | submodules: recursive 62 | - name: Install dist 63 | # we specify bash to get pipefail; it guards against the `curl` command 64 | # failing. otherwise `sh` won't catch that `curl` returned non-0 65 | shell: bash 66 | run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.28.0/cargo-dist-installer.sh | sh" 67 | - name: Cache dist 68 | uses: actions/upload-artifact@v4 69 | with: 70 | name: cargo-dist-cache 71 | path: ~/.cargo/bin/dist 72 | # sure would be cool if github gave us proper conditionals... 73 | # so here's a doubly-nested ternary-via-truthiness to try to provide the best possible 74 | # functionality based on whether this is a pull_request, and whether it's from a fork. 75 | # (PRs run on the *source* but secrets are usually on the *target* -- that's *good* 76 | # but also really annoying to build CI around when it needs secrets to work right.) 77 | - id: plan 78 | run: | 79 | dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json 80 | echo "dist ran successfully" 81 | cat plan-dist-manifest.json 82 | echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" 83 | - name: "Upload dist-manifest.json" 84 | uses: actions/upload-artifact@v4 85 | with: 86 | name: artifacts-plan-dist-manifest 87 | path: plan-dist-manifest.json 88 | 89 | # Build and packages all the platform-specific things 90 | build-local-artifacts: 91 | name: build-local-artifacts (${{ join(matrix.targets, ', ') }}) 92 | # Let the initial task tell us to not run (currently very blunt) 93 | needs: 94 | - plan 95 | if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }} 96 | strategy: 97 | fail-fast: false 98 | # Target platforms/runners are computed by dist in create-release. 99 | # Each member of the matrix has the following arguments: 100 | # 101 | # - runner: the github runner 102 | # - dist-args: cli flags to pass to dist 103 | # - install-dist: expression to run to install dist on the runner 104 | # 105 | # Typically there will be: 106 | # - 1 "global" task that builds universal installers 107 | # - N "local" tasks that build each platform's binaries and platform-specific installers 108 | matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }} 109 | runs-on: ${{ matrix.runner }} 110 | container: ${{ matrix.container && matrix.container.image || null }} 111 | env: 112 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 113 | BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json 114 | steps: 115 | - name: enable windows longpaths 116 | run: | 117 | git config --global core.longpaths true 118 | - uses: actions/checkout@v4 119 | with: 120 | submodules: recursive 121 | - name: Install Rust non-interactively if not already installed 122 | if: ${{ matrix.container }} 123 | run: | 124 | if ! command -v cargo > /dev/null 2>&1; then 125 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 126 | echo "$HOME/.cargo/bin" >> $GITHUB_PATH 127 | fi 128 | - name: Install dist 129 | run: ${{ matrix.install_dist.run }} 130 | # Get the dist-manifest 131 | - name: Fetch local artifacts 132 | uses: actions/download-artifact@v4 133 | with: 134 | pattern: artifacts-* 135 | path: target/distrib/ 136 | merge-multiple: true 137 | - name: Install dependencies 138 | run: | 139 | ${{ matrix.packages_install }} 140 | - name: Build artifacts 141 | run: | 142 | # Actually do builds and make zips and whatnot 143 | dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json 144 | echo "dist ran successfully" 145 | - id: cargo-dist 146 | name: Post-build 147 | # We force bash here just because github makes it really hard to get values up 148 | # to "real" actions without writing to env-vars, and writing to env-vars has 149 | # inconsistent syntax between shell and powershell. 150 | shell: bash 151 | run: | 152 | # Parse out what we just built and upload it to scratch storage 153 | echo "paths<> "$GITHUB_OUTPUT" 154 | dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT" 155 | echo "EOF" >> "$GITHUB_OUTPUT" 156 | 157 | cp dist-manifest.json "$BUILD_MANIFEST_NAME" 158 | - name: "Upload artifacts" 159 | uses: actions/upload-artifact@v4 160 | with: 161 | name: artifacts-build-local-${{ join(matrix.targets, '_') }} 162 | path: | 163 | ${{ steps.cargo-dist.outputs.paths }} 164 | ${{ env.BUILD_MANIFEST_NAME }} 165 | 166 | # Build and package all the platform-agnostic(ish) things 167 | build-global-artifacts: 168 | needs: 169 | - plan 170 | - build-local-artifacts 171 | runs-on: "ubuntu-20.04" 172 | env: 173 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 174 | BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json 175 | steps: 176 | - uses: actions/checkout@v4 177 | with: 178 | submodules: recursive 179 | - name: Install cached dist 180 | uses: actions/download-artifact@v4 181 | with: 182 | name: cargo-dist-cache 183 | path: ~/.cargo/bin/ 184 | - run: chmod +x ~/.cargo/bin/dist 185 | # Get all the local artifacts for the global tasks to use (for e.g. checksums) 186 | - name: Fetch local artifacts 187 | uses: actions/download-artifact@v4 188 | with: 189 | pattern: artifacts-* 190 | path: target/distrib/ 191 | merge-multiple: true 192 | - id: cargo-dist 193 | shell: bash 194 | run: | 195 | dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json 196 | echo "dist ran successfully" 197 | 198 | # Parse out what we just built and upload it to scratch storage 199 | echo "paths<> "$GITHUB_OUTPUT" 200 | jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT" 201 | echo "EOF" >> "$GITHUB_OUTPUT" 202 | 203 | cp dist-manifest.json "$BUILD_MANIFEST_NAME" 204 | - name: "Upload artifacts" 205 | uses: actions/upload-artifact@v4 206 | with: 207 | name: artifacts-build-global 208 | path: | 209 | ${{ steps.cargo-dist.outputs.paths }} 210 | ${{ env.BUILD_MANIFEST_NAME }} 211 | # Determines if we should publish/announce 212 | host: 213 | needs: 214 | - plan 215 | - build-local-artifacts 216 | - build-global-artifacts 217 | # Only run if we're "publishing", and only if local and global didn't fail (skipped is fine) 218 | if: ${{ always() && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} 219 | env: 220 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 221 | runs-on: "ubuntu-20.04" 222 | outputs: 223 | val: ${{ steps.host.outputs.manifest }} 224 | steps: 225 | - uses: actions/checkout@v4 226 | with: 227 | submodules: recursive 228 | - name: Install cached dist 229 | uses: actions/download-artifact@v4 230 | with: 231 | name: cargo-dist-cache 232 | path: ~/.cargo/bin/ 233 | - run: chmod +x ~/.cargo/bin/dist 234 | # Fetch artifacts from scratch-storage 235 | - name: Fetch artifacts 236 | uses: actions/download-artifact@v4 237 | with: 238 | pattern: artifacts-* 239 | path: target/distrib/ 240 | merge-multiple: true 241 | - id: host 242 | shell: bash 243 | run: | 244 | dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json 245 | echo "artifacts uploaded and released successfully" 246 | cat dist-manifest.json 247 | echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" 248 | - name: "Upload dist-manifest.json" 249 | uses: actions/upload-artifact@v4 250 | with: 251 | # Overwrite the previous copy 252 | name: artifacts-dist-manifest 253 | path: dist-manifest.json 254 | # Create a GitHub Release while uploading all files to it 255 | - name: "Download GitHub Artifacts" 256 | uses: actions/download-artifact@v4 257 | with: 258 | pattern: artifacts-* 259 | path: artifacts 260 | merge-multiple: true 261 | - name: Cleanup 262 | run: | 263 | # Remove the granular manifests 264 | rm -f artifacts/*-dist-manifest.json 265 | - name: Create GitHub Release 266 | env: 267 | PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}" 268 | ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}" 269 | ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}" 270 | RELEASE_COMMIT: "${{ github.sha }}" 271 | run: | 272 | # Write and read notes from a file to avoid quoting breaking things 273 | echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt 274 | 275 | gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* 276 | 277 | announce: 278 | needs: 279 | - plan 280 | - host 281 | # use "always() && ..." to allow us to wait for all publish jobs while 282 | # still allowing individual publish jobs to skip themselves (for prereleases). 283 | # "host" however must run to completion, no skipping allowed! 284 | if: ${{ always() && needs.host.result == 'success' }} 285 | runs-on: "ubuntu-20.04" 286 | env: 287 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 288 | steps: 289 | - uses: actions/checkout@v4 290 | with: 291 | submodules: recursive 292 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.0.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "anstyle" 16 | version = "1.0.1" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" 19 | 20 | [[package]] 21 | name = "anyhow" 22 | version = "1.0.52" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3" 25 | 26 | [[package]] 27 | name = "autocfg" 28 | version = "1.1.0" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 31 | 32 | [[package]] 33 | name = "bitflags" 34 | version = "1.3.2" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 37 | 38 | [[package]] 39 | name = "cfg-if" 40 | version = "1.0.0" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 43 | 44 | [[package]] 45 | name = "clap" 46 | version = "4.3.11" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d" 49 | dependencies = [ 50 | "clap_builder", 51 | ] 52 | 53 | [[package]] 54 | name = "clap_builder" 55 | version = "4.3.11" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b" 58 | dependencies = [ 59 | "anstyle", 60 | "clap_lex", 61 | "terminal_size", 62 | ] 63 | 64 | [[package]] 65 | name = "clap_lex" 66 | version = "0.5.0" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" 69 | 70 | [[package]] 71 | name = "console" 72 | version = "0.15.8" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" 75 | dependencies = [ 76 | "encode_unicode", 77 | "lazy_static", 78 | "libc", 79 | "unicode-width", 80 | "windows-sys 0.52.0", 81 | ] 82 | 83 | [[package]] 84 | name = "encode_unicode" 85 | version = "0.3.6" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" 88 | 89 | [[package]] 90 | name = "errno" 91 | version = "0.3.9" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" 94 | dependencies = [ 95 | "libc", 96 | "windows-sys 0.52.0", 97 | ] 98 | 99 | [[package]] 100 | name = "getrandom" 101 | version = "0.2.4" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" 104 | dependencies = [ 105 | "cfg-if", 106 | "libc", 107 | "wasi", 108 | ] 109 | 110 | [[package]] 111 | name = "hermit-abi" 112 | version = "0.3.9" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 115 | 116 | [[package]] 117 | name = "io-lifetimes" 118 | version = "1.0.11" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" 121 | dependencies = [ 122 | "hermit-abi", 123 | "libc", 124 | "windows-sys 0.48.0", 125 | ] 126 | 127 | [[package]] 128 | name = "lazy_static" 129 | version = "1.4.0" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 132 | 133 | [[package]] 134 | name = "libc" 135 | version = "0.2.158" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" 138 | 139 | [[package]] 140 | name = "linux-raw-sys" 141 | version = "0.3.8" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" 144 | 145 | [[package]] 146 | name = "memchr" 147 | version = "2.5.0" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 150 | 151 | [[package]] 152 | name = "memoffset" 153 | version = "0.7.1" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" 156 | dependencies = [ 157 | "autocfg", 158 | ] 159 | 160 | [[package]] 161 | name = "nix" 162 | version = "0.26.2" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" 165 | dependencies = [ 166 | "bitflags", 167 | "cfg-if", 168 | "libc", 169 | "memoffset", 170 | "static_assertions", 171 | ] 172 | 173 | [[package]] 174 | name = "regex" 175 | version = "1.9.1" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" 178 | dependencies = [ 179 | "aho-corasick", 180 | "memchr", 181 | "regex-automata", 182 | "regex-syntax", 183 | ] 184 | 185 | [[package]] 186 | name = "regex-automata" 187 | version = "0.3.2" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf" 190 | dependencies = [ 191 | "aho-corasick", 192 | "memchr", 193 | "regex-syntax", 194 | ] 195 | 196 | [[package]] 197 | name = "regex-syntax" 198 | version = "0.7.3" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846" 201 | 202 | [[package]] 203 | name = "rustix" 204 | version = "0.37.27" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" 207 | dependencies = [ 208 | "bitflags", 209 | "errno", 210 | "io-lifetimes", 211 | "libc", 212 | "linux-raw-sys", 213 | "windows-sys 0.48.0", 214 | ] 215 | 216 | [[package]] 217 | name = "socket2" 218 | version = "0.5.7" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" 221 | dependencies = [ 222 | "libc", 223 | "windows-sys 0.52.0", 224 | ] 225 | 226 | [[package]] 227 | name = "static_assertions" 228 | version = "1.1.0" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 231 | 232 | [[package]] 233 | name = "systemfd" 234 | version = "0.4.6" 235 | dependencies = [ 236 | "anyhow", 237 | "clap", 238 | "console", 239 | "lazy_static", 240 | "libc", 241 | "nix", 242 | "regex", 243 | "socket2", 244 | "uuid", 245 | "windows-sys 0.52.0", 246 | ] 247 | 248 | [[package]] 249 | name = "terminal_size" 250 | version = "0.2.6" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237" 253 | dependencies = [ 254 | "rustix", 255 | "windows-sys 0.48.0", 256 | ] 257 | 258 | [[package]] 259 | name = "unicode-width" 260 | version = "0.1.9" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" 263 | 264 | [[package]] 265 | name = "uuid" 266 | version = "1.4.0" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be" 269 | dependencies = [ 270 | "getrandom", 271 | ] 272 | 273 | [[package]] 274 | name = "wasi" 275 | version = "0.10.2+wasi-snapshot-preview1" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 278 | 279 | [[package]] 280 | name = "windows-sys" 281 | version = "0.48.0" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 284 | dependencies = [ 285 | "windows-targets 0.48.5", 286 | ] 287 | 288 | [[package]] 289 | name = "windows-sys" 290 | version = "0.52.0" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 293 | dependencies = [ 294 | "windows-targets 0.52.6", 295 | ] 296 | 297 | [[package]] 298 | name = "windows-targets" 299 | version = "0.48.5" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 302 | dependencies = [ 303 | "windows_aarch64_gnullvm 0.48.5", 304 | "windows_aarch64_msvc 0.48.5", 305 | "windows_i686_gnu 0.48.5", 306 | "windows_i686_msvc 0.48.5", 307 | "windows_x86_64_gnu 0.48.5", 308 | "windows_x86_64_gnullvm 0.48.5", 309 | "windows_x86_64_msvc 0.48.5", 310 | ] 311 | 312 | [[package]] 313 | name = "windows-targets" 314 | version = "0.52.6" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 317 | dependencies = [ 318 | "windows_aarch64_gnullvm 0.52.6", 319 | "windows_aarch64_msvc 0.52.6", 320 | "windows_i686_gnu 0.52.6", 321 | "windows_i686_gnullvm", 322 | "windows_i686_msvc 0.52.6", 323 | "windows_x86_64_gnu 0.52.6", 324 | "windows_x86_64_gnullvm 0.52.6", 325 | "windows_x86_64_msvc 0.52.6", 326 | ] 327 | 328 | [[package]] 329 | name = "windows_aarch64_gnullvm" 330 | version = "0.48.5" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 333 | 334 | [[package]] 335 | name = "windows_aarch64_gnullvm" 336 | version = "0.52.6" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 339 | 340 | [[package]] 341 | name = "windows_aarch64_msvc" 342 | version = "0.48.5" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 345 | 346 | [[package]] 347 | name = "windows_aarch64_msvc" 348 | version = "0.52.6" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 351 | 352 | [[package]] 353 | name = "windows_i686_gnu" 354 | version = "0.48.5" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 357 | 358 | [[package]] 359 | name = "windows_i686_gnu" 360 | version = "0.52.6" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 363 | 364 | [[package]] 365 | name = "windows_i686_gnullvm" 366 | version = "0.52.6" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 369 | 370 | [[package]] 371 | name = "windows_i686_msvc" 372 | version = "0.48.5" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 375 | 376 | [[package]] 377 | name = "windows_i686_msvc" 378 | version = "0.52.6" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 381 | 382 | [[package]] 383 | name = "windows_x86_64_gnu" 384 | version = "0.48.5" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 387 | 388 | [[package]] 389 | name = "windows_x86_64_gnu" 390 | version = "0.52.6" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 393 | 394 | [[package]] 395 | name = "windows_x86_64_gnullvm" 396 | version = "0.48.5" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 399 | 400 | [[package]] 401 | name = "windows_x86_64_gnullvm" 402 | version = "0.52.6" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 405 | 406 | [[package]] 407 | name = "windows_x86_64_msvc" 408 | version = "0.48.5" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 411 | 412 | [[package]] 413 | name = "windows_x86_64_msvc" 414 | version = "0.52.6" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 417 | -------------------------------------------------------------------------------- /demo/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.20.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "axum" 22 | version = "0.8.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8" 25 | dependencies = [ 26 | "axum-core", 27 | "bytes", 28 | "form_urlencoded", 29 | "futures-util", 30 | "http", 31 | "http-body", 32 | "http-body-util", 33 | "hyper", 34 | "hyper-util", 35 | "itoa 1.0.14", 36 | "matchit", 37 | "memchr", 38 | "mime", 39 | "percent-encoding", 40 | "pin-project-lite", 41 | "rustversion", 42 | "serde", 43 | "serde_json", 44 | "serde_path_to_error", 45 | "serde_urlencoded", 46 | "sync_wrapper", 47 | "tokio", 48 | "tower", 49 | "tower-layer", 50 | "tower-service", 51 | "tracing", 52 | ] 53 | 54 | [[package]] 55 | name = "axum-core" 56 | version = "0.5.0" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733" 59 | dependencies = [ 60 | "bytes", 61 | "futures-util", 62 | "http", 63 | "http-body", 64 | "http-body-util", 65 | "mime", 66 | "pin-project-lite", 67 | "rustversion", 68 | "sync_wrapper", 69 | "tower-layer", 70 | "tower-service", 71 | "tracing", 72 | ] 73 | 74 | [[package]] 75 | name = "backtrace" 76 | version = "0.3.68" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" 79 | dependencies = [ 80 | "addr2line", 81 | "cc", 82 | "cfg-if", 83 | "libc", 84 | "miniz_oxide", 85 | "object", 86 | "rustc-demangle", 87 | ] 88 | 89 | [[package]] 90 | name = "bytes" 91 | version = "1.9.0" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" 94 | 95 | [[package]] 96 | name = "cc" 97 | version = "1.0.79" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" 100 | 101 | [[package]] 102 | name = "cfg-if" 103 | version = "1.0.0" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 106 | 107 | [[package]] 108 | name = "demo" 109 | version = "0.1.0" 110 | dependencies = [ 111 | "axum", 112 | "listenfd", 113 | "tokio", 114 | ] 115 | 116 | [[package]] 117 | name = "fnv" 118 | version = "1.0.7" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 121 | 122 | [[package]] 123 | name = "form_urlencoded" 124 | version = "1.2.1" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 127 | dependencies = [ 128 | "percent-encoding", 129 | ] 130 | 131 | [[package]] 132 | name = "futures-channel" 133 | version = "0.3.19" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b" 136 | dependencies = [ 137 | "futures-core", 138 | ] 139 | 140 | [[package]] 141 | name = "futures-core" 142 | version = "0.3.31" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 145 | 146 | [[package]] 147 | name = "futures-task" 148 | version = "0.3.31" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 151 | 152 | [[package]] 153 | name = "futures-util" 154 | version = "0.3.31" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 157 | dependencies = [ 158 | "futures-core", 159 | "futures-task", 160 | "pin-project-lite", 161 | "pin-utils", 162 | ] 163 | 164 | [[package]] 165 | name = "gimli" 166 | version = "0.27.3" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" 169 | 170 | [[package]] 171 | name = "http" 172 | version = "1.2.0" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" 175 | dependencies = [ 176 | "bytes", 177 | "fnv", 178 | "itoa 1.0.14", 179 | ] 180 | 181 | [[package]] 182 | name = "http-body" 183 | version = "1.0.1" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 186 | dependencies = [ 187 | "bytes", 188 | "http", 189 | ] 190 | 191 | [[package]] 192 | name = "http-body-util" 193 | version = "0.1.2" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" 196 | dependencies = [ 197 | "bytes", 198 | "futures-util", 199 | "http", 200 | "http-body", 201 | "pin-project-lite", 202 | ] 203 | 204 | [[package]] 205 | name = "httparse" 206 | version = "1.8.0" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" 209 | 210 | [[package]] 211 | name = "httpdate" 212 | version = "1.0.2" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 215 | 216 | [[package]] 217 | name = "hyper" 218 | version = "1.5.2" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" 221 | dependencies = [ 222 | "bytes", 223 | "futures-channel", 224 | "futures-util", 225 | "http", 226 | "http-body", 227 | "httparse", 228 | "httpdate", 229 | "itoa 1.0.14", 230 | "pin-project-lite", 231 | "smallvec", 232 | "tokio", 233 | ] 234 | 235 | [[package]] 236 | name = "hyper-util" 237 | version = "0.1.10" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" 240 | dependencies = [ 241 | "bytes", 242 | "futures-util", 243 | "http", 244 | "http-body", 245 | "hyper", 246 | "pin-project-lite", 247 | "tokio", 248 | "tower-service", 249 | ] 250 | 251 | [[package]] 252 | name = "itoa" 253 | version = "0.4.8" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" 256 | 257 | [[package]] 258 | name = "itoa" 259 | version = "1.0.14" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" 262 | 263 | [[package]] 264 | name = "libc" 265 | version = "0.2.169" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" 268 | 269 | [[package]] 270 | name = "listenfd" 271 | version = "1.0.2" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "b87bc54a4629b4294d0b3ef041b64c40c611097a677d9dc07b2c67739fe39dba" 274 | dependencies = [ 275 | "libc", 276 | "uuid", 277 | "winapi", 278 | ] 279 | 280 | [[package]] 281 | name = "log" 282 | version = "0.4.25" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" 285 | 286 | [[package]] 287 | name = "matchit" 288 | version = "0.8.4" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" 291 | 292 | [[package]] 293 | name = "memchr" 294 | version = "2.4.1" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 297 | 298 | [[package]] 299 | name = "mime" 300 | version = "0.3.16" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 303 | 304 | [[package]] 305 | name = "miniz_oxide" 306 | version = "0.7.1" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" 309 | dependencies = [ 310 | "adler", 311 | ] 312 | 313 | [[package]] 314 | name = "mio" 315 | version = "1.0.3" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 318 | dependencies = [ 319 | "libc", 320 | "wasi", 321 | "windows-sys", 322 | ] 323 | 324 | [[package]] 325 | name = "object" 326 | version = "0.31.1" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" 329 | dependencies = [ 330 | "memchr", 331 | ] 332 | 333 | [[package]] 334 | name = "once_cell" 335 | version = "1.20.2" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 338 | 339 | [[package]] 340 | name = "percent-encoding" 341 | version = "2.3.1" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 344 | 345 | [[package]] 346 | name = "pin-project-lite" 347 | version = "0.2.16" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 350 | 351 | [[package]] 352 | name = "pin-utils" 353 | version = "0.1.0" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 356 | 357 | [[package]] 358 | name = "proc-macro2" 359 | version = "1.0.93" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" 362 | dependencies = [ 363 | "unicode-ident", 364 | ] 365 | 366 | [[package]] 367 | name = "quote" 368 | version = "1.0.38" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" 371 | dependencies = [ 372 | "proc-macro2", 373 | ] 374 | 375 | [[package]] 376 | name = "rustc-demangle" 377 | version = "0.1.23" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 380 | 381 | [[package]] 382 | name = "rustversion" 383 | version = "1.0.19" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" 386 | 387 | [[package]] 388 | name = "ryu" 389 | version = "1.0.9" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" 392 | 393 | [[package]] 394 | name = "serde" 395 | version = "1.0.217" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" 398 | dependencies = [ 399 | "serde_derive", 400 | ] 401 | 402 | [[package]] 403 | name = "serde_derive" 404 | version = "1.0.217" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" 407 | dependencies = [ 408 | "proc-macro2", 409 | "quote", 410 | "syn", 411 | ] 412 | 413 | [[package]] 414 | name = "serde_json" 415 | version = "1.0.74" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "ee2bb9cd061c5865d345bb02ca49fcef1391741b672b54a0bf7b679badec3142" 418 | dependencies = [ 419 | "itoa 1.0.14", 420 | "ryu", 421 | "serde", 422 | ] 423 | 424 | [[package]] 425 | name = "serde_path_to_error" 426 | version = "0.1.16" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" 429 | dependencies = [ 430 | "itoa 1.0.14", 431 | "serde", 432 | ] 433 | 434 | [[package]] 435 | name = "serde_urlencoded" 436 | version = "0.7.0" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" 439 | dependencies = [ 440 | "form_urlencoded", 441 | "itoa 0.4.8", 442 | "ryu", 443 | "serde", 444 | ] 445 | 446 | [[package]] 447 | name = "smallvec" 448 | version = "1.13.2" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 451 | 452 | [[package]] 453 | name = "socket2" 454 | version = "0.5.8" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" 457 | dependencies = [ 458 | "libc", 459 | "windows-sys", 460 | ] 461 | 462 | [[package]] 463 | name = "syn" 464 | version = "2.0.96" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" 467 | dependencies = [ 468 | "proc-macro2", 469 | "quote", 470 | "unicode-ident", 471 | ] 472 | 473 | [[package]] 474 | name = "sync_wrapper" 475 | version = "1.0.2" 476 | source = "registry+https://github.com/rust-lang/crates.io-index" 477 | checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 478 | 479 | [[package]] 480 | name = "tokio" 481 | version = "1.43.0" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" 484 | dependencies = [ 485 | "backtrace", 486 | "libc", 487 | "mio", 488 | "pin-project-lite", 489 | "socket2", 490 | "tokio-macros", 491 | "windows-sys", 492 | ] 493 | 494 | [[package]] 495 | name = "tokio-macros" 496 | version = "2.5.0" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 499 | dependencies = [ 500 | "proc-macro2", 501 | "quote", 502 | "syn", 503 | ] 504 | 505 | [[package]] 506 | name = "tower" 507 | version = "0.5.2" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" 510 | dependencies = [ 511 | "futures-core", 512 | "futures-util", 513 | "pin-project-lite", 514 | "sync_wrapper", 515 | "tokio", 516 | "tower-layer", 517 | "tower-service", 518 | "tracing", 519 | ] 520 | 521 | [[package]] 522 | name = "tower-layer" 523 | version = "0.3.3" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 526 | 527 | [[package]] 528 | name = "tower-service" 529 | version = "0.3.3" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 532 | 533 | [[package]] 534 | name = "tracing" 535 | version = "0.1.41" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 538 | dependencies = [ 539 | "log", 540 | "pin-project-lite", 541 | "tracing-core", 542 | ] 543 | 544 | [[package]] 545 | name = "tracing-core" 546 | version = "0.1.33" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 549 | dependencies = [ 550 | "once_cell", 551 | ] 552 | 553 | [[package]] 554 | name = "unicode-ident" 555 | version = "1.0.10" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" 558 | 559 | [[package]] 560 | name = "uuid" 561 | version = "1.4.0" 562 | source = "registry+https://github.com/rust-lang/crates.io-index" 563 | checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be" 564 | 565 | [[package]] 566 | name = "wasi" 567 | version = "0.11.0+wasi-snapshot-preview1" 568 | source = "registry+https://github.com/rust-lang/crates.io-index" 569 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 570 | 571 | [[package]] 572 | name = "winapi" 573 | version = "0.3.9" 574 | source = "registry+https://github.com/rust-lang/crates.io-index" 575 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 576 | dependencies = [ 577 | "winapi-i686-pc-windows-gnu", 578 | "winapi-x86_64-pc-windows-gnu", 579 | ] 580 | 581 | [[package]] 582 | name = "winapi-i686-pc-windows-gnu" 583 | version = "0.4.0" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 586 | 587 | [[package]] 588 | name = "winapi-x86_64-pc-windows-gnu" 589 | version = "0.4.0" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 592 | 593 | [[package]] 594 | name = "windows-sys" 595 | version = "0.52.0" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 598 | dependencies = [ 599 | "windows-targets", 600 | ] 601 | 602 | [[package]] 603 | name = "windows-targets" 604 | version = "0.52.6" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 607 | dependencies = [ 608 | "windows_aarch64_gnullvm", 609 | "windows_aarch64_msvc", 610 | "windows_i686_gnu", 611 | "windows_i686_gnullvm", 612 | "windows_i686_msvc", 613 | "windows_x86_64_gnu", 614 | "windows_x86_64_gnullvm", 615 | "windows_x86_64_msvc", 616 | ] 617 | 618 | [[package]] 619 | name = "windows_aarch64_gnullvm" 620 | version = "0.52.6" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 623 | 624 | [[package]] 625 | name = "windows_aarch64_msvc" 626 | version = "0.52.6" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 629 | 630 | [[package]] 631 | name = "windows_i686_gnu" 632 | version = "0.52.6" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 635 | 636 | [[package]] 637 | name = "windows_i686_gnullvm" 638 | version = "0.52.6" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 641 | 642 | [[package]] 643 | name = "windows_i686_msvc" 644 | version = "0.52.6" 645 | source = "registry+https://github.com/rust-lang/crates.io-index" 646 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 647 | 648 | [[package]] 649 | name = "windows_x86_64_gnu" 650 | version = "0.52.6" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 653 | 654 | [[package]] 655 | name = "windows_x86_64_gnullvm" 656 | version = "0.52.6" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 659 | 660 | [[package]] 661 | name = "windows_x86_64_msvc" 662 | version = "0.52.6" 663 | source = "registry+https://github.com/rust-lang/crates.io-index" 664 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 665 | --------------------------------------------------------------------------------