graphrecords-query 0.5.0

High-performance graph-based data records
Documentation
use crate::{
    Diagnostic,
    registry::{
        ArgumentDescriptor, ArgumentMissingPolicy, ArgumentValueSource, ArityDescriptor,
        IndexDescriptor, LaneShapeDescriptor, OperandDescriptor, OrderDescriptor, ValueDescriptor,
        ValueRole,
    },
};
use std::{
    error::Error,
    fmt::{self, Display, Formatter},
};

struct TypeName(&'static str);

impl Display for TypeName {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
        formatter.write_str(self.0.rsplit_once("::").map_or(self.0, |split| split.1))
    }
}

struct IndexState<'a>(&'a IndexDescriptor);

impl Display for IndexState<'_> {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
        match self.0 {
            IndexDescriptor::Domain(domain) => TypeName(domain.name()).fmt(formatter),
            IndexDescriptor::Expanded { parent, child } => {
                write!(formatter, "{} expanded by {}", Self(parent), Self(child))
            }
            IndexDescriptor::ExpandedSource { parent } => {
                write!(formatter, "{} expansion sources", Self(parent))
            }
        }
    }
}

struct ValueState<'a>(&'a ValueDescriptor);

impl Display for ValueState<'_> {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
        match self.0.role() {
            ValueRole::Value => write!(formatter, "{} values", TypeName(self.0.domain().name())),
            ValueRole::Index(index) => write!(formatter, "{} index values", IndexState(index)),
            ValueRole::EntityReference(index) => {
                write!(formatter, "{} entity references", IndexState(index))
            }
            ValueRole::Unit => formatter.write_str("unit values"),
        }
    }
}

struct OperandState<'a>(&'a OperandDescriptor);

impl Display for OperandState<'_> {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
        match self.0 {
            OperandDescriptor::Lane { shape, arity } => {
                let arity = match arity {
                    ArityDescriptor::Multiple {
                        order: OrderDescriptor::Ordered,
                    } => "an ordered",
                    ArityDescriptor::Multiple {
                        order: OrderDescriptor::Unordered,
                    } => "an unordered",
                    ArityDescriptor::Single => "a single-element",
                    ArityDescriptor::Definite => "a definite",
                };

                match shape {
                    LaneShapeDescriptor::Indexed { index, value } => write!(
                        formatter,
                        "{arity} lane of {} indexed by {}",
                        ValueState(value),
                        IndexState(index)
                    ),
                    LaneShapeDescriptor::Bare { value } => {
                        write!(formatter, "{arity} bare lane of {}", ValueState(value))
                    }
                }
            }
            OperandDescriptor::Group {
                member,
                key,
                payload,
            } => write!(
                formatter,
                "a group of {} members keyed by {} containing {}",
                IndexState(member),
                IndexState(key),
                Self(payload)
            ),
        }
    }
}

struct ArgumentSourceState<'a>(&'a ArgumentValueSource);

impl Display for ArgumentSourceState<'_> {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
        match self.0 {
            ArgumentValueSource::Literal(value) => {
                write!(formatter, "a literal of {}", ValueState(value))
            }
            ArgumentValueSource::Operand(operand) => {
                write!(formatter, "values from {}", OperandState(operand))
            }
        }
    }
}

struct ArgumentState<'a>(&'a ArgumentDescriptor);

impl Display for ArgumentState<'_> {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
        match self.0 {
            ArgumentDescriptor::Value(argument) => {
                ArgumentSourceState(argument.source()).fmt(formatter)?;

                match argument.missing() {
                    ArgumentMissingPolicy::None => Ok(()),
                    ArgumentMissingPolicy::Drop => formatter.write_str(" dropping missing values"),
                    ArgumentMissingPolicy::Replace(replacement) => write!(
                        formatter,
                        " replacing missing values with {}",
                        ArgumentSourceState(replacement)
                    ),
                }
            }
            ArgumentDescriptor::Field(domain) => {
                write!(formatter, "a field of type {}", TypeName(domain.name()))
            }
            ArgumentDescriptor::Selector(domain) => {
                write!(formatter, "a selector of type {}", TypeName(domain.name()))
            }
            ArgumentDescriptor::Operand(operand) => OperandState(operand).fmt(formatter),
        }
    }
}

#[derive(Debug)]
pub struct OperationNotApplicable {
    method: String,
    input: OperandDescriptor,
    arguments: Vec<ArgumentDescriptor>,
}

impl OperationNotApplicable {
    #[must_use]
    pub const fn new(
        method: String,
        input: OperandDescriptor,
        arguments: Vec<ArgumentDescriptor>,
    ) -> Self {
        Self {
            method,
            input,
            arguments,
        }
    }

    #[must_use]
    pub fn method(&self) -> &str {
        &self.method
    }

    #[must_use]
    pub const fn input(&self) -> &OperandDescriptor {
        &self.input
    }

    #[must_use]
    pub fn arguments(&self) -> &[ArgumentDescriptor] {
        &self.arguments
    }
}

impl Display for OperationNotApplicable {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
        write!(
            formatter,
            "operation `{}` is not applicable to {}",
            self.method,
            OperandState(&self.input)
        )?;

        let Some(first) = self.arguments.first() else {
            return formatter.write_str(" without arguments");
        };

        write!(formatter, " with arguments {}", ArgumentState(first))?;

        self.arguments
            .iter()
            .skip(1)
            .try_for_each(|argument| write!(formatter, ", {}", ArgumentState(argument)))
    }
}

impl Error for OperationNotApplicable {}

impl Diagnostic for OperationNotApplicable {
    fn name() -> &'static str {
        "OperationNotApplicable"
    }

    fn help(&self) -> Option<String> {
        Some("choose an operation and arguments supported by the operand descriptor".to_string())
    }
}

#[derive(Debug)]
pub struct UnsupportedValueRole {
    capability: &'static str,
    role: &'static str,
}

impl UnsupportedValueRole {
    #[must_use]
    pub const fn new(capability: &'static str, role: &'static str) -> Self {
        Self { capability, role }
    }

    #[must_use]
    pub const fn capability(&self) -> &'static str {
        self.capability
    }

    #[must_use]
    pub const fn role(&self) -> &'static str {
        self.role
    }
}

impl Display for UnsupportedValueRole {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
        write!(
            formatter,
            "a {} value does not support `{}`",
            self.role, self.capability
        )
    }
}

impl Error for UnsupportedValueRole {}

impl Diagnostic for UnsupportedValueRole {
    fn name() -> &'static str {
        "UnsupportedValueRole"
    }

    fn help(&self) -> Option<String> {
        Some(
            "handle the failing elements with `on_error(...)` before using them as grouping keys"
                .to_string(),
        )
    }
}