weavatrix-rust 2.1.1

Protocol-independent Rust repository intelligence: typed evidence graphs for impact, architecture, APIs, Git, search, and memory
Documentation
use super::{BTreeSet, Bindings, Call, Token, TokenKind};
use crate::operations::syntax::matching_delimiter;

pub(super) fn assigned_variable(
    tokens: &[Token],
    source: &str,
    name_index: usize,
) -> Option<String> {
    let start = name_index.saturating_sub(12);
    let equals = (start..name_index)
        .rev()
        .find(|index| tokens[*index].text(source) == "=")?;
    (start..equals).rev().find_map(|index| {
        (tokens[index].kind == TokenKind::Identifier).then(|| tokens[index].text(source).to_owned())
    })
}

pub(super) fn call_name_index(tokens: &[Token], source: &str, open: usize) -> Option<usize> {
    let previous = open.checked_sub(1)?;
    if tokens[previous].kind == TokenKind::Identifier {
        return Some(previous);
    }
    if tokens[previous].text(source) == ">" {
        let mut depth = 0_usize;
        for index in (0..previous).rev() {
            match tokens[index].text(source) {
                ">" => depth += 1,
                "<" if depth == 0 => {
                    return index
                        .checked_sub(1)
                        .filter(|candidate| tokens[*candidate].kind == TokenKind::Identifier);
                }
                "<" => depth = depth.saturating_sub(1),
                _ => {}
            }
        }
    }
    (tokens[previous].text(source) == "!")
        .then(|| previous.checked_sub(1))
        .flatten()
        .filter(|index| tokens[*index].kind == TokenKind::Identifier)
}

pub(super) fn matching_close(tokens: &[Token], source: &str, open: usize) -> Option<usize> {
    matching_delimiter(tokens, source, open, ("(", ")"))
}

pub(super) fn call_chain(tokens: &[Token], source: &str, name_index: usize) -> String {
    let mut start = name_index;
    loop {
        if start >= 2
            && tokens[start - 1].text(source) == "."
            && tokens[start - 2].kind == TokenKind::Identifier
        {
            start -= 2;
        } else if start >= 3
            && tokens[start - 1].text(source) == ":"
            && tokens[start - 2].text(source) == ":"
            && tokens[start - 3].kind == TokenKind::Identifier
        {
            start -= 3;
        } else {
            break;
        }
    }
    tokens[start..=name_index]
        .iter()
        .map(|token| token.text(source))
        .collect::<String>()
}

pub(super) fn receiver_name(tokens: &[Token], source: &str, name_index: usize) -> Option<String> {
    if name_index >= 2
        && tokens[name_index - 1].text(source) == "."
        && tokens[name_index - 2].kind == TokenKind::Identifier
    {
        return Some(tokens[name_index - 2].text(source).to_owned());
    }
    (name_index >= 3
        && tokens[name_index - 1].text(source) == ":"
        && tokens[name_index - 2].text(source) == ":"
        && tokens[name_index - 3].kind == TokenKind::Identifier)
        .then(|| tokens[name_index - 3].text(source).to_owned())
}

pub(super) fn resource_values(
    call: &Call<'_, '_>,
    properties: &[&str],
    positional: usize,
    bindings: &Bindings,
) -> Vec<Option<String>> {
    let properties = property_values(call, properties);
    if properties.is_empty() {
        positional_values(call, positional, bindings)
    } else {
        properties.into_iter().map(Some).collect()
    }
}

pub(super) fn first_value(
    call: &Call<'_, '_>,
    properties: &[&str],
    positional: usize,
    bindings: &Bindings,
) -> Option<String> {
    resource_values(call, properties, positional, bindings)
        .into_iter()
        .flatten()
        .next()
}

pub(super) fn property(call: &Call<'_, '_>, names: &[&str]) -> Option<String> {
    property_values(call, names).into_iter().next()
}

