use hyprlang::{Color, Config, ConfigValue};
use std::collections::HashMap;
use std::path::PathBuf;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut config_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
config_path.push("examples/example.conf");
println!("╔═══════════════════════════════════════════════════════════════╗");
println!("║ Hyprlang Parser - Pretty Print Example ║");
println!("╚═══════════════════════════════════════════════════════════════╝\n");
println!("📄 Parsing: {}\n", config_path.display());
let mut config = Config::new();
config.parse_file(&config_path)?;
println!("✅ Successfully parsed configuration!\n");
pretty_print_config(&config);
Ok(())
}
fn pretty_print_config(config: &Config) {
println!("╭─────────────────────────────────────────────────────────────╮");
println!("│ VARIABLES │");
println!("╰─────────────────────────────────────────────────────────────╯\n");
print_variables(config);
println!("\n╭─────────────────────────────────────────────────────────────╮");
println!("│ CONFIGURATION VALUES │");
println!("╰─────────────────────────────────────────────────────────────╯\n");
print_categorized_values(config);
println!("╭─────────────────────────────────────────────────────────────╮");
println!("│ SUMMARY │");
println!("╰─────────────────────────────────────────────────────────────╯\n");
print_summary(config);
}
fn print_variables(config: &Config) {
let all_keys = config.keys();
let mut var_count = 0;
for key in &all_keys {
if !key.contains(':') && !key.starts_with('$') {
if let Ok(value) = config.get(key) {
if let ConfigValue::String(s) = value {
if !s.is_empty() {
continue;
}
}
}
}
}
println!(" Variables defined:");
for key in &all_keys {
if !key.contains(':') {
if let Ok(value) = config.get(key) {
match value {
ConfigValue::String(s) if !s.is_empty() && !key.starts_with('$') => {
println!(" ${:<20} = \"{}\"", key, s);
var_count += 1;
}
ConfigValue::Int(i) if !key.starts_with('$') => {
println!(" ${:<20} = {}", key, i);
var_count += 1;
}
_ => {}
}
}
}
}
if var_count == 0 {
println!(" ℹ️ No variables found");
}
}
fn print_categorized_values(config: &Config) {
let all_keys = config.keys();
let mut by_category: HashMap<String, Vec<(String, ConfigValue)>> = HashMap::new();
for key in &all_keys {
if let Ok(value) = config.get(key) {
if key.contains(':') {
let parts: Vec<&str> = key.split(':').collect();
let category = parts[0].to_string();
let subkey = parts[1..].join(":");
by_category
.entry(category)
.or_insert_with(Vec::new)
.push((subkey, value.clone()));
} else if !is_variable_like(key, value) {
by_category
.entry("".to_string())
.or_insert_with(Vec::new)
.push((key.to_string(), value.clone()));
}
}
}
if let Some(root_values) = by_category.get("") {
println!("┌─ Root Level ─────────────────────────────────────────────┐\n");
for (key, value) in root_values {
println!(" {:<30} = {}", key, format_value(value));
}
println!();
}
let mut categories: Vec<_> = by_category
.keys()
.filter(|k| !k.is_empty())
.cloned()
.collect();
categories.sort();
for category in categories {
let values = by_category.get(&category).unwrap();
print_category(&category, values);
}
}
fn print_category(name: &str, values: &[(String, ConfigValue)]) {
let header = format!("┌─ {} ", name);
let padding = "─".repeat(62 - header.len());
println!("{}{}", header, padding);
println!("│");
let mut sorted_values = values.to_vec();
sorted_values.sort_by_key(|(k, _)| k.clone());
for (key, value) in sorted_values {
let indent = get_indent_level(&key);
let display_key = get_display_key(&key);
let formatted_value = format_value(&value);
println!(
"│ {}{:<28} = {}",
" ".repeat(indent),
display_key,
formatted_value
);
}
println!("│");
println!("└{}", "─".repeat(62));
println!();
}
fn print_summary(config: &Config) {
let all_keys = config.keys();
let total_values = all_keys.len();
let mut categories = std::collections::HashSet::new();
for key in &all_keys {
if let Some(category) = key.split(':').next() {
if key.contains(':') {
categories.insert(category);
}
}
}
println!(" 📊 Total configuration values: {}", total_values);
println!(" 📁 Total categories: {}", categories.len());
let mut type_counts: HashMap<String, usize> = HashMap::new();
for key in &all_keys {
if let Ok(value) = config.get(key) {
*type_counts
.entry(value.type_name().to_string())
.or_insert(0) += 1;
}
}
println!(" 📝 Value types:");
let mut types: Vec<_> = type_counts.iter().collect();
types.sort_by_key(|(name, _)| *name);
for (type_name, count) in types {
println!(" • {}: {}", type_name, count);
}
println!();
}
fn format_value(value: &ConfigValue) -> String {
match value {
ConfigValue::Int(i) => {
if *i >= 0 && *i <= 1 {
format!("{} ({})", i, if *i == 1 { "true" } else { "false" })
} else if *i > 1000000 {
format!("0x{:X}", i)
} else {
format!("{}", i)
}
}
ConfigValue::Float(f) => format!("{}", f),
ConfigValue::String(s) => {
if s.is_empty() {
"\"\"".to_string()
} else if s.len() > 50 {
format!("\"{}...\"", &s[..47])
} else {
format!("\"{}\"", s)
}
}
ConfigValue::Vec2(v) => format!("({}, {})", v.x, v.y),
ConfigValue::Color(c) => format_color(c),
ConfigValue::Custom { type_name, .. } => format!("<{}>", type_name),
}
}
fn format_color(color: &Color) -> String {
format!(
"rgba({}, {}, {}, {}) │ #{:02x}{:02x}{:02x}{:02x}",
color.r, color.g, color.b, color.a, color.r, color.g, color.b, color.a
)
}
fn get_indent_level(key: &str) -> usize {
key.matches(':').count()
}
fn get_display_key(key: &str) -> String {
if let Some(last_part) = key.split(':').last() {
last_part.to_string()
} else {
key.to_string()
}
}
fn is_variable_like(key: &str, value: &ConfigValue) -> bool {
!key.contains(':') && matches!(value, ConfigValue::String(_) | ConfigValue::Int(_))
}