#![allow(clippy::uninlined_format_args)]
use std::env;
use crate::utils::{
module::{export_default, ModuleInfo},
sysinfo::{ARCH, PLATFORM},
};
use rquickjs::{
module::{Declarations, Exports, ModuleDef},
prelude::Func,
Ctx, Exception, Object, Result,
};
#[cfg(feature = "system")]
use sysinfo::System;
#[cfg(unix)]
use self::unix::{
get_priority, get_release, get_type, get_user_info, get_version, set_priority, DEV_NULL, EOL,
};
#[cfg(unix)]
mod unix;
#[cfg(feature = "network")]
use self::network::get_network_interfaces;
#[cfg(feature = "statistics")]
use self::statistics::{get_cpus, get_free_mem, get_total_mem};
#[cfg(feature = "network")]
mod network;
#[cfg(feature = "statistics")]
mod statistics;
fn get_available_parallelism() -> usize {
num_cpus::get()
}
fn get_endianness() -> &'static str {
#[cfg(target_endian = "little")]
{
"LE"
}
#[cfg(target_endian = "big")]
{
"BE"
}
}
fn get_home_dir(ctx: Ctx<'_>) -> Result<String> {
home::home_dir()
.map(|val| val.to_string_lossy().into_owned())
.ok_or_else(|| Exception::throw_message(&ctx, "Could not determine home directory"))
}
#[cfg(feature = "system")]
fn get_host_name(ctx: Ctx<'_>) -> Result<String> {
System::host_name().ok_or_else(|| Exception::throw_reference(&ctx, "System::host_name"))
}
#[cfg(feature = "system")]
fn get_load_avg() -> Vec<f64> {
let load_avg = System::load_average();
vec![load_avg.one, load_avg.five, load_avg.fifteen]
}
#[cfg(feature = "system")]
fn get_machine() -> String {
System::cpu_arch()
}
fn get_tmp_dir() -> String {
env::temp_dir().to_string_lossy().to_string()
}
#[cfg(feature = "system")]
fn get_uptime() -> u64 {
System::uptime()
}
pub struct OsModule;
impl ModuleDef for OsModule {
fn declare(declare: &Declarations) -> Result<()> {
declare.declare("arch")?;
declare.declare("availableParallelism")?;
declare.declare("devNull")?;
declare.declare("endianness")?;
declare.declare("EOL")?;
declare.declare("getPriority")?;
declare.declare("homedir")?;
declare.declare("platform")?;
declare.declare("release")?;
declare.declare("setPriority")?;
declare.declare("tmpdir")?;
declare.declare("type")?;
declare.declare("userInfo")?;
declare.declare("version")?;
#[cfg(feature = "network")]
{
declare.declare("networkInterfaces")?;
}
#[cfg(feature = "statistics")]
{
declare.declare("cpus")?;
declare.declare("freemem")?;
declare.declare("totalmem")?;
}
#[cfg(feature = "system")]
{
declare.declare("hostname")?;
declare.declare("loadavg")?;
declare.declare("machine")?;
declare.declare("uptime")?;
}
declare.declare("default")?;
Ok(())
}
fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> Result<()> {
export_default(ctx, exports, |default| {
fill(default)?;
Ok(())
})
}
}
pub fn fill(target: &Object<'_>) -> Result<()> {
use crate::permissions::check_sys;
use ferrijs_permissions::SysInfo;
use rquickjs::{function::Opt, Value};
target.set("arch", Func::from(|| ARCH))?;
target.set(
"availableParallelism",
Func::from(get_available_parallelism),
)?;
target.set("devNull", DEV_NULL)?;
target.set("endianness", Func::from(get_endianness))?;
target.set("EOL", EOL)?;
target.set(
"getPriority",
Func::from(|ctx: Ctx<'_>, who: Opt<u32>| -> Result<i32> {
check_sys(&ctx, SysInfo::Priority)?;
Ok(get_priority(who))
}),
)?;
target.set(
"homedir",
Func::from(|ctx: Ctx<'_>| -> Result<String> {
check_sys(&ctx, SysInfo::HomeDir)?;
get_home_dir(ctx)
}),
)?;
target.set("platform", Func::from(|| PLATFORM))?;
target.set(
"release",
Func::from(|ctx: Ctx<'_>| -> Result<&'static str> {
check_sys(&ctx, SysInfo::OsRelease)?;
Ok(get_release())
}),
)?;
target.set(
"setPriority",
Func::from(|ctx: Ctx<'_>, args: rquickjs::function::Rest<Value<'_>>| -> Result<()> {
check_sys(&ctx, SysInfo::Priority)?;
set_priority(ctx, args)
}),
)?;
target.set("tmpdir", Func::from(get_tmp_dir))?;
target.set("type", Func::from(get_type))?;
target.set("userInfo", Func::from(user_info_guarded))?;
target.set(
"version",
Func::from(|ctx: Ctx<'_>| -> Result<&'static str> {
check_sys(&ctx, SysInfo::OsRelease)?;
Ok(get_version())
}),
)?;
#[cfg(feature = "network")]
{
target.set("networkInterfaces", Func::from(network_interfaces_guarded))?;
}
#[cfg(feature = "statistics")]
{
target.set("cpus", Func::from(cpus_guarded))?;
target.set(
"freemem",
Func::from(|ctx: Ctx<'_>| -> Result<u64> {
check_sys(&ctx, SysInfo::SystemMemory)?;
Ok(get_free_mem())
}),
)?;
target.set(
"totalmem",
Func::from(|ctx: Ctx<'_>| -> Result<u64> {
check_sys(&ctx, SysInfo::SystemMemory)?;
Ok(get_total_mem())
}),
)?;
}
#[cfg(feature = "system")]
{
target.set(
"hostname",
Func::from(|ctx: Ctx<'_>| -> Result<String> {
check_sys(&ctx, SysInfo::Hostname)?;
get_host_name(ctx)
}),
)?;
target.set(
"loadavg",
Func::from(|ctx: Ctx<'_>| -> Result<Vec<f64>> {
check_sys(&ctx, SysInfo::LoadAvg)?;
Ok(get_load_avg())
}),
)?;
target.set(
"machine",
Func::from(|ctx: Ctx<'_>| -> Result<String> {
check_sys(&ctx, SysInfo::OsRelease)?;
Ok(get_machine())
}),
)?;
target.set(
"uptime",
Func::from(|ctx: Ctx<'_>| -> Result<u64> {
check_sys(&ctx, SysInfo::OsUptime)?;
Ok(get_uptime())
}),
)?;
}
Ok(())
}
fn user_info_guarded<'js>(ctx: Ctx<'js>, options: rquickjs::function::Opt<rquickjs::Value<'js>>) -> Result<Object<'js>> {
crate::permissions::check_sys(&ctx, ferrijs_permissions::SysInfo::Username)?;
get_user_info(ctx, options)
}
#[cfg(feature = "network")]
fn network_interfaces_guarded<'js>(
ctx: Ctx<'js>,
) -> Result<std::collections::HashMap<String, Vec<Object<'js>>>> {
crate::permissions::check_sys(&ctx, ferrijs_permissions::SysInfo::NetworkInterfaces)?;
get_network_interfaces(ctx)
}
#[cfg(feature = "statistics")]
fn cpus_guarded<'js>(ctx: Ctx<'js>) -> Result<Vec<Object<'js>>> {
crate::permissions::check_sys(&ctx, ferrijs_permissions::SysInfo::Cpus)?;
get_cpus(ctx)
}
pub fn os_object<'js>(ctx: &Ctx<'js>) -> Result<Object<'js>> {
let obj = Object::new(ctx.clone())?;
fill(&obj)?;
Ok(obj)
}
impl From<OsModule> for ModuleInfo<OsModule> {
fn from(val: OsModule) -> Self {
ModuleInfo {
name: "os",
module: val,
}
}
}
#[cfg(test)]
mod tests {
use crate::test::{call_test, test_async_with, ModuleEvaluator};
use rquickjs::{Ctx, Value};
use super::*;
async fn run_test_return_string(
ctx: &Ctx<'_>,
name: &str,
is_function: bool,
expected_assertion: impl Fn(String),
) {
ModuleEvaluator::eval_rust::<OsModule>(ctx.clone(), "os")
.await
.unwrap();
let brackets = if is_function { "()" } else { "" };
let module = ModuleEvaluator::eval_js(
ctx.clone(),
"test",
&format!(
r#"
import {{ {} }} from 'os';
export async function test() {{
return {}{}
}}
"#,
name, name, brackets
),
)
.await
.unwrap();
let result = call_test::<String, _>(ctx, &module, ()).await;
expected_assertion(result);
}
async fn run_test_return_number(ctx: &Ctx<'_>, name: &str, expected_assertion: impl Fn(Value)) {
ModuleEvaluator::eval_rust::<OsModule>(ctx.clone(), "os")
.await
.unwrap();
let module = ModuleEvaluator::eval_js(
ctx.clone(),
"test",
&format!(
r#"
import {{ {} }} from 'os';
export async function test() {{
return {}()
}}
"#,
name, name
),
)
.await
.unwrap();
let result = call_test::<Value, _>(ctx, &module, ()).await;
expected_assertion(result);
}
#[tokio::test]
async fn test_available_parallelism() {
test_async_with(|ctx| {
Box::pin(async move {
run_test_return_number(&ctx, "availableParallelism", |result| {
assert!(result.is_number()); })
.await;
})
})
.await;
}
#[tokio::test]
async fn test_arch() {
test_async_with(|ctx| {
Box::pin(async move {
run_test_return_string(&ctx, "type", true, |result| {
assert!(!result.is_empty()); })
.await;
})
})
.await;
}
#[tokio::test]
async fn test_devnull() {
test_async_with(|ctx| {
Box::pin(async move {
run_test_return_string(&ctx, "devNull", false, |result| {
assert_eq!(result, DEV_NULL);
})
.await;
})
})
.await;
}
#[tokio::test]
async fn test_endianness() {
test_async_with(|ctx| {
Box::pin(async move {
run_test_return_string(&ctx, "endianness", true, |result| {
let endianness = if cfg!(target_endian = "little") {
"LE".to_string()
} else {
"BE".to_string()
};
assert_eq!(result, endianness);
})
.await;
})
})
.await;
}
#[tokio::test]
async fn test_eol() {
test_async_with(|ctx| {
Box::pin(async move {
run_test_return_string(&ctx, "EOL", false, |result| {
assert_eq!(result, EOL);
})
.await;
})
})
.await;
}
#[cfg(feature = "statistics")]
#[tokio::test]
async fn test_freemem() {
test_async_with(|ctx| {
Box::pin(async move {
run_test_return_number(&ctx, "freemem", |result| {
assert!(result.is_number()); })
.await;
})
})
.await;
}
#[tokio::test]
async fn test_homedir() {
test_async_with(|ctx| {
Box::pin(async move {
run_test_return_string(&ctx, "homedir", true, |result| {
assert!(!result.is_empty()); })
.await;
})
})
.await;
}
#[cfg(feature = "system")]
#[tokio::test]
async fn test_hostname() {
test_async_with(|ctx| {
Box::pin(async move {
run_test_return_string(&ctx, "hostname", true, |result| {
assert!(!result.is_empty()); })
.await;
})
})
.await;
}
#[cfg(feature = "system")]
#[tokio::test]
async fn test_machine() {
test_async_with(|ctx| {
Box::pin(async move {
run_test_return_string(&ctx, "machine", true, |result| {
assert!(!result.is_empty()); })
.await;
})
})
.await;
}
#[tokio::test]
async fn test_platform() {
test_async_with(|ctx| {
Box::pin(async move {
run_test_return_string(&ctx, "platform", true, |result| {
assert!(!result.is_empty()); })
.await;
})
})
.await;
}
#[tokio::test]
async fn test_release() {
test_async_with(|ctx| {
Box::pin(async move {
run_test_return_string(&ctx, "release", true, |result| {
assert!(!result.is_empty()); })
.await;
})
})
.await;
}
#[tokio::test]
async fn test_tmpdir() {
test_async_with(|ctx| {
Box::pin(async move {
run_test_return_string(&ctx, "tmpdir", true, |result| {
assert!(!result.is_empty()); })
.await;
})
})
.await;
}
#[cfg(feature = "statistics")]
#[tokio::test]
async fn test_totalmem() {
test_async_with(|ctx| {
Box::pin(async move {
run_test_return_number(&ctx, "totalmem", |result| {
assert!(result.is_number()); })
.await;
})
})
.await;
}
#[tokio::test]
async fn test_type() {
test_async_with(|ctx| {
Box::pin(async move {
run_test_return_string(&ctx, "type", true, |result| {
assert!(result == "Linux" || result == "Windows_NT" || result == "Darwin");
})
.await;
})
})
.await;
}
#[cfg(feature = "system")]
#[tokio::test]
async fn test_uptime() {
test_async_with(|ctx| {
Box::pin(async move {
run_test_return_number(&ctx, "uptime", |result| {
assert!(result.is_number()); })
.await;
})
})
.await;
}
#[tokio::test]
async fn test_version() {
test_async_with(|ctx| {
Box::pin(async move {
run_test_return_string(&ctx, "version", true, |result| {
assert!(!result.is_empty()); })
.await;
})
})
.await;
}
}