diff --git a/Cargo.toml b/Cargo.toml index b914197..c734a78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,10 @@ -[package] -name = "vanten-s" -version = "0.1.0" -edition = "2021" +[workspace] -[dependencies] -log = "0.4.22" -wasm-logger = "0.2.0" -web-sys = "0.3.76" -# this is the development version of Yew -yew = { git = "https://github.com/yewstack/yew/", features = ["csr"] } +resolver = "2" +default-members = [ "backend" ] + +members = [ + "backend", + "frontend" +] diff --git a/backend/Cargo.toml b/backend/Cargo.toml new file mode 100644 index 0000000..14da38e --- /dev/null +++ b/backend/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "backend" +version = "0.1.0" +edition = "2021" + +[dependencies] +actix-files = "0.6.6" +actix-web = "4.9.0" +env_logger = "0.11.6" +tokio = { version = "1.42.0", features = ["full"] } diff --git a/backend/src/main.rs b/backend/src/main.rs new file mode 100644 index 0000000..dc9c970 --- /dev/null +++ b/backend/src/main.rs @@ -0,0 +1,16 @@ +use actix_files::Files; +use actix_web::{middleware::Logger, App, HttpServer}; + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); + HttpServer::new(|| { + App::new() + .wrap(Logger::default()) + .service(Files::new("/fs", "fs").index_file("index.html")) + .service(Files::new("/", "dist").index_file("index.html")) + }) + .bind(("127.0.0.1", 8080))? + .run() + .await +} diff --git a/frontend/Cargo.toml b/frontend/Cargo.toml new file mode 100644 index 0000000..692d3a3 --- /dev/null +++ b/frontend/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "vanten-s" +version = "0.1.0" +edition = "2021" + +[dependencies] +log = "0.4.22" +wasm-logger = "0.2.0" +web-sys = "0.3.76" +yew = { git = "https://github.com/yewstack/yew/", features = ["csr"] } +serde-wasm-bindgen = "0.6.5" +wasm-bindgen-futures = "0.4.49" +futures = "0.3.31" + diff --git a/main.css b/frontend/main.css similarity index 96% rename from main.css rename to frontend/main.css index e7f2e1d..654115b 100644 --- a/main.css +++ b/frontend/main.css @@ -16,6 +16,7 @@ body { font: inherit; padding: 0; border: none; + width: max; } .input:focus { diff --git a/frontend/src/commands/cat.rs b/frontend/src/commands/cat.rs new file mode 100644 index 0000000..208720f --- /dev/null +++ b/frontend/src/commands/cat.rs @@ -0,0 +1,78 @@ +use std::sync::Arc; + +use wasm_bindgen_futures::{spawn_local, JsFuture}; +use web_sys::{console, wasm_bindgen::UnwrapThrowExt, window, Response}; +use super::{Environment, Handle, Rader}; + +#[derive(Clone)] +struct Fetcher { + next: Option>, + filename: String, + previous_text: String, + rader: Rader, + handle: Handle +} + +pub fn cat(mut env: Environment) { + if env.arguments.len() == 1 { + env.rader.push("cat: no filename provided\n".to_string()); + env.handle.set(env.rader); + return; + } + + let mut previous_fetcher = None; + for filename in env.arguments[1..].iter().rev() { + let next_fetcher = Fetcher { + next: previous_fetcher, + filename: filename.to_string(), + previous_text: String::new(), + rader: env.rader.clone(), + handle: env.handle.clone(), + }; + previous_fetcher = Some(Arc::new(next_fetcher)); + } + + spawn_local(fetch((*previous_fetcher.unwrap_throw()).clone())); + +} + +async fn fetch(mut fetcher: Fetcher) { + let text = fetch_single_file(fetcher.filename).await; + + let text = fetcher.previous_text + &text; + + if let Some(fetcher) = fetcher.next { + let mut fetcher = (*fetcher).clone(); + fetcher.previous_text = text; + Box::pin(fetch(fetcher.clone())).await + } else { + fetcher.rader.push(text.clone()); + fetcher.handle.set(fetcher.rader); + } +} + +async fn fetch_single_file(filename: String) -> String { + let resource = format!("/fs/{filename}"); + + let window = window().unwrap_throw(); + + console::log_1(&"Got Window".into()); + + let future: JsFuture = window.fetch_with_str(&resource).into(); + let response = future.await.unwrap_throw(); + let response = Response::from(response); + + if response.status() == 404 { + return format!("cat: {filename}: No such file or directory\n"); + } + + console::log_1(&"Got Response".into()); + + let text: JsFuture = response.text().unwrap_throw().into(); + let text = text.await.unwrap_throw(); + let text = text.as_string().unwrap_throw(); + + console::log_1(&"Got Text".into()); + + text +} diff --git a/frontend/src/commands/clear.rs b/frontend/src/commands/clear.rs new file mode 100644 index 0000000..17e73b6 --- /dev/null +++ b/frontend/src/commands/clear.rs @@ -0,0 +1,5 @@ +use super::Environment; + +pub fn clear(env: Environment) { + env.handle.set(vec![]); +} diff --git a/frontend/src/commands/echo.rs b/frontend/src/commands/echo.rs new file mode 100644 index 0000000..3cbce81 --- /dev/null +++ b/frontend/src/commands/echo.rs @@ -0,0 +1,12 @@ +use super::Environment; + +pub fn echo(mut env: Environment) { + let mut text = "".to_string(); + for argument in &env.arguments[1..] { + text += argument; + text += " "; + }; + text += "\n"; + env.rader.push(text); + env.handle.set(env.rader); +} diff --git a/frontend/src/commands/mod.rs b/frontend/src/commands/mod.rs new file mode 100644 index 0000000..0dc9756 --- /dev/null +++ b/frontend/src/commands/mod.rs @@ -0,0 +1,45 @@ +use log::{error, info}; +use yew::UseStateHandle; + +mod clear; +mod cat; +mod echo; + +pub type Rader = Vec; +pub type Handle = UseStateHandle; + +struct Environment<'a> { + pub rader: Rader, + pub handle: Handle, + pub arguments: Vec<&'a str>, +} + +pub fn run(command: String, rader: Rader, handle: Handle) { + let arguments = command.trim().split_whitespace().collect::>(); + + info!("{:?}", arguments); + + if arguments.len() == 0 { + handle.set(rader); + return; + } + + let environment = Environment { + rader, + handle, + arguments, + }; + + match environment.arguments[0] { + "clear" => clear::clear(environment), + "cat" => cat::cat(environment), + "echo" => echo::echo(environment), + _ => unrecognized_command(environment), + } +} + +fn unrecognized_command(mut env: Environment) { + env.rader.push(format!("vsh: {}: command not found\n", env.arguments[0])); + env.handle.set(env.rader); + error!("Unrecognized Command"); +} diff --git a/src/main.rs b/frontend/src/main.rs similarity index 90% rename from src/main.rs rename to frontend/src/main.rs index 8a6c210..18ad92c 100644 --- a/src/main.rs +++ b/frontend/src/main.rs @@ -20,9 +20,8 @@ fn App() -> Html { info!("{}", rad); let handle = rader_handle.clone(); let mut val = (*handle).clone(); - val.push(get_prompt() + &rad); - val = commands::run(rad, val); - rader_handle.set(val); + val.push(get_prompt() + &rad + "\n"); + commands::run(rad, val, handle); }; html! { @@ -73,8 +72,7 @@ fn Terminal(props: &TerminalProps) -> Html { .map(|rad| { html! { <> - { rad } -
+ { rad } } }) diff --git a/fs/hihi.txt b/fs/hihi.txt new file mode 100644 index 0000000..569cf52 --- /dev/null +++ b/fs/hihi.txt @@ -0,0 +1 @@ +:3 diff --git a/index.html b/index.html index c8eb871..d3d57de 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,10 @@ Yew App - + - + + + + diff --git a/src/commands/clear.rs b/src/commands/clear.rs deleted file mode 100644 index 834d151..0000000 --- a/src/commands/clear.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub fn clear(_rader: Vec) -> Vec { - return vec![]; -} diff --git a/src/commands/mod.rs b/src/commands/mod.rs deleted file mode 100644 index 307a567..0000000 --- a/src/commands/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -use log::error; - -mod clear; - -pub fn run(command: String, rader: Vec) -> Vec { - match command.as_str() { - "" => rader, - "clear" => clear::clear(rader), - unrecognized => unrecognized_command(unrecognized, rader), - } -} - -fn unrecognized_command(command: &str, mut rader: Vec) -> Vec { - rader.push(format!("Unrecognized Command \"{command}\"")); - error!("Unrecognized Command"); - rader -}