template
Simple and fast string templating library.
- Compiles to an array of generator functions for
faster string generation
. -
Abstracted input data
to support not only string maps.
Synopsis
Generating a string using a map
string
string
TemplateData
import prantlf.template { parse_template }
source := '## [{version}]({repo_url}/compare/v{prev_version}...v{version}) ({date})'
template := parse_template(source)!
vars := {
'date': '2023-04-27'
'prev_version': '14.0.2'
'version': '14.0.3'
'repo_url': 'https://github.com/prantlf/jsonlint'
}
output := template.generate(vars)
Output:
## [14.0.3](https://github.com/prantlf/jsonlint/compare/v14.0.2...v14.0.3) (2023-04-27)
Generating a string using a map
string
[]string
TemplateData
import prantlf.template { parse_template }
source := '* {description} ([{short_hash}]({repo_url}/commit/{hash})){if issues}
fixes [{for issues}{notfirst}), [{end}#{value}]({repo_url}/issues/{value}{end}){end}'
template := parse_template(source)!
vars := {
'description': ['Ensure error location by custom parsing']
'short_hash': ['9757213']
'hash': ['9757213eda5de9684099024d0c4f59e4d4f59c97']
'repo_url': ['https://github.com/prantlf/jsonlint']
'issues': ['87', '101']
}
output := template.generate(vars)
Output (obvious parts shortened by
...
* Ensure error location by custom parsing ([9757213](https://github.com/prantlf/jsonlint/commit/9...7))
fixes [#87](https://github.com/prantlf/jsonlint/issues/87), [#101](https://github.com/p...s/101)
Installation
You can install this package either from
VPM
v install prantlf.template
v install --git https://github.com/prantlf/v-template
Syntax
The
{
{
\
\{
If not escaped, the leading
{
}
{
}
Literal
A sequence of ordinary characters will be just copied to the output.
template: "# Changes"
vars: {}
output: "# Changes"
Variable
A value directive
{<variable>}
{
}
template: "# {title}"
vars: { title: 'Changes' }
output: "# Changes"
Variable name must not start with
#
Items
A value directive
{#items <variable>}
,
template: "Issues: {#items issues}"
vars: { issues: ['#87', '#101'] }
output: "Issues: #87, #101"
Instead of a variable name, directives
#value
#index
Lines
A value directive
{#lines <variable>}
\n
template: "{#lines issues}"
vars: { issues: ['#87', '#101'] }
output: "#87\n#101"
Instead of a variable name, directives
#value
#index
If
An block directive
{#if <variable>}...{#end}
{#if <variable>}
{#end}
template: "{#if issue}Issue: {issue}{#end}"
vars: { issue: '#87' }
output: "Issue: #87"
Instead of a variable name, directives
#value
#index
Unless
An block directive
{#unless <variable>}...{#end}
{#unless <variable>}
{#end}
template: "{#unless issue}no issue attached{#end}"
vars: { issue: '#87' }
output: ""
Instead of a variable name, directives
#value
#index
For
An block directive
{#for <variable>}...{#end}
{#for <variable>}
{#end}
template: "{#for issues}.{#end}"
vars: { issues: ['#87', '#101'] }
output: ".."
Instead of a variable name, directives
#value
#index
Index
A value directive
{#index}
for
template: "Counter:{#for issues} {#index}{#end}"
vars: { issues: ['#87', '#101'] }
output: "Counter: 1 2"
If
for
../
{../#index}
../
Value
A value directive
{#value}
for
template: "Issues:{#for issues} {#value}{#end}"
vars: { issues: ['#87', '#101'] }
output: "Issues: #87 #101"
If
for
../
{../#value}
../
First
An block directive
{#first}...{#end}
for
{#first}
{#end}
template: "Issues:{#for issues} {#first}*{#end}{#value}{#first}*{#end}{#end}"
vars: { issues: ['#87', '#101'] }
output: "Issues: *#87* #101"
If
for
../
{../#index}
../
NotFirst
An block directive
{#notfirst}...{#end}
for
{#notfirst}
{#end}
template: "Issues: {#for issues}{#notfirst}, {#end}{#value}{#end}"
vars: { issues: ['#87', '#101'] }
output: "Issues: #87, #101"
If
for
../
{../#index}
../
Middle
An block directive
{#middle}...{#end}
for
{#middle}
{#end}
template: "Issues: {#for issues}{#middle}, {#end}{#notfirst}{#last} and {#end}{#end}{#index}{#value}{#end}"
vars: { issues: ['#87', '#95', '#101'] }
output: "Issues: #87, #95 and #101"
If
for
../
{../#index}
../
NotLast
An block directive
{#notlast}...{#end}
for
{#notlast}
{#end}
template: "Issues: {#for issues}{#value}{#notlast}, {#end}{#end}"
vars: { issues: ['#87', '#101'] }
output: "Issues: #87, #101"
If
for
../
{../#index}
../
Last
An block directive
{#last}...{#end}
for
{#last}
{#end}
template: "Issues:{#for issues} {#last}*{#end}{#value}{#last}*{#end}{#end}"
vars: { issues: ['#87', '#101'] }
output: "Issues: #87 *#101*"
If
for
../
{../#index}
../
End
An trailing directive -
{#end}
if
unless
for
first
notfirst
middle
notlast
last
API
The following functions and types are exported:
parse_template(source string) !Template
Parses a template string and returns a
Template
import prantlf.template { parse_template }
template := parse_template('# {title}')!
Template.generate(vars TemplateData) string
Generates a string from the template using the
vars
TemplateData
import prantlf.template { parse_template }
template := parse_template('# {title}')!
output := template.generate({
'title': 'Overview'
})
// output: # Overview
parse_replacer(source string) !Replacer
Parses a template with a reduced syntax - only variables are supported. Returns a
Replacer
import prantlf.template { parse_replacer }
template := parse_replacer('# {title}')!
parse_replacer_opt(source string, opts &ReplacerOpts) !Replacer
Parses a template with a reduced syntax - only variables are supported. Returns a
Replacer
ReplacerOpts
Field | Type | Default | Description |
---|---|---|---|
vars |
[]string |
[] |
list of variable names |
exclude |
bool |
false |
if the listed variables should be excluded |
If the
vars
import prantlf.template { parse_replacer }
template := parse_replacer('# {title} ({date})', ReplacerOpts{
vars: ['title']
})!
output := template.replace({
'title': 'Overview'
})
// output: # Overview ({date})
If the
exclude
import prantlf.template { ReplacerOpts, parse_replacer_opt }
template := parse_replacer_opt('# {title} ({date})', ReplacerOpts{
vars: ['date']
exclude: true
})!
output := template.replace({
'title': 'Overview'
})
// output: # Overview ({date})
Replacer.replace(vars TemplateData) string
Replaces variable placeholders in a template using the
vars
TemplateData
import prantlf.template { parse_template }
template := parse_replacer('# {title}')!
output := template.replace({
'title': 'Overview'
})
// output: # Overview
See the implementation of
TemplateData
string
string
Abstractions
Instead of accepting a string map only, any data can be accepted, as long is it implements the
TemplateData
interface TemplateData {
has(name string) bool
get_one(name string) string
get_more(name string) []string
}
For
example
string
string
fn (m map[string]string) has(name string) bool {
return name in m
}
fn (m map[string]string) get_one(name string) string {
return m[name]
}
fn (m map[string]string) get_more(name string) []string {
return if name in m {
[m[name]]
} else {
[]
}
}
For
example
string
[]string
fn (m map[string][]string) has(name string) bool {
return name in m
}
fn (m map[string][]string) get_one(name string) string {
val := m[name]
return if val.len > 0 {
val[0]
} else {
''
}
}
fn (m map[string][]string) get_more(name string) []string {
return m[name]
}
For
example
MapData
string
string
string
[]string
struct MapData {
singles map[string]string
arrays map[string][]string
}
fn (d &MapData) has(name string) bool {
return name in d.singles || name in d.arrays
}
fn (d &MapData) get_one(name string) string {
return if name in d.singles {
d.singles[name]
} else {
val := d.arrays[name]
if val.len > 0 {
val[0]
} else {
''
}
}
}
fn (d &MapData) get_more(name string) []string {
return if name in d.arrays {
d.arrays[name]
} else if name in d.singles {
[d.singles[name]]
} else {
[]string{}
}
}
For
example
Data
string
[]string
struct Data {
description string
issues []string
}
fn (d &Data) has(name string) bool {
return has_field(d, name)
}
fn (d &Data) get_one(name string) string {
return get_one_field(d, name)
}
fn (d &Data) get_more(name string) []string {
return get_more_field(d, name)
}
fn has_field[T](data &T, name string) bool {
$for field in T.fields {
if field.name == name {
return true
}
}
return false
}
fn get_one_field[T](data &T, name string) string {
$for field in T.fields {
if field.name == name {
$if field.is_array {
val := data.$(field.name)
return if val.len > 0 {
val[0]
} else {
''
}
} $else $if field.typ is string {
return data.$(field.name)
}
}
}
return ''
}
fn get_more_field[T](data &T, name string) []string {
$for field in T.fields {
if field.name == name {
$if field.is_array {
return data.$(field.name)
} $else $if field.typ is string {
return [data.$(field.name)]
}
}
}
return []
}
Contributing
In lieu of a formal styleguide, take care to maintain the existing coding style. Lint and test your code.
License
Copyright (c) 2023-2024 Ferdinand Prantl
Licensed under the MIT license.