init
This commit is contained in:
141
src/main.rs
Normal file
141
src/main.rs
Normal file
@@ -0,0 +1,141 @@
|
||||
//! # mharkup
|
||||
//!
|
||||
//! another markup language
|
||||
//!
|
||||
//! ## sections
|
||||
//!
|
||||
//! Sections are delimited by double newlines (\n\n).
|
||||
//! A double newline can be included in any section by surrounding
|
||||
//! the section with at least one set of square brackets.
|
||||
//!
|
||||
//! ## input
|
||||
//!
|
||||
//! Must be utf-8 and must end with a linebreak.
|
||||
//! Linebreaks are \n (no \r).
|
||||
|
||||
use std::{
|
||||
io::{Read, Write},
|
||||
net::Shutdown,
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
use crate::{doc::Document, parse::parse};
|
||||
|
||||
mod doc;
|
||||
mod parse;
|
||||
mod to;
|
||||
|
||||
fn main() {
|
||||
let args = std::env::args().skip(1).collect::<Vec<_>>();
|
||||
if args.len() < 2 {
|
||||
eprintln!("Usage: <filename>.mharkup <format> [specs...]");
|
||||
eprintln!("output formats and (available specs)");
|
||||
eprintln!("` html: a static html+css document (html, texm)");
|
||||
eprintln!("` html-http: html with auto-refresh");
|
||||
return;
|
||||
}
|
||||
let specs = &args[2..];
|
||||
let src = || std::fs::read_to_string(&args[0]).unwrap();
|
||||
match args[1].to_lowercase().trim() {
|
||||
"html" => println!(
|
||||
"{}",
|
||||
to::html::page(&parse(&src()).unwrap(), specs, &mut to::html::State::new())
|
||||
),
|
||||
"html-http" => {
|
||||
let addr = std::env::var("ADDR");
|
||||
let addr = addr.as_ref().map_or("localhost:8000", |v| v.as_str());
|
||||
let wait = std::env::var("WAIT")
|
||||
.ok()
|
||||
.and_then(|v| v.trim().parse().ok())
|
||||
.unwrap_or(100);
|
||||
eprintln!();
|
||||
eprintln!("Binding to {addr} (ADDR env var)");
|
||||
eprintln!("Checking file mtime every {wait}ms while handling a request (WAIT env var)");
|
||||
let listener = std::net::TcpListener::bind(addr).unwrap();
|
||||
eprintln!();
|
||||
eprintln!("With your browser, open http://{addr}/");
|
||||
eprintln!("Without javascript, use http://{addr}/s");
|
||||
eprintln!("For a static page, open http://{addr}/1");
|
||||
let mut ptime = SystemTime::UNIX_EPOCH + Duration::from_mins(9);
|
||||
loop {
|
||||
let mut state = to::html::State::new();
|
||||
let mut buf1 = vec![String::new(); 1024];
|
||||
let mut buf2 = vec![None; 1024];
|
||||
let mut i = 0;
|
||||
// connection might time out after 30 seconds,
|
||||
// so send a 304 before that can happen.
|
||||
let maxchecks = 25 * 1000 / wait;
|
||||
'accept_connection: while i < 1024 {
|
||||
let (mut connection, _) = listener.accept().unwrap();
|
||||
let mut buf = [0u8; 8];
|
||||
if connection.read_exact(&mut buf).is_err() {
|
||||
continue;
|
||||
}
|
||||
let allow_delay = if let Ok(req) = str::from_utf8(&buf) {
|
||||
if req.contains(" /u") {
|
||||
state.head_to_body = "</head>\n<body>";
|
||||
true
|
||||
} else if req.contains(" /1") {
|
||||
state.head_to_body = "</head>\n<body>";
|
||||
false
|
||||
} else if req.contains(" /s") {
|
||||
state.head_to_body =
|
||||
"<meta http-equiv=\"refresh\" content=\"1\">\n</head>\n<body>";
|
||||
true
|
||||
} else {
|
||||
state.head_to_body = r#"<script>
|
||||
const BODY_REGEX = new RegExp("<bo"+"dy>.*<\\/bo"+"dy>", "misv");
|
||||
(async () => { while (true) {
|
||||
let res = await fetch("/u");
|
||||
if (!res.ok) continue;
|
||||
let str = (await res.text()).match(BODY_REGEX)[0];
|
||||
document.body.outerHTML = str;
|
||||
} })();
|
||||
</script>
|
||||
</head>
|
||||
<body>"#;
|
||||
false
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
if allow_delay {
|
||||
'delay_connection: {
|
||||
for _ in 0..maxchecks {
|
||||
let mtime =
|
||||
std::fs::metadata(&args[0]).unwrap().modified().unwrap();
|
||||
if mtime != ptime {
|
||||
ptime = mtime;
|
||||
break 'delay_connection;
|
||||
} else {
|
||||
std::thread::sleep(std::time::Duration::from_millis(wait));
|
||||
}
|
||||
}
|
||||
connection
|
||||
.write_all(
|
||||
b"HTTP/1.1 304 Not Modified\r\nContent-Length: 0\r\n\r\n",
|
||||
)
|
||||
.ok();
|
||||
connection.shutdown(Shutdown::Both).ok();
|
||||
continue 'accept_connection;
|
||||
}
|
||||
}
|
||||
buf1[i] = src();
|
||||
let src = unsafe { &*((&buf1[i]) as *const String) };
|
||||
buf2[i] = Some(parse(src).unwrap());
|
||||
let doc = unsafe { &*(buf2[i].as_ref().unwrap() as *const Document<'_>) };
|
||||
i += 1;
|
||||
let mut http =
|
||||
"HTTP/1.1 200 Ok\r\nContent-Length: 0000000000\r\n\r\n".to_owned();
|
||||
let len = http.len();
|
||||
to::html::page_to(doc, specs, &mut state, &mut http);
|
||||
let content_length = format!("{}", http.len() - len);
|
||||
http.replace_range(len - 4 - content_length.len()..len - 4, &content_length);
|
||||
connection.write_all(http.as_bytes()).ok();
|
||||
connection.shutdown(Shutdown::Both).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => eprintln!("Unknown format, run without arguments for help"),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user