pub(super) fn property_values(call: &Call<'_, '_>, names: &[&str]) -> Vec<String> {
    let names = names
        .iter()
        .map(|name| name.to_ascii_lowercase())
        .collect::<BTreeSet<_>>();
    let mut values = Vec::new();
    for index in 0..call.args.len() {
        if call.args[index].kind != TokenKind::Identifier
            || !names.contains(&call.args[index].text(call.source).to_ascii_lowercase())
        {
            continue;
        }
        let Some(separator) = call.args.get(index + 1) else {
            continue;
        };
        if !matches!(separator.text(call.source), ":" | "=" | "(") {
            continue;
        }
        let mut depth = 0_i32;
        for token in call.args.iter().skip(index + 2) {
            match token.text(call.source) {
                "[" | "{" | "(" => depth += 1,
                "]" | "}" | ")" => depth -= 1,
                "," if depth <= 0 => break,
                _ => {}
            }
            if token.kind == TokenKind::String
                && let Some(value) = literal_value(token.text(call.source))
            {
                values.push(value);
            }
        }
    }
    values
}

pub(super) fn positional_values(
    call: &Call<'_, '_>,
    position: usize,
    bindings: &Bindings,
) -> Vec<Option<String>> {
    let segments = argument_segments(call.args, call.source);
    let Some(segment) = segments.get(position) else {
        return vec![None];
    };
    let literals = segment
        .iter()
        .filter(|token| token.kind == TokenKind::String)
        .filter_map(|token| literal_value(token.text(call.source)))
        .map(Some)
        .collect::<Vec<_>>();
    if !literals.is_empty() {
        return literals;
    }
    let identifier = segment.iter().find_map(|token| {
        (token.kind == TokenKind::Identifier).then(|| token.text(call.source).to_owned())
    });
    if let Some(identifier) = identifier
        && let Some((_, value)) = bindings.resources.get(&identifier)
    {
        return vec![Some(value.clone())];
    }
    vec![None]
}

pub(super) fn positional_identifier(call: &Call<'_, '_>, position: usize) -> Option<String> {
    argument_segments(call.args, call.source)
        .get(position)?
        .iter()
        .find_map(|token| {
            (token.kind == TokenKind::Identifier).then(|| token.text(call.source).to_owned())
        })
}

pub(super) fn argument_segments<'tokens>(
    tokens: &'tokens [Token],
    source: &str,
) -> Vec<&'tokens [Token]> {
    let mut segments = Vec::new();
    let mut start = 0_usize;
    let mut depth = 0_i32;
    for (index, token) in tokens.iter().enumerate() {
        match token.text(source) {
            "(" | "[" | "{" => depth += 1,
            ")" | "]" | "}" => depth -= 1,
            "," if depth == 0 => {
                segments.push(&tokens[start..index]);
                start = index + 1;
            }
            _ => {}
        }
    }
    if start < tokens.len() {
        segments.push(&tokens[start..]);
    }
    segments
}

pub(super) fn literal_value(raw: &str) -> Option<String> {
    let trimmed = raw.trim();
    let quote = trimmed.find(['"', '\'', '`'])?;
    let mark = trimmed.as_bytes().get(quote).copied()? as char;
    let tail = &trimmed[quote + mark.len_utf8()..];
    let end = tail.rfind(mark)?;
    let value = &tail[..end];
    if value.is_empty() || value.contains("${") || value.contains("#{") {
        None
    } else {
        Some(value.to_owned())
    }
}

pub(super) fn source_line(source: &str, line: u32) -> String {
    source
        .lines()
        .nth(usize::try_from(line.saturating_sub(1)).unwrap_or(usize::MAX))
        .map(str::trim)
        .unwrap_or_default()
        .chars()
        .take(300)
        .collect()
}

pub(super) fn non_empty_resources(resources: Vec<Option<String>>) -> Vec<Option<String>> {
    if resources.is_empty() {
        vec![None]
    } else {
        resources
    }
}