You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lib/src/util/support/Messages.ts

145 lines
3.9 KiB

import {collect} from '../collection/Collection'
import {InvalidJSONStateError, JSONState, Rehydratable} from './Rehydratable'
import {Pipeline} from './Pipe'
/**
* A class for building and working with messages grouped by keys.
*/
export class Messages implements Rehydratable {
protected messages: {[key: string]: string[]} = {}
/**
* @param [allMessages] an initial array of messages to put in the "all" key
*/
constructor(allMessages?: string[]) {
if ( allMessages ) {
this.messages.all = allMessages
}
}
/** All group keys of messages. */
public keys(): string[] {
return Object.keys(this.messages)
}
/** Add a message to a group. */
public put(key: string, message: string): this {
if ( !this.messages[key] ) {
this.messages[key] = []
}
this.messages[key].push(message)
return this
}
/** Returns true if the given group has the message. */
public has(key: string, message: string): boolean {
return Boolean(this.messages[key]?.includes(message))
}
/**
* Returns true if any messages are found.
* @param [forKeys] if provided, only search the given keys
*/
public any(forKeys?: string[]): boolean {
if ( forKeys ) {
return forKeys.map(key => this.messages[key])
.filter(Boolean)
.some((bag: string[]) => bag.length)
}
return Object.values(this.messages).some((bag: string[]) => bag.length)
}
/**
* Returns the first message.
* @param [forKey] if provided, only search the given key
*/
public first(forKey?: string): string | undefined {
if ( !forKey ) {
forKey = Object.keys(this.messages)[0]
}
if ( forKey && this.messages[forKey].length ) {
return this.messages[forKey][0]
}
}
/**
* Return all messages in a flat array.
* @param [forKey] if provided, only search the given key
*/
public all(forKey?: string): string[] {
if ( forKey ) {
return this.messages[forKey] || []
}
return collect<string[]>(Object.values(this.messages)).collapse()
.all() as string[]
}
/**
* Flat array of only distinct messages.
* @param [forKey] if provided, only search the given key
*/
public unique(forKey?: string): string[] {
return collect<string>(this.all(forKey)).unique<string>()
.all()
}
/**
* Get the total number of messages.
* @param [forKey] if provided, only search the given key
*/
public count(forKey?: string): number {
return this.all(forKey).length
}
async dehydrate(): Promise<JSONState> {
return this.messages
}
rehydrate(state: JSONState): void {
if ( typeof state === 'object' && !Array.isArray(state) ) {
let all = true
for ( const key in state ) {
if ( !Object.prototype.hasOwnProperty.call(state, key) ) {
continue
}
const set = state[key]
if ( !(Array.isArray(set) && set.every(x => typeof x === 'string')) ) {
all = false
break
}
}
if ( all ) {
this.messages = state as any
}
}
throw new InvalidJSONStateError('Invalid message state object.', { state })
}
toJSON(): {[key: string]: string[]} {
return this.messages
}
toString(): string {
return JSON.stringify(this.toJSON())
}
/**
* Return a new Pipe of this collection.
*/
pipeTo<TOut>(pipeline: Pipeline<this, TOut>): TOut {
return pipeline.apply(this)
}
/** Build and apply a pipeline. */
pipe<TOut>(builder: (pipeline: Pipeline<this, this>) => Pipeline<this, TOut>): TOut {
return builder(Pipeline.id()).apply(this)
}
}