<commit_before>use parser::{Comparer, Span, StatementIndex, StatementAddress, StatementRef, SourceInfo, Token,
             TokenIndex, TokenAddress};
use segment_set::SegmentSet;
use std::fmt::Display;
use std::sync::Arc;

#[derive(Debug,Clone,Eq,PartialEq)]
pub enum Diagnostic {
    BadCharacter(Span, u8),
    BadCommentEnd(Span, Span),
    BadFloating,
    BadLabel(Span),
    CommentMarkerNotStart(Span),
    ConstantNotTopLevel,
    DisjointSingle,
    DjNotVariable(TokenIndex),
    DjRepeatedVariable(TokenIndex, TokenIndex),
    DuplicateLabel(StatementAddress),
    EmptyFilename,
    EmptyMathString,
    EssentialAtTopLevel,
    ExprNotConstantPrefix(TokenIndex),
    FilenameDollar,
    FilenameSpaces,
    FloatNotConstant(TokenIndex),
    FloatNotVariable(TokenIndex),
    FloatRedeclared(StatementAddress),
    IoError(String),
    MidStatementCommentMarker(Span),
    MissingLabel,
    MissingProof(Span),
    NestedComment(Span, Span),
    NotActiveSymbol(TokenIndex),
    ProofDvViolation,
    ProofExcessEnd,
    ProofIncomplete,
    ProofInvalidSave,
    ProofMalformedVarint,
    ProofNoSteps,
    ProofUnderflow,
    ProofUnterminatedRoster,
    ProofWrongExprEnd,
    ProofWrongTypeEnd,
    RepeatedLabel(Span, Span),
    SpuriousLabel(Span),
    SpuriousProof(Span),
    StepEssenWrong,
    StepEssenWrongType,
    StepFloatWrongType,
    StepMissing(Token),
    StepOutOfRange,
    StepUsedAfterScope(Token),
    StepUsedBeforeDefinition(Token),
    SymbolDuplicatesLabel(TokenIndex, StatementAddress),
    SymbolRedeclared(TokenIndex, TokenAddress),
    UnclosedBeforeEof,
    UnclosedBeforeInclude(StatementIndex),
    UnclosedComment(Span),
    UnclosedInclude,
    UnclosedMath,
    UnclosedProof,
    UnknownKeyword(Span),
    UnmatchedCloseGroup,
    VariableMissingFloat(TokenIndex),
    VariableRedeclaredAsConstant(TokenIndex, TokenAddress),
}

#[derive(Debug)]
pub enum Level {
    Note,
    Warning,
    Error,
}
use self::Level::*;

