razer-linux-desktop/src/app/helpers/formatting.rs
2026-05-17 21:33:41 +02:00

137 lines
4.5 KiB
Rust

fn js_error_to_string(value: JsValue) -> String {
value
.as_string()
.or_else(|| {
js_sys::Reflect::get(&value, &JsValue::from_str("message"))
.ok()
.and_then(|message| message.as_string())
})
.unwrap_or_else(|| "Tauri command failed".to_string())
}
fn color_to_hex(color: &[u8; 3]) -> String {
format!("#{:02x}{:02x}{:02x}", color[0], color[1], color[2])
}
fn hex_to_rgb(hex: &str) -> [u8; 3] {
if hex.len() != 7 || !hex.starts_with('#') {
return [255, 255, 255];
}
[
u8::from_str_radix(&hex[1..3], 16).unwrap_or(255),
u8::from_str_radix(&hex[3..5], 16).unwrap_or(255),
u8::from_str_radix(&hex[5..7], 16).unwrap_or(255),
]
}
fn bytes_to_hex(bytes: &[u8]) -> String {
bytes
.iter()
.map(|byte| format!("{byte:02x}"))
.collect::<Vec<_>>()
.join(" ")
}
fn parse_hex_bytes(input: &str) -> Vec<u8> {
input
.split(|ch: char| ch.is_whitespace() || ch == ',' || ch == ':')
.filter(|segment| !segment.is_empty())
.filter_map(|segment| {
let cleaned = segment.strip_prefix("0x").unwrap_or(segment);
u8::from_str_radix(cleaned, 16).ok()
})
.collect()
}
fn format_log_timestamp(timestamp_ms: u64) -> String {
if timestamp_ms == 0 {
return "--:--:--".to_string();
}
let date = js_sys::Date::new(&JsValue::from_f64(timestamp_ms as f64));
format!(
"{:02}:{:02}:{:02}",
date.get_hours(),
date.get_minutes(),
date.get_seconds()
)
}
fn format_macro_operations_yaml(operations: &[MacroOperationState]) -> Result<String, String> {
let yaml_ops = operations
.iter()
.map(|operation| (operation.category.clone(), operation.payload.clone()))
.collect::<Vec<_>>();
serde_yaml::to_string(&yaml_ops).map_err(|error| error.to_string())
}
fn parse_macro_operations_yaml(input: &str) -> Result<Vec<MacroOperationState>, String> {
let value = serde_yaml::from_str::<serde_yaml::Value>(input).map_err(|error| error.to_string())?;
let Some(items) = value.as_sequence() else {
return Err("Macro YAML must be a list of operations".to_string());
};
items.iter()
.map(|item| {
if let Some(mapping) = item.as_mapping() {
let category = mapping
.get(serde_yaml::Value::String("category".to_string()))
.and_then(serde_yaml::Value::as_str)
.ok_or_else(|| "Macro operation object is missing category".to_string())?;
let payload = mapping
.get(serde_yaml::Value::String("payload".to_string()))
.cloned()
.unwrap_or(serde_yaml::Value::Null);
return Ok(MacroOperationState {
category: category.to_string(),
payload: serde_json::to_value(payload).map_err(|error| error.to_string())?,
});
}
let Some(pair) = item.as_sequence() else {
return Err("Macro operation must be a [category, payload] pair or object".to_string());
};
if pair.len() != 2 {
return Err("Macro operation pair must contain exactly two items".to_string());
}
let category = pair[0]
.as_str()
.ok_or_else(|| "Macro operation category must be a string".to_string())?;
Ok(MacroOperationState {
category: category.to_string(),
payload: serde_json::to_value(pair[1].clone()).map_err(|error| error.to_string())?,
})
})
.collect()
}
fn mock_led_state(profile: String) -> LedState {
LedState {
profile,
regions: vec![
LedRegionState {
region: "wheel".to_string(),
effect: "wave".to_string(),
mode: 1,
speed: 180,
colors: vec![],
brightness: 200,
},
LedRegionState {
region: "logo".to_string(),
effect: "static".to_string(),
mode: 0,
speed: 0,
colors: vec![[0, 255, 0]],
brightness: 180,
},
LedRegionState {
region: "strip".to_string(),
effect: "spectrum".to_string(),
mode: 1,
speed: 180,
colors: vec![],
brightness: 255,
},
],
}
}