hawdb-core 0.26.0

An embedded Rust graph database for local data-plane workloads
Documentation
// Copyright 2026 Nowledge
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! openCypher list and string-or-list scalar functions.

/// Maximum number of elements one `range()` call may produce.
///
/// The limit applies before the result is allocated. Query memory budgets
/// apply in addition when the evaluator runs under a memory account.
pub const MAX_RANGE_LENGTH: usize = 1_000_000;

/// Maximum list work units one projection evaluation may spend: elements
/// generated by `range()`, comprehension iterations, elements copied by
/// `tail`/`reverse`/slices, elements compared by `IN` (including nested list
/// elements), and characters scanned by string `size`/`reverse`.
///
/// Comprehensions can nest and filter without retaining their input, so the
/// memory budget alone does not bound their work; this limit does.
pub const MAX_LIST_EXPRESSION_WORK: usize = 10_000_000;

/// Maximum bytes list expressions may materialize in one evaluation when no
/// query memory account is available (for example aggregate arguments and
/// mutation preflight). With an account, the account's budget applies instead.
pub const MAX_UNACCOUNTED_LIST_BYTES: usize = 64 * 1024 * 1024;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum ListFunction {
    /// `size(list|string)`: element count or Unicode scalar count.
    Size,
    /// `head(list)`: first element, or `NULL` for an empty list.
    Head,
    /// `last(list)`: last element, or `NULL` for an empty list.
    Last,
    /// `tail(list)`: the list without its first element.
    Tail,
    /// `reverse(list|string)`.
    Reverse,
    /// `range(start, end[, step])` with an inclusive end.
    Range,
    /// `list_append(list, value)`: append one value without flattening it.
    Append,
}

impl ListFunction {
    pub const ALL: [Self; 7] = [
        Self::Size,
        Self::Head,
        Self::Last,
        Self::Tail,
        Self::Reverse,
        Self::Range,
        Self::Append,
    ];

    pub fn from_function_name(name: &str) -> Option<Self> {
        Self::ALL
            .into_iter()
            .find(|function| name.eq_ignore_ascii_case(function.function_name()))
    }

    pub const fn function_name(self) -> &'static str {
        match self {
            Self::Size => "size",
            Self::Head => "head",
            Self::Last => "last",
            Self::Tail => "tail",
            Self::Reverse => "reverse",
            Self::Range => "range",
            Self::Append => "list_append",
        }
    }

    /// Inclusive minimum and maximum argument counts.
    pub const fn arity(self) -> (usize, usize) {
        match self {
            Self::Range => (2, 3),
            Self::Append => (2, 2),
            _ => (1, 1),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn function_names_round_trip_case_insensitively() {
        for function in ListFunction::ALL {
            assert_eq!(
                ListFunction::from_function_name(&function.function_name().to_uppercase()),
                Some(function)
            );
        }
        assert_eq!(ListFunction::from_function_name("collect"), None);
        assert_eq!(ListFunction::Range.arity(), (2, 3));
        assert_eq!(ListFunction::Size.arity(), (1, 1));
    }
}