iocaine 3.4.0

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

#![allow(clippy::result_large_err)]

use figment::{
    Error, error,
    value::{Dict, Value},
};
use ipnet::IpNet;
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_firewall(&self) -> Result<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());
                }
                "persist" => {
                    if let Some(path) = child.get_string_property("path")? {
                        server.insert("persist-path".into(), path.into());
                    }
                    if let Some(interval) = child.get_string_property("interval")? {
                        server.insert("persist-interval".into(), interval.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_firewall(&self) -> Result<Dict, Error> {
        let mut firewall = Dict::new();

        for child in self.iter_children() {
            match child.name().value() {
                "enable" | "counters" => {
                    firewall.insert(child.name().value().into(), child.get_bool()?.into());
                }
                "table" | "timeout" | "gc-interval" => {
                    let value = child.get_first_string_argument()?;
                    firewall.insert(child.name().value().into(), value.into());
                }
                "size" => {
                    let value = child
                        .assert_single_argument()?
                        .get_first_integer_argument()?;
                    let value = u64::try_from(value).map_err(|_| {
                        Error::from(format!(
                            "firewall set size out of bounds at {}",
                            child.span().offset()
                        ))
                    })?;
                    firewall.insert("size".into(), value.into());
                }
                "priority" => {
                    let value = child
                        .assert_single_argument()?
                        .get_first_integer_argument()?;
                    let value = i32::try_from(value).map_err(|_| {
                        Error::from(format!(
                            "firewall priority out of bounds at {}",
                            child.span().offset()
                        ))
                    })?;
                    firewall.insert("priority".into(), value.into());
                }
                "allow" => {
                    let mut allows = Vec::new();
                    for entry in child.entries() {
                        if entry.name().is_some() {
                            return Err(Error::from(format!(
                                "named properties for firewall.allow are not allowed at {}",
                                entry.span().offset()
                            )));
                        }
                        let net: IpNet = entry.as_string()?.parse().map_err(|_| {
                            Error::from(format!(
                                "unable to parse IP network: `{}` at {}",
                                entry.value(),
                                entry.span().offset()
                            ))
                        })?;
                        allows.push(net.to_string());
                    }
                    firewall.insert("allow".into(), allows.into());
                }
                "batch" => {
                    if let Some(batch_size) = child.get_integer_property("size")? {
                        let value = usize::try_from(batch_size).map_err(|_| {
                            Error::from(format!(
                                "firewall batch size out of bounds at {}",
                                child.span().offset()
                            ))
                        })?;
                        firewall.insert("batch-size".into(), value.into());
                    }
                    if let Some(flush_interval) = child.get_integer_property("flush-interval")? {
                        let value = u64::try_from(flush_interval).map_err(|_| {
                            Error::from(format!(
                                "firewall batch flush interval out of bounds at {}",
                                child.span().offset()
                            ))
                        })?;
                        firewall.insert("batch-flush-interval".into(), value.into());
                    }
                }
                name => {
                    let kind = error::Kind::Unsupported(error::Actual::Str(name.to_owned()));
                    return Err(Error::from(kind));
                }
            }
        }

        Ok(firewall)
    }

    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))
    }
}