Skip to content

Commit

Permalink
@8thgencore PR-36 with tests (#37)
Browse files Browse the repository at this point in the history
* add: format parameters
* placeholder fix
* formatter tests

---------

Co-authored-by: 8thgencore <axelmaker@ya.ru>
  • Loading branch information
bobiverse and 8thgencore committed Jul 11, 2024
1 parent 419186a commit 3fcd3bc
Show file tree
Hide file tree
Showing 9 changed files with 227 additions and 33 deletions.
59 changes: 48 additions & 11 deletions Param.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ type Param struct {

Separator string // {{Usernames SEPERATOR}}

Trigger *ParamTrigger
Trigger *ParamTrigger
Formatter *ParamFormatter

RowPlaceholder string
Index int //slice data index,expandPlaceholders function needs
Expand Down Expand Up @@ -74,6 +75,8 @@ func NewParamFromRaw(raw []byte) *Param {
p := NewParam(string(matches[0][2]))
p.Separator = strings.TrimSpace(string(matches[0][3]))
p.Trigger = NewParamTrigger(matches[0][4])
p.Formatter = NewFormatter(matches[0][4])

return p
}

Expand All @@ -97,20 +100,32 @@ func (p *Param) SetValue(val any) {

// Placeholder .. {{Key}}
func (p *Param) Placeholder() string {
var trigger string = ""
var formatter, trigger, params string
if p.Formatter != nil {
formatter = p.Formatter.String()
}
if p.Trigger != nil {
trigger = " " + p.Trigger.String()
trigger = p.Trigger.String()
}
return "{{" + p.AbsoluteKey + trigger + "}}"
if p.Formatter != nil || p.Trigger != nil {
params = " " + formatter + trigger
}
return "{{" + p.AbsoluteKey + params + "}}"
}

// PlaceholderKey .. {{#Key}}
func (p *Param) PlaceholderKey() string {
var trigger string
var formatter, trigger, params string
if p.Formatter != nil {
formatter = p.Formatter.String()
}
if p.Trigger != nil {
trigger = " " + p.Trigger.String()
trigger = p.Trigger.String()
}
return "{{#" + p.AbsoluteKey + trigger + "}}"
if p.Formatter != nil || p.Trigger != nil {
params = " " + formatter + trigger
}
return "{{#" + p.AbsoluteKey + params + "}}"
}

// PlaceholderInline .. {{Key ,}}
Expand Down Expand Up @@ -213,6 +228,31 @@ func (p *Param) extractTriggerFrom(buf []byte) *ParamTrigger {
return nil
}

// Try to extract trigger from raw contents specific to this param
func (p *Param) extractFormatter(buf []byte) *ParamFormatter {
prefixes := []string{
p.PlaceholderInline(),
p.PlaceholderKeyInline(),
}
for _, pref := range prefixes {
bpref := []byte(pref)
if !bytes.Contains(buf, bpref) {
continue
}

// Get part where trigger is (remove plaheolder prefix)
buf := bytes.SplitN(buf, bpref, 2)[1]

// Remove placeholder suffix and only raw trigger part left
buf = bytes.SplitN(buf, []byte("}}"), 2)[0]

p.Formatter = NewFormatter(buf)
return p.Formatter
}

return nil
}

// RunTrigger - execute trigger
func (p *Param) RunTrigger(xnode *xmlNode) {
if p == nil || p.Trigger == nil {
Expand Down Expand Up @@ -240,7 +280,6 @@ func (p *Param) RunTrigger(xnode *xmlNode) {

n := xnode.closestUp(ntypes)
if n == nil {
// aurora.Red("EMPTY parent of %v", xnode.Tag())
return
}

Expand All @@ -254,7 +293,6 @@ func (p *Param) RunTrigger(xnode *xmlNode) {
n.parent.childFirst.iterate(func(wpNode *xmlNode) bool {
isitem, listid := wpNode.IsListItem()
if !isitem || listid != listID {
// aurora.Red("--- %s [%s]", wpNode, wpNode.AllContents())
return false
}
if p.Trigger.Command == TriggerCommandRemove {
Expand All @@ -267,7 +305,6 @@ func (p *Param) RunTrigger(xnode *xmlNode) {

// Simple cases
if p.Trigger.Command == TriggerCommandRemove {
// fmt.Printf("Trigger: [%s] [%s]\t Command=[%s]\n", aurora.Blue(p.AbsoluteKey), aurora.Magenta(p.Trigger.String()), aurora.BgMagenta(p.Trigger.Command))
n.delete()
return
}
Expand All @@ -279,13 +316,13 @@ func (p *Param) RunTrigger(xnode *xmlNode) {
})
return
}

}

// String - compact debug information as string
func (p *Param) String() string {
s := fmt.Sprintf("%34s=%-20s", p.AbsoluteKey, p.Value)
s += fmt.Sprintf("\tSeparator[%s]", p.Separator)
s += fmt.Sprintf("\tFormatter[%s]", p.Formatter)
s += fmt.Sprintf("\tTrigger[%s]", p.Trigger)
return s
}
85 changes: 85 additions & 0 deletions ParamFormatter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package docxplate

import (
"bytes"
"strings"

"golang.org/x/text/cases"
"golang.org/x/text/language"
)

// Format - how to format an element
const (
FormatLower = ":lower"
FormatUpper = ":upper"
FormatTitle = ":title"
FormatCapitalize = ":capitalize"
)

type ParamFormatter struct {
raw string

Format string
}

// NewFormatter - take raw ":empty:remove:list" and make formatter and its fields from it
func NewFormatter(raw []byte) *ParamFormatter {
raw = bytes.TrimSpace(raw)
raw = bytes.ToLower(raw)

// init with defaults
f := &ParamFormatter{
raw: string(raw),
}

// Always must start with ":"
if !strings.HasPrefix(f.raw, ":") {
return nil
}

// Remove the first ":" so split parts counting is more readable
// Split into parts
parts := strings.Split(f.raw[1:], ":")

for _, part := range parts {
switch part {
case "lower", "upper", "title", "capitalize":
f.Format = ":" + part
}
}

return f
}

// applyFormat - apply formatting to the given content based on the formatter
func (p *ParamFormatter) ApplyFormat(format string, content []byte) []byte {
switch format {
case FormatLower:
return bytes.ToLower(content)
case FormatUpper:
return bytes.ToUpper(content)
case FormatTitle:
titleCaser := cases.Title(language.Und)
return []byte(titleCaser.String(string(content)))
case FormatCapitalize:
content = bytes.TrimSpace(content)
if len(content) > 0 {
content[0] = bytes.ToUpper([]byte{content[0]})[0]
if len(content) > 1 {
content = append([]byte{content[0]}, bytes.ToLower(content[1:])...)
}
}
return content
default:
return content
}
}

// String - return rebuilt formatter string
func (p *ParamFormatter) String() string {
if p == nil {
return ""
}
s := p.Format
return s
}
1 change: 1 addition & 0 deletions ParamList.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ func rowParams(row []byte) ParamList {
p.RowPlaceholder = string(match[0])
p.Separator = string(match[3])
p.Trigger = NewParamTrigger(match[4])
p.Formatter = NewFormatter(match[4])
list = append(list, p)
}
return list
Expand Down
38 changes: 22 additions & 16 deletions ParamTrigger.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ type ParamTrigger struct {
Scope string
}

// NewParamTrigger - take raw ":empty:remove:list" and make
// trigger and it's fields from it
// NewParamTrigger - take raw ":empty:remove:list" and make trigger and its fields from it
func NewParamTrigger(raw []byte) *ParamTrigger {
raw = bytes.TrimSpace(raw)
raw = bytes.ToLower(raw)
Expand All @@ -59,28 +58,34 @@ func NewParamTrigger(raw []byte) *ParamTrigger {
if !strings.HasPrefix(tr.raw, ":") {
return nil
}
// remove first ":" so split parts counting is more readable
tr.raw = strings.TrimPrefix(tr.raw, ":")

// Must be at least set two parts ":empty:remove"
if strings.Count(tr.raw, ":") < 2 {
return nil
// Remove the first ":" so split parts counting is more readable
// Split into parts
parts := strings.Split(tr.raw[1:], ":")

var countCommandParts = 0
for _, part := range parts {
switch part {
case "unknown", "empty", "=":
countCommandParts++
tr.On = ":" + part
case "remove", "clear":
countCommandParts++
tr.Command = ":" + part
case "placeholder", "cell", "row", "list", "table", "section":
countCommandParts++
tr.Scope = ":" + part
}
}
// Extract fields
arr := strings.SplitN(tr.raw, ":", 3)
tr.On = ":" + arr[0]
tr.Command = ":" + arr[1]

// Scope: set default if not found
if len(arr) >= 3 {
tr.Scope = ":" + arr[2]

if countCommandParts != 3 {
return nil
}

if !tr.isValid() {
return nil
}

// fmt.Printf("TRIGGER: %+v", tr)
return tr
}

Expand All @@ -89,6 +94,7 @@ func (tr *ParamTrigger) isValid() bool {

// On
if !inSlice(tr.On, []string{
TriggerOnUnknown,
TriggerOnEmpty,
TriggerOnValue,
}) {
Expand Down
20 changes: 15 additions & 5 deletions Template.stage.funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ func (t *Template) triggerMissingParams(xnode *xmlNode) {
if !n.isRowElement() || !n.HaveParams() {
return
}

p := NewParamFromRaw(n.AllContents())
if p != nil && p.Trigger != nil {
triggerParams = append(triggerParams, p)
Expand Down Expand Up @@ -68,9 +67,15 @@ func (t *Template) expandPlaceholders(xnode *xmlNode) {
placeholderType = rowPlaceholder
}

var trigger string
var formatter, trigger, params string
if rowParam.Formatter != nil {
formatter = rowParam.Formatter.String()
}
if rowParam.Trigger != nil {
trigger = " " + rowParam.Trigger.String()
trigger = rowParam.Trigger.String()
}
if rowParam.Formatter != nil || rowParam.Trigger != nil {
params = " " + formatter + trigger
}

paramData := t.params.FindAllByKey(rowParam.AbsoluteKey)
Expand All @@ -80,7 +85,7 @@ func (t *Template) expandPlaceholders(xnode *xmlNode) {
placeholders := make([]string, paramData[len(paramData)-1].Index)

for _, param := range paramData {
placeholders[param.Index-1] = "{{" + param.AbsoluteKey + trigger + "}}"
placeholders[param.Index-1] = "{{" + param.AbsoluteKey + params + "}}"
}
rowPlaceholders[rowParam.RowPlaceholder] = &placeholder{
Type: placeholderType,
Expand Down Expand Up @@ -109,16 +114,17 @@ func (t *Template) expandPlaceholders(xnode *xmlNode) {
if nnews[i] == nil {
nnews[i] = nrow.cloneAndAppend()
}

nnews[i].Walk(func(n *xmlNode) {
if !inSlice(n.XMLName.Local, []string{"w-t"}) || len(n.Content) == 0 {
return
}

replaceData := oldPlaceholder
if i < len(newPlaceholder.Placeholders) && newPlaceholder.Placeholders[i] != "" {
replaceData = newPlaceholder.Placeholders[i]
}
n.Content = bytes.ReplaceAll(n.Content, []byte(oldPlaceholder), []byte(replaceData))

})
}
}
Expand Down Expand Up @@ -169,6 +175,10 @@ func (t *Template) replaceAndRunTrigger(p *Param, n *xmlNode, triggerParamOnly b
// Repalce by type
switch p.Type {
case StringParam:
if p.Formatter = p.extractFormatter(n.Content); p.Formatter != nil {
result := p.Formatter.ApplyFormat(p.Formatter.Format, []byte(p.Value))
p.Value = string(result)
}
t.replaceTextParam(n, p)
case ImageParam:
t.replaceImageParams(n, p)
Expand Down
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ module github.com/bobiverse/docxplate

go 1.22.4

require github.com/logrusorgru/aurora/v4 v4.0.0
require (
github.com/logrusorgru/aurora/v4 v4.0.0
golang.org/x/text v0.16.0
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading

0 comments on commit 3fcd3bc

Please sign in to comment.