mirror of
https://codeberg.org/insects/cargo-spiel
synced 2025-05-23 10:33:42 +00:00
106 lines
3 KiB
Rust
106 lines
3 KiB
Rust
use std::{
|
|
fs::File,
|
|
io::{Read, Write},
|
|
path::PathBuf,
|
|
process::Command,
|
|
};
|
|
|
|
use axum::{
|
|
Json, Router,
|
|
body::Bytes,
|
|
extract::{Request, State},
|
|
http::{HeaderMap, HeaderValue, StatusCode, header::CONTENT_TYPE},
|
|
middleware::{self, Next},
|
|
response::IntoResponse,
|
|
routing::get,
|
|
};
|
|
use include_dir::{Dir, include_dir};
|
|
use serde::Serialize;
|
|
|
|
static PUBLIC_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/public");
|
|
|
|
#[derive(Serialize)]
|
|
struct OutputResponse {
|
|
pub stdout: Vec<u8>,
|
|
pub stderr: Vec<u8>,
|
|
}
|
|
|
|
async fn serve_mem(request: Request, _next: Next) -> impl IntoResponse {
|
|
let uri = request.uri();
|
|
let filename = if *uri == *"/" {
|
|
"index.html".to_string()
|
|
} else {
|
|
uri.to_string().trim_start_matches("/").into()
|
|
};
|
|
if let Some(file) = PUBLIC_DIR.get_file(filename) {
|
|
let ext = file.path().extension();
|
|
let mut headers = HeaderMap::new();
|
|
let mime = if let Some(Some(ext)) = ext.map(|f| f.to_str()) {
|
|
match ext {
|
|
"html" => "text/html",
|
|
"js" => "application/javascript",
|
|
"css" => "text/css",
|
|
"svg" => "image/svg+xml",
|
|
_ => "text/plain",
|
|
}
|
|
} else {
|
|
"text/plain"
|
|
};
|
|
headers.insert(CONTENT_TYPE, HeaderValue::from_str(mime).unwrap());
|
|
(headers, file.contents()).into_response()
|
|
} else {
|
|
StatusCode::NOT_FOUND.into_response()
|
|
}
|
|
}
|
|
|
|
async fn get_code(State(state): State<ServerState>) -> impl IntoResponse {
|
|
let mut path = state.path.clone();
|
|
path.push("src/main.rs");
|
|
let mut file = File::open(path).unwrap();
|
|
let mut contents = String::new();
|
|
file.read_to_string(&mut contents).unwrap();
|
|
contents.into_response()
|
|
}
|
|
|
|
async fn put_code(State(state): State<ServerState>, body: Bytes) -> impl IntoResponse {
|
|
let mut path = state.path.clone();
|
|
path.push("src/main.rs");
|
|
let mut file = File::options()
|
|
.write(true)
|
|
.truncate(true)
|
|
.open(path)
|
|
.unwrap();
|
|
file.write_all(&body[..]).unwrap();
|
|
StatusCode::OK.into_response()
|
|
}
|
|
|
|
async fn run_code(State(state): State<ServerState>) -> impl IntoResponse {
|
|
let mut path = state.path.clone();
|
|
path.push("Cargo.toml");
|
|
let cargo = std::env::var("CARGO").unwrap();
|
|
let output = Command::new(cargo)
|
|
.args(["run", "--manifest-path", path.to_str().unwrap()])
|
|
.output()
|
|
.unwrap();
|
|
let res = OutputResponse {
|
|
stdout: output.stdout,
|
|
stderr: output.stderr,
|
|
};
|
|
Json(res)
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
struct ServerState {
|
|
pub path: PathBuf,
|
|
}
|
|
|
|
pub async fn start(path: PathBuf) {
|
|
let app = Router::new()
|
|
.layer(middleware::from_fn(serve_mem))
|
|
.route("/code", get(get_code).post(run_code).put(put_code))
|
|
.with_state(ServerState { path });
|
|
let listener = tokio::net::TcpListener::bind("127.0.0.1:4555")
|
|
.await
|
|
.unwrap();
|
|
axum::serve(listener, app).await.unwrap();
|
|
}
|