iocaine 3.0.0

The deadliest poison known to AI
Documentation
// SPDX-FileCopyrightText: 2025 Gergely Nagy
// SPDX-FileContributor: Gergely Nagy
//
// SPDX-License-Identifier: MIT

use figment::{
    Error, error,
    value::{Dict, Value},
};
use kdl::KdlNode;
use std::cmp::Ordering;

use super::traits::{KdlEntryExt, KdlNodeExt};

pub trait ParseExt {
    fn parse_request_server(&self, mode: &str) -> Result<(String, Value), Error>;
    fn parse_prometheus_server(&self) -> Result<(String, Value), Error>;
    fn parse_declare_handler(&self) -> Result<(&str, Dict), Error>;

    fn parse_use(&self) -> Result<Dict, Error>;
    fn parse_bind(&self) -> Result<(Option<&str>, Option<&str>), Error>;
    fn parse_as_figment_value(&self) -> Result<(&str, Value), Error>;
}

impl ParseExt for KdlNode {
    fn parse_request_server(&self, mode: &str) -> Result<(String, Value), Error> {
        let name = format!(
            "{mode}:{}",
            self.assert_single_argument()?.get_first_string_argument()?
        );

        let mut server = Dict::new();
        server.insert("mode".into(), mode.into());

        for child in self.iter_children() {
            match child.name().value() {
                "initial-seed" | "initial-seed-file" => {
                    let arg = child
                        .assert_no_children()?
                        .assert_single_argument()?
                        .get_first_string_argument()?;
                    server.insert(child.name().value().into(), arg.into());
                }
                "use" => {
                    let uses = child.assert_no_children()?.parse_use()?;
                    server.insert("use".into(), uses.into());
                }
                "bind" => {
                    let (bind, socket_access) = child.parse_bind()?;
                    if let Some(bind) = bind {
                        server.insert("bind".into(), bind.into());
                    }
                    if let Some(socket_access) = socket_access {
                        server.insert("unix-socket-access".into(), socket_access.into());
                    }
                }
                name => {
                    let kind = error::Kind::Unsupported(error::Actual::Str(name.to_owned()));
                    return Err(Error::from(kind));
                }
            }
        }

        Ok((name, server.into()))
    }

    fn parse_prometheus_server(&self) -> Result<(String, Value), Error> {
        let name = format!(
            "prometheus:{}",
            self.assert_single_argument()?.get_first_string_argument()?
        );

        let mut server = Dict::new();
        server.insert("mode".into(), "prometheus".into());

        for child in self.iter_children() {
            match child.name().value() {
                "persist-path" | "persist-interval" => {
                    let arg = child
                        .assert_no_children()?
                        .assert_single_argument()?
                        .get_first_string_argument()?;
                    server.insert(child.name().value().into(), arg.into());
                }
                "bind" => {
                    let (bind, socket_access) = child.parse_bind()?;
                    if let Some(bind) = bind {
                        server.insert("bind".into(), bind.into());
                    }
                    if let Some(socket_access) = socket_access {
                        server.insert("unix-socket-access".into(), socket_access.into());
                    }
                }
                name => {
                    let kind = error::Kind::Unsupported(error::Actual::Str(name.to_owned()));
                    return Err(Error::from(kind));
                }
            }
        }

        Ok((name, server.into()))
    }

    fn parse_use(&self) -> Result<Dict, Error> {
        let mut uses = Dict::new();

        for entry in self.entries() {
            let Some(name) = entry.name() else {
                return Err(Error::from(format!(
                    r#""use" at {} cannot have positional arguments"#,
                    self.span().offset()
                )));
            };
            match name.value() {
                "metrics" => {
                    let node_name = format!("prometheus:{}", entry.as_string()?);
                    uses.insert("metrics".into(), Value::from(node_name));
                }
                "handler-from" => {
                    uses.insert("handler-from".into(), entry.to_figment_value());
                }
                name => {
                    let kind = error::Kind::Unsupported(error::Actual::Str(name.to_owned()));
                    return Err(Error::from(kind));
                }
            }
        }
        Ok(uses)
    }

    fn parse_bind(&self) -> Result<(Option<&str>, Option<&str>), Error> {
        let bind = self.get(0).and(Some(self.get_first_string_argument()?));
        let socket_access = self
            .assert_max_arguments(2)?
            .get_string_property("unix-socket-access")?;
        Ok((bind, socket_access))
    }

    fn parse_declare_handler(&self) -> Result<(&str, Dict), Error> {
        let mut arg_count = 0;
        for entry in self.iter() {
            match entry.name() {
                None => arg_count += 1,
                Some(name) => match name.value() {
                    "language" | "compiler" | "path" => {}
                    name => {
                        let kind = error::Kind::Unsupported(error::Actual::Str(name.to_owned()));
                        return Err(Error::from(kind));
                    }
                },
            }
        }
        if arg_count != 1 {
            return Err(Error::from(format!(
                r#""{}" at {} requires exactly one positional argument"#,
                self.name().value(),
                self.span().offset()
            )));
        }

        let name = self.get_first_string_argument()?;

        let mut config = Dict::new();
        for child in self.iter_children() {
            let (name, value) = child.parse_as_figment_value()?;
            config.insert(name.into(), value);
        }

        let mut handler = Dict::new();
        if let Some(language) = self.get_string_property("language")? {
            handler.insert("language".into(), language.into());
        }
        if let Some(path) = self.get_string_property("path")? {
            handler.insert("path".into(), path.into());
        }
        if let Some(compiler) = self.get_string_property("compiler")? {
            handler.insert("compiler".into(), compiler.into());
        }
        if !config.is_empty() {
            handler.insert("config".into(), config.into());
        }

        Ok((name, handler))
    }

    fn parse_as_figment_value(&self) -> Result<(&str, Value), Error> {
        if !self.is_empty() && self.children().is_some() {
            return Err(Error::from(format!(
                r#""{}" at {} can't have arguments and children"#,
                self.name().value(),
                self.span().offset(),
            )));
        }
        let name = self.name().value();
        if name == "-" {
            return Err(Error::from(format!(
                r#"node at {} cannot be named "-""#,
                self.span().offset(),
            )));
        }

        let value;

        match self.len().cmp(&1) {
            Ordering::Equal => {
                if let Some(entry) = self.entry(0) {
                    value = entry.to_figment_value();
                } else {
                    return Err(Error::from(format!(
                        r#""{}" at {} can't have named arguments"#,
                        self.name().value(),
                        self.span().offset(),
                    )));
                }
            }
            Ordering::Greater => {
                let mut vals: Vec<Value> = Vec::new();
                for entry in self.iter() {
                    if entry.name().is_none() {
                        let v = entry.to_figment_value();
                        vals.push(v);
                    } else {
                        return Err(Error::from(format!(
                            r#""{}" at {} can't have named arguments"#,
                            self.name().value(),
                            self.span().offset(),
                        )));
                    }
                }
                value = Value::from(vals);
            }
            Ordering::Less => {
                let mut vals = Dict::new();
                for child in self.iter_children() {
                    let (name, val) = child.parse_as_figment_value()?;
                    vals.insert(name.into(), val);
                }
                value = Value::from(vals);
            }
        }

        Ok((name, value))
    }
}