haematite-cli 0.21.3

The haematite command: serve a store, its door and Studio, answer MCP, query with jq, and host the store's assistant seat
Documentation
use super::{Input, Question, Refused, Span, Spanned, Stage, first};
use crate::serve::protocol::Metric;

/// Parse one rerank stage and preserve its original byte position.
pub(super) fn read(text: &str, start: usize) -> Result<Spanned<Stage>, Refused> {
    let mut input = Input { text, rest: text };
    parse(&mut input)
        .map(|value| Spanned {
            value,
            span: Span {
                start,
                end: start + input.position(),
            },
        })
        .map_err(|refused| offset(refused, start))
}

fn parse(input: &mut Input<'_>) -> Result<Stage, Refused> {
    input.trim();
    if input.word() != "rerank" {
        return Err(input.parse("a rerank stage starts with `rerank`"));
    }
    input.advance(6);
    input.trim();
    if !input.take("(") {
        return Err(Refused::Parse(
            "`rerank` takes its arguments in brackets".to_owned(),
            Span { start: 0, end: 6 },
        ));
    }
    input.require(".", "`rerank` names a vector field with a leading dot")?;
    let length = input
        .rest
        .find(|character| !first(character))
        .unwrap_or(input.rest.len());
    if length == 0 {
        return Err(input.parse("`rerank` names a field after its dot"));
    }
    let field = input.rest.split_at(length).0.to_owned();
    input.advance(length);
    separator(input)?;
    let question = if input.rest.starts_with('"') {
        let text = input.string("the rerank question")?;
        if text.trim().is_empty() {
            return Err(input.parse("`rerank` takes nonempty question words"));
        }
        Question::Text(text)
    } else {
        if input.word() != "record" {
            return Err(input.parse("`rerank` takes quoted words or `record(\"id\")`"));
        }
        input.advance(6);
        input.require("(", "`record` takes its id in brackets")?;
        let id = input.string("the rerank record id")?;
        if id.trim().is_empty() {
            return Err(input.parse("the rerank record id is nonempty"));
        }
        input.require(")", "`record(` takes one quoted id and a `)`")?;
        Question::Record(id)
    };
    separator(input)?;
    let name = input.word();
    let measure = match name {
        "cosine" => Metric::Cosine,
        "l2" => Metric::L2,
        "dot" => Metric::Dot,
        _ => return Err(input.parse("`rerank` names its measure: cosine, l2 or dot")),
    };
    input.advance(name.len());
    input.require(")", "`rerank(` is not closed")?;
    Ok(Stage::Rerank {
        field,
        question,
        measure,
    })
}

fn separator(input: &mut Input<'_>) -> Result<(), Refused> {
    input.require(
        ";",
        "`rerank` separates its field, question and measure with `;`",
    )?;
    input.trim();
    Ok(())
}

fn offset(refused: Refused, start: usize) -> Refused {
    let span = refused.span();
    let span = Span {
        start: span.start + start,
        end: span.end + start,
    };
    match refused {
        Refused::NotYet(message, _) => Refused::NotYet(message, span),
        Refused::Unserved(message, _) => Refused::Unserved(message, span),
        Refused::Parse(message, _) => Refused::Parse(message, span),
        Refused::WholeRecords(message, _) => Refused::WholeRecords(message, span),
        Refused::Percentile(message, _) => Refused::Percentile(message, span),
        Refused::Flag(message, _) => Refused::Flag(message, span),
        Refused::Window(code, message, _) => Refused::Window(code, message, span),
    }
}