//
// Syd: rock-solid unikernel
// src/syd-err.rs: Given a number, print the matching errno name and exit.
//                 Given a glob, print case-insensitively matching errno names and exit.
//
// Copyright (c) 2024 Ali Polatel <alip@chesswob.org>
//
// SPDX-License-Identifier: GPL-3.0-or-later

use std::process::ExitCode;

use nix::errno::Errno;
use syd::{
    err::SydResult,
    wildmatch::{is_literal, wildmatch},
};

// Set global allocator to mimalloc.
#[global_allocator]
static GLOBAL: mimalloc2_rust::GlobalMiMalloc = mimalloc2_rust::GlobalMiMalloc;

fn main() -> SydResult<ExitCode> {
    syd::set_sigpipe_dfl()?;

    // Configure mimalloc.
    syd::configure_mimalloc();

    let mut args = std::env::args();
    match args.nth(1).as_deref() {
        None | Some("-h") => {
            println!("Usage: syd-err number|name-regex");
            println!("Given a number, print the matching errno name and exit.");
            println!("Given a glob, print case-insensitively matching errno names and exit.");
        }
        Some(value) => {
            match value.parse::<u16>() {
                Ok(0) => {
                    return Ok(ExitCode::FAILURE);
                }
                Ok(num) => {
                    // number -> name
                    let errno = Errno::from_raw(i32::from(num));
                    if errno == Errno::UnknownErrno {
                        return Ok(ExitCode::FAILURE);
                    }
                    let estr = errno.to_string();
                    let mut iter = estr.split(": ");
                    let name = iter.next().unwrap_or("?");
                    let desc = iter.next().unwrap_or("?");
                    println!("{num}\t{name}\t{desc}");
                }
                Err(_) => {
                    // glob -> [number]
                    let glob = if !is_literal(value.as_bytes()) {
                        value.to_string()
                    } else {
                        format!("*{value}*")
                    };
                    let mut ok = false;
                    for errno in (1..u8::MAX).map(|n| Errno::from_raw(i32::from(n))) {
                        if errno == Errno::UnknownErrno {
                            continue;
                        }
                        let estr = errno.to_string();
                        let mut iter = estr.split(": ");
                        let name = iter.next().unwrap_or("?");
                        let desc = iter.next().unwrap_or("?");
                        if wildmatch(glob.as_bytes(), estr.as_bytes()) {
                            println!("{}\t{}\t{}", errno as i32, name, desc);
                            ok = true;
                        }
                    }
                    if !ok {
                        return Ok(ExitCode::FAILURE);
                    }
                }
            }
        }
    }

    Ok(ExitCode::SUCCESS)
}
