nushell on your web browser
nushell wasm terminal
at main 7.5 kB view raw
1use js_sys::Reflect; 2use js_sys::global; 3use nu_protocol::Type; 4use nu_protocol::{ 5 Category, IntoPipelineData, PipelineData, Record, ShellError, Signature, Value, 6 engine::{Command, EngineState, Stack}, 7}; 8use wasm_bindgen::JsValue; 9 10#[derive(Clone)] 11pub struct Sys; 12 13impl Command for Sys { 14 fn name(&self) -> &str { 15 "sys" 16 } 17 18 fn signature(&self) -> Signature { 19 Signature::build("sys") 20 .input_output_type(Type::Nothing, Type::record()) 21 .category(Category::System) 22 } 23 24 fn description(&self) -> &str { 25 "return information about the browser environment (when running in wasm)" 26 } 27 28 fn run( 29 &self, 30 _engine_state: &EngineState, 31 _stack: &mut Stack, 32 call: &nu_protocol::engine::Call, 33 _input: PipelineData, 34 ) -> Result<PipelineData, ShellError> { 35 let head = call.head; 36 let mut rec = Record::new(); 37 38 let g = global(); 39 40 // navigator-derived fields 41 if let Ok(nav) = Reflect::get(&g, &JsValue::from_str("navigator")) { 42 if !nav.is_undefined() && !nav.is_null() { 43 let ua = Reflect::get(&nav, &JsValue::from_str("userAgent")) 44 .ok() 45 .and_then(|v| v.as_string()) 46 .unwrap_or_default(); 47 rec.push("user_agent", Value::string(ua, head)); 48 49 let platform = Reflect::get(&nav, &JsValue::from_str("platform")) 50 .ok() 51 .and_then(|v| v.as_string()) 52 .unwrap_or_default(); 53 rec.push("platform", Value::string(platform, head)); 54 55 let vendor = Reflect::get(&nav, &JsValue::from_str("vendor")) 56 .ok() 57 .and_then(|v| v.as_string()) 58 .unwrap_or_default(); 59 rec.push("vendor", Value::string(vendor, head)); 60 61 let product = Reflect::get(&nav, &JsValue::from_str("product")) 62 .ok() 63 .and_then(|v| v.as_string()) 64 .unwrap_or_default(); 65 rec.push("product", Value::string(product, head)); 66 67 let app_name = Reflect::get(&nav, &JsValue::from_str("appName")) 68 .ok() 69 .and_then(|v| v.as_string()) 70 .unwrap_or_default(); 71 rec.push("app_name", Value::string(app_name, head)); 72 73 let language = Reflect::get(&nav, &JsValue::from_str("language")) 74 .ok() 75 .and_then(|v| v.as_string()) 76 .unwrap_or_default(); 77 rec.push("language", Value::string(language, head)); 78 79 // booleans now use Value::bool 80 let online = Reflect::get(&nav, &JsValue::from_str("onLine")) 81 .ok() 82 .and_then(|v| v.as_bool()) 83 .unwrap_or(false); 84 rec.push("online", Value::bool(online, head)); 85 86 let cookie_enabled = Reflect::get(&nav, &JsValue::from_str("cookieEnabled")) 87 .ok() 88 .and_then(|v| v.as_bool()) 89 .unwrap_or(false); 90 rec.push("cookie_enabled", Value::bool(cookie_enabled, head)); 91 92 // numeric-ish fields 93 let hardware_concurrency = 94 Reflect::get(&nav, &JsValue::from_str("hardwareConcurrency")) 95 .ok() 96 .and_then(|v| v.as_f64()) 97 .map(|f| f as i64); 98 if let Some(hc) = hardware_concurrency { 99 rec.push("hardware_concurrency", Value::int(hc, head)); 100 } 101 102 let device_memory = Reflect::get(&nav, &JsValue::from_str("deviceMemory")) 103 .ok() 104 .and_then(|v| v.as_f64()) 105 .map(|f| f as i64); 106 if let Some(dm) = device_memory { 107 rec.push("device_memory_gb", Value::int(dm, head)); 108 } 109 110 let max_touch_points = Reflect::get(&nav, &JsValue::from_str("maxTouchPoints")) 111 .ok() 112 .and_then(|v| v.as_f64()) 113 .map(|f| f as i64); 114 if let Some(tp) = max_touch_points { 115 rec.push("max_touch_points", Value::int(tp, head)); 116 } 117 118 let dnt = Reflect::get(&nav, &JsValue::from_str("doNotTrack")) 119 .ok() 120 .and_then(|v| v.as_string()) 121 .unwrap_or_default(); 122 if !dnt.is_empty() { 123 rec.push("do_not_track", Value::string(dnt, head)); 124 } 125 } 126 } 127 128 // screen dimensions (if available) 129 if let Ok(screen) = Reflect::get(&g, &JsValue::from_str("screen")) { 130 if !screen.is_undefined() && !screen.is_null() { 131 let width = Reflect::get(&screen, &JsValue::from_str("width")) 132 .ok() 133 .and_then(|v| v.as_f64()) 134 .map(|f| f as i64); 135 let height = Reflect::get(&screen, &JsValue::from_str("height")) 136 .ok() 137 .and_then(|v| v.as_f64()) 138 .map(|f| f as i64); 139 if let Some(w) = width { 140 rec.push("screen_width", Value::int(w, head)); 141 } 142 if let Some(h) = height { 143 rec.push("screen_height", Value::int(h, head)); 144 } 145 } 146 } 147 148 // performance.memory (optional) 149 if let Ok(perf) = Reflect::get(&g, &JsValue::from_str("performance")) { 150 if !perf.is_undefined() && !perf.is_null() { 151 if let Ok(mem) = Reflect::get(&perf, &JsValue::from_str("memory")) { 152 if !mem.is_undefined() && !mem.is_null() { 153 let used = Reflect::get(&mem, &JsValue::from_str("usedJSHeapSize")) 154 .ok() 155 .and_then(|v| v.as_f64()) 156 .map(|f| f as i64); 157 let limit = Reflect::get(&mem, &JsValue::from_str("jsHeapSizeLimit")) 158 .ok() 159 .and_then(|v| v.as_f64()) 160 .map(|f| f as i64); 161 let mut mrec = Record::new(); 162 if let Some(u) = used { 163 mrec.push("used_js_heap_size", Value::filesize(u, head)); 164 } 165 if let Some(l) = limit { 166 mrec.push("js_heap_size_limit", Value::filesize(l, head)); 167 } 168 if !mrec.is_empty() { 169 rec.push("performance_memory", Value::record(mrec, head)); 170 } 171 } 172 } 173 } 174 } 175 176 if rec.is_empty() { 177 rec.push( 178 "error", 179 Value::string("not running in a browser environment", head), 180 ); 181 } 182 183 let date = compile_time::unix!(); 184 let rustc = compile_time::rustc_version_str!(); 185 186 rec.push("build_time", Value::int(date, head)); 187 rec.push("rustc_version", Value::string(rustc, head)); 188 189 Ok(Value::record(rec, head).into_pipeline_data()) 190 } 191}