Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 22 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ i.e. you have following template: `"Hello {0}, we are greeting you here: {1}!"`
if you call Format with args "manager" and "salesApp" :

```go
formattedStr := Format("Hello {0}, we are greeting you here: {1}!", "manager", "salesApp")
formattedStr := stringFormatter.Format("Hello {0}, we are greeting you here: {1}!", "manager", "salesApp")
```

you get string `"Hello manager, we are greeting you here: salesApp!"`
Expand All @@ -44,15 +44,17 @@ you get string `"Hello manager, we are greeting you here: salesApp!"`

i.e. you have following template: `"Hello {user} what are you doing here {app} ?"`

if you call `FormatComplex` with args `"vpupkin"` and `"mn_console"` `FormatComplex("Hello {user} what are you doing here {app} ?", map[string]interface{}{"user":"vpupkin", "app":"mn_console"})`
if you call `FormatComplex` with args `"vpupkin"` and `"mn_console"` `FormatComplex("Hello {user} what are you doing here {app} ?", map[string]any{"user":"vpupkin", "app":"mn_console"})`

you get string `"Hello vpupkin what are you doing here mn_console ?"`

another example is:

```go
strFormatResult = FormatComplex("Current app settings are: ipAddr: {ipaddr}, port: {port}, use ssl: {ssl}.",
map[string]interface{}{"ipaddr":"127.0.0.1", "port":5432, "ssl":false})
strFormatResult = stringFormatter.FormatComplex(
"Current app settings are: ipAddr: {ipaddr}, port: {port}, use ssl: {ssl}.",
map[string]any{"ipaddr":"127.0.0.1", "port":5432, "ssl":false},
)
```
a result will be: `"Current app settings are: ipAddr: 127.0.0.1, port: 5432, use ssl: false."``

Expand All @@ -66,23 +68,24 @@ benchmark could be running using following commands from command line:

#### 2.1 Map to string utility

Map to string function allow to convert map to string using one of predefined line format:
* `key => value`
* `key : value`
* `value`
`MapToString` function allows to convert map with primitive key to string using format, including key and value, e.g.:
* `{key} => {value}`
* `{key} : {value}`
* `{value}`

For example see code from test (`text_utils_test.go`):
For example:
```go
options := map[string]interface{}{
"connectTimeout": 1000,
"useSsl": true,
"login": "sa",
"password": "sa",
}

str := MapToString(&options, KeyValueWithSemicolonSepFormat, ", ")
assert.True(t, len(str) > 0)
assert.Equal(t, "connectTimeout : 1000, useSsl : true, login : sa, password : sa", str)
options := map[string]any{
"connectTimeout": 1000,
"useSsl": true,
"login": "sa",
"password": "sa",
}

