File: tags_include.go

package info (click to toggle)
golang-github-flosch-pongo2.v4 4.0.2-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bookworm-backports, trixie
  • size: 860 kB
  • sloc: makefile: 3
file content (146 lines) | stat: -rw-r--r-- 4,227 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package pongo2

type tagIncludeNode struct {
	tpl               *Template
	filenameEvaluator IEvaluator
	lazy              bool
	only              bool
	filename          string
	withPairs         map[string]IEvaluator
	ifExists          bool
}

func (node *tagIncludeNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
	// Building the context for the template
	includeCtx := make(Context)

	// Fill the context with all data from the parent
	if !node.only {
		includeCtx.Update(ctx.Public)
		includeCtx.Update(ctx.Private)
	}

	// Put all custom with-pairs into the context
	for key, value := range node.withPairs {
		val, err := value.Evaluate(ctx)
		if err != nil {
			return err
		}
		includeCtx[key] = val
	}

	// Execute the template
	if node.lazy {
		// Evaluate the filename
		filename, err := node.filenameEvaluator.Evaluate(ctx)
		if err != nil {
			return err
		}

		if filename.String() == "" {
			return ctx.Error("Filename for 'include'-tag evaluated to an empty string.", nil)
		}

		// Get include-filename
		includedFilename := ctx.template.set.resolveFilename(ctx.template, filename.String())

		includedTpl, err2 := ctx.template.set.FromFile(includedFilename)
		if err2 != nil {
			// if this is ReadFile error, and "if_exists" flag is enabled
			if node.ifExists && err2.(*Error).Sender == "fromfile" {
				return nil
			}
			return err2.(*Error)
		}
		err2 = includedTpl.ExecuteWriter(includeCtx, writer)
		if err2 != nil {
			return err2.(*Error)
		}
		return nil
	}
	// Template is already parsed with static filename
	err := node.tpl.ExecuteWriter(includeCtx, writer)
	if err != nil {
		return err.(*Error)
	}
	return nil
}

type tagIncludeEmptyNode struct{}

func (node *tagIncludeEmptyNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
	return nil
}

func tagIncludeParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
	includeNode := &tagIncludeNode{
		withPairs: make(map[string]IEvaluator),
	}

	if filenameToken := arguments.MatchType(TokenString); filenameToken != nil {
		// prepared, static template

		// "if_exists" flag
		ifExists := arguments.Match(TokenIdentifier, "if_exists") != nil

		// Get include-filename
		includedFilename := doc.template.set.resolveFilename(doc.template, filenameToken.Val)

		// Parse the parent
		includeNode.filename = includedFilename
		includedTpl, err := doc.template.set.FromFile(includedFilename)
		if err != nil {
			// if this is ReadFile error, and "if_exists" token presents we should create and empty node
			if err.(*Error).Sender == "fromfile" && ifExists {
				return &tagIncludeEmptyNode{}, nil
			}
			return nil, err.(*Error).updateFromTokenIfNeeded(doc.template, filenameToken)
		}
		includeNode.tpl = includedTpl
	} else {
		// No String, then the user wants to use lazy-evaluation (slower, but possible)
		filenameEvaluator, err := arguments.ParseExpression()
		if err != nil {
			return nil, err.updateFromTokenIfNeeded(doc.template, filenameToken)
		}
		includeNode.filenameEvaluator = filenameEvaluator
		includeNode.lazy = true
		includeNode.ifExists = arguments.Match(TokenIdentifier, "if_exists") != nil // "if_exists" flag
	}

	// After having parsed the filename we're gonna parse the with+only options
	if arguments.Match(TokenIdentifier, "with") != nil {
		for arguments.Remaining() > 0 {
			// We have at least one key=expr pair (because of starting "with")
			keyToken := arguments.MatchType(TokenIdentifier)
			if keyToken == nil {
				return nil, arguments.Error("Expected an identifier", nil)
			}
			if arguments.Match(TokenSymbol, "=") == nil {
				return nil, arguments.Error("Expected '='.", nil)
			}
			valueExpr, err := arguments.ParseExpression()
			if err != nil {
				return nil, err.updateFromTokenIfNeeded(doc.template, keyToken)
			}

			includeNode.withPairs[keyToken.Val] = valueExpr

			// Only?
			if arguments.Match(TokenIdentifier, "only") != nil {
				includeNode.only = true
				break // stop parsing arguments because it's the last option
			}
		}
	}

	if arguments.Remaining() > 0 {
		return nil, arguments.Error("Malformed 'include'-tag arguments.", nil)
	}

	return includeNode, nil
}

func init() {
	RegisterTag("include", tagIncludeParser)
}