mod config;
mod dialog;
mod manager;
mod persistence;
#[cfg(test)]
mod tests;
mod types;
pub use crate::style::MenuEffects;
pub use config::MenuConfig;
pub use dialog::{MenuConfigDialog, MenuConfigPanel};
pub use manager::{
apply_overrides, current_effects, init, install_policy, manager, set_enabled,
use_verification_policy, MenuConfigManager,
};
pub use persistence::{ConfigLoadError, ConfigPersistence, CURRENT_CONFIG_VERSION};
pub use types::{HardwareCapabilities, PerformanceLevel, UserOverrides};
use crate::compat::{String, Vec};
pub fn actions_to_json(actions: &[crate::action::ActionDefinition]) -> String {
let mut out = String::from("[");
for (index, action) in actions.iter().enumerate() {
if index != 0 {
out.push(',');
}
out.push_str(&action.to_json());
}
out.push(']');
out
}
pub fn actions_from_json(text: &str) -> Result<Vec<crate::action::ActionDefinition>, String> {
let trimmed = text.trim();
let body = trimmed
.strip_prefix('[')
.and_then(|rest| rest.strip_suffix(']'))
.ok_or_else(|| String::from("actions JSON must be an array"))?;
let mut actions = Vec::new();
let mut depth = 0usize;
let mut in_string = false;
let mut escaped = false;
let mut start = None;
for (index, ch) in body.char_indices() {
if in_string {
if escaped {
escaped = false;
} else if ch == '\\' {
escaped = true;
} else if ch == '"' {
in_string = false;
}
continue;
}
match ch {
'"' => in_string = true,
'{' => {
if depth == 0 {
start = Some(index);
}
depth += 1;
}
'}' => {
if depth == 0 {
return Err(String::from("actions JSON has an unmatched `}`"));
}
depth -= 1;
if depth == 0 {
let begin = start.take().ok_or_else(|| {
String::from("actions JSON has an object with no opening brace")
})?;
let object = &body[begin..=index];
actions.push(
crate::action::ActionDefinition::from_json(object)
.map_err(|reason| format!("invalid action entry: {reason}"))?,
);
}
}
_ => {}
}
}
if depth != 0 || in_string {
return Err(String::from("actions JSON ends inside an object or string"));
}
Ok(actions)
}