str := stringFormatter.MapToString(&options, "{key} : {value}", ", ")
// NOTE: order of key-value pairs is not guranteed though
// str will be something like:
"connectTimeout : 1000, useSsl : true, login : sa, password : sa"
```

#### 2.2 Benchmarks of the MapToStr function
Expand Down
233 changes: 107 additions & 126 deletions formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,80 +21,81 @@ import (
* - args - values that are using for formatting with template
* Returns formatted string
*/
func Format(template string, args ...interface{}) string {
func Format(template string, args ...any) string {
if args == nil {
return template
}

templateLen := len(template)
var formattedStr = &strings.Builder{}
formattedStr.Grow(templateLen + 22*len(args))
j := -1
start := strings.Index(template, "{")
if start < 0 {
return template
}

templateLen := len(template)
formattedStr := &strings.Builder{}
formattedStr.Grow(templateLen + 22*len(args))
j := -1 //nolint:ineffassign

formattedStr.WriteString(template[:start])
for i := start; i < templateLen; i++ {

if template[i] == '{' {
// possibly it is a template placeholder
if i == templateLen-1 {
break
}

if template[i+1] == '{' { // todo: umv: this not considering {{0}}
formattedStr.WriteByte('{')
continue
} else {
// find end of placeholder
j = i + 2
for {
if j >= templateLen {
break
}
if template[j] == '{' {
// multiple nested curly brackets ...
formattedStr.WriteString(template[i:j])
i = j
}
if template[j] == '}' {
break
}
j++
}
// find end of placeholder
j = i + 2
for {
if j >= templateLen {
break
}
// double curly brackets processed here, convert {{N}} -> {N}
// so we catch here {{N}
if j+1 < templateLen && template[j+1] == '}' && template[i-1] == '{' {

formattedStr.WriteString(template[i+1 : j+1])
i = j + 1
} else {
argNumberStr := template[i+1 : j]
var argNumber int
var err error
if len(argNumberStr) == 1 {
// this makes work a little faster then AtoI
argNumber = int(argNumberStr[0] - '0')
} else {
argNumber, err = strconv.Atoi(argNumberStr)
}
//argNumber, err := strconv.Atoi(argNumberStr)
if err == nil && len(args) > argNumber {
// get number from placeholder
strVal := getItemAsStr(&args[argNumber])
formattedStr.WriteString(strVal)
} else {
formattedStr.WriteByte('{')
formattedStr.WriteString(argNumberStr)
formattedStr.WriteByte('}')
}
if template[j] == '{' {
// multiple nested curly brackets ...
formattedStr.WriteString(template[i:j])
i = j
}
}

if template[j] == '}' {
break
}

j++
}
// double curly brackets processed here, convert {{N}} -> {N}
// so we catch here {{N}
if j+1 < templateLen && template[j+1] == '}' && template[i-1] == '{' {
formattedStr.WriteString(template[i+1 : j+1])
i = j + 1
} else {
argNumberStr := template[i+1 : j]
var argNumber int
var err error
if len(argNumberStr) == 1 {
// this makes work a little faster than AtoI
argNumber = int(argNumberStr[0] - '0')
} else {
argNumber, err = strconv.Atoi(argNumberStr)
}
//argNumber, err := strconv.Atoi(argNumberStr)
if err == nil && len(args) > argNumber {
// get number from placeholder
strVal := getItemAsStr(&args[argNumber])
formattedStr.WriteString(strVal)
} else {
formattedStr.WriteByte('{')
formattedStr.WriteString(argNumberStr)
formattedStr.WriteByte('}')
}
i = j
}
} else {
j = i
j = i //nolint:ineffassign
formattedStr.WriteByte(template[i])
}
}
Expand All @@ -109,72 +110,70 @@ func Format(template string, args ...interface{}) string {
* - args - values (dictionary: string key - any value) that are using for formatting with template
* Returns formatted string
*/
func FormatComplex(template string, args map[string]interface{}) string {
func FormatComplex(template string, args map[string]any) string {
if args == nil {
return template
}

templateLen := len(template)
var formattedStr = &strings.Builder{}
formattedStr.Grow(templateLen + 22*len(args))
j := -1
start := strings.Index(template, "{")
if start < 0 {
return template
}

templateLen := len(template)
formattedStr := &strings.Builder{}
formattedStr.Grow(templateLen + 22*len(args))
j := -1 //nolint:ineffassign
formattedStr.WriteString(template[:start])
for i := start; i < templateLen; i++ {

if template[i] == '{' {
// possibly it is a template placeholder
if i == templateLen-1 {
break
}

if template[i+1] == '{' { // todo: umv: this not considering {{0}}
formattedStr.WriteByte('{')
continue
} else {
// find end of placeholder
j = i + 2
for {
if j >= templateLen {
break
}
if template[j] == '{' {
// multiple nested curly brackets ...
formattedStr.WriteString(template[i:j])
i = j
}
if template[j] == '}' {
break
}
j++
}
// double curly brackets processed here, convert {{N}} -> {N}
// so we catch here {{N}
if j+1 < templateLen && template[j+1] == '}' {
}

formattedStr.WriteString(template[i+1 : j+1])
i = j + 1
} else {
argNumberStr := template[i+1 : j]
arg, ok := args[argNumberStr]
if ok {
// get number from placeholder
strVal := getItemAsStr(&arg)
formattedStr.WriteString(strVal)
} else {
formattedStr.WriteByte('{')
formattedStr.WriteString(argNumberStr)
formattedStr.WriteByte('}')
}
// find end of placeholder
j = i + 2
for {
if j >= templateLen {
break
}
if template[j] == '{' {
// multiple nested curly brackets ...
formattedStr.WriteString(template[i:j])
i = j
}
if template[j] == '}' {
break
}
j++
}
// double curly brackets processed here, convert {{N}} -> {N}
// so we catch here {{N}
if j+1 < templateLen && template[j+1] == '}' {
formattedStr.WriteString(template[i+1 : j+1])
i = j + 1
} else {
argNumberStr := template[i+1 : j]
arg, ok := args[argNumberStr]
if ok {
// get number from placeholder
strVal := getItemAsStr(&arg)
formattedStr.WriteString(strVal)
} else {
formattedStr.WriteByte('{')
formattedStr.WriteString(argNumberStr)
formattedStr.WriteByte('}')
}
i = j
}

} else {
j = i
j = i //nolint:ineffassign
formattedStr.WriteByte(template[i])
}
}
Expand All @@ -183,55 +182,37 @@ func FormatComplex(template string, args map[string]interface{}) string {
}

// todo: umv: impl format passing as param
func getItemAsStr(item *interface{}) string {
var strVal string
//var err error
switch (*item).(type) {
func getItemAsStr(item *any) string {
switch v := (*item).(type) {
case string:
strVal = (*item).(string)
break
return v
case int8:
strVal = strconv.FormatInt(int64((*item).(int8)), 10)
break
return strconv.FormatInt(int64(v), 10)
case int16:
strVal = strconv.FormatInt(int64((*item).(int16)), 10)
break
return strconv.FormatInt(int64(v), 10)
case int32:
strVal = strconv.FormatInt(int64((*item).(int32)), 10)
break
return strconv.FormatInt(int64(v), 10)
case int64:
strVal = strconv.FormatInt((*item).(int64), 10)
break
return strconv.FormatInt(v, 10)
case int:
strVal = strconv.FormatInt(int64((*item).(int)), 10)
break
return strconv.FormatInt(int64(v), 10)
case uint8:
strVal = strconv.FormatUint(uint64((*item).(uint8)), 10)
break
return strconv.FormatUint(uint64(v), 10)
case uint16:
strVal = strconv.FormatUint(uint64((*item).(uint16)), 10)
break
return strconv.FormatUint(uint64(v), 10)
case uint32:
strVal = strconv.FormatUint(uint64((*item).(uint32)), 10)
break
return strconv.FormatUint(uint64(v), 10)
case uint64:
strVal = strconv.FormatUint((*item).(uint64), 10)
break
return strconv.FormatUint(v, 10)
case uint:
strVal = strconv.FormatUint(uint64((*item).(uint)), 10)
break
return strconv.FormatUint(uint64(v), 10)
case bool:
strVal = strconv.FormatBool((*item).(bool))
break
return strconv.FormatBool(v)
case float32:
strVal = strconv.FormatFloat(float64((*item).(float32)), 'f', -1, 32)
break
return strconv.FormatFloat(float64(v), 'f', -1, 32)
case float64:
strVal = strconv.FormatFloat((*item).(float64), 'f', -1, 64)
break
return strconv.FormatFloat(v, 'f', -1, 64)
default:
strVal = fmt.Sprintf("%v", *item)
break
return fmt.Sprintf("%v", v)
}
return strVal
}
Loading