import { Decimal , DecimalConstants } from '@phensley/decimal' ; import { PluralRules } from '@phensley/plurals' ; import { MessageArg , MessageArgs , MessageNamedArgs } from './args' ; import { MessageArgConverter } from './converter' ; import { MessageCode , MessageOpType , PluralChoiceType , PluralNumberType } from '../parser' ; public export type MessageFormatFunc = ( args : MessageArg [ ] , options : string [ ] ) => string ; public export type MessageFormatFuncMap = { [ name ] : MessageFormatFunc ; } ; const get = ( key , args ) : MessageArg => { const res = args . named [ key ] ; return res !== undefined ? res : typeof key === 'number' ? args . positional [ key ] : undefined ; } ; const DECIMAL_EXACT = { 0 : DecimalConstants . ZERO , 1 : DecimalConstants . ONE , 2 : DecimalConstants . TWO , } ; public export class MessageEngine { private buf : string = '' ; constructor ( private plurals , private converter , private formatters , private code ) { } /**
     * Evaluate the message code against the given arguments.
     */ evaluate ( positional , named = { } ) { return this . _evaluate ( this . code , { positional , named } ) ; } private _evaluate ( code , args , argsub ? ) { switch ( code [ 0 ] ) { case MessageOpType . TEXT : this . buf += code [ 1 ] ; break ; case MessageOpType . BLOCK : for ( const n of code [ 1 ] ) { this . _evaluate ( n , args , argsub ) ; } break ; case MessageOpType . ARG : { const arg = get ( code [ 1 ] , args ) ; this . buf += this . converter . asString ( arg ) ; break ; } case MessageOpType . ARGSUB : { this . buf += this . converter . asString ( argsub ) ; break ; } case MessageOpType . PLURAL : { const arg = get ( code [ 1 ] [ 0 ] , args ) ; const offset = code [ 2 ] ; const num = this . converter . asDecimal ( arg ) ; argsub = offset ? num . subtract ( offset ) : num ; const category = code [ 3 ] === PluralNumberType . CARDINAL ? this . plurals . cardinal ( argsub ) : this . plurals . ordinal ( argsub ) ; let other ; let found = 0 ; loop : for ( const c of code [ 4 ] ) { switch ( c [ 0 ] ) { case PluralChoiceType . EXACT : let v = DECIMAL_EXACT [ c [ 1 ] ] ; if ( v === undefined ) { v = this . converter . asDecimal ( c [ 1 ] ) ; } if ( num . compare ( v ) === 0 ) { this . _evaluate ( c [ 2 ] , args , num ) ; found = 1 ; break loop ; } break ; case PluralChoiceType . CATEGORY : if ( c [ 1 ] === category ) { this . _evaluate ( c [ 2 ] , args , argsub ) ; found = 1 ; break loop ; } else if ( c [ 1 ] === 'other' ) { other = c [ 2 ] ; } break ; } } if ( ! found && other ) { this . _evaluate ( other , args , argsub ) ; } break ; } case MessageOpType . SELECT : { const arg = get ( code [ 1 ] [ 0 ] , args ) ; const str = this . converter . asString ( arg ) ; let other ; let found = 0 ; loop : for ( const c of code [ 2 ] ) { if ( c [ 0 ] === str ) { this . _evaluate ( c [ 1 ] , args , arg ) ; found = 1 ; break loop ; } if ( c [ 0 ] === 'other' ) { other = c [ 1 ] ; } } if ( ! found && other ) { this . _evaluate ( other , args , arg ) ; } break ; } case MessageOpType . SIMPLE : { const name = code [ 1 ] ; const f = this . formatters [ name ] ; if ( f !== undefined ) { const _args = code [ 2 ] . map ( ( k ) => get ( k , args ) ) ; this . buf += f ( _args , code [ 3 ] ) ; } break ; } } return this . buf ; } }