oxedyne/fe2o3/fe2o3_net/src/bin/localserve.rs
1.9 KiB, 1 run
created by r1870400018:58835, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | //! `localserve` -- a std-only static file server for a directory, from the command line. |
| 2 | //! |
| 3 | //! The tokio-free [`LocalServer`](oxedyne_fe2o3_net::http::local::LocalServer) as a standalone tool: the |
| 4 | //! drop-in replacement for `python3 -m http.server` a dev-preview loop reaches for. It has no |
| 5 | //! required-features, so it builds under `--no-default-features` -- the whole point being a static server |
| 6 | //! that pulls in neither an async runtime nor a TLS stack. |
| 7 | //! |
| 8 | //! ```text |
| 9 | //! localserve <DIR> [--port N] [--lan] |
| 10 | //! ``` |
| 11 | //! |
| 12 | //! Loopback (`127.0.0.1`) by default; `--lan` binds `0.0.0.0` so a phone on the same network can reach |
| 13 | //! the preview. Port `0` (the default) asks the OS for an ephemeral one and prints what it chose. |
| 14 | |
| 15 | use oxedyne_fe2o3_net::http::local::{ |
| 16 | Listen, |
| 17 | LocalServer, |
| 18 | }; |
| 19 | |
| 20 | use oxedyne_fe2o3_core::prelude::*; |
| 21 | |
| 22 | fn main() -> Outcome<()> { |
| 23 | let args: Vec<String> = std::env::args().skip(1).collect(); |
| 24 | let dir = match args.first().filter(|a| !a.starts_with("--")) { |
| 25 | Some(d) => d.clone(), |
| 26 | None => { |
| 27 | println!("usage: localserve <DIR> [--port N] [--lan]"); |
| 28 | return Ok(()); |
| 29 | }, |
| 30 | }; |
| 31 | let port = match flag_value(&args, "--port") { |
| 32 | Some(s) => match s.parse::<u16>() { |
| 33 | Ok(v) => v, |
| 34 | Err(e) => return Err(err!("'{}' is not a valid --port value: {}.", s, e; Input, Invalid)), |
| 35 | }, |
| 36 | None => 0, |
| 37 | }; |
| 38 | let lan = has_flag(&args, "--lan"); |
| 39 | let listen = if lan { Listen::Any(port) } else { Listen::Loopback(port) }; |
| 40 | |
| 41 | let listener = res!(LocalServer::bind(listen)); |
| 42 | let bound = res!(listener.local_addr(), IO, Network); |
| 43 | let host = if lan { "0.0.0.0" } else { "127.0.0.1" }; |
| 44 | println!("localserve: serving {} at http://{}:{}/", dir, host, bound.port()); |
| 45 | |
| 46 | LocalServer::dir(dir).serve(listener) |
| 47 | } |
| 48 | |
| 49 | fn flag_value(args: &[String], name: &str) -> Option<String> { |
| 50 | args.iter().position(|a| a == name).and_then(|i| args.get(i + 1)).cloned() |
| 51 | } |
| 52 | |
| 53 | fn has_flag(args: &[String], name: &str) -> bool { |
| 54 | args.iter().any(|a| a == name) |
| 55 | } |