mod dir_mode;
mod file_mode;
mod http;
use crate::Options;
use std::net::{TcpListener, TcpStream};
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use std::thread;
pub enum ServeTarget {
File(PathBuf),
Dir(PathBuf),
}
pub(crate) struct State {
pub target: ServeTarget,
pub opts: Options,
pub generation: Mutex<u64>,
}
pub fn serve(target: ServeTarget, port: u16, opts: Options) -> std::io::Result<()> {
let state = Arc::new(State {
target,
opts,
generation: Mutex::new(0),
});
if let ServeTarget::File(_) = state.target {
let watcher = state.clone();
thread::spawn(move || file_mode::watch(watcher));
}
let addr = format!("127.0.0.1:{port}");
let listener = TcpListener::bind(&addr)?;
match &state.target {
ServeTarget::File(f) => {
eprintln!(
"lini serve: {} → http://{addr}/ (Ctrl-C to stop)",
f.display()
)
}
ServeTarget::Dir(d) => eprintln!(
"lini playground: {} → http://{addr}/ (Ctrl-C to stop)",
d.display()
),
}
for stream in listener.incoming() {
let stream = stream?;
let state = state.clone();
thread::spawn(move || {
if let Err(e) = handle(stream, &state) {
if e.kind() != std::io::ErrorKind::BrokenPipe
&& e.kind() != std::io::ErrorKind::ConnectionReset
{
eprintln!("conn: {e}");
}
}
});
}
Ok(())
}
fn handle(mut stream: TcpStream, state: &State) -> std::io::Result<()> {
let req = http::read_request(&mut stream)?;
match state.target {
ServeTarget::File(_) => file_mode::handle(&mut stream, &req, state),
ServeTarget::Dir(_) => dir_mode::handle(&mut stream, &req, state),
}
}