Upgrade to latest (by rev) Lua / gc-arena
This commit is contained in:
		
							parent
							
								
									243132eeec
								
							
						
					
					
						commit
						1015bcb4ad
					
				
							
								
								
									
										9
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										9
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -246,8 +246,7 @@ dependencies = [ | |||||||
| [[package]] | [[package]] | ||||||
| name = "gc-arena" | name = "gc-arena" | ||||||
| version = "0.5.3" | version = "0.5.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "git+https://github.com/kyren/gc-arena.git?rev=5a7534b883b703f23cfb8c3cfdf033460aa77ea9#5a7534b883b703f23cfb8c3cfdf033460aa77ea9" | ||||||
| checksum = "3cd70cf88a32937834aae9614ff2569b5d9467fa0c42c5d7762fd94a8de88266" |  | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "allocator-api2", |  "allocator-api2", | ||||||
|  "gc-arena-derive", |  "gc-arena-derive", | ||||||
| @ -258,8 +257,7 @@ dependencies = [ | |||||||
| [[package]] | [[package]] | ||||||
| name = "gc-arena-derive" | name = "gc-arena-derive" | ||||||
| version = "0.5.3" | version = "0.5.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "git+https://github.com/kyren/gc-arena.git?rev=5a7534b883b703f23cfb8c3cfdf033460aa77ea9#5a7534b883b703f23cfb8c3cfdf033460aa77ea9" | ||||||
| checksum = "c612a69f5557a11046b77a7408d2836fe77077f842171cd211c5ef504bd3cddd" |  | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2", |  "proc-macro2", | ||||||
|  "quote", |  "quote", | ||||||
| @ -813,8 +811,7 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" | |||||||
| [[package]] | [[package]] | ||||||
| name = "piccolo" | name = "piccolo" | ||||||
| version = "0.3.3" | version = "0.3.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "git+https://github.com/kyren/piccolo.git?rev=fcbaabc924292170d6549c55440ecbd0522b275a#fcbaabc924292170d6549c55440ecbd0522b275a" | ||||||
| checksum = "003bf52de285e1ff1adcbc6572588db3849988ea660a2d55af3a2ffbc81f597f" |  | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "ahash", |  "ahash", | ||||||
|  "allocator-api2", |  "allocator-api2", | ||||||
|  | |||||||
| @ -9,7 +9,7 @@ edition = "2021" | |||||||
| im = "15.1.0" | im = "15.1.0" | ||||||
| itertools = "0.13.0" | itertools = "0.13.0" | ||||||
| nom = "7.1.3" | nom = "7.1.3" | ||||||
| piccolo = "0.3.3" | piccolo = { git = "https://github.com/kyren/piccolo.git", rev = "fcbaabc924292170d6549c55440ecbd0522b275a" } | ||||||
| unicode-segmentation = "1.11.0" | unicode-segmentation = "1.11.0" | ||||||
| unicode-width = "0.1.13" | unicode-width = "0.1.13" | ||||||
| wasm-bindgen = "0.2.92" | wasm-bindgen = "0.2.92" | ||||||
| @ -21,4 +21,4 @@ console_error_panic_hook = "0.1.7" | |||||||
| anyhow = "1.0.86" | anyhow = "1.0.86" | ||||||
| serde = "1.0.209" | serde = "1.0.209" | ||||||
| serde_json = "1.0.127" | serde_json = "1.0.127" | ||||||
| gc-arena = "0.5.3" | gc-arena = { git = "https://github.com/kyren/gc-arena.git", rev = "5a7534b883b703f23cfb8c3cfdf033460aa77ea9" } | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								rustfmt.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								rustfmt.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | edition = "2021" | ||||||
| @ -1,8 +1,8 @@ | |||||||
| use self::{frames::*, muds::*}; | use self::{frames::*, muds::*}; | ||||||
| use anyhow::Error; | use anyhow::Error; | ||||||
| use piccolo::{ | use piccolo::{ | ||||||
|     Closure, Executor, FromValue, Function, IntoValue, Lua, StashedExecutor, StaticError, Table, |     Callback, Closure, Context, Executor, ExternError, FromValue, Function, Lua, StashedExecutor, | ||||||
|     Value, Variadic, |     Table, Value, Variadic, | ||||||
| }; | }; | ||||||
| use yew::UseStateSetter; | use yew::UseStateSetter; | ||||||
| 
 | 
 | ||||||
| @ -26,9 +26,9 @@ impl LuaState { | |||||||
|         Ok(LuaState { interp, exec }) |         Ok(LuaState { interp, exec }) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn try_set_current_frame(&mut self, frame: &TermFrame) -> Result<(), StaticError> { |     fn try_set_current_frame(&mut self, frame: &TermFrame) -> Result<(), ExternError> { | ||||||
|         self.interp.try_enter(|ctx| { |         self.interp.try_enter(|ctx| { | ||||||
|             let info_table = Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"info")))?; |             let info_table = Table::from_value(ctx, ctx.get_global("info")?)?; | ||||||
|             info_table.set( |             info_table.set( | ||||||
|                 ctx, |                 ctx, | ||||||
|                 ctx.intern_static(b"current_frame"), |                 ctx.intern_static(b"current_frame"), | ||||||
| @ -65,9 +65,8 @@ impl LuaState { | |||||||
|     ) -> Result<(), String> { |     ) -> Result<(), String> { | ||||||
|         self.interp |         self.interp | ||||||
|             .try_enter(|ctx| { |             .try_enter(|ctx| { | ||||||
|                 let commands = |                 let commands = ctx.get_global::<Table>("commands")?; | ||||||
|                     Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"commands")))?; |                 let command_value: Value = commands.get(ctx, ctx.intern(command.as_bytes()))?; | ||||||
|                 let command_value: Value = commands.get(ctx, ctx.intern(command.as_bytes())); |  | ||||||
|                 if command_value.is_nil() { |                 if command_value.is_nil() { | ||||||
|                     Err(anyhow::Error::msg("Unknown command"))?; |                     Err(anyhow::Error::msg("Unknown command"))?; | ||||||
|                 } |                 } | ||||||
| @ -95,11 +94,7 @@ pub fn install_lua_globals( | |||||||
|     global_memo: &GlobalMemoCell, |     global_memo: &GlobalMemoCell, | ||||||
|     global_layout: UseStateSetter<GlobalLayoutCell>, |     global_layout: UseStateSetter<GlobalLayoutCell>, | ||||||
| ) -> Result<(), String> { | ) -> Result<(), String> { | ||||||
|     global_memo |     global_memo.lua_engine.borrow_mut().interp.try_enter(|ctx| { | ||||||
|         .lua_engine |  | ||||||
|         .borrow_mut() |  | ||||||
|         .interp |  | ||||||
|         .try_enter(|ctx| { |  | ||||||
|         let cmd_table = Table::new(&ctx); |         let cmd_table = Table::new(&ctx); | ||||||
|         macro_rules! register_command { |         macro_rules! register_command { | ||||||
|             ($sym: ident) => { |             ($sym: ident) => { | ||||||
| @ -123,26 +118,14 @@ pub fn install_lua_globals( | |||||||
|         register_command!(hsplit); |         register_command!(hsplit); | ||||||
|         register_command!(panel_merge); |         register_command!(panel_merge); | ||||||
|         register_command!(vsplit); |         register_command!(vsplit); | ||||||
|             ctx.set_global(ctx.intern_static(b"commands").into_value(ctx), cmd_table) |         ctx.set_global("commands", cmd_table); | ||||||
|                 .map(|_| ()) |  | ||||||
|                 .map_err(|_| Error::msg("Can't set commands key"))?; |  | ||||||
| 
 |  | ||||||
|         let info_table = Table::new(&ctx); |         let info_table = Table::new(&ctx); | ||||||
|             ctx.set_global(ctx.intern_static(b"info").into_value(ctx), info_table) |         ctx.set_global("info", info_table); | ||||||
|                 .map(|_| ()) |  | ||||||
|                 .map_err(|_| Error::msg("Can't set info key"))?; |  | ||||||
|         let muds_table = Table::new(&ctx); |         let muds_table = Table::new(&ctx); | ||||||
|             ctx.set_global(ctx.intern_static(b"muds").into_value(ctx), muds_table) |         ctx.set_global("muds", muds_table); | ||||||
|                 .map(|_| ()) |  | ||||||
|                 .map_err(|_| Error::msg("Can't set muds key"))?; |  | ||||||
| 
 | 
 | ||||||
|         let handlers_table = Table::new(&ctx); |         let handlers_table = Table::new(&ctx); | ||||||
|             ctx.set_global( |         ctx.set_global("handlers", handlers_table); | ||||||
|                 ctx.intern_static(b"handlers").into_value(ctx), |  | ||||||
|                 handlers_table, |  | ||||||
|             ) |  | ||||||
|             .map(|_| ()) |  | ||||||
|             .map_err(|_| Error::msg("Can't set handlers key"))?; |  | ||||||
|         macro_rules! register_handler { |         macro_rules! register_handler { | ||||||
|             ($sym: ident) => { |             ($sym: ident) => { | ||||||
|                 handlers_table |                 handlers_table | ||||||
| @ -155,10 +138,40 @@ pub fn install_lua_globals( | |||||||
|             }; |             }; | ||||||
|         } |         } | ||||||
|         register_handler!(mudoutput); |         register_handler!(mudoutput); | ||||||
|  |         register_handler!(mudoutput_line); | ||||||
|  |         register_handler!(mudoutput_prompt); | ||||||
|  |         register_handler!(mudoutput_will); | ||||||
|  |         register_handler!(mudoutput_wont); | ||||||
|  |         register_handler!(mudoutput_do); | ||||||
|  |         register_handler!(mudoutput_dont); | ||||||
|  |         register_handler!(mudoutput_subnegotiation); | ||||||
|  | 
 | ||||||
|  |         macro_rules! register_nop_handler { | ||||||
|  |             ($sym: ident) => { | ||||||
|  |                 handlers_table | ||||||
|  |                     .set( | ||||||
|  |                         ctx, | ||||||
|  |                         ctx.intern_static(stringify!($sym).as_bytes()), | ||||||
|  |                         lua_nop(ctx), | ||||||
|  |                     ) | ||||||
|  |                     .map_err(|_| Error::msg("Can't add handler"))?; | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         register_nop_handler!(mudoutput_break); | ||||||
|  |         register_nop_handler!(mudoutput_sync); | ||||||
|  |         register_nop_handler!(mudoutput_interrupt); | ||||||
|  |         register_nop_handler!(mudoutput_abort_output); | ||||||
|  |         register_nop_handler!(mudoutput_areyouthere); | ||||||
| 
 | 
 | ||||||
|         Ok(()) |         Ok(()) | ||||||
|         }) |     }); | ||||||
|         .map_err(|e| e.to_string())?; |  | ||||||
| 
 | 
 | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | pub fn lua_nop(ctx: Context<'_>) -> Callback<'_> { | ||||||
|  |     Callback::from_fn(&ctx, |_ctx, _ex, _stack| { | ||||||
|  |         Ok(piccolo::CallbackReturn::Return) | ||||||
|  |     }) | ||||||
|  | } | ||||||
|  | |||||||
| @ -16,7 +16,12 @@ pub fn echo_frame_raw<'gc, 'a>( | |||||||
| ) -> Callback<'gc> { | ) -> Callback<'gc> { | ||||||
|     let global_memo = global_memo.clone(); |     let global_memo = global_memo.clone(); | ||||||
|     Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { |     Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { | ||||||
|         let frame: TermFrame = try_unwrap_frame(ctx, &stack.pop_front())?; |         let frame: TermFrame = try_unwrap_frame( | ||||||
|  |             ctx, | ||||||
|  |             &stack | ||||||
|  |                 .pop_front() | ||||||
|  |                 .ok_or_else(|| anyhow::Error::msg("Missing frame"))?, | ||||||
|  |         )?; | ||||||
|         let message: piccolo::String = stack.from_front(ctx)?; |         let message: piccolo::String = stack.from_front(ctx)?; | ||||||
|         let message_str = str::from_utf8(message.as_bytes()) |         let message_str = str::from_utf8(message.as_bytes()) | ||||||
|             .map_err(|_| "Expected message to echo to be UTF-8.".into_value(ctx))?; |             .map_err(|_| "Expected message to echo to be UTF-8.".into_value(ctx))?; | ||||||
| @ -31,14 +36,15 @@ pub fn echo_frame<'gc>( | |||||||
|     _global_layout: &UseStateSetter<GlobalLayoutCell>, |     _global_layout: &UseStateSetter<GlobalLayoutCell>, | ||||||
| ) -> Callback<'gc> { | ) -> Callback<'gc> { | ||||||
|     Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { |     Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { | ||||||
|         let commands: Table<'gc> = |         let commands: Table<'gc> = ctx.get_global("commands")?; | ||||||
|             Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"commands")))?; |         let function: Function = commands.get(ctx, "echo_frame_raw")?; | ||||||
|         let function = Function::from_value(ctx, commands.get(ctx, "echo_frame_raw"))?; |         let frame_no: Value = stack | ||||||
|         let frame_no: Value = stack.pop_front(); |             .pop_front() | ||||||
|  |             .ok_or_else(|| anyhow::Error::msg("Missing frame number"))?; | ||||||
|         let all_parts: Vec<String> = stack |         let all_parts: Vec<String> = stack | ||||||
|             .consume::<Variadic<Vec<Value>>>(ctx)? |             .consume::<Variadic<Vec<Value>>>(ctx)? | ||||||
|             .into_iter() |             .into_iter() | ||||||
|             .map(|v| format!("{}", v)) |             .map(|v| format!("{:?}", v)) | ||||||
|             .collect(); |             .collect(); | ||||||
|         stack.push_front(frame_no); |         stack.push_front(frame_no); | ||||||
|         let message = ctx.intern((all_parts.join(" ") + "\r\n").as_bytes()); |         let message = ctx.intern((all_parts.join(" ") + "\r\n").as_bytes()); | ||||||
| @ -56,11 +62,10 @@ pub fn echo<'gc>( | |||||||
|     _global_layout: &UseStateSetter<GlobalLayoutCell>, |     _global_layout: &UseStateSetter<GlobalLayoutCell>, | ||||||
| ) -> Callback<'gc> { | ) -> Callback<'gc> { | ||||||
|     Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { |     Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { | ||||||
|         let commands: Table<'gc> = |         let commands: Table<'gc> = ctx.get_global("commands")?; | ||||||
|             Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"commands")))?; |         let function: Function = commands.get(ctx, "echo_frame")?; | ||||||
|         let function = Function::from_value(ctx, commands.get(ctx, "echo_frame"))?; |         let info: Table<'gc> = ctx.get_global("info")?; | ||||||
|         let info: Table<'gc> = Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"info")))?; |         let cur_frame: Value = info.get(ctx, ctx.intern_static(b"current_frame"))?; | ||||||
|         let cur_frame = info.get(ctx, ctx.intern_static(b"current_frame")); |  | ||||||
|         stack.push_front(cur_frame); |         stack.push_front(cur_frame); | ||||||
|         Ok(piccolo::CallbackReturn::Call { |         Ok(piccolo::CallbackReturn::Call { | ||||||
|             function, |             function, | ||||||
|  | |||||||
| @ -1,6 +1,9 @@ | |||||||
| use anyhow::Error; | use anyhow::Error; | ||||||
| use gc_arena::{Gc, Rootable}; | use gc_arena::{Gc, Rootable}; | ||||||
| use piccolo::{self, Callback, Context, FromValue, Function, Table, UserData, Value}; | use piccolo::{ | ||||||
|  |     self, Callback, CallbackReturn, Context, FromValue, Function, IntoValue, StashedValue, Table, | ||||||
|  |     UserData, Value, | ||||||
|  | }; | ||||||
| use wasm_bindgen::JsValue; | use wasm_bindgen::JsValue; | ||||||
| use web_sys::console; | use web_sys::console; | ||||||
| use yew::UseStateSetter; | use yew::UseStateSetter; | ||||||
| @ -8,6 +11,7 @@ use yew::UseStateSetter; | |||||||
| use crate::{ | use crate::{ | ||||||
|     command_handler::debrace, |     command_handler::debrace, | ||||||
|     id_intern::{intern_id, unintern_id}, |     id_intern::{intern_id, unintern_id}, | ||||||
|  |     telnet::{parse_telnet_buf, TelnetOutput}, | ||||||
|     websocket::{connect_websocket, send_message_to_mud, WebSocketId}, |     websocket::{connect_websocket, send_message_to_mud, WebSocketId}, | ||||||
|     GlobalLayoutCell, GlobalMemoCell, |     GlobalLayoutCell, GlobalMemoCell, | ||||||
| }; | }; | ||||||
| @ -19,7 +23,7 @@ fn try_unwrap_socketid<'gc>( | |||||||
|     value: &Value<'gc>, |     value: &Value<'gc>, | ||||||
| ) -> Result<WebSocketId, piccolo::Error<'gc>> { | ) -> Result<WebSocketId, piccolo::Error<'gc>> { | ||||||
|     let value = if let Ok(sockname) = String::from_value(ctx, *value) { |     let value = if let Ok(sockname) = String::from_value(ctx, *value) { | ||||||
|         let ret = Table::from_value(ctx, ctx.get_global("muds"))?.get(ctx, sockname); |         let ret: Value<'gc> = ctx.get_global::<Table<'gc>>("muds")?.get(ctx, sockname)?; | ||||||
|         if ret.is_nil() { |         if ret.is_nil() { | ||||||
|             Err(Error::msg( |             Err(Error::msg( | ||||||
|                 "Could not find a MUD connection with that name.", |                 "Could not find a MUD connection with that name.", | ||||||
| @ -30,7 +34,7 @@ fn try_unwrap_socketid<'gc>( | |||||||
|         *value |         *value | ||||||
|     }; |     }; | ||||||
|     Ok(UserData::from_value(ctx, value)? |     Ok(UserData::from_value(ctx, value)? | ||||||
|         .downcast::<Rootable!['gcb => Gc<'gcb, WebSocketId>]>()? |         .downcast::<Rootable![Gc<'_, WebSocketId>]>()? | ||||||
|         .as_ref() |         .as_ref() | ||||||
|         .clone()) |         .clone()) | ||||||
| } | } | ||||||
| @ -43,9 +47,8 @@ pub fn handle_websocket_output( | |||||||
|     engine |     engine | ||||||
|         .interp |         .interp | ||||||
|         .try_enter(|ctx| { |         .try_enter(|ctx| { | ||||||
|             let handlers = Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"handlers")))?; |             let handlers: Table = ctx.get_global("handlers")?; | ||||||
|             let input_fn = |             let input_fn: Function = handlers.get(ctx, ctx.intern_static(b"mudoutput"))?; | ||||||
|                 Function::from_value(ctx, handlers.get(ctx, ctx.intern_static(b"mudoutput")))?; |  | ||||||
|             ctx.fetch(&engine.exec).restart( |             ctx.fetch(&engine.exec).restart( | ||||||
|                 ctx, |                 ctx, | ||||||
|                 input_fn, |                 input_fn, | ||||||
| @ -77,9 +80,8 @@ pub fn handle_websocket_close(socket: &WebSocketId, engine: &mut LuaState) -> Re | |||||||
|     engine |     engine | ||||||
|         .interp |         .interp | ||||||
|         .try_enter(|ctx| { |         .try_enter(|ctx| { | ||||||
|             let handlers = Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"handlers")))?; |             let handlers: Table = ctx.get_global("handlers")?; | ||||||
|             let input_fn = |             let input_fn: Function = handlers.get(ctx, ctx.intern_static(b"mudclose"))?; | ||||||
|                 Function::from_value(ctx, handlers.get(ctx, ctx.intern_static(b"mudclose")))?; |  | ||||||
|             ctx.fetch(&engine.exec).restart( |             ctx.fetch(&engine.exec).restart( | ||||||
|                 ctx, |                 ctx, | ||||||
|                 input_fn, |                 input_fn, | ||||||
| @ -111,7 +113,12 @@ pub(super) fn sendmud_raw<'gc>( | |||||||
| ) -> Callback<'gc> { | ) -> Callback<'gc> { | ||||||
|     let global_memo = global_memo.clone(); |     let global_memo = global_memo.clone(); | ||||||
|     Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { |     Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { | ||||||
|         let mud: WebSocketId = try_unwrap_socketid(ctx, &stack.pop_front())?; |         let mud: WebSocketId = try_unwrap_socketid( | ||||||
|  |             ctx, | ||||||
|  |             &stack | ||||||
|  |                 .pop_front() | ||||||
|  |                 .ok_or_else(|| anyhow::Error::msg("Missing MUD argument"))?, | ||||||
|  |         )?; | ||||||
|         let msg: piccolo::String = stack.from_front(ctx)?; |         let msg: piccolo::String = stack.from_front(ctx)?; | ||||||
|         send_message_to_mud(&mud, msg.as_bytes(), &global_memo)?; |         send_message_to_mud(&mud, msg.as_bytes(), &global_memo)?; | ||||||
|         Ok(piccolo::CallbackReturn::Return) |         Ok(piccolo::CallbackReturn::Return) | ||||||
| @ -136,8 +143,8 @@ pub(super) fn connect_mud<'gc>( | |||||||
|         }; |         }; | ||||||
|         let name: Value<'gc> = ctx.intern(debrace(&name).as_bytes()).into(); |         let name: Value<'gc> = ctx.intern(debrace(&name).as_bytes()).into(); | ||||||
| 
 | 
 | ||||||
|         let muds = Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"muds")))?; |         let muds: Table = ctx.get_global("muds")?; | ||||||
|         if !muds.get_value(name).is_nil() { |         if !muds.get_value(ctx, name).is_nil() { | ||||||
|             Err(Error::msg( |             Err(Error::msg( | ||||||
|                 "Attempt to create MUD connection using name that's already taken", |                 "Attempt to create MUD connection using name that's already taken", | ||||||
|             ))? |             ))? | ||||||
| @ -170,7 +177,9 @@ pub(super) fn close_mud<'gc>( | |||||||
| ) -> Callback<'gc> { | ) -> Callback<'gc> { | ||||||
|     let global_memo = global_memo.clone(); |     let global_memo = global_memo.clone(); | ||||||
|     Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { |     Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { | ||||||
|         let mud_value: Value<'gc> = stack.pop_front(); |         let mud_value: Value<'gc> = stack | ||||||
|  |             .pop_front() | ||||||
|  |             .ok_or_else(|| anyhow::Error::msg("Missing MUD value argument"))?; | ||||||
|         let socket_id = try_unwrap_socketid(ctx, &mud_value)?; |         let socket_id = try_unwrap_socketid(ctx, &mud_value)?; | ||||||
|         match global_memo.ws_registry.borrow_mut().get_mut(&socket_id) { |         match global_memo.ws_registry.borrow_mut().get_mut(&socket_id) { | ||||||
|             None => Err(Error::msg("That MUD connection doesn't exist."))?, |             None => Err(Error::msg("That MUD connection doesn't exist."))?, | ||||||
| @ -197,8 +206,8 @@ pub(super) fn delete_mud<'gc>( | |||||||
|         let name: String = stack.from_front(ctx)?; |         let name: String = stack.from_front(ctx)?; | ||||||
|         let name: Value<'gc> = ctx.intern(debrace(&name).as_bytes()).into(); |         let name: Value<'gc> = ctx.intern(debrace(&name).as_bytes()).into(); | ||||||
| 
 | 
 | ||||||
|         let muds = Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"muds")))?; |         let muds: Table = ctx.get_global("muds")?; | ||||||
|         let mud_value = muds.get_value(name); |         let mud_value = muds.get_value(ctx, name); | ||||||
|         if mud_value.is_nil() { |         if mud_value.is_nil() { | ||||||
|             Err(Error::msg( |             Err(Error::msg( | ||||||
|                 "Attempt to delete MUD connection that wasn't found", |                 "Attempt to delete MUD connection that wasn't found", | ||||||
| @ -208,7 +217,7 @@ pub(super) fn delete_mud<'gc>( | |||||||
|         let socket_id = try_unwrap_socketid(ctx, &mud_value)?; |         let socket_id = try_unwrap_socketid(ctx, &mud_value)?; | ||||||
| 
 | 
 | ||||||
|         // Delete the MUD data if possible.
 |         // Delete the MUD data if possible.
 | ||||||
|         let _ = muds.set_value(&ctx, mud_value, Value::Nil); |         let _ = muds.set_raw(&ctx, mud_value, Value::Nil)?; | ||||||
|         for (k, v) in muds.iter() { |         for (k, v) in muds.iter() { | ||||||
|             match UserData::from_value(ctx, v) { |             match UserData::from_value(ctx, v) { | ||||||
|                 Err(_) => continue, |                 Err(_) => continue, | ||||||
| @ -218,7 +227,7 @@ pub(super) fn delete_mud<'gc>( | |||||||
|                     _ => {} |                     _ => {} | ||||||
|                 }, |                 }, | ||||||
|             } |             } | ||||||
|             let _ = muds.set_value(&ctx, k, Value::Nil); |             let _ = muds.set_raw(&ctx, k, Value::Nil); | ||||||
|         } |         } | ||||||
|         unintern_id(ctx, &socket_id); |         unintern_id(ctx, &socket_id); | ||||||
|         match global_memo.ws_registry.borrow_mut().remove(&socket_id) { |         match global_memo.ws_registry.borrow_mut().remove(&socket_id) { | ||||||
| @ -231,28 +240,124 @@ pub(super) fn delete_mud<'gc>( | |||||||
|     }) |     }) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub(super) fn mudoutput<'gc>(ctx: Context<'gc>, global_memo: &GlobalMemoCell) -> Callback<'gc> { | pub(super) fn mudoutput<'gc>(ctx: Context<'gc>, _global_memo: &GlobalMemoCell) -> Callback<'gc> { | ||||||
|     let global_memo = global_memo.clone(); |  | ||||||
|     Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { |     Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { | ||||||
|         // Temporary hack for testing... alias to echo
 |         let mud: Value<'gc> = stack | ||||||
|         let echo = Function::from_value( |             .pop_front() | ||||||
|  |             .ok_or_else(|| anyhow::Error::msg("Missing argument to mudoutput"))?; | ||||||
|  |         let output: &'gc [u8] = stack.from_front::<piccolo::String<'gc>>(ctx)?.as_bytes(); | ||||||
|  | 
 | ||||||
|  |         let conntab: Table<'gc> = Table::from_value( | ||||||
|             ctx, |             ctx, | ||||||
|             Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"commands")))? |             Table::from_value(ctx, ctx.get_global("muds")?)?.get_value(ctx, mud), | ||||||
|                 .get(ctx, ctx.intern_static(b"echo")), |  | ||||||
|         )?; |         )?; | ||||||
| 
 | 
 | ||||||
|         let mud: Value<'gc> = stack.pop_front(); |         let buf: &'gc [u8] = | ||||||
|         let output: piccolo::String<'gc> = stack.from_front(ctx)?; |             piccolo::String::from_value(ctx, conntab.get(ctx, ctx.intern_static(b"buffer"))?)? | ||||||
|  |                 .as_bytes(); | ||||||
|  |         let mut cur_buf: Vec<u8> = [buf, output].concat(); | ||||||
| 
 | 
 | ||||||
|         let conntab = |         let handlers = Table::from_value(ctx, ctx.get_global("handlers")?)?; | ||||||
|             Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"muds")))?.get_value(mud); | 
 | ||||||
|         if conntab.is_nil() { |         let mut fns: Vec<(&'static [u8], Vec<StashedValue>)> = vec![]; | ||||||
|             Err(Error::msg("Received input from MUD not in muds table."))? |         loop { | ||||||
|  |             match parse_telnet_buf(&cur_buf) { | ||||||
|  |                 (new_buf, None) => { | ||||||
|  |                     conntab.set(ctx, ctx.intern_static(b"buffer"), ctx.intern(&new_buf))?; | ||||||
|  | 
 | ||||||
|  |                     let seq = piccolo::async_sequence(&ctx, |locals, seq| async { | ||||||
|  |                         Ok(piccolo::SequenceReturn::Return) | ||||||
|  |                     }); | ||||||
|  | 
 | ||||||
|  |                     return Ok(piccolo::CallbackReturn::Sequence(seq)); | ||||||
|  |                 } | ||||||
|  |                 (new_buf, Some(cmd)) => { | ||||||
|  |                     cur_buf = new_buf; | ||||||
|  |                     match cmd { | ||||||
|  |                         TelnetOutput::Line(l) => fns.push(( | ||||||
|  |                             b"mudoutput_line", | ||||||
|  |                             vec![ctx.stash(mud), ctx.stash(ctx.intern(&l).into_value(ctx))], | ||||||
|  |                         )), | ||||||
|  |                         TelnetOutput::Prompt(p) => fns.push(( | ||||||
|  |                             b"mudoutput_prompt", | ||||||
|  |                             vec![ctx.stash(mud), ctx.stash(ctx.intern(&p).into_value(ctx))], | ||||||
|  |                         )), | ||||||
|  |                         TelnetOutput::Nop => {} | ||||||
|  |                         TelnetOutput::Break => fns.push((b"mudoutput_break", vec![ctx.stash(mud)])), | ||||||
|  |                         TelnetOutput::Sync => fns.push((b"mudoutput_sync", vec![ctx.stash(mud)])), | ||||||
|  |                         TelnetOutput::Interrupt => { | ||||||
|  |                             fns.push((b"mudoutput_interrupt", vec![ctx.stash(mud)])) | ||||||
|                         } |                         } | ||||||
| 
 | 
 | ||||||
|         Ok(piccolo::CallbackReturn::Call { |                         TelnetOutput::AbortOutput => { | ||||||
|             function: echo, |                             fns.push((b"mudoutput_abort_output", vec![ctx.stash(mud)])) | ||||||
|             then: None, |                         } | ||||||
|         }) |                         TelnetOutput::AreYouThere => { | ||||||
|  |                             fns.push((b"mudoutput_areyouthere", vec![ctx.stash(mud)])) | ||||||
|  |                         } | ||||||
|  |                         TelnetOutput::Will(v) => fns.push(( | ||||||
|  |                             b"mudoutput_will", | ||||||
|  |                             vec![ctx.stash(mud), ctx.stash(v.into_value(ctx))], | ||||||
|  |                         )), | ||||||
|  |                         TelnetOutput::Wont(v) => fns.push(( | ||||||
|  |                             b"mudoutput_wont", | ||||||
|  |                             vec![ctx.stash(mud), ctx.stash(v.into_value(ctx))], | ||||||
|  |                         )), | ||||||
|  |                         TelnetOutput::Do(v) => fns.push(( | ||||||
|  |                             b"mudoutput_do", | ||||||
|  |                             vec![ctx.stash(mud), ctx.stash(v.into_value(ctx))], | ||||||
|  |                         )), | ||||||
|  |                         TelnetOutput::Dont(v) => fns.push(( | ||||||
|  |                             b"mudoutput_dont", | ||||||
|  |                             vec![ctx.stash(mud), ctx.stash(v.into_value(ctx))], | ||||||
|  |                         )), | ||||||
|  |                         TelnetOutput::Subnegotiation(t) => fns.push(( | ||||||
|  |                             b"mudoutput_subnegotiation", | ||||||
|  |                             vec![ctx.stash(mud), ctx.stash(t.into_value(ctx))], | ||||||
|  |                         )), | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     }) |     }) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | pub(super) fn mudoutput_line<'gc>( | ||||||
|  |     ctx: Context<'gc>, | ||||||
|  |     _global_memo: &GlobalMemoCell, | ||||||
|  | ) -> Callback<'gc> { | ||||||
|  |     Callback::from_fn(&ctx, move |_ctx, _ex, _stack| Ok(CallbackReturn::Return)) | ||||||
|  | } | ||||||
|  | pub(super) fn mudoutput_prompt<'gc>( | ||||||
|  |     ctx: Context<'gc>, | ||||||
|  |     _global_memo: &GlobalMemoCell, | ||||||
|  | ) -> Callback<'gc> { | ||||||
|  |     Callback::from_fn(&ctx, move |_ctx, _ex, _stack| Ok(CallbackReturn::Return)) | ||||||
|  | } | ||||||
|  | pub(super) fn mudoutput_will<'gc>( | ||||||
|  |     ctx: Context<'gc>, | ||||||
|  |     _global_memo: &GlobalMemoCell, | ||||||
|  | ) -> Callback<'gc> { | ||||||
|  |     Callback::from_fn(&ctx, move |_ctx, _ex, _stack| Ok(CallbackReturn::Return)) | ||||||
|  | } | ||||||
|  | pub(super) fn mudoutput_wont<'gc>( | ||||||
|  |     ctx: Context<'gc>, | ||||||
|  |     _global_memo: &GlobalMemoCell, | ||||||
|  | ) -> Callback<'gc> { | ||||||
|  |     Callback::from_fn(&ctx, move |_ctx, _ex, _stack| Ok(CallbackReturn::Return)) | ||||||
|  | } | ||||||
|  | pub(super) fn mudoutput_do<'gc>(ctx: Context<'gc>, _global_memo: &GlobalMemoCell) -> Callback<'gc> { | ||||||
|  |     Callback::from_fn(&ctx, move |_ctx, _ex, _stack| Ok(CallbackReturn::Return)) | ||||||
|  | } | ||||||
|  | pub(super) fn mudoutput_dont<'gc>( | ||||||
|  |     ctx: Context<'gc>, | ||||||
|  |     _global_memo: &GlobalMemoCell, | ||||||
|  | ) -> Callback<'gc> { | ||||||
|  |     Callback::from_fn(&ctx, move |_ctx, _ex, _stack| Ok(CallbackReturn::Return)) | ||||||
|  | } | ||||||
|  | pub(super) fn mudoutput_subnegotiation<'gc>( | ||||||
|  |     ctx: Context<'gc>, | ||||||
|  |     _global_memo: &GlobalMemoCell, | ||||||
|  | ) -> Callback<'gc> { | ||||||
|  |     Callback::from_fn(&ctx, move |_ctx, _ex, _stack| Ok(CallbackReturn::Return)) | ||||||
|  | } | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ pub mod lineengine; | |||||||
| pub mod lua_engine; | pub mod lua_engine; | ||||||
| pub mod parsing; | pub mod parsing; | ||||||
| pub mod split_panel; | pub mod split_panel; | ||||||
|  | pub mod telnet; | ||||||
| pub mod term_split; | pub mod term_split; | ||||||
| pub mod term_view; | pub mod term_view; | ||||||
| pub mod websocket; | pub mod websocket; | ||||||
|  | |||||||
							
								
								
									
										288
									
								
								src/telnet.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										288
									
								
								src/telnet.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,288 @@ | |||||||
|  | use wasm_bindgen::JsValue; | ||||||
|  | use web_sys::console; | ||||||
|  | 
 | ||||||
|  | #[derive(Eq, PartialEq, Debug)] | ||||||
|  | pub enum TelnetOutput { | ||||||
|  |     Line(Vec<u8>), | ||||||
|  |     Prompt(Vec<u8>), // Like a line but without a newline.
 | ||||||
|  |     Nop, | ||||||
|  |     Break, | ||||||
|  |     Sync, | ||||||
|  |     Interrupt, | ||||||
|  |     AbortOutput, | ||||||
|  |     AreYouThere, | ||||||
|  |     Will(u8), | ||||||
|  |     Wont(u8), | ||||||
|  |     Do(u8), | ||||||
|  |     Dont(u8), | ||||||
|  |     Subnegotiation(Vec<u8>), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const IAC: u8 = 255; | ||||||
|  | const NOP: u8 = 241; | ||||||
|  | const DATA_MARK: u8 = 242; | ||||||
|  | const BREAK: u8 = 243; | ||||||
|  | const INTERRUPT: u8 = 244; | ||||||
|  | const ABORT: u8 = 245; | ||||||
|  | const AYT: u8 = 246; | ||||||
|  | const ERASECHAR: u8 = 247; | ||||||
|  | const ERASELINE: u8 = 248; | ||||||
|  | const GOAHEAD: u8 = 249; | ||||||
|  | const EOR: u8 = 239; | ||||||
|  | const STARTSUB: u8 = 250; | ||||||
|  | const ENDSUB: u8 = 240; | ||||||
|  | const WILL: u8 = 251; | ||||||
|  | const WONT: u8 = 252; | ||||||
|  | const DO: u8 = 253; | ||||||
|  | const DONT: u8 = 254; | ||||||
|  | 
 | ||||||
|  | pub fn parse_telnet_buf(input: &[u8]) -> (Vec<u8>, Option<TelnetOutput>) { | ||||||
|  |     let mut ptr: &[u8] = input; | ||||||
|  |     let mut textbuf: Vec<u8> = vec![]; | ||||||
|  |     loop { | ||||||
|  |         match ptr.first() { | ||||||
|  |             None => return (textbuf.to_owned(), None), | ||||||
|  |             Some(b'\n') => { | ||||||
|  |                 textbuf.push(b'\n'); | ||||||
|  |                 return (ptr[1..].to_owned(), Some(TelnetOutput::Line(textbuf))); | ||||||
|  |             } | ||||||
|  |             Some(&IAC) => { | ||||||
|  |                 ptr = &ptr[1..]; | ||||||
|  |                 match ptr.first() { | ||||||
|  |                     None => { | ||||||
|  |                         textbuf.push(IAC); | ||||||
|  |                         return (textbuf, None); | ||||||
|  |                     } | ||||||
|  |                     Some(&NOP) => {} | ||||||
|  |                     Some(&DATA_MARK) => {} | ||||||
|  |                     Some(&BREAK) => { | ||||||
|  |                         textbuf.extend_from_slice(&ptr[1..]); | ||||||
|  |                         return (textbuf, Some(TelnetOutput::Break)); | ||||||
|  |                     } | ||||||
|  |                     Some(&INTERRUPT) => { | ||||||
|  |                         textbuf.extend_from_slice(&ptr[1..]); | ||||||
|  |                         return (textbuf, Some(TelnetOutput::Interrupt)); | ||||||
|  |                     } | ||||||
|  |                     Some(&ABORT) => { | ||||||
|  |                         textbuf.extend_from_slice(&ptr[1..]); | ||||||
|  |                         return (textbuf, Some(TelnetOutput::AbortOutput)); | ||||||
|  |                     } | ||||||
|  |                     Some(&AYT) => { | ||||||
|  |                         textbuf.extend_from_slice(&ptr[1..]); | ||||||
|  |                         return (textbuf, Some(TelnetOutput::AreYouThere)); | ||||||
|  |                     } | ||||||
|  |                     Some(&ERASECHAR) => { | ||||||
|  |                         if !textbuf.is_empty() { | ||||||
|  |                             textbuf = textbuf[0..textbuf.len() - 1].to_owned(); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     Some(&ERASELINE) => { | ||||||
|  |                         textbuf = vec![]; | ||||||
|  |                     } | ||||||
|  |                     Some(&GOAHEAD) => { | ||||||
|  |                         if textbuf.len() > 1 { | ||||||
|  |                             return (ptr[1..].to_owned(), Some(TelnetOutput::Prompt(textbuf))); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     Some(&EOR) => { | ||||||
|  |                         if textbuf.len() > 1 { | ||||||
|  |                             return (ptr[1..].to_owned(), Some(TelnetOutput::Prompt(textbuf))); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     Some(&WILL) => { | ||||||
|  |                         ptr = &ptr[1..]; | ||||||
|  |                         match ptr.first() { | ||||||
|  |                             None => { | ||||||
|  |                                 textbuf.push(IAC); | ||||||
|  |                                 textbuf.push(WILL); | ||||||
|  |                                 return (textbuf, None); | ||||||
|  |                             } | ||||||
|  |                             Some(c) => { | ||||||
|  |                                 textbuf.extend_from_slice(&ptr[1..]); | ||||||
|  |                                 return (textbuf, Some(TelnetOutput::Will(*c))); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     Some(&WONT) => { | ||||||
|  |                         ptr = &ptr[1..]; | ||||||
|  |                         match ptr.first() { | ||||||
|  |                             None => { | ||||||
|  |                                 textbuf.push(IAC); | ||||||
|  |                                 textbuf.push(WONT); | ||||||
|  |                                 return (textbuf, None); | ||||||
|  |                             } | ||||||
|  |                             Some(c) => { | ||||||
|  |                                 textbuf.extend_from_slice(&ptr[1..]); | ||||||
|  |                                 return (textbuf, Some(TelnetOutput::Wont(*c))); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     Some(&DO) => { | ||||||
|  |                         ptr = &ptr[1..]; | ||||||
|  |                         match ptr.first() { | ||||||
|  |                             None => { | ||||||
|  |                                 textbuf.push(IAC); | ||||||
|  |                                 textbuf.push(DO); | ||||||
|  |                                 return (textbuf, None); | ||||||
|  |                             } | ||||||
|  |                             Some(c) => { | ||||||
|  |                                 textbuf.extend_from_slice(&ptr[1..]); | ||||||
|  |                                 return (textbuf, Some(TelnetOutput::Do(*c))); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     Some(&DONT) => { | ||||||
|  |                         ptr = &ptr[1..]; | ||||||
|  |                         match ptr.first() { | ||||||
|  |                             None => { | ||||||
|  |                                 textbuf.push(IAC); | ||||||
|  |                                 textbuf.push(DONT); | ||||||
|  |                                 return (textbuf, None); | ||||||
|  |                             } | ||||||
|  |                             Some(c) => { | ||||||
|  |                                 textbuf.extend_from_slice(&ptr[1..]); | ||||||
|  |                                 return (textbuf, Some(TelnetOutput::Dont(*c))); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     Some(&STARTSUB) => { | ||||||
|  |                         match (1..ptr.len() - 1).find(|i| ptr[*i] == IAC && ptr[*i + 1] == ENDSUB) { | ||||||
|  |                             None => { | ||||||
|  |                                 // Including the STARTSUB...
 | ||||||
|  |                                 textbuf.push(IAC); | ||||||
|  |                                 textbuf.extend_from_slice(ptr); | ||||||
|  |                                 return (textbuf, None); | ||||||
|  |                             } | ||||||
|  |                             Some(end_idx) => { | ||||||
|  |                                 textbuf.extend_from_slice(&ptr[end_idx + 2..]); | ||||||
|  |                                 return ( | ||||||
|  |                                     textbuf, | ||||||
|  |                                     Some(TelnetOutput::Subnegotiation(ptr[1..end_idx].to_owned())), | ||||||
|  |                                 ); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     Some(c) => { | ||||||
|  |                         // Completely unexpected command. Warn and ignore.
 | ||||||
|  |                         console::log_1(&JsValue::from_str(&format!( | ||||||
|  |                             "Received unknown IAC command {}, assuming single byte and ignoring.", | ||||||
|  |                             c | ||||||
|  |                         ))); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             Some(c) => textbuf.push(*c), | ||||||
|  |         } | ||||||
|  |         ptr = &ptr[1..]; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(test)] | ||||||
|  | mod tests { | ||||||
|  |     use super::*; | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn parse_telnet_works() { | ||||||
|  |         assert_eq!(parse_telnet_buf(b"Hello"), (b"Hello".to_vec(), None)); | ||||||
|  |         assert_eq!( | ||||||
|  |             parse_telnet_buf(b"Hello\r\n"), | ||||||
|  |             ( | ||||||
|  |                 b"".to_vec(), | ||||||
|  |                 Some(TelnetOutput::Line(b"Hello\r\n".to_vec())) | ||||||
|  |             ) | ||||||
|  |         ); | ||||||
|  |         assert_eq!( | ||||||
|  |             parse_telnet_buf(b"Hello\r\nWorld"), | ||||||
|  |             ( | ||||||
|  |                 b"World".to_vec(), | ||||||
|  |                 Some(TelnetOutput::Line(b"Hello\r\n".to_vec())) | ||||||
|  |             ) | ||||||
|  |         ); | ||||||
|  |         assert_eq!( | ||||||
|  |             parse_telnet_buf(b"Hello> \xff\xf9World"), // Go Ahead after prompt.
 | ||||||
|  |             ( | ||||||
|  |                 b"World".to_vec(), | ||||||
|  |                 Some(TelnetOutput::Prompt(b"Hello> ".to_vec())) | ||||||
|  |             ) | ||||||
|  |         ); | ||||||
|  |         assert_eq!( | ||||||
|  |             parse_telnet_buf(b"Hello> \xff\xefWorld"), // End Of Record after prompt.
 | ||||||
|  |             ( | ||||||
|  |                 b"World".to_vec(), | ||||||
|  |                 Some(TelnetOutput::Prompt(b"Hello> ".to_vec())) | ||||||
|  |             ) | ||||||
|  |         ); | ||||||
|  |         assert_eq!( | ||||||
|  |             parse_telnet_buf(b"Hello \xff\xf1World\r\nWoo!"), // NOP in the middle.
 | ||||||
|  |             ( | ||||||
|  |                 b"Woo!".to_vec(), | ||||||
|  |                 Some(TelnetOutput::Line(b"Hello World\r\n".to_vec())) | ||||||
|  |             ) | ||||||
|  |         ); | ||||||
|  |         assert_eq!( | ||||||
|  |             parse_telnet_buf(b"Hello \xff\xf2World\r\nWoo!"), // DATA MARK in the middle.
 | ||||||
|  |             ( | ||||||
|  |                 b"Woo!".to_vec(), | ||||||
|  |                 Some(TelnetOutput::Line(b"Hello World\r\n".to_vec())) | ||||||
|  |             ) | ||||||
|  |         ); | ||||||
|  |         assert_eq!( | ||||||
|  |             parse_telnet_buf(b"Hello \xff\xf3World\r\nWoo!"), // BREAK in the middle.
 | ||||||
|  |             (b"Hello World\r\nWoo!".to_vec(), Some(TelnetOutput::Break)) | ||||||
|  |         ); | ||||||
|  |         assert_eq!( | ||||||
|  |             parse_telnet_buf(b"Hello \xff\xf4World\r\nWoo!"), // INTERRUPT in the middle.
 | ||||||
|  |             ( | ||||||
|  |                 b"Hello World\r\nWoo!".to_vec(), | ||||||
|  |                 Some(TelnetOutput::Interrupt) | ||||||
|  |             ) | ||||||
|  |         ); | ||||||
|  |         assert_eq!( | ||||||
|  |             parse_telnet_buf(b"Hello \xff"), // Incomplete IAC command.
 | ||||||
|  |             (b"Hello \xff".to_vec(), None) | ||||||
|  |         ); | ||||||
|  |         assert_eq!( | ||||||
|  |             parse_telnet_buf(b"Hello \xff\xf5World!"), // Abort
 | ||||||
|  |             (b"Hello World!".to_vec(), Some(TelnetOutput::AbortOutput)) | ||||||
|  |         ); | ||||||
|  |         assert_eq!( | ||||||
|  |             parse_telnet_buf(b"Hello \xff\xf6World!"), // Are You There
 | ||||||
|  |             (b"Hello World!".to_vec(), Some(TelnetOutput::AreYouThere)) | ||||||
|  |         ); | ||||||
|  |         assert_eq!( | ||||||
|  |             parse_telnet_buf(b"Hello B\xff\xf7World!"), // Erase Char
 | ||||||
|  |             (b"Hello World!".to_vec(), None) | ||||||
|  |         ); | ||||||
|  |         assert_eq!( | ||||||
|  |             parse_telnet_buf(b"Whoops \xff\xf8Hello World!"), // Erase Line
 | ||||||
|  |             (b"Hello World!".to_vec(), None) | ||||||
|  |         ); | ||||||
|  |         assert_eq!( | ||||||
|  |             parse_telnet_buf(b"Hello \xff\xfb\x01World!"), // Will
 | ||||||
|  |             (b"Hello World!".to_vec(), Some(TelnetOutput::Will(1))) | ||||||
|  |         ); | ||||||
|  |         assert_eq!( | ||||||
|  |             parse_telnet_buf(b"Hello \xff\xfc\x02World!"), // Wont
 | ||||||
|  |             (b"Hello World!".to_vec(), Some(TelnetOutput::Wont(2))) | ||||||
|  |         ); | ||||||
|  |         assert_eq!( | ||||||
|  |             parse_telnet_buf(b"Hello \xff\xfd\x03World!"), // Do
 | ||||||
|  |             (b"Hello World!".to_vec(), Some(TelnetOutput::Do(3))) | ||||||
|  |         ); | ||||||
|  |         assert_eq!( | ||||||
|  |             parse_telnet_buf(b"Hello \xff\xfe\x04World!"), // Dont
 | ||||||
|  |             (b"Hello World!".to_vec(), Some(TelnetOutput::Dont(4))) | ||||||
|  |         ); | ||||||
|  |         assert_eq!( | ||||||
|  |             parse_telnet_buf(b"Hello \xff\xfablah"), // Partial subnegotiation
 | ||||||
|  |             (b"Hello \xff\xfablah".to_vec(), None) | ||||||
|  |         ); | ||||||
|  |         assert_eq!( | ||||||
|  |             parse_telnet_buf(b"Hello \xff\xfablah\xff\xf0World!"), // Partial subnegotiation
 | ||||||
|  |             ( | ||||||
|  |                 b"Hello World!".to_vec(), | ||||||
|  |                 Some(TelnetOutput::Subnegotiation(b"blah".to_vec())) | ||||||
|  |             ) | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -216,36 +216,30 @@ mod tests { | |||||||
|     #[test] |     #[test] | ||||||
|     fn modify_at_pathstr_works() { |     fn modify_at_pathstr_works() { | ||||||
|         use TermSplit::*; |         use TermSplit::*; | ||||||
|         let mut t = Term { |         let t = Term { | ||||||
|             frame: TermFrame(1), |             frame: TermFrame(1), | ||||||
|         }; |         }; | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             t.modify_at_pathstr("", |v| { |             t.modify_at_pathstr("", |_v| { | ||||||
|                 *v = Term { |                 Ok(Term { | ||||||
|                     frame: TermFrame(2), |                     frame: TermFrame(2), | ||||||
|                 }; |                 }) | ||||||
|                 Ok(()) |  | ||||||
|             }), |             }), | ||||||
|             Ok(()) |             Ok(Term { | ||||||
|         ); |                 frame: TermFrame(2), | ||||||
|         assert_eq!( |             }) | ||||||
|             t, |  | ||||||
|             Term { |  | ||||||
|                 frame: TermFrame(2) |  | ||||||
|             } |  | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             t.modify_at_pathstr("tlr", |v| { |             t.modify_at_pathstr("tlr", |_v| { | ||||||
|                 *v = Term { |                 Ok(Term { | ||||||
|                     frame: TermFrame(2), |                     frame: TermFrame(2), | ||||||
|                 }; |                 }) | ||||||
|                 Ok(()) |  | ||||||
|             }), |             }), | ||||||
|             Err("In split path, found trailing junk tlr after addressing terminal".to_owned()) |             Err("In split path, found trailing junk tlr after addressing terminal".to_owned()) | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         let mut t = Vertical { |         let t = Vertical { | ||||||
|             top: Horizontal { |             top: Horizontal { | ||||||
|                 left: Horizontal { |                 left: Horizontal { | ||||||
|                     left: Term { |                     left: Term { | ||||||
| @ -277,26 +271,17 @@ mod tests { | |||||||
|             .into(), |             .into(), | ||||||
|         }; |         }; | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             t.modify_at_pathstr("tlr", |v| { |             t.modify_at_pathstr("tlr", |_v| { | ||||||
|                 *v = Term { |                 Ok(Term { | ||||||
|                     frame: TermFrame(2), |                     frame: TermFrame(2), | ||||||
|                 }; |                 }) | ||||||
|                 Ok(()) |             }) | ||||||
|             }), |             .and_then(|t| t.modify_at_pathstr("bb", |_v| { | ||||||
|             Ok(()) |                 Ok(Term { | ||||||
|         ); |  | ||||||
|         assert_eq!( |  | ||||||
|             t.modify_at_pathstr("bb", |v| { |  | ||||||
|                 *v = Term { |  | ||||||
|                     frame: TermFrame(3), |                     frame: TermFrame(3), | ||||||
|                 }; |                 }) | ||||||
|                 Ok(()) |             })), | ||||||
|             }), |             Ok(Vertical { | ||||||
|             Ok(()) |  | ||||||
|         ); |  | ||||||
|         assert_eq!( |  | ||||||
|             t, |  | ||||||
|             Vertical { |  | ||||||
|                 top: Horizontal { |                 top: Horizontal { | ||||||
|                     left: Horizontal { |                     left: Horizontal { | ||||||
|                         left: Term { |                         left: Term { | ||||||
| @ -326,7 +311,7 @@ mod tests { | |||||||
|                     .into(), |                     .into(), | ||||||
|                 } |                 } | ||||||
|                 .into(), |                 .into(), | ||||||
|             } |             }) | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user