pub struct Notation {
    pub source: Arc<SourceInfo>,
    pub message: &'static str,
    pub span: Span,
    pub level: Level,
    pub args: Vec<(&'static str, String)>,
}

pub fn to_annotations(sset: &SegmentSet,
                      mut diags: Vec<(StatementAddress, Diagnostic)>)
                      -> Vec<Notation> {
    diags.sort_by(|x, y| sset.order.cmp(&x.0, &y.0));
    let mut out = Vec::new();
    for (saddr, diag) in diags {
        annotate_diagnostic(&mut out, sset, sset.statement(saddr), &diag);
    }
    out
}

fn annotate(notes: &mut Vec<Notation>,
            stmt: StatementRef,
            message: &'static str,
            span: Span,
            level: Level,
            args: Vec<(&'static str, String)>) {
    notes.push(Notation {
        source: stmt.segment.segment.source.clone(),
        message: message,
        span: span,
        level: level,
        args: args,
    })
}

fn annotate_diagnostic(notes: &mut Vec<Notation>,
                       sset: &SegmentSet,
                       stmt: StatementRef,
                       diag: &Diagnostic) {
    use self::Diagnostic::*;
    fn d<V: Display>(v: V) -> String {
        format!("{}", v)
    }

    fn t(v: &Token) -> String {
        String::from_utf8(v.to_owned()).expect("utf-8 is checked before making Token")
    }

    match *diag {
        BadCharacter(span, byte) => {
            annotate(notes,
                     stmt,
                     "Invalid character (byte value {byte}); Metamath source files are limited \
                      to US-ASCII with controls TAB, CR, LF, FF)",
                     span,
                     Error,
                     vec![("byte", d(byte))]);
        }
        BadCommentEnd(tok, opener) => {
            annotate(notes,
                     stmt,
                     "$) sequence must be surrounded by whitespace to end a comment",
                     tok,
                     Warning,
                     Vec::new());
            annotate(notes,
                     stmt,
                     "Comment started here",
                     opener,
                     Note,
                     Vec::new());
        }
        BadFloating => {
            annotate(notes,
                     stmt,
                     "A $f statement must have exactly two math tokens",
                     stmt.span(),
                     Error,
                     Vec::new());
        }
        BadLabel(lbl) => {
            annotate(notes,
                     stmt,
                     "Statement labels may contain only alphanumeric characters and - _ .",
                     lbl,
                     Error,
                     Vec::new());
        }
        CommentMarkerNotStart(marker) => {
            annotate(notes,
                     stmt,
                     "This comment marker must be the first token in the comment to be effective",
                     marker,
                     Warning,
                     Vec::new());
        }
        ConstantNotTopLevel => {
            annotate(notes,
                     stmt,
                     "$c statements are not allowed in nested groups",
                     stmt.span(),
                     Error,
                     Vec::new());
        }
        DisjointSingle => {
            annotate(notes,
                     stmt,
                     "A $d statement which lists only one variable is meaningless",
                     stmt.span(),
                     Warning,
                     Vec::new());
        }
        DjNotVariable(index) => {
            annotate(notes,
                     stmt,
                     "$d constraints are not applicable to constants",
                     stmt.math_span(index),
                     Error,
                     Vec::new());
        }
        DjRepeatedVariable(index1, index2) => {
            annotate(notes,
                     stmt,
                     "A variable may not be used twice in the same $d constraint",
                     stmt.math_span(index1),
                     Error,
                     Vec::new());
            annotate(notes,
                     stmt,
                     "Previous appearance was here",
                     stmt.math_span(index2),
                     Note,
                     Vec::new());
        }
        DuplicateLabel(prevstmt) => {
            annotate(notes,
                     stmt,
                     "Statement labels must be unique",
                     stmt.span(),
                     Error,
                     Vec::new());
            let stmt2 = sset.statement(prevstmt);
            annotate(notes,
                     stmt2,
                     "Label was previously used here",
                     stmt2.span(),
                     Note,
                     Vec::new());
        }
        EmptyFilename => {
            annotate(notes,
                     stmt,
                     "Filename included by a $[ directive must not be empty",
                     stmt.span(),
                     Error,
                     Vec::new());
        }
        EmptyMathString => {
            annotate(notes,
                     stmt,
                     "A math string must have at least one token",
                     stmt.span(),
                     Error,
                     Vec::new());
        }
        EssentialAtTopLevel => {
            annotate(notes,
                     stmt,
                     "$e statements must be inside scope brackets, not at the top level",
                     stmt.span(),
                     Error,
                     Vec::new());
        }
        ExprNotConstantPrefix(index) => {
            annotate(notes,
                     stmt,
                     "The math string of an $a, $e, or $p assertion must start with a constant, \
                      not a variable",
                     stmt.math_span(index),
                     Error,
                     Vec::new());
        }
        FilenameDollar => {
            annotate(notes,
                     stmt,
                     "Filenames included by $[ are not allowed to contain the $ character",
                     stmt.span(),
                     Error,
                     Vec::new());
        }
        FilenameSpaces => {
            annotate(notes,
                     stmt,
                     "Filenames included by $[ are not allowed to contain whitespace",
                     stmt.span(),
                     Error,
                     Vec::new());
        }
        FloatNotConstant(index) => {
            annotate(notes,
                     stmt,
                     "The first token of a $f statement must be a declared constant (typecode)",
                     stmt.math_span(index),
                     Error,
                     Vec::new());
        }
        FloatNotVariable(index) => {
            annotate(notes,
                     stmt,
                     "The second token of a $f statement must be a declared variable (to \
                      associate the type)",
                     stmt.math_span(index),
                     Error,
                     Vec::new());
        }
        FloatRedeclared(saddr) => {
            annotate(notes,
                     stmt,
                     "There is already an active $f for this variable",
                     stmt.span(),
                     Error,
                     Vec::new());
            let stmt2 = sset.statement(saddr);
            annotate(notes,
                     stmt2,
                     "Previous $f was here",
                     stmt2.span(),
                     Note,
                     Vec::new());
        }
        IoError(ref err) => {
            annotate(notes,
                     stmt,
                     "Source file could not be read (error: {error})",
                     stmt.span(),
                     Error,
                     vec![("error", err.clone())]);
        }
        MidStatementCommentMarker(marker) => {
            annotate(notes,
                     stmt,
                     "Marked comments are only effective between statements, not inside them",
                     marker,
                     Warning,
                     Vec::new());
        }
        MissingLabel => {
            annotate(notes,
                     stmt,
                     "This statement type requires a label",
                     stmt.span(),
                     Error,
                     Vec::new());
        }
        MissingProof(math_end) => {
            annotate(notes,
                     stmt,
                     "Provable assertion requires a proof introduced with $= here; use $= ? $. \
                      if you do not have a proof yet",
                     math_end,
                     Error,
                     Vec::new());
        }
        NestedComment(tok, opener) => {
            annotate(notes,
                     stmt,
                     "Nested comments are not supported - comment will end at the first $)",
                     tok,
                     Warning,
                     Vec::new());
            annotate(notes,
                     stmt,
                     "Comment started here",
                     opener,
                     Note,
                     Vec::new());
        }
        NotActiveSymbol(index) => {
            annotate(notes,
                     stmt,
                     "Token used here must be active in the current scope",
                     stmt.math_span(index),
                     Error,
                     Vec::new());
        }
        ProofDvViolation => {
            let s = "Disjoint variable constraint violated";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        ProofExcessEnd => {
            let s = "Must be exactly one statement on stack at end of proof";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        ProofIncomplete => {
            let s = "Proof is incomplete";
            annotate(notes, stmt, s, stmt.span(), Warning, vec![]);
        }
        ProofInvalidSave => {
            let s = "Z must appear immediately after a complete step integer";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        ProofMalformedVarint => {
            let s = "Proof step number too long or missing terminator";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        ProofNoSteps => {
            let s = "Proof must have at least one step (use ? if deliberately incomplete)";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        ProofUnderflow => {
            let s = "Too few statements on stack to satisfy step's mandatory hypotheses";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        ProofUnterminatedRoster => {
            let s = "List of referenced assertions in a compressed proof must be terminated by )";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        ProofWrongExprEnd => {
            let s = "Final step statement does not match assertion";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        ProofWrongTypeEnd => {
            let s = "Final step typecode does not match assertion";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        RepeatedLabel(lspan, fspan) => {
            annotate(notes,
                     stmt,
                     "A statement may have only one label",
                     lspan,
                     Error,
                     Vec::new());
            annotate(notes, stmt, "First label was here", fspan, Note, Vec::new());
        }
        SpuriousLabel(lspan) => {
            annotate(notes,
                     stmt,
                     "Labels are only permitted for statements of type $a, $e, $f, or $p",
                     lspan,
                     Error,
                     Vec::new());
        }
        SpuriousProof(math_end) => {
            annotate(notes,
                     stmt,
                     "Proofs are only allowed on $p assertions",
                     math_end,
                     Error,
                     Vec::new());
        }
        StepEssenWrong => {
            let s = "Step used for $e hypothesis does not match statement";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        StepEssenWrongType => {
            let s = "Step used for $e hypothesis does not match typecode";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        StepFloatWrongType => {
            let s = "Step used for $f hypothesis does not match typecode";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        StepMissing(ref tok) => {
            let s = "Step {step} referenced by proof does not correspond to a $p statement (or is \
                     malformed)";
            annotate(notes, stmt, s, stmt.span(), Error, vec![("step", t(tok))]);
        }
        StepOutOfRange => {
            let s = "Step in compressed proof is out of range of defined steps";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        StepUsedAfterScope(ref tok) => {
            let s = "Step {step} referenced by proof is a hypothesis not active in this scope";
            annotate(notes, stmt, s, stmt.span(), Error, vec![("step", t(tok))]);
        }
        StepUsedBeforeDefinition(ref tok) => {
            let s = "Step {step} referenced by proof has not yet been proved";
            annotate(notes, stmt, s, stmt.span(), Error, vec![("step", t(tok))]);
        }
        SymbolDuplicatesLabel(index, saddr) => {
            annotate(notes,
                     stmt,
                     "Metamath spec forbids symbols which are the same as labels in the same \
                      database",
                     stmt.math_span(index),
                     Warning,
                     Vec::new());
            let stmt2 = sset.statement(saddr);
            annotate(notes,
                     stmt2,
                     "Symbol was used as a label here",
                     stmt2.span(),
                     Note,
                     Vec::new());
        }
        SymbolRedeclared(index, taddr) => {
            annotate(notes,
                     stmt,
                     "This symbol is already active in this scope",
                     stmt.math_span(index),
                     Error,
                     Vec::new());
            let stmt2 = sset.statement(taddr.statement);
            annotate(notes,
                     stmt2,
                     "Symbol was previously declared here",
                     stmt2.math_span(taddr.token_index),
                     Note,
                     Vec::new());
        }
        UnclosedBeforeEof => {
            annotate(notes,
                     stmt,
                     "${ group must be closed with a $} before end of file",
                     stmt.span(),
                     Error,
                     Vec::new());
        }
        UnclosedBeforeInclude(index) => {
            annotate(notes,
                     stmt,
                     "${ group must be closed with a $} before another file can be included",
                     stmt.span(),
                     Error,
                     Vec::new());
            let stmt2 = stmt.segment.statement(index);
            annotate(notes,
                     stmt2,
                     "Include statement is here",
                     stmt2.span(),
                     Note,
                     Vec::new());
        }
        UnclosedComment(comment) => {
            annotate(notes,
                     stmt,
                     "Comment requires closing $) before end of file",
                     comment,
                     Error,
                     Vec::new());
        }
        UnclosedInclude => {
            annotate(notes,
                     stmt,
                     "$[ requires a matching $]",
                     stmt.span(),
                     Error,
                     Vec::new());
        }
        UnclosedMath => {
            annotate(notes,
                     stmt,
                     "A math string must be closed with $= or $.",
                     stmt.span(),
                     Error,
                     Vec::new());
        }
        UnclosedProof => {
            annotate(notes,
                     stmt,
                     "A proof must be closed with $.",
                     stmt.span(),
                     Error,
                     Vec::new());
        }
        UnknownKeyword(kwspan) => {
            annotate(notes,
                     stmt,
                     "Statement-starting keyword must be one of $a $c $d $e $f $p $v",
                     kwspan,
                     Error,
                     Vec::new());
        }
        UnmatchedCloseGroup => {
            annotate(notes,
                     stmt,
                     "This $} does not match any open ${",
                     stmt.span(),
                     Error,
                     Vec::new());
        }
        VariableMissingFloat(index) => {
            annotate(notes,
                     stmt,
                     "Variable token used in statement must have an active $f",
                     stmt.math_span(index),
                     Error,
                     Vec::new());
        }
        VariableRedeclaredAsConstant(index, taddr) => {
            annotate(notes,
                     stmt,
                     "Symbol cannot be used as a variable here and as a constant later",
                     stmt.math_span(index),
                     Error,
                     Vec::new());
            let stmt2 = sset.statement(taddr.statement);
            annotate(notes,
                     stmt2,
                     "Symbol will be used as a constant here",
                     stmt2.math_span(taddr.token_index),
                     Note,
                     Vec::new());
        }
    }
}
<commit_msg>cleanup diagnostic formatting rules<commit_after>use parser::{Comparer, Span, StatementIndex, StatementAddress, StatementRef, SourceInfo, Token,
             TokenIndex, TokenAddress};
use segment_set::SegmentSet;
use std::fmt::Display;
use std::sync::Arc;

#[derive(Debug,Clone,Eq,PartialEq)]
pub enum Diagnostic {
    BadCharacter(Span, u8),
    BadCommentEnd(Span, Span),
    BadFloating,
    BadLabel(Span),
    CommentMarkerNotStart(Span),
    ConstantNotTopLevel,
    DisjointSingle,
    DjNotVariable(TokenIndex),
    DjRepeatedVariable(TokenIndex, TokenIndex),
    DuplicateLabel(StatementAddress),
    EmptyFilename,
    EmptyMathString,
    EssentialAtTopLevel,
    ExprNotConstantPrefix(TokenIndex),
    FilenameDollar,
    FilenameSpaces,
    FloatNotConstant(TokenIndex),
    FloatNotVariable(TokenIndex),
    FloatRedeclared(StatementAddress),
    IoError(String),
    MidStatementCommentMarker(Span),
    MissingLabel,
    MissingProof(Span),
    NestedComment(Span, Span),
    NotActiveSymbol(TokenIndex),
    ProofDvViolation,
    ProofExcessEnd,
    ProofIncomplete,
    ProofInvalidSave,
    ProofMalformedVarint,
    ProofNoSteps,
    ProofUnderflow,
    ProofUnterminatedRoster,
    ProofWrongExprEnd,
    ProofWrongTypeEnd,
    RepeatedLabel(Span, Span),
    SpuriousLabel(Span),
    SpuriousProof(Span),
    StepEssenWrong,
    StepEssenWrongType,
    StepFloatWrongType,
    StepMissing(Token),
    StepOutOfRange,
    StepUsedAfterScope(Token),
    StepUsedBeforeDefinition(Token),
    SymbolDuplicatesLabel(TokenIndex, StatementAddress),
    SymbolRedeclared(TokenIndex, TokenAddress),
    UnclosedBeforeEof,
    UnclosedBeforeInclude(StatementIndex),
    UnclosedComment(Span),
    UnclosedInclude,
    UnclosedMath,
    UnclosedProof,
    UnknownKeyword(Span),
    UnmatchedCloseGroup,
    VariableMissingFloat(TokenIndex),
    VariableRedeclaredAsConstant(TokenIndex, TokenAddress),
}

#[derive(Debug)]
pub enum Level {
    Note,
    Warning,
    Error,
}
use self::Level::*;

pub struct Notation {
    pub source: Arc<SourceInfo>,
    pub message: &'static str,
    pub span: Span,
    pub level: Level,
    pub args: Vec<(&'static str, String)>,
}

pub fn to_annotations(sset: &SegmentSet,
                      mut diags: Vec<(StatementAddress, Diagnostic)>)
                      -> Vec<Notation> {
    diags.sort_by(|x, y| sset.order.cmp(&x.0, &y.0));
    let mut out = Vec::new();
    for (saddr, diag) in diags {
        annotate_diagnostic(&mut out, sset, sset.statement(saddr), &diag);
    }
    out
}

fn annotate(notes: &mut Vec<Notation>,
            stmt: StatementRef,
            message: &'static str,
            span: Span,
            level: Level,
            args: Vec<(&'static str, String)>) {
    notes.push(Notation {
        source: stmt.segment.segment.source.clone(),
        message: message,
        span: span,
        level: level,
        args: args,
    })
}

fn annotate_diagnostic(notes: &mut Vec<Notation>,
                       sset: &SegmentSet,
                       stmt: StatementRef,
                       diag: &Diagnostic) {
    use self::Diagnostic::*;
    fn d<V: Display>(v: V) -> String {
        format!("{}", v)
    }

    fn t(v: &Token) -> String {
        String::from_utf8(v.to_owned()).expect("utf-8 is checked before making Token")
    }

    match *diag {
        BadCharacter(span, byte) => {
            let s = "Invalid character (byte value {byte}); Metamath source files are limited to \
                     US-ASCII with controls TAB, CR, LF, FF)";
            annotate(notes, stmt, s, span, Error, vec![("byte", d(byte))]);
        }
        BadCommentEnd(tok, opener) => {
            let s = "$) sequence must be surrounded by whitespace to end a comment";
            annotate(notes, stmt, s, tok, Warning, Vec::new());
            let s = "Comment started here";
            annotate(notes, stmt, s, opener, Note, Vec::new());
        }
        BadFloating => {
            let s = "A $f statement must have exactly two math tokens";
            annotate(notes, stmt, s, stmt.span(), Error, Vec::new());
        }
        BadLabel(lbl) => {
            let s = "Statement labels may contain only alphanumeric characters and - _ .";
            annotate(notes, stmt, s, lbl, Error, Vec::new());
        }
        CommentMarkerNotStart(marker) => {
            let s = "This comment marker must be the first token in the comment to be effective";
            annotate(notes, stmt, s, marker, Warning, Vec::new());
        }
        ConstantNotTopLevel => {
            let s = "$c statements are not allowed in nested groups";
            annotate(notes, stmt, s, stmt.span(), Error, Vec::new());
        }
        DisjointSingle => {
            let s = "A $d statement which lists only one variable is meaningless";
            annotate(notes, stmt, s, stmt.span(), Warning, Vec::new());
        }
        DjNotVariable(index) => {
            let s = "$d constraints are not applicable to constants";
            annotate(notes, stmt, s, stmt.math_span(index), Error, Vec::new());
        }
        DjRepeatedVariable(index1, index2) => {
            let s = "A variable may not be used twice in the same $d constraint";
            annotate(notes, stmt, s, stmt.math_span(index1), Error, Vec::new());
            let s = "Previous appearance was here";
            annotate(notes, stmt, s, stmt.math_span(index2), Note, Vec::new());
        }
        DuplicateLabel(prevstmt) => {
            let s = "Statement labels must be unique";
            annotate(notes, stmt, s, stmt.span(), Error, Vec::new());
            let stmt2 = sset.statement(prevstmt);
            let s = "Label was previously used here";
            annotate(notes, stmt2, s, stmt2.span(), Note, Vec::new());
        }
        EmptyFilename => {
            let s = "Filename included by a $[ directive must not be empty";
            annotate(notes, stmt, s, stmt.span(), Error, Vec::new());
        }
        EmptyMathString => {
            let s = "A math string must have at least one token";
            annotate(notes, stmt, s, stmt.span(), Error, Vec::new());
        }
        EssentialAtTopLevel => {
            let s = "$e statements must be inside scope brackets, not at the top level";
            annotate(notes, stmt, s, stmt.span(), Error, Vec::new());
        }
        ExprNotConstantPrefix(index) => {
            let s = "The math string of an $a, $e, or $p assertion must start with a constant, \
                     not a variable";
            annotate(notes, stmt, s, stmt.math_span(index), Error, Vec::new());
        }
        FilenameDollar => {
            let s = "Filenames included by $[ are not allowed to contain the $ character";
            annotate(notes, stmt, s, stmt.span(), Error, Vec::new());
        }
        FilenameSpaces => {
            let s = "Filenames included by $[ are not allowed to contain whitespace";
            annotate(notes, stmt, s, stmt.span(), Error, Vec::new());
        }
        FloatNotConstant(index) => {
            let s = "The first token of a $f statement must be a declared constant (typecode)";
            annotate(notes, stmt, s, stmt.math_span(index), Error, Vec::new());
        }
        FloatNotVariable(index) => {
            let s = "The second token of a $f statement must be a declared variable (to \
                     associate the type)";
            annotate(notes, stmt, s, stmt.math_span(index), Error, Vec::new());
        }
        FloatRedeclared(saddr) => {
            let s = "There is already an active $f for this variable";
            annotate(notes, stmt, s, stmt.span(), Error, Vec::new());
            let stmt2 = sset.statement(saddr);
            let s = "Previous $f was here";
            annotate(notes, stmt2, s, stmt2.span(), Note, Vec::new());
        }
        IoError(ref err) => {
            let s = "Source file could not be read (error: {error})";
            let p = vec![("error", err.clone())];
            annotate(notes, stmt, s, stmt.span(), Error, p);
        }
        MidStatementCommentMarker(marker) => {
            let s = "Marked comments are only effective between statements, not inside them";
            annotate(notes, stmt, s, marker, Warning, Vec::new());
        }
        MissingLabel => {
            let s = "This statement type requires a label";
            annotate(notes, stmt, s, stmt.span(), Error, Vec::new());
        }
        MissingProof(math_end) => {
            let s = "Provable assertion requires a proof introduced with $= here; use $= ? $. \
                     if you do not have a proof yet";
            annotate(notes, stmt, s, math_end, Error, Vec::new());
        }
        NestedComment(tok, opener) => {
            let s = "Nested comments are not supported - comment will end at the first $)";
            annotate(notes, stmt, s, tok, Warning, Vec::new());
            let s = "Comment started here";
            annotate(notes, stmt, s, opener, Note, Vec::new());
        }
        NotActiveSymbol(index) => {
            let s = "Token used here must be active in the current scope";
            annotate(notes, stmt, s, stmt.math_span(index), Error, Vec::new());
        }
        ProofDvViolation => {
            let s = "Disjoint variable constraint violated";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        ProofExcessEnd => {
            let s = "Must be exactly one statement on stack at end of proof";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        ProofIncomplete => {
            let s = "Proof is incomplete";
            annotate(notes, stmt, s, stmt.span(), Warning, vec![]);
        }
        ProofInvalidSave => {
            let s = "Z must appear immediately after a complete step integer";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        ProofMalformedVarint => {
            let s = "Proof step number too long or missing terminator";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        ProofNoSteps => {
            let s = "Proof must have at least one step (use ? if deliberately incomplete)";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        ProofUnderflow => {
            let s = "Too few statements on stack to satisfy step's mandatory hypotheses";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        ProofUnterminatedRoster => {
            let s = "List of referenced assertions in a compressed proof must be terminated by )";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        ProofWrongExprEnd => {
            let s = "Final step statement does not match assertion";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        ProofWrongTypeEnd => {
            let s = "Final step typecode does not match assertion";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        RepeatedLabel(lspan, fspan) => {
            let s = "A statement may have only one label";
            annotate(notes, stmt, s, lspan, Error, Vec::new());
            annotate(notes, stmt, "First label was here", fspan, Note, Vec::new());
        }
        SpuriousLabel(lspan) => {
            let s = "Labels are only permitted for statements of type $a, $e, $f, or $p";
            annotate(notes, stmt, s, lspan, Error, Vec::new());
        }
        SpuriousProof(math_end) => {
            let s = "Proofs are only allowed on $p assertions";
            annotate(notes, stmt, s, math_end, Error, Vec::new());
        }
        StepEssenWrong => {
            let s = "Step used for $e hypothesis does not match statement";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        StepEssenWrongType => {
            let s = "Step used for $e hypothesis does not match typecode";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        StepFloatWrongType => {
            let s = "Step used for $f hypothesis does not match typecode";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        StepMissing(ref tok) => {
            let s = "Step {step} referenced by proof does not correspond to a $p statement (or is \
                     malformed)";
            annotate(notes, stmt, s, stmt.span(), Error, vec![("step", t(tok))]);
        }
        StepOutOfRange => {
            let s = "Step in compressed proof is out of range of defined steps";
            annotate(notes, stmt, s, stmt.span(), Error, vec![]);
        }
        StepUsedAfterScope(ref tok) => {
            let s = "Step {step} referenced by proof is a hypothesis not active in this scope";
            annotate(notes, stmt, s, stmt.span(), Error, vec![("step", t(tok))]);
        }
        StepUsedBeforeDefinition(ref tok) => {
            let s = "Step {step} referenced by proof has not yet been proved";
            annotate(notes, stmt, s, stmt.span(), Error, vec![("step", t(tok))]);
        }
        SymbolDuplicatesLabel(index, saddr) => {
            let s = "Metamath spec forbids symbols which are the same as labels in the same \
                     database";
            annotate(notes, stmt, s, stmt.math_span(index), Warning, Vec::new());
            let stmt2 = sset.statement(saddr);
            let s = "Symbol was used as a label here";
            annotate(notes, stmt2, s, stmt2.span(), Note, Vec::new());
        }
        SymbolRedeclared(index, taddr) => {
            let s = "This symbol is already active in this scope";
            annotate(notes, stmt, s, stmt.math_span(index), Error, Vec::new());
            let stmt2 = sset.statement(taddr.statement);
            let s = "Symbol was previously declared here";
            let sp = stmt2.math_span(taddr.token_index);
            annotate(notes, stmt2, s, sp, Note, Vec::new());
        }
        UnclosedBeforeEof => {
            let s = "${ group must be closed with a $} before end of file";
            annotate(notes, stmt, s, stmt.span(), Error, Vec::new());
        }
        UnclosedBeforeInclude(index) => {
            let s = "${ group must be closed with a $} before another file can be included";
            annotate(notes, stmt, s, stmt.span(), Error, Vec::new());
            let stmt2 = stmt.segment.statement(index);
            let s = "Include statement is here";
            annotate(notes, stmt2, s, stmt2.span(), Note, Vec::new());
        }
        UnclosedComment(comment) => {
            let s = "Comment requires closing $) before end of file";
            annotate(notes, stmt, s, comment, Error, Vec::new());
        }
        UnclosedInclude => {
            let s = "$[ requires a matching $]";
            annotate(notes, stmt, s, stmt.span(), Error, Vec::new());
        }
        UnclosedMath => {
            let s = "A math string must be closed with $= or $.";
            annotate(notes, stmt, s, stmt.span(), Error, Vec::new());
        }
        UnclosedProof => {
            let s = "A proof must be closed with $.";
            annotate(notes, stmt, s, stmt.span(), Error, Vec::new());
        }
        UnknownKeyword(kwspan) => {
            let s = "Statement-starting keyword must be one of $a $c $d $e $f $p $v";
            annotate(notes, stmt, s, kwspan, Error, Vec::new());
        }
        UnmatchedCloseGroup => {
            let s = "This $} does not match any open ${";
            annotate(notes, stmt, s, stmt.span(), Error, Vec::new());
        }
        VariableMissingFloat(index) => {
            let s = "Variable token used in statement must have an active $f";
            annotate(notes, stmt, s, stmt.math_span(index), Error, Vec::new());
        }
        VariableRedeclaredAsConstant(index, taddr) => {
            let s = "Symbol cannot be used as a variable here and as a constant later";
            annotate(notes, stmt, s, stmt.math_span(index), Error, Vec::new());
            let stmt2 = sset.statement(taddr.statement);
            let s = "Symbol will be used as a constant here";
            let sp = stmt2.math_span(taddr.token_index);
            annotate(notes, stmt2, s, sp, Note, Vec::new());
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

// `echo "$(grep '\\u' src/r2k/dict.rs | wc -l)-1" | bc`
const DEFAULT_CAPACITY: usize = 218;

pub type Dict = HashMap<String, String>;

pub trait KanaConvertionTable {
    fn new() -> Dict;
}

impl KanaConvertionTable for Dict {
    fn new() -> Dict {
        let ret = (vec![// Hiragana
                        ("a", "\u{3042}"), // あ
                        ("i", "\u{3044}"), // い
                        ("u", "\u{3046}"), // う
                        ("e", "\u{3048}"), // え
                        ("o", "\u{304a}"), // お
                        ("ka", "\u{304b}"), // か
                        ("ga", "\u{304c}"), // が
                        ("ki", "\u{304d}"), // き
                        ("gi", "\u{304e}"), // ぎ
                        ("ku", "\u{304f}"), // く
                        ("gu", "\u{3050}"), // ぐ
                        ("ke", "\u{3051}"), // け
                        ("ge", "\u{3052}"), // げ
                        ("ko", "\u{3053}"), // こ
                        ("go", "\u{3054}"), // ご
                        ("sa", "\u{3055}"), // さ
                        ("za", "\u{3056}"), // ざ
                        ("shi", "\u{3057}"), // し
                        ("ji", "\u{3058}"), // じ
                        ("su", "\u{3059}"), // す
                        ("zu", "\u{305a}"), // ず
                        ("se", "\u{305b}"), // せ
                        ("ze", "\u{305c}"), // ぜ
                        ("so", "\u{305d}"), // そ
                        ("zo", "\u{305e}"), // ぞ
                        ("ta", "\u{305f}"), // た
                        ("da", "\u{3060}"), // だ
                        ("chi", "\u{3061}"), // ち
                        ("dji", "\u{3062}"), // ぢ
                        ("tsu", "\u{3064}"), // つ
                        ("dzu", "\u{3065}"), // づ
                        ("te", "\u{3066}"), // て
                        ("de", "\u{3067}"), // で
                        ("to", "\u{3068}"), // と
                        ("do", "\u{3069}"), // ど
                        ("na", "\u{306a}"), // な
                        ("ni", "\u{306b}"), // に
                        ("nu", "\u{306c}"), // ぬ
                        ("ne", "\u{306d}"), // ね
                        ("no", "\u{306d}"), // の
                        ("ha", "\u{306f}"), // は
                        ("ba", "\u{3070}"), // ば
                        ("pa", "\u{3071}"), // ぱ
                        ("hi", "\u{3072}"), // ひ
                        ("bi", "\u{3073}"), // び
                        ("pi", "\u{3074}"), // ぴ
                        ("fu", "\u{3075}"), // ふ
                        ("bu", "\u{3076}"), // ぶ
                        ("pu", "\u{3077}"), // ぷ
                        ("he", "\u{3078}"), // へ
                        ("be", "\u{3079}"), // べ
                        ("pe", "\u{307a}"), // ぺ
                        ("ho", "\u{307b}"), // ほ
                        ("bo", "\u{307c}"), // ぼ
                        ("po", "\u{307d}"), // ぽ
                        ("ma", "\u{307e}"), // ま
                        ("mi", "\u{307f}"), // み
                        ("mu", "\u{3080}"), // む
                        ("me", "\u{3081}"), // め
                        ("mo", "\u{3082}"), // も
                        ("ya", "\u{3084}"), // や
                        ("yu", "\u{3086}"), // ゆ
                        ("yo", "\u{3088}"), // よ
                        ("ra", "\u{3089}"), // ら
                        ("ri", "\u{308a}"), // り
                        ("ru", "\u{308b}"), // る
                        ("re", "\u{308c}"), // れ
                        ("ro", "\u{308d}"), // ろ
                        ("wa", "\u{308f}"), // わ
                        ("kya", "\u{304d}\u{3083}"), // き
                        ("kyu", "\u{304d}\u{3085}"), // き
                        ("kyo", "\u{304d}\u{3087}"), // き
                        ("gya", "\u{304e}\u{3083}"), //
                        ("gyu", "\u{304e}\u{3085}"), //
                        ("gyo", "\u{304e}\u{3087}"), //
                        ("sha", "\u{3057}\u{3083}"), //
                        ("shu", "\u{3057}\u{3085}"), //
                        ("sho", "\u{3057}\u{3087}"), //
                        ("ja", "\u{3058}\u{3083}"), //
                        ("ju", "\u{3058}\u{3085}"), //
                        ("jo", "\u{3058}\u{3087}"), //
                        ("cha", "\u{3061}\u{3083}"), //
                        ("chu", "\u{3061}\u{3085}"), //
                        ("cho", "\u{3061}\u{3087}"), //
                        ("dja", "\u{3062}\u{3083}"), //
                        ("dju", "\u{3062}\u{3085}"), //
                        ("djo", "\u{3062}\u{3087}"), //
                        ("hya", "\u{3072}\u{3083}"), //
                        ("hyu", "\u{3072}\u{3085}"), //
                        ("hyo", "\u{3072}\u{3087}"), //
                        ("bya", "\u{3073}\u{3083}"), //
                        ("byu", "\u{3073}\u{3085}"), //
                        ("byo", "\u{3073}\u{3087}"), //
                        ("pya", "\u{3074}\u{3083}"), //
                        ("pyu", "\u{3074}\u{3085}"), //
                        ("pyo", "\u{3074}\u{3087}"), //
                        ("nya", "\u{306b}\u{3083}"), //
                        ("nyu", "\u{306b}\u{3085}"), //
                        ("nyo", "\u{306b}\u{3087}"), //
                        ("wi", "\u{3090}"), // ゐ
                        ("we", "\u{3091}"), // ゑ
                        ("wo", "\u{3092}"), // を
                        ("n", "\u{3093}"), // ん
                        ("vu", "\u{3094}"), // ゔ
                        // lazy ass aproach to pauses (small tsu) goes here
                        //
                        // Katakana
                        ("A", "\u{30a2}"), // ア
                        ("I", "\u{30a4}"), // イ
                        ("U", "\u{30a6}"), // ウ
                        ("E", "\u{30a8}"), // エ
                        ("O", "\u{30aa}"), // オ
                        ("KA", "\u{30ab}"), // カ
                        ("GA", "\u{30ac}"), // ガ
                        ("KI", "\u{30ad}"), // キ
                        ("GI", "\u{30ae}"), // ギ
                        ("KU", "\u{30af}"), // ク
                        ("GU", "\u{30b0}"), // グ
                        ("KE", "\u{30b1}"), // ケ
                        ("GE", "\u{30b2}"), // ゲ
                        ("KO", "\u{30b3}"), // コ
                        ("GO", "\u{30b4}"), // ゴ
                        ("SA", "\u{30b5}"), // サ
                        ("ZA", "\u{30b6}"), // ザ
                        ("SHI", "\u{30b7}"), // シ
                        ("JI", "\u{30b8}"), // ジ
                        ("SU", "\u{30b9}"), // ス
                        ("ZU", "\u{30ba}"), // ズ
                        ("SE", "\u{30bb}"), // セ
                        ("ZE", "\u{30bc}"), // ゼ
                        ("SO", "\u{30bd}"), // ソ
                        ("ZO", "\u{30be}"), // ゾ
                        ("TA", "\u{30bf}"), // タ
                        ("DA", "\u{30c0}"), // ダ
                        ("CHI", "\u{30c1}"), // チ
                        ("DJI", "\u{30c2}"), // ヂ
                        ("TSU", "\u{30c4}"), // ツ
                        ("DZU", "\u{30c5}"), // ヅ
                        ("TE", "\u{30c6}"), // テ
                        ("DE", "\u{30c7}"), // デ
                        ("TO", "\u{30c8}"), // ト
                        ("DO", "\u{30c9}"), // ド
                        ("NA", "\u{30ca}"), // ナ
                        ("NI", "\u{30cb}"), // ニ
                        ("NU", "\u{30cc}"), // ヌ
                        ("NE", "\u{30cd}"), // ネ
                        ("NO", "\u{30ce}"), // ノ
                        ("HA", "\u{30cf}"), // ハ
                        ("BA", "\u{30d0}"), // バ
                        ("PA", "\u{30d1}"), // パ
                        ("HI", "\u{30d2}"), // ヒ
                        ("BI", "\u{30d3}"), // ビ
                        ("PI", "\u{30d4}"), // ピ
                        ("FU", "\u{30d5}"), // フ
                        ("BU", "\u{30d6}"), // ブ
                        ("PU", "\u{30d7}"), // プ
                        ("HE", "\u{30d8}"), // ヘ
                        ("BE", "\u{30d9}"), // ベ
                        ("PE", "\u{30da}"), // ペ
                        ("HO", "\u{30db}"), // ホ
                        ("BO", "\u{30dc}"), // ボ
                        ("PO", "\u{30dd}"), // ポ
                        ("MA", "\u{30de}"), // マ
                        ("MI", "\u{30df}"), // ミ
                        ("MU", "\u{30e0}"), // ム
                        ("ME", "\u{30e1}"), // メ
                        ("MO", "\u{30e2}"), // モ
                        ("YA", "\u{30e4}"), // ヤ
                        ("YU", "\u{30e6}"), // ユ
                        ("YO", "\u{30e8}"), // ヨ
                        ("RA", "\u{30e9}"), // ラ
                        ("RI", "\u{30ea}"), // リ
                        ("RU", "\u{30eb}"), // ル
                        ("RE", "\u{30ec}"), // レ
                        ("RO", "\u{30ed}"), // ロ
                        ("WA", "\u{30ef}"), // ワ
                        ("KYA", "\u{30ad}\u{30e3}"), //
                        ("KYU", "\u{30ad}\u{30e5}"), //
                        ("KYO", "\u{30ad}\u{30e7}"), //
                        ("GYA", "\u{30ae}\u{30e3}"), //
                        ("GYU", "\u{30ae}\u{30e5}"), //
                        ("GYO", "\u{30ae}\u{30e7}"), //
                        ("SHA", "\u{30b7}\u{30e3}"), //
                        ("SHU", "\u{30b7}\u{30e5}"), //
                        ("SHO", "\u{30b7}\u{30e7}"), //
                        ("JA", "\u{30b8}\u{30e3}"), //
                        ("JU", "\u{30b8}\u{30e5}"), //
                        ("JO", "\u{30b8}\u{30e7}"), //
                        ("CHA", "\u{30c1}\u{30e3}"), //
                        ("CHU", "\u{30c1}\u{30e5}"), //
                        ("CHO", "\u{30c1}\u{30e7}"), //
                        ("DJA", "\u{30c2}\u{30e3}"), //
                        ("DJU", "\u{30c2}\u{30e5}"), //
                        ("DJO", "\u{30c2}\u{30e7}"), //
                        ("HYA", "\u{30d2}\u{30e3}"), //
                        ("HYU", "\u{30d2}\u{30e5}"), //
                        ("HYO", "\u{30d2}\u{30e7}"), //
                        ("BYA", "\u{30d3}\u{30e3}"), //
                        ("BYU", "\u{30d3}\u{30e5}"), //
                        ("BYO", "\u{30d3}\u{30e7}"), //
                        ("PYA", "\u{30d4}\u{30e3}"), //
                        ("PYU", "\u{30d4}\u{30e5}"), //
                        ("PYO", "\u{30d4}\u{30e7}"), //
                        ("NYA", "\u{30cb}\u{30e3}"), //
                        ("NYU", "\u{30cb}\u{30e5}"), //
                        ("NYO", "\u{30cb}\u{30e7}"), //
                        ("WI", "\u{30f0}"), // ヰ
                        ("WE", "\u{30f1}"), // ヱ
                        ("WO", "\u{30f2}"), // ヲ
                        ("N", "\u{30f3}"), // ン
                        ("VU", "\u{30f4}"), // ヴ
                        // lazy ass aproach to long vowels in Katakana
                        ("AA", "\u{30a2}\u{30fc}"), // アー
                        ("II", "\u{30a4}\u{30fc}"), // イー
                        ("UU", "\u{30a6}\u{30fc}"), // ウー
                        ("EE", "\u{30a8}\u{30fc}"), // エー
                        ("OO", "\u{30aa}\u{30fc}"), // オー
                        // lazy ass aproach to pauses (small tsu) goes here
                        //
                        // Punctuation
                        (".", "\u{3002}"), // 。
                        (",", "\u{3001}"), // 、
                        ("!", "\u{ff01}"), // ！
                        ("?", "\u{ff1f}"), // ？
                        ("-", "\u{30fc}") /* ー */])
            .into_iter()
            .map(|(k, v)| (k.to_string(), v.to_string()))
            .collect();
        ret
    }
}
<commit_msg>Simplify `dict.rs`<commit_after>use std::collections::HashMap;

pub type Dict = HashMap<String, String>;

pub trait KanaConvertionTable {
    fn dnew() -> Dict;
}

impl KanaConvertionTable for Dict {
    fn dnew() -> Dict {
        let ret: Dict = (vec![// Hiragana
                              ("a", "\u{3042}"), // あ
                              ("i", "\u{3044}"), // い
                              ("u", "\u{3046}"), // う
                              ("e", "\u{3048}"), // え
                              ("o", "\u{304a}"), // お
                              ("ka", "\u{304b}"), // か
                              ("ga", "\u{304c}"), // が
                              ("ki", "\u{304d}"), // き
                              ("gi", "\u{304e}"), // ぎ
                              ("ku", "\u{304f}"), // く
                              ("gu", "\u{3050}"), // ぐ
                              ("ke", "\u{3051}"), // け
                              ("ge", "\u{3052}"), // げ
                              ("ko", "\u{3053}"), // こ
                              ("go", "\u{3054}"), // ご
                              ("sa", "\u{3055}"), // さ
                              ("za", "\u{3056}"), // ざ
                              ("shi", "\u{3057}"), // し
                              ("ji", "\u{3058}"), // じ
                              ("su", "\u{3059}"), // す
                              ("zu", "\u{305a}"), // ず
                              ("se", "\u{305b}"), // せ
                              ("ze", "\u{305c}"), // ぜ
                              ("so", "\u{305d}"), // そ
                              ("zo", "\u{305e}"), // ぞ
                              ("ta", "\u{305f}"), // た
                              ("da", "\u{3060}"), // だ
                              ("chi", "\u{3061}"), // ち
                              ("dji", "\u{3062}"), // ぢ
                              ("tsu", "\u{3064}"), // つ
                              ("dzu", "\u{3065}"), // づ
                              ("te", "\u{3066}"), // て
                              ("de", "\u{3067}"), // で
                              ("to", "\u{3068}"), // と
                              ("do", "\u{3069}"), // ど
                              ("na", "\u{306a}"), // な
                              ("ni", "\u{306b}"), // に
                              ("nu", "\u{306c}"), // ぬ
                              ("ne", "\u{306d}"), // ね
                              ("no", "\u{306d}"), // の
                              ("ha", "\u{306f}"), // は
                              ("ba", "\u{3070}"), // ば
                              ("pa", "\u{3071}"), // ぱ
                              ("hi", "\u{3072}"), // ひ
                              ("bi", "\u{3073}"), // び
                              ("pi", "\u{3074}"), // ぴ
                              ("fu", "\u{3075}"), // ふ
                              ("bu", "\u{3076}"), // ぶ
                              ("pu", "\u{3077}"), // ぷ
                              ("he", "\u{3078}"), // へ
                              ("be", "\u{3079}"), // べ
                              ("pe", "\u{307a}"), // ぺ
                              ("ho", "\u{307b}"), // ほ
                              ("bo", "\u{307c}"), // ぼ
                              ("po", "\u{307d}"), // ぽ
                              ("ma", "\u{307e}"), // ま
                              ("mi", "\u{307f}"), // み
                              ("mu", "\u{3080}"), // む
                              ("me", "\u{3081}"), // め
                              ("mo", "\u{3082}"), // も
                              ("ya", "\u{3084}"), // や
                              ("yu", "\u{3086}"), // ゆ
                              ("yo", "\u{3088}"), // よ
                              ("ra", "\u{3089}"), // ら
                              ("ri", "\u{308a}"), // り
                              ("ru", "\u{308b}"), // る
                              ("re", "\u{308c}"), // れ
                              ("ro", "\u{308d}"), // ろ
                              ("wa", "\u{308f}"), // わ
                              ("kya", "\u{304d}\u{3083}"), // き
                              ("kyu", "\u{304d}\u{3085}"), // き
                              ("kyo", "\u{304d}\u{3087}"), // き
                              ("gya", "\u{304e}\u{3083}"), //
                              ("gyu", "\u{304e}\u{3085}"), //
                              ("gyo", "\u{304e}\u{3087}"), //
                              ("sha", "\u{3057}\u{3083}"), //
                              ("shu", "\u{3057}\u{3085}"), //
                              ("sho", "\u{3057}\u{3087}"), //
                              ("ja", "\u{3058}\u{3083}"), //
                              ("ju", "\u{3058}\u{3085}"), //
                              ("jo", "\u{3058}\u{3087}"), //
                              ("cha", "\u{3061}\u{3083}"), //
                              ("chu", "\u{3061}\u{3085}"), //
                              ("cho", "\u{3061}\u{3087}"), //
                              ("dja", "\u{3062}\u{3083}"), //
                              ("dju", "\u{3062}\u{3085}"), //
                              ("djo", "\u{3062}\u{3087}"), //
                              ("hya", "\u{3072}\u{3083}"), //
                              ("hyu", "\u{3072}\u{3085}"), //
                              ("hyo", "\u{3072}\u{3087}"), //
                              ("bya", "\u{3073}\u{3083}"), //
                              ("byu", "\u{3073}\u{3085}"), //
                              ("byo", "\u{3073}\u{3087}"), //
                              ("pya", "\u{3074}\u{3083}"), //
                              ("pyu", "\u{3074}\u{3085}"), //
                              ("pyo", "\u{3074}\u{3087}"), //
                              ("nya", "\u{306b}\u{3083}"), //
                              ("nyu", "\u{306b}\u{3085}"), //
                              ("nyo", "\u{306b}\u{3087}"), //
                              ("wi", "\u{3090}"), // ゐ
                              ("we", "\u{3091}"), // ゑ
                              ("wo", "\u{3092}"), // を
                              ("n", "\u{3093}"), // ん
                              ("vu", "\u{3094}"), // ゔ
                              // lazy ass aproach to pauses (small tsu) goes here
                              //
                              // Katakana
                              ("A", "\u{30a2}"), // ア
                              ("I", "\u{30a4}"), // イ
                              ("U", "\u{30a6}"), // ウ
                              ("E", "\u{30a8}"), // エ
                              ("O", "\u{30aa}"), // オ
                              ("KA", "\u{30ab}"), // カ
                              ("GA", "\u{30ac}"), // ガ
                              ("KI", "\u{30ad}"), // キ
                              ("GI", "\u{30ae}"), // ギ
                              ("KU", "\u{30af}"), // ク
                              ("GU", "\u{30b0}"), // グ
                              ("KE", "\u{30b1}"), // ケ
                              ("GE", "\u{30b2}"), // ゲ
                              ("KO", "\u{30b3}"), // コ
                              ("GO", "\u{30b4}"), // ゴ
                              ("SA", "\u{30b5}"), // サ
                              ("ZA", "\u{30b6}"), // ザ
                              ("SHI", "\u{30b7}"), // シ
                              ("JI", "\u{30b8}"), // ジ
                              ("SU", "\u{30b9}"), // ス
                              ("ZU", "\u{30ba}"), // ズ
                              ("SE", "\u{30bb}"), // セ
                              ("ZE", "\u{30bc}"), // ゼ
                              ("SO", "\u{30bd}"), // ソ
                              ("ZO", "\u{30be}"), // ゾ
                              ("TA", "\u{30bf}"), // タ
                              ("DA", "\u{30c0}"), // ダ
                              ("CHI", "\u{30c1}"), // チ
                              ("DJI", "\u{30c2}"), // ヂ
                              ("TSU", "\u{30c4}"), // ツ
                              ("DZU", "\u{30c5}"), // ヅ
                              ("TE", "\u{30c6}"), // テ
                              ("DE", "\u{30c7}"), // デ
                              ("TO", "\u{30c8}"), // ト
                              ("DO", "\u{30c9}"), // ド
                              ("NA", "\u{30ca}"), // ナ
                              ("NI", "\u{30cb}"), // ニ
                              ("NU", "\u{30cc}"), // ヌ
                              ("NE", "\u{30cd}"), // ネ
                              ("NO", "\u{30ce}"), // ノ
                              ("HA", "\u{30cf}"), // ハ
                              ("BA", "\u{30d0}"), // バ
                              ("PA", "\u{30d1}"), // パ
                              ("HI", "\u{30d2}"), // ヒ
                              ("BI", "\u{30d3}"), // ビ
                              ("PI", "\u{30d4}"), // ピ
                              ("FU", "\u{30d5}"), // フ
                              ("BU", "\u{30d6}"), // ブ
                              ("PU", "\u{30d7}"), // プ
                              ("HE", "\u{30d8}"), // ヘ
                              ("BE", "\u{30d9}"), // ベ
                              ("PE", "\u{30da}"), // ペ
                              ("HO", "\u{30db}"), // ホ
                              ("BO", "\u{30dc}"), // ボ
                              ("PO", "\u{30dd}"), // ポ
                              ("MA", "\u{30de}"), // マ
                              ("MI", "\u{30df}"), // ミ
                              ("MU", "\u{30e0}"), // ム
                              ("ME", "\u{30e1}"), // メ
                              ("MO", "\u{30e2}"), // モ
                              ("YA", "\u{30e4}"), // ヤ
                              ("YU", "\u{30e6}"), // ユ
                              ("YO", "\u{30e8}"), // ヨ
                              ("RA", "\u{30e9}"), // ラ
                              ("RI", "\u{30ea}"), // リ
                              ("RU", "\u{30eb}"), // ル
                              ("RE", "\u{30ec}"), // レ
                              ("RO", "\u{30ed}"), // ロ
                              ("WA", "\u{30ef}"), // ワ
                              ("KYA", "\u{30ad}\u{30e3}"), //
                              ("KYU", "\u{30ad}\u{30e5}"), //
                              ("KYO", "\u{30ad}\u{30e7}"), //
                              ("GYA", "\u{30ae}\u{30e3}"), //
                              ("GYU", "\u{30ae}\u{30e5}"), //
                              ("GYO", "\u{30ae}\u{30e7}"), //
                              ("SHA", "\u{30b7}\u{30e3}"), //
                              ("SHU", "\u{30b7}\u{30e5}"), //
                              ("SHO", "\u{30b7}\u{30e7}"), //
                              ("JA", "\u{30b8}\u{30e3}"), //
                              ("JU", "\u{30b8}\u{30e5}"), //
                              ("JO", "\u{30b8}\u{30e7}"), //
                              ("CHA", "\u{30c1}\u{30e3}"), //
                              ("CHU", "\u{30c1}\u{30e5}"), //
                              ("CHO", "\u{30c1}\u{30e7}"), //
                              ("DJA", "\u{30c2}\u{30e3}"), //
                              ("DJU", "\u{30c2}\u{30e5}"), //
                              ("DJO", "\u{30c2}\u{30e7}"), //
                              ("HYA", "\u{30d2}\u{30e3}"), //
                              ("HYU", "\u{30d2}\u{30e5}"), //
                              ("HYO", "\u{30d2}\u{30e7}"), //
                              ("BYA", "\u{30d3}\u{30e3}"), //
                              ("BYU", "\u{30d3}\u{30e5}"), //
                              ("BYO", "\u{30d3}\u{30e7}"), //
                              ("PYA", "\u{30d4}\u{30e3}"), //
                              ("PYU", "\u{30d4}\u{30e5}"), //
                              ("PYO", "\u{30d4}\u{30e7}"), //
                              ("NYA", "\u{30cb}\u{30e3}"), //
                              ("NYU", "\u{30cb}\u{30e5}"), //
                              ("NYO", "\u{30cb}\u{30e7}"), //
                              ("WI", "\u{30f0}"), // ヰ
                              ("WE", "\u{30f1}"), // ヱ
                              ("WO", "\u{30f2}"), // ヲ
                              ("N", "\u{30f3}"), // ン
                              ("VU", "\u{30f4}"), // ヴ
                              // lazy ass aproach to long vowels in Katakana
                              ("AA", "\u{30a2}\u{30fc}"), // ア
                              ("II", "\u{30a4}\u{30fc}"), // イ
                              ("UU", "\u{30a6}\u{30fc}"), // ウ
                              ("EE", "\u{30a8}\u{30fc}"), // エ
                              ("OO", "\u{30aa}\u{30fc}"), // オ
                              // lazy ass aproach to pauses (small tsu) goes here
                              //
                              // Punctuation
                              (".", "\u{3002}"), // 。
                              (",", "\u{3001}"), // 、
                              ("!", "\u{ff01}"), // ！
                              ("?", "\u{ff1f}"), // ？
                              ("-", "\u{30fc}") /* ー */])
            .into_iter()
            .map(|(k, v)| (k.to_string(), v.to_string()))
            .collect();
        ret
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use parse::Env;
use var::Var;
use source::Src;

pub trait Eval {
    fn eval (&self, path: Option<&[&str]>, lookup: &str) -> Option<Var>;

    fn eval_bare (&self, lookup: &str) -> Option<Var> {
        let mut lookups: Vec<&str> = lookup.split_terminator('.').collect();
        let lookup = lookups.pop().unwrap();

        let path;
        if lookups.len() > 0 { path = Some(&lookups[..]); }
        else { path = None }

        self.eval(path, lookup)
    }
}

pub struct Evaluator<'e, 'd, D:Eval + 'd> {
    data: &'d D,
    env: &'e mut Env,
    next_node: String,
    await_node: String,
}

impl<'e, 'd, D:Eval + 'd> Iterator for Evaluator<'e, 'd, D>
    where D: Eval + 'd {

    type Item = (Vec<Var>,Option<String>); //here we only return node name as an option to advance
    fn next(&mut self) -> Option<Self::Item> {
        let nn = {
            if !self.await_node.is_empty() {
                let an = self.await_node.clone();
                self.await_node.clear();
                an
            }
            else if !self.next_node.is_empty() { self.next_node.clone() }
            else { return None }
        };

        let mut r = self.run(&nn);
        let nn = r.1.clone();
        if nn.is_none() { self.next_node.clear(); }
        else { self.next_node = nn.unwrap(); }

        if self.await_node.is_empty() { r.1 = None; } //no need to return node name

        Some(r)
    }
}

impl<'e, 'd, D:Eval> Evaluator<'e, 'd, D> {
    pub fn new (env: &'e mut Env, data: &'d D) -> Evaluator<'e, 'd, D> {
        Evaluator {
            env: env, data: data,
            next_node: "root".to_owned(),
            await_node: "".to_owned()
        }
    }
    pub fn advance (&mut self) {
        if let Some(b) = self.env.src.get_mut(&self.await_node) {
            b.await_idx = 0; //reset on advance
        }

        self.await_node.clear();
    }

    pub fn run (&mut self, node_name: &str)
                -> (Vec<Var>,Option<String>)
        where D: Eval + 'd
    {
        let mut r = vec!();
        let mut node = None;
        let mut if_valid = false; //track for OR

        if let Some(b) = self.env.src.get_mut(node_name) { //println!("src:{:?}",b.src);
            let mut state: HashMap<String,bool> = HashMap::new();
            state.insert("this.visited".to_owned(), b.visited);
            b.visited = true;

            let await_idx = b.await_idx;
            b.await_idx = 0;

            for (i,src) in b.src[await_idx..].iter().enumerate() {
                match src {
                    &Src::Await(ref nn) => {
                        b.await_idx = i+1;
                        node = nn.clone();
                        break
                    },
                    &Src::Or(_,_) => {
                        if !if_valid {
                            continue
                        }
                        else { if_valid = false; }
                    }
                    &Src::If(_,_,_) => { if_valid = true; }
                    _ => { if_valid = false; },
                }

                let (mut vars, node_) = src.eval(&mut state, self.data);

                // reset if if was successful
                if (vars.len() > 0) || node_.is_some() { if_valid = false; }

                for n in vars.drain(..) { r.push(n); }
                if let Some((node_,await)) = node_ {
                    if await {
                        b.await_idx = i+1;
                        self.await_node = node_name.to_owned();
                    }

                    node = Some(node_);
                    break;
                }
            }
        }

        for var in r.iter_mut() {
            let mut val = None;
            match var {
                &mut Var::String(ref mut s) => { //format string
                    let mut fs = String::new();
                    let mut started = false;

                    // NOTE: we should move this out to a SYM varkind instead
                    // (parsed earlier)
                    if s.split_terminator(' ').count() == 1 {
                        if s.chars().next().unwrap() == '`' {
                            if let Some(ref val_) = self.data.eval_bare(&s[1..]) {
                                val = Some(val_.clone());
                            }
                        }
                    }
                    else {
                        for word in s.split_terminator(' ') {
                            if started { fs.push(' '); }

                            if word.chars().next().unwrap() == '`' {
                                if let Some(ref val_) = self.data.eval_bare(&word[1..]) {
                                    fs.push_str(&val_.to_string());
                                }
                            }
                            else {
                                fs.push_str(word);
                            }

                            started = true;
                        }
                        *s = fs;
                    }
                },
                _ => {}
            }

            if let Some(val) = val {
                *var = val;
            }
        }

        return (r,node)
    }
}
<commit_msg>rename for clarity<commit_after>use std::collections::HashMap;
use parse::Env;
use var::Var;
use source::Src;

pub trait Eval {
    fn eval (&self, path: Option<&[&str]>, lookup: &str) -> Option<Var>;

    fn eval_bare (&self, lookup: &str) -> Option<Var> {
        let mut lookups: Vec<&str> = lookup.split_terminator('.').collect();
        let lookup = lookups.pop().unwrap();

        let path;
        if lookups.len() > 0 { path = Some(&lookups[..]); }
        else { path = None }

        self.eval(path, lookup)
    }
}

pub struct Evaluator<'e, 'd, D:Eval + 'd> {
    data: &'d D,
    env: &'e mut Env,
    next_node: String,
    await_node: String,
}

impl<'e, 'd, D:Eval + 'd> Iterator for Evaluator<'e, 'd, D>
    where D: Eval + 'd {

    type Item = (Vec<Var>,Option<String>); //here we only return node name as an option to advance
    fn next(&mut self) -> Option<Self::Item> {
        let nn = {
            if !self.await_node.is_empty() {
                let an = self.await_node.clone();
                self.await_node.clear();
                an
            }
            else if !self.next_node.is_empty() { self.next_node.clone() }
            else { return None }
        };

        let mut r = self.run(&nn);
        let nn = r.1.clone();
        if nn.is_none() { self.next_node.clear(); }
        else { self.next_node = nn.unwrap(); }

        if self.await_node.is_empty() { r.1 = None; } //no need to return node name

        Some(r)
    }
}

impl<'e, 'd, D:Eval> Evaluator<'e, 'd, D> {
    pub fn new (env: &'e mut Env, data: &'d D) -> Evaluator<'e, 'd, D> {
        Evaluator {
            env: env, data: data,
            next_node: "root".to_owned(),
            await_node: "".to_owned()
        }
    }
    pub fn advance (&mut self) {
        if let Some(b) = self.env.src.get_mut(&self.await_node) {
            b.await_idx = 0; //reset on advance
        }

        self.await_node.clear();
    }

    pub fn run (&mut self, node_name: &str)
                -> (Vec<Var>,Option<String>)
        where D: Eval + 'd
    {
        let mut r = vec!();
        let mut node = None;
        let mut or_valid = false; //track for OR

        if let Some(b) = self.env.src.get_mut(node_name) { //println!("src:{:?}",b.src);
            let mut state: HashMap<String,bool> = HashMap::new();
            state.insert("this.visited".to_owned(), b.visited);
            b.visited = true;

            let await_idx = b.await_idx;
            b.await_idx = 0;

            for (i,src) in b.src[await_idx..].iter().enumerate() {
                match src {
                    &Src::Await(ref nn) => {
                        b.await_idx = i+1;
                        node = nn.clone();
                        break
                    },
                    &Src::Or(_,_) => {
                        if !or_valid {
                            continue
                        }
                        else { or_valid = false; }
                    }
                    &Src::If(_,_,_) => { or_valid = true; }
                    _ => { or_valid = false; },
                }

                let (mut vars, node_) = src.eval(&mut state, self.data);

                // reset if if was successful
                if (vars.len() > 0) || node_.is_some() { or_valid = false; }

                for n in vars.drain(..) { r.push(n); }
                if let Some((node_,await)) = node_ {
                    if await {
                        b.await_idx = i+1;
                        self.await_node = node_name.to_owned();
                    }

                    node = Some(node_);
                    break;
                }
            }
        }

        for var in r.iter_mut() {
            let mut val = None;
            match var {
                &mut Var::String(ref mut s) => { //format string
                    let mut fs = String::new();
                    let mut started = false;

                    // NOTE: we should move this out to a SYM varkind instead
                    // (parsed earlier)
                    if s.split_terminator(' ').count() == 1 {
                        if s.chars().next().unwrap() == '`' {
                            if let Some(ref val_) = self.data.eval_bare(&s[1..]) {
                                val = Some(val_.clone());
                            }
                        }
                    }
                    else {
                        for word in s.split_terminator(' ') {
                            if started { fs.push(' '); }

                            if word.chars().next().unwrap() == '`' {
                                if let Some(ref val_) = self.data.eval_bare(&word[1..]) {
                                    fs.push_str(&val_.to_string());
                                }
                            }
                            else {
                                fs.push_str(word);
                            }

                            started = true;
                        }
                        *s = fs;
                    }
                },
                _ => {}
            }

            if let Some(val) = val {
                *var = val;
            }
        }

        return (r,node)
    }
}
<|endoftext|>


<commit_before>/* Copyright (C)  2016 nokaa <nokaa@cock.li>
 * This software is licensed under the terms of the
 * GNU Affero General Public License. You should have
 * received a copy of this license with this software.
 * The license may also be found at https://gnu.org/licenses/agpl.txt
 */

use std::fs::File;
use std::io::{self, Read, Write};

#[allow(dead_code)]
/// Helper function for reading from a file. Reads from `filename`
/// and returns a `Vec<u8>`.
pub fn read_file(filename: &str) -> Result<Vec<u8>, io::Error> {
    let mut f = try!(File::open(filename));
    let mut buf: Vec<u8> = vec![];
    try!(f.read_to_end(&mut buf));

    Ok(buf)
}

/// Reads all data from `filename`, putting each line into a
/// `Vec<u8>`. A vector of all the lines, `Vec<Vec<u8>>` is
/// returned if successful.
pub fn read_file_lines(filename: &str, line_length: usize) -> Result<Vec<Vec<u8>>, io::Error> {
    let f = try!(File::open(filename));
    let mut lines: Vec<Vec<u8>> = vec![];
    let mut line: Vec<u8> = vec![];

    for byte in f.bytes() {
        match byte {
            Err(e) => return Err(e),
            Ok(b) => match b {
                b'\n' => {
                    line.push(b);
                    lines.push(line);
                    line = vec![];
                }
                // We do nothing on the carriage return for now.
                // TODO(nokaa): We should decide what to do here.
                b'\r' => { }
                _ => {
                    if line.len() == line_length {
                        lines.push(line);
                        line = vec![b];
                    } else {
                        line.push(b);
                    }
                }
            }
        }
    }

    Ok(lines)
}

/// Returns `true` if `filename` exists, `false` otherwise.
pub fn file_exists(filename: &str) -> bool {
    match File::open(filename) {
        Ok(_) => true,
        Err(_) => false,
    }
}

#[allow(dead_code)]
/// Helper function for writing to a file. Writes `contents` to `filename`.
pub fn write_file(filename: &str, data: &[u8]) -> Result<(), io::Error> {
    let mut file = try!(File::create(filename));
    try!(file.write_all(data));
    Ok(())
}

pub fn write_file_lines(filename: &str, data: &Vec<Vec<u8>>) -> Result<(), io::Error> {
    let mut file = try!(File::create(filename));
    for line in data {
        try!(file.write(&line[..]));
    }

    Ok(())
}

#[allow(dead_code)]
/// Takes a `&[u8]` and returns a `Vec<u8>` containing all values
/// until the first 0.
pub fn get_nonzero_bytes(data: &[u8]) -> Vec<u8> {
    let mut buf: Vec<u8> = vec![];
    for ch in data {
        if *ch == 0u8 {
            break;
        } else {
            buf.push(*ch);
        }
    }

    buf
}
<commit_msg>Clean up file functions<commit_after>/* Copyright (C)  2016 nokaa <nokaa@cock.li>
 * This software is licensed under the terms of the
 * GNU Affero General Public License. You should have
 * received a copy of this license with this software.
 * The license may also be found at https://gnu.org/licenses/agpl.txt
 */

use std::fs::File;
use std::io::{self, Read, Write};

/// Reads all data from `filename`, putting each line into a
/// `Vec<u8>`. A vector of all the lines, `Vec<Vec<u8>>` is
/// returned if successful.
pub fn read_file_lines(filename: &str, line_length: usize) -> Result<Vec<Vec<u8>>, io::Error> {
    let f = try!(File::open(filename));
    let mut lines: Vec<Vec<u8>> = vec![];
    let mut line: Vec<u8> = vec![];

    for byte in f.bytes() {
        match byte {
            Err(e) => return Err(e),
            Ok(b) => match b {
                b'\n' => {
                    line.push(b);
                    lines.push(line);
                    line = vec![];
                }
                // We do nothing on the carriage return for now.
                // TODO(nokaa): We should decide what to do here.
                b'\r' => { }
                _ => {
                    if line.len() == line_length {
                        lines.push(line);
                        line = vec![b];
                    } else {
                        line.push(b);
                    }
                }
            }
        }
    }

    Ok(lines)
}

/// Returns `true` if `filename` exists, `false` otherwise.
pub fn file_exists(filename: &str) -> bool {
    match File::open(filename) {
        Ok(_) => true,
        Err(_) => false,
    }
}

/// Writes all data in `data` to `filename`.
pub fn write_file_lines(filename: &str, data: &Vec<Vec<u8>>) -> Result<(), io::Error> {
    let mut file = try!(File::create(filename));
    for line in data {
        try!(file.write(&line[..]));
    }

    Ok(())
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;

/// A flat buffer over a (multi channel) image.
///
/// Note that the strides need not conform to the assumption that constructed
/// indices actually refer inside the underlying buffer but return values
/// of library functions will always guarantee this. To manually make this
/// check use `check_index_validities` and maybe put that inside an assert.
pub struct FlatSamples<T, C: AsRef<[T]>> {
    /// Underlying linear container holding sample values.
    pub samples: C,

    /// Add this to an index to get to the next sample in x-direction.
    pub horizontal_stride: usize,

    /// Add this to an index to get to the next sample in y-direction.
    pub vertical_stride: usize,

    /// Add this to an index to get to the sample in the next channel.
    pub channel_stride: usize,

    /// The width of the represented image.
    pub width: u32,

    /// The height of the represented image.
    pub height: u32,

    /// The number of channels in the color representation of the image.
    pub channels: u8,

    /// Notes the sample type to the compiler.
    pub phantom: PhantomData<T>,
}

impl<T, C: AsRef<[T]>> FlatSamples<T, C> {
    /// Get the strides for indexing matrix-like [(h, w, c)].
    ///
    /// For a row-major layout with grouped samples, this tuple is strictl
    /// decreasing.
    pub fn strides_hwc(&self) -> (usize, usize, usize) {
        (self.horizontal_stride, self.vertical_stride, self.channel_stride)
    }

    /// Get the dimensions (height, width, channels).
    ///
    /// Warning: width and height are swapped compared to 2D size methods such
    /// as `ImageBuffer::dimensions`. The interface is optimized for use with
    /// `strides_hwc` instead.
    pub fn extents(&self) -> (usize, usize, usize) {
        (self.channels as usize, self.width as usize, self.height as usize)
    }

    /// Get a reference based version.
    pub fn as_ref(&self) -> FlatSamples<T, &[T]> {
        FlatSamples {
            samples: self.samples.as_ref(),
            horizontal_stride: self.horizontal_stride,
            vertical_stride: self.vertical_stride,
            channel_stride: self.channel_stride,
            width: self.width,
            height: self.height,
            channels: self.channels,
            phantom: PhantomData,
        }
    }

    /// View the samples as a slice.
    pub fn as_slice(&self) -> &[T] {
        self.samples.as_ref()
    }

    /// Check if every indexable pixel refers inside the samples.
    ///
    /// This method will allow zero strides, allowing compact representations
    /// of monochrome images. To check that no aliasing occurs, try
    /// `check_alias_invariants` [WIP].
    pub fn check_index_validities(&self) -> bool {
        // Construct the maximum index, and test that.  Note that when one
        // dimensions is `0`, there is no maximum index so the check should
        // succeed.
        let max = self.index(
            self.width.saturating_sub(1),
            self.height.saturating_sub(1),
            self.channels.saturating_sub(1),
        );

        max < Some(self.as_slice().len())
    }

    /// Resolve the index of a particular sample.
    ///
    /// `None` if the index is outside the bounds or does not fit into a `usize`.
    pub fn index(&self, x: u32, y: u32, channel: u8) -> Option<usize> {
        let base = if x < self.width && y < self.height && channel < self.channels {
            Some(0usize)
        } else {
            None
        };

        let idx_x = (x as usize).checked_mul(self.vertical_stride);
        let idx_y = (y as usize).checked_mul(self.horizontal_stride);
        let idx_c = (channel as usize).checked_mul(self.channel_stride);

        base
            .and_then(|b| idx_x.and_then(|x| b.checked_add(x)))
            .and_then(|b| idx_y.and_then(|y| b.checked_add(y)))
            .and_then(|b| idx_c.and_then(|c| b.checked_add(c)))
    }
}
<commit_msg>Conversion error type and normalization variants<commit_after>use std::marker::PhantomData;

/// A flat buffer over a (multi channel) image.
///
/// Note that the strides need not conform to the assumption that constructed
/// indices actually refer inside the underlying buffer but return values
/// of library functions will always guarantee this. To manually make this
/// check use `check_index_validities` and maybe put that inside an assert.
pub struct FlatSamples<T, C: AsRef<[T]>> {
    /// Underlying linear container holding sample values.
    pub samples: C,

    /// Add this to an index to get to the next sample in x-direction.
    pub horizontal_stride: usize,

    /// Add this to an index to get to the next sample in y-direction.
    pub vertical_stride: usize,

    /// Add this to an index to get to the sample in the next channel.
    pub channel_stride: usize,

    /// The width of the represented image.
    pub width: u32,

    /// The height of the represented image.
    pub height: u32,

    /// The number of channels in the color representation of the image.
    pub channels: u8,

    /// Notes the sample type to the compiler.
    pub phantom: PhantomData<T>,
}

impl<T, C: AsRef<[T]>> FlatSamples<T, C> {
    /// Get the strides for indexing matrix-like [(h, w, c)].
    ///
    /// For a row-major layout with grouped samples, this tuple is strictly
    /// decreasing.
    pub fn strides_hwc(&self) -> (usize, usize, usize) {
        (self.horizontal_stride, self.vertical_stride, self.channel_stride)
    }

    /// Get the dimensions (height, width, channels).
    ///
    /// Warning: width and height are swapped compared to 2D size methods such
    /// as `ImageBuffer::dimensions`. The interface is optimized for use with
    /// `strides_hwc` instead.
    pub fn extents(&self) -> (usize, usize, usize) {
        (self.channels as usize, self.width as usize, self.height as usize)
    }

    /// Get a reference based version.
    pub fn as_ref(&self) -> FlatSamples<T, &[T]> {
        FlatSamples {
            samples: self.samples.as_ref(),
            horizontal_stride: self.horizontal_stride,
            vertical_stride: self.vertical_stride,
            channel_stride: self.channel_stride,
            width: self.width,
            height: self.height,
            channels: self.channels,
            phantom: PhantomData,
        }
    }

    /// View the samples as a slice.
    pub fn as_slice(&self) -> &[T] {
        self.samples.as_ref()
    }

    /// Check if every indexable pixel refers inside the samples.
    ///
    /// This method will allow zero strides, allowing compact representations
    /// of monochrome images. To check that no aliasing occurs, try
    /// `check_alias_invariants` [WIP].
    pub fn has_valid_indexing(&self) -> bool {
        // Construct the maximum index, and test that.  Note that when one
        // dimensions is `0`, there is no maximum index so the check should
        // succeed.
        let max = self.index(
            self.width.saturating_sub(1),
            self.height.saturating_sub(1),
            self.channels.saturating_sub(1),
        );

        max < Some(self.as_slice().len())
    }

    /// If there are any samples aliasing each other.
    ///
    /// If this is not the case, it would always be safe to allow mutable access to two different
    /// samples at the same time. Otherwise, this operation would need additional checks. When one
    /// dimension overflows `usize` with its stride we also consider this aliasing.
    pub fn has_aliased_samples(&self) -> bool {
        // Order extents by strides, then check that each is less equal than the next stride.
        let strides = self.strides_hwc();
        let sizes = self.extents();
        let grouped: [(usize, usize); 3] = [
            (strides.0, sizes.0),
            (strides.1, sizes.1),
            (strides.2, sizes.2)];

        let min_dim = grouped[0].min(grouped[1]).min(grouped[2]);
        let max_dim = grouped[0].max(grouped[1]).max(grouped[2]);
        let mid_dim = (grouped[0].max(grouped[1]))
            .min(grouped[0].max(grouped[2]));

        let min_size = match min_dim.0.checked_mul(min_dim.1) {
            None => return true,
            Some(size) => size,
        };

        let mid_size = match mid_dim.0.checked_mul(mid_dim.1) {
            None => return true,
            Some(size) => size,
        };

        let _max_size = match max_dim.0.checked_mul(max_dim.1) {
            None => return true,
            Some(_) => (), // Only want to know this didn't overflow.
        };

        return min_size > mid_dim.0 || mid_size > max_dim.0;
    }

    /// Resolve the index of a particular sample.
    ///
    /// `None` if the index is outside the bounds or does not fit into a `usize`.
    pub fn index(&self, x: u32, y: u32, channel: u8) -> Option<usize> {
        let base = if x < self.width && y < self.height && channel < self.channels {
            Some(0usize)
        } else {
            None
        };

        let idx_x = (x as usize).checked_mul(self.vertical_stride);
        let idx_y = (y as usize).checked_mul(self.horizontal_stride);
        let idx_c = (channel as usize).checked_mul(self.channel_stride);

        base
            .and_then(|b| idx_x.and_then(|x| b.checked_add(x)))
            .and_then(|b| idx_y.and_then(|y| b.checked_add(y)))
            .and_then(|b| idx_c.and_then(|c| b.checked_add(c)))
    }
}

/// Denotes invalid flat sample buffers when trying to convert to stricter types.
///
/// The biggest use case being `ImageBuffer` which expects closely packed
/// samples in a row major matrix representation. But this error type may be
/// resused for other import functions. A more versatile user may also try to
/// correct the underlying representation depending on the error variant.
pub enum ImportError {
    /// The pixels may not alias to be used underlying the target, but they do.
    Aliasing,

    /// There may be no holes using the image strides.
    ///
    /// In other words, the target requires all strides to be strictly equal to the stride below
    /// them times the size in that dimension.
    Unpacked,

    /// The represented image was too large.
    ///
    /// The optional value denotes a possibly accepted maximal bound.
    TooLarge,

    /// The represented image can not use this representation.
    InvalidFormat {
        /// The normalized form that would be accepted.
        require: Option<NormalForm>,
    },
}

/// Different normal forms of buffers.
///
/// A normal form is an unaliased buffer with some additional constraints.  The `ÌmageBuffer` uses
/// row major form with packed samples.
pub enum NormalForm {
    /// No further constraints than no pixel aliases another.
    Unaliased,

    /// At least pixels are packed.
    ///
    /// Images of these types can wrap `[T]`-slices into the standard color types. This is a
    /// precondition for `GenericImage` which requires by-reference access to pixels.
    PixelPacked,

    /// The samples are in row-major form and all samples are packed.
    ///
    /// In addition to `PixelPacked` this also asserts that the pixel matrix is in row-major form
    /// and all rows and columns are also packed. Therefore, the number of elements in the
    /// underlying buffer is exactly `channels*width*height`.
    RowMajorPacked,
}
<|endoftext|>