@@ -2,12 +2,17 @@ import { toString as dateToString } from "./Date.js";
22import Decimal from "./Decimal.js" ;
33import Long , * as _Long from "./Long.js" ;
44import { escape } from "./RegExp.js" ;
5+ import { isIterable , isUnionLike } from "./Util.js" ;
6+
7+ type Numeric = number | Long | Decimal ;
8+
9+ export interface IStringable {
10+ ToString ( ) : string ;
11+ }
512
613const fsFormatRegExp = / ( ^ | [ ^ % ] ) % ( [ 0 + \- ] * ) ( \d + ) ? (?: \. ( \d + ) ) ? ( \w ) / ;
714const formatRegExp = / \{ ( \d + ) ( , - ? \d + ) ? (?: \: ( [ a - z A - Z ] ) ( \d { 0 , 2 } ) | \: ( .+ ?) ) ? \} / g;
815
9- type Numeric = number | Long | Decimal ;
10-
1116// These are used for formatting and only take longs and decimals into account (no bigint)
1217function isNumeric ( x : any ) {
1318 return typeof x === "number" || x instanceof Long || x instanceof Decimal ;
@@ -219,6 +224,8 @@ function formatOnce(str2: string, rep: any) {
219224 rep = String ( rep ) ;
220225 break ;
221226 }
227+ } else {
228+ rep = toString ( rep ) ;
222229 }
223230 padLength = parseInt ( padLength , 10 ) ;
224231 if ( ! isNaN ( padLength ) ) {
@@ -309,6 +316,8 @@ export function format(str: string, ...args: any[]) {
309316 }
310317 } else if ( rep instanceof Date ) {
311318 rep = dateToString ( rep , pattern || format ) ;
319+ } else {
320+ rep = toString ( rep )
312321 }
313322 padLength = parseInt ( ( padLength || " " ) . substring ( 1 ) , 10 ) ;
314323 if ( ! isNaN ( padLength ) ) {
@@ -318,6 +327,80 @@ export function format(str: string, ...args: any[]) {
318327 } ) ;
319328}
320329
330+ export function isStringable < T > ( x : T | IStringable ) : x is IStringable {
331+ return x != null && typeof ( x as IStringable ) . ToString === "function" ;
332+ }
333+
334+ function unionToStringPrivate ( self : any ) : string {
335+ const name = self . cases ( ) [ self . tag ] ;
336+ if ( self . fields . length === 0 ) {
337+ return name ;
338+ } else {
339+ let fields = "" ;
340+ let withParens = true ;
341+ if ( self . fields . length === 1 ) {
342+ const field = toString ( self . fields [ 0 ] ) ;
343+ withParens = field . indexOf ( " " ) >= 0 ;
344+ fields = field ;
345+ }
346+ else {
347+ fields = self . fields . map ( ( x : any ) => toString ( x ) ) . join ( ", " ) ;
348+ }
349+ return name + ( withParens ? " (" : " " ) + fields + ( withParens ? ")" : "" ) ;
350+ }
351+ }
352+
353+ export function unionToString ( self : any ) {
354+ return isStringable ( self ) ? self . ToString ( ) : unionToStringPrivate ( self ) ;
355+ }
356+
357+ function recordToStringPrivate ( self : any , callStack = 0 ) : string {
358+ return callStack > 10
359+ ? Object . getPrototypeOf ( self ) . constructor . name
360+ : "{ " + Object . entries ( self ) . map ( ( [ k , v ] ) => k + " = " + toString ( v , callStack ) ) . join ( "\n " ) + " }" ;
361+ }
362+
363+ export function recordToString ( self : any ) : string {
364+ return isStringable ( self ) ? self . ToString ( ) : recordToStringPrivate ( self ) ;
365+ }
366+
367+ export function objectToString ( self : any ) : string {
368+ return isStringable ( self ) ? self . ToString ( ) : Object . getPrototypeOf ( self ) . constructor . name ;
369+ }
370+
371+ export function seqToString < T > ( self : Iterable < T > ) : string {
372+ let count = 0 ;
373+ let str = "[" ;
374+ for ( let x of self ) {
375+ if ( count === 0 ) {
376+ str += toString ( x ) ;
377+ } else if ( count === 100 ) {
378+ str += "; ..." ;
379+ break ;
380+ } else {
381+ str += "; " + toString ( x ) ;
382+ }
383+ count ++ ;
384+ }
385+ return str + "]" ;
386+ }
387+
388+ export function toString ( x : any , callStack = - 1 ) : string {
389+ if ( x == null || typeof x !== "object" ) {
390+ return String ( x ) ;
391+ } else if ( isStringable ( x ) ) {
392+ return x . ToString ( ) ;
393+ } else if ( isIterable ( x ) ) {
394+ return seqToString ( x ) ;
395+ } else if ( isUnionLike ( x ) ) {
396+ return unionToStringPrivate ( x ) ;
397+ } else {
398+ // TODO: Defaulting to recordToString until we have a way
399+ // to tell records apart from other objects in runtime
400+ return recordToStringPrivate ( x , callStack + 1 ) ;
401+ }
402+ }
403+
321404export function endsWith ( str : string , search : string ) {
322405 const idx = str . lastIndexOf ( search ) ;
323406 return idx >= 0 && idx === str . length - search . length ;
0 commit comments