vv-agent 0.6.2

VectorVein agent runtime, SDK, CLI, tools, and workspace backends
Documentation
use super::identifiers::is_identifier_continue;
use super::SettingsLiteralError;

pub(super) fn extract_assignment_literal<'a>(
    source: &'a str,
    targets: &[&str],
) -> Result<&'a str, SettingsLiteralError> {
    for target in targets {
        if let Some(value_start) = find_top_level_assignment_value(source, target) {
            return collect_balanced_literal(&source[value_start..]);
        }
    }
    Err(SettingsLiteralError::MissingAssignment)
}

fn find_top_level_assignment_value(source: &str, target: &str) -> Option<usize> {
    let mut line_start = 0usize;
    for line in source.split_inclusive('\n') {
        let trimmed = line.trim_start();
        let indent_len = line.len().saturating_sub(trimmed.len());
        if indent_len == 0 && starts_with_name(trimmed, target) {
            let after_name = &trimmed[target.len()..];
            let next = after_name.chars().next();
            if matches!(next, Some(':') | Some('=')) || next.is_some_and(|ch| ch.is_whitespace()) {
                if let Some(eq_index) = trimmed.find('=') {
                    return Some(line_start + indent_len + eq_index + 1);
                }
            }
        }
        line_start += line.len();
    }
    None
}

fn starts_with_name(source: &str, name: &str) -> bool {
    source.starts_with(name)
        && source[name.len()..]
            .chars()
            .next()
            .is_none_or(|ch| !is_identifier_continue(ch))
}

fn collect_balanced_literal(source: &str) -> Result<&str, SettingsLiteralError> {
    let start = first_literal_char(source).ok_or_else(|| {
        SettingsLiteralError::InvalidLiteral("assignment has no literal value".to_string())
    })?;
    let tail = &source[start..];
    let mut depth = 0usize;
    let mut quote: Option<char> = None;
    let mut escaped = false;
    let mut started = false;
    let mut in_comment = false;

    for (index, ch) in tail.char_indices() {
        if in_comment {
            if matches!(ch, '\n' | '\r') {
                in_comment = false;
            }
            continue;
        }

        if let Some(active_quote) = quote {
            if escaped {
                escaped = false;
                continue;
            }
            if ch == '\\' {
                escaped = true;
                continue;
            }
            if ch == active_quote {
                quote = None;
                if started && depth == 0 {
                    let end = index + ch.len_utf8();
                    return Ok(tail[..end].trim());
                }
            }
            continue;
        }

        match ch {
            '\'' | '"' => {
                started = true;
                quote = Some(ch);
            }
            '#' => {
                in_comment = true;
            }
            '{' | '[' | '(' => {
                started = true;
                depth += 1;
            }
            '}' | ']' | ')' => {
                if depth == 0 {
                    return Err(SettingsLiteralError::InvalidLiteral(
                        "unbalanced closing delimiter".to_string(),
                    ));
                }
                depth -= 1;
                if started && depth == 0 {
                    let end = index + ch.len_utf8();
                    return Ok(tail[..end].trim());
                }
            }
            _ if !ch.is_whitespace() => {
                started = true;
                if depth == 0 {
                    let end = tail[index..]
                        .find(['\n', '\r'])
                        .map(|offset| index + offset)
                        .unwrap_or(tail.len());
                    return Ok(tail[..end].trim());
                }
            }
            _ => {}
        }
    }

    Err(SettingsLiteralError::InvalidLiteral(
        "unterminated literal assignment".to_string(),
    ))
}

fn first_literal_char(source: &str) -> Option<usize> {
    let mut in_comment = false;
    for (index, ch) in source.char_indices() {
        if in_comment {
            if matches!(ch, '\n' | '\r') {
                in_comment = false;
            }
            continue;
        }
        if ch == '#' {
            in_comment = true;
            continue;
        }
        if !ch.is_whitespace() {
            return Some(index);
        }
    }
    None
}