hamelin_lib 0.7.12

Core library for Hamelin query language
Documentation
//! String operators and functions

use std::any::TypeId;

use crate::func::def::{FunctionDef, ParameterBindingProvider, Parameters};
use crate::operator::Operator;
use crate::types::matcher::ExactMatcher;
use crate::types::{Type, BOOLEAN, INT, STRING};

/// String concatenation operator: `left + right`
#[derive(Default, Clone)]
pub struct StringConcat;

impl FunctionDef for StringConcat {
    fn name(&self) -> &'static str {
        Operator::Plus.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", ExactMatcher::of(STRING))
            .with("right", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(STRING)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// replace(string, pattern) - Remove all occurrences of pattern.
#[derive(Default, Clone)]
pub struct Replace2;

impl FunctionDef for Replace2 {
    fn name(&self) -> &'static str {
        "replace"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("string", ExactMatcher::of(STRING))
            .with("pattern", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(STRING)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// replace(string, pattern, replacement) - Replace all occurrences of pattern with replacement.
#[derive(Default, Clone)]
pub struct Replace3;

impl FunctionDef for Replace3 {
    fn name(&self) -> &'static str {
        "replace"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("string", ExactMatcher::of(STRING))
            .with("pattern", ExactMatcher::of(STRING))
            .with("replacement", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(STRING)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// substr(string, start) - Extract substring from start to end.
#[derive(Default, Clone)]
pub struct Substr2;

impl FunctionDef for Substr2 {
    fn name(&self) -> &'static str {
        "substr"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("string", ExactMatcher::of(STRING))
            .with("start", ExactMatcher::of(INT))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(STRING)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// substr(string, start, end) - Extract substring from start to end.
#[derive(Default, Clone)]
pub struct Substr3;

impl FunctionDef for Substr3 {
    fn name(&self) -> &'static str {
        "substr"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("string", ExactMatcher::of(STRING))
            .with("start", ExactMatcher::of(INT))
            .with("end", ExactMatcher::of(INT))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(STRING)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// starts_with(string, prefix) - Check if string starts with prefix.
#[derive(Default, Clone)]
pub struct StartsWith;

impl FunctionDef for StartsWith {
    fn name(&self) -> &'static str {
        "starts_with"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("string", ExactMatcher::of(STRING))
            .with("prefix", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// ends_with(string, suffix) - Check if string ends with suffix.
#[derive(Default, Clone)]
pub struct EndsWith;

impl FunctionDef for EndsWith {
    fn name(&self) -> &'static str {
        "ends_with"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("string", ExactMatcher::of(STRING))
            .with("suffix", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// contains(string, substring) - Check if string contains substring.
#[derive(Default, Clone)]
pub struct Contains;

impl FunctionDef for Contains {
    fn name(&self) -> &'static str {
        "contains"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("string", ExactMatcher::of(STRING))
            .with("substring", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// cidr_contains(cidr, ip) - Check if CIDR block contains IP address.
#[derive(Default, Clone)]
pub struct CidrContains;

impl FunctionDef for CidrContains {
    fn name(&self) -> &'static str {
        "cidr_contains"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("cidr", ExactMatcher::of(STRING))
            .with("ip", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// is_ipv4(ip) - Check if string is a valid IPv4 address.
#[derive(Default, Clone)]
pub struct IsIpv4;

impl FunctionDef for IsIpv4 {
    fn name(&self) -> &'static str {
        "is_ipv4"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new().with("ip", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// is_ipv6(ip) - Check if string is a valid IPv6 address.
#[derive(Default, Clone)]
pub struct IsIpv6;

impl FunctionDef for IsIpv6 {
    fn name(&self) -> &'static str {
        "is_ipv6"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new().with("ip", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// lower(string) - Convert string to lowercase.
#[derive(Default, Clone)]
pub struct Lower;

impl FunctionDef for Lower {
    fn name(&self) -> &'static str {
        "lower"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new().with("string", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(STRING)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// upper(string) - Convert string to uppercase.
#[derive(Default, Clone)]
pub struct Upper;

impl FunctionDef for Upper {
    fn name(&self) -> &'static str {
        "upper"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new().with("string", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(STRING)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// len(string) - Get the length of a string.
#[derive(Default, Clone)]
pub struct StringLen;

impl FunctionDef for StringLen {
    fn name(&self) -> &'static str {
        "len"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new().with("x", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(INT)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// uuid() - Generate a random UUID string.
///
/// Returns a new random UUID v4 string each time it is called.
/// Useful for generating unique row identifiers.
#[derive(Default, Clone)]
pub struct Uuid;

impl FunctionDef for Uuid {
    fn name(&self) -> &'static str {
        "uuid"
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(STRING)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }

    fn is_deterministic(&self) -> bool {
        false
    }
}

/// uuid5(name) - Generate a deterministic UUID v5 string (RFC 4122).
///
/// Uses a default namespace (e.g. NAMESPACE_OID). Same input always yields the same UUID.
#[derive(Default, Clone)]
pub struct Uuid5;

impl FunctionDef for Uuid5 {
    fn name(&self) -> &'static str {
        "uuid5"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new().with("name", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(STRING)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}