lang: core: Move template to golang namespace

I don't think this template function should be in any way authoritative,
so let's namespace it.
This commit is contained in:
James Shubin
2024-09-13 15:51:24 -04:00
parent 5bbc06d8bc
commit 29eebd0d07
33 changed files with 110 additions and 43 deletions

View File

@@ -349,7 +349,7 @@ apt install libvirt-dev libaugeas-dev
### Why do function names inside of templates include underscores?
The golang template library which we use to implement the template() function
The golang template library which we use to implement the golang.template() func
doesn't support the dot notation, so we import all our normal functions, and
just replace dots with underscores. As an example, the standard `datetime.print`
function is shown within mcl scripts as datetime_print after being imported.

View File

@@ -1,7 +1,8 @@
import "datetime"
import "golang"
$d = datetime.now()
consul:kv "love" {
key => "mgmt/time",
value => template("hello! it is now: {{ datetime_print . }}\n", $d),
value => golang.template("hello! it is now: {{ datetime_print . }}\n", $d),
}

View File

@@ -1,9 +1,10 @@
import "datetime"
import "golang"
$d = datetime.now()
file "/tmp/mgmt/datetime" {
state => $const.res.file.state.exists,
content => template("Hello! It is now: {{ datetime_print . }}\n", $d),
content => golang.template("Hello! It is now: {{ datetime_print . }}\n", $d),
}
file "/tmp/mgmt/" {

View File

@@ -1,4 +1,5 @@
import "datetime"
import "golang"
import "sys"
$secplusone = datetime.now() + $ayear
@@ -13,6 +14,6 @@ $theload float = sys.load()->x1 # ambiguous so we specify the type!
if 5 > 3 {
file "/tmp/mgmt/datetime" {
state => $const.res.file.state.exists,
content => template("Now + 1 year is: {{ .year }} seconds, aka: {{ datetime_print .year }}\n\nload average: {{ .load }}\n", $tmplvalues),
content => golang.template("Now + 1 year is: {{ .year }} seconds, aka: {{ datetime_print .year }}\n\nload average: {{ .load }}\n", $tmplvalues),
}
}

View File

@@ -1,4 +1,5 @@
import "datetime"
import "golang"
import "sys"
import "example"
@@ -15,5 +16,5 @@ $vumeter = example.vumeter("====", 10, 0.9)
file "/tmp/mgmt/datetime" {
state => $const.res.file.state.exists,
content => template("Now + 1 year is: {{ .year }} seconds, aka: {{ datetime_print .year }}\n\nload average: {{ .load }}\n\nvu: {{ .vumeter }}\n", $tmplvalues),
content => golang.template("Now + 1 year is: {{ .year }} seconds, aka: {{ datetime_print .year }}\n\nload average: {{ .load }}\n\nvu: {{ .vumeter }}\n", $tmplvalues),
}

View File

@@ -1,4 +1,5 @@
import "datetime"
import "golang"
import "sys"
import "example"
@@ -6,5 +7,5 @@ $now = datetime.now()
file "/tmp/mgmt-datetime" {
state => $const.res.file.state.exists,
content => template("Il est l'or Monseignor: {{ . }}\n", datetime.format($now, "15:04:05")),
content => golang.template("Il est l'or Monseignor: {{ . }}\n", datetime.format($now, "15:04:05")),
}

View File

@@ -6,6 +6,7 @@
# time ./mgmt run --hostname h4 --seeds=http://127.0.0.1:2379 --client-urls=http://127.0.0.1:2385 --server-urls=http://127.0.0.1:2386 --tmp-prefix --no-pgp empty
# time ./mgmt deploy --no-git --seeds=http://127.0.0.1:2379 lang examples/lang/exchange0.mcl
import "golang"
import "sys"
import "world"
@@ -15,5 +16,5 @@ $exchanged = world.exchange("keyns", $rand)
$host = sys.hostname()
file "/tmp/mgmt/exchange-${host}" {
state => $const.res.file.state.exists,
content => template("Found: {{ . }}\n", $exchanged),
content => golang.template("Found: {{ . }}\n", $exchanged),
}

View File

@@ -1,3 +1,4 @@
import "golang"
import "iter"
import "math"
@@ -9,6 +10,6 @@ $in1 = [8, -1, 0, 2, 4, 5, 13,]
$out1 = iter.filter($in1, $fn1)
$t1 = template("out1: {{ . }}", $out1)
$t1 = golang.template("out1: {{ . }}", $out1)
test [$t1,] {}

View File

@@ -1,3 +1,4 @@
import "golang"
import "iter"
import "math"
@@ -9,6 +10,6 @@ $in1 = ["xxxxxx", "a", "bb", "ccc", "dddd", "eeeee",]
$out1 = iter.filter($in1, $fn1)
$t1 = template("out1: {{ . }}", $out1)
$t1 = golang.template("out1: {{ . }}", $out1)
test [$t1,] {}

View File

@@ -1,4 +1,5 @@
import "datetime"
import "golang"
$dt = datetime.now()
@@ -6,5 +7,5 @@ $hystvalues = {"ix0" => $dt, "ix1" => history($dt, 1), "ix2" => history($dt, 2),
file "/tmp/mgmt/history" {
state => $const.res.file.state.exists,
content => template("Index(0) {{.ix0}}: {{ datetime_print .ix0 }}\nIndex(1) {{.ix1}}: {{ datetime_print .ix1 }}\nIndex(2) {{.ix2}}: {{ datetime_print .ix2 }}\nIndex(3) {{.ix3}}: {{ datetime_print .ix3 }}\n", $hystvalues),
content => golang.template("Index(0) {{.ix0}}: {{ datetime_print .ix0 }}\nIndex(1) {{.ix1}}: {{ datetime_print .ix1 }}\nIndex(2) {{.ix2}}: {{ datetime_print .ix2 }}\nIndex(3) {{.ix3}}: {{ datetime_print .ix3 }}\n", $hystvalues),
}

View File

@@ -1,8 +1,9 @@
import "golang"
import "sys"
file "/tmp/mgmt/systemload" {
state => $const.res.file.state.exists,
content => template("load average: {{ .load }} threshold: {{ .threshold }}\n", $tmplvalues),
content => golang.template("load average: {{ .load }} threshold: {{ .threshold }}\n", $tmplvalues),
}
$tmplvalues = struct{load => $theload, threshold => $threshold,}

View File

@@ -1,4 +1,5 @@
import "datetime"
import "golang"
import "iter"
import "math"
@@ -19,7 +20,7 @@ $in1 = if $mod {
$out1 = iter.map($in1, $fn)
$t1 = template("out1: {{ . }}", $out1)
$t1 = golang.template("out1: {{ . }}", $out1)
test "example1" {
anotherstr => $t1,

View File

@@ -1,3 +1,4 @@
import "golang"
import "iter"
$fn = func($x) { # notable because concrete type is fn(t1) t2, where t1 != t2
@@ -8,6 +9,6 @@ $in1 = ["a", "bb", "ccc", "dddd", "eeeee",]
$out1 = iter.map($in1, $fn)
$t1 = template("out1: {{ . }}", $out1)
$t1 = golang.template("out1: {{ . }}", $out1)
test [$t1,] {}

View File

@@ -1,4 +1,5 @@
import "datetime"
import "golang"
import "iter"
import "math"
@@ -23,6 +24,6 @@ $in1 = if $mod {
$out1 = iter.map($in1, $fn)
$t1 = template("out1: {{ . }}", $out1)
$t1 = golang.template("out1: {{ . }}", $out1)
test [$t1,] {}

View File

@@ -1,4 +1,5 @@
import "datetime"
import "golang"
import "iter"
import "math"
@@ -35,7 +36,7 @@ $in1 = if $modb {
$out1 = iter.map($in1, $fn)
$t1 = template("out1: {{ . }}", $out1)
$t1 = golang.template("out1: {{ . }}", $out1)
test [$t1,] {}

View File

@@ -1,11 +1,12 @@
import "datetime"
import "fmt"
import "golang"
$now = datetime.now()
$day = datetime.weekday($now)
$is_friday = $day == "friday"
$s1 = template("Hello! It is now: {{ datetime_print . }}\n", $now)
$s1 = golang.template("Hello! It is now: {{ datetime_print . }}\n", $now)
$s2 = if $is_friday {
"It's friday!!! (don't break anything, read-only)"
} else {

View File

@@ -1,3 +1,4 @@
import "golang"
import "sys"
import "world"
@@ -19,5 +20,5 @@ $set = world.schedule("xsched", $opts)
$host = sys.hostname()
file "/tmp/mgmt/scheduled-${host}" {
state => $const.res.file.state.exists,
content => template("set: {{ . }}\n", $set),
content => golang.template("set: {{ . }}\n", $set),
}

View File

@@ -1,4 +1,5 @@
import "fmt"
import "golang"
import "example"
$answer = 42
@@ -9,5 +10,5 @@ print "print1" {
}
print "print2" {
msg => template("an str is: {{ int2str . }}", $answer),
msg => golang.template("an str is: {{ int2str . }}", $answer),
}

View File

@@ -43,6 +43,7 @@ import (
_ "github.com/purpleidea/mgmt/lang/core/example"
_ "github.com/purpleidea/mgmt/lang/core/example/nested"
_ "github.com/purpleidea/mgmt/lang/core/fmt"
_ "github.com/purpleidea/mgmt/lang/core/golang"
_ "github.com/purpleidea/mgmt/lang/core/iter"
_ "github.com/purpleidea/mgmt/lang/core/list"
_ "github.com/purpleidea/mgmt/lang/core/local"

View File

@@ -33,6 +33,7 @@
import "convert"
import "deploy"
import "fmt"
import "golang"
import "golang/strings"
import "net"
import "os"
@@ -570,12 +571,12 @@ class base:host($name, $config) {
if $bios {
tftp:file "${bios_menu}" { # for bios
data => template(deploy.readfile("/files/bios-menu.tmpl"), $tftp_menu_template),
data => golang.template(deploy.readfile("/files/bios-menu.tmpl"), $tftp_menu_template),
}
} else {
tftp:file "${uefi_menu}" { # for uefi
# XXX: linuxefi & initrdefi VS. kernel & append ?
data => template(deploy.readfile("/files/uefi-menu.tmpl"), $tftp_menu_template),
data => golang.template(deploy.readfile("/files/uefi-menu.tmpl"), $tftp_menu_template),
#Depend => Pkg[$pkgs_uefi],
#Depend => Exec["uefi-extract"],
@@ -611,11 +612,11 @@ class base:host($name, $config) {
$kickstart_file = "${kickstart_http_prefix}${hkey}.ks"
file "${kickstart_file}" {
state => $const.res.file.state.exists,
content => template(deploy.readfile("/files/kickstart.ks.tmpl"), $http_kickstart_template),
content => golang.template(deploy.readfile("/files/kickstart.ks.tmpl"), $http_kickstart_template),
}
http:file "/fedora/kickstart/${hkey}.ks" { # usually $mac or `default`
#data => template(deploy.readfile("/files/kickstart.ks.tmpl"), $http_kickstart_template),
#data => golang.template(deploy.readfile("/files/kickstart.ks.tmpl"), $http_kickstart_template),
path => $kickstart_file,
Before => Print["ready"],

View File

@@ -0,0 +1,35 @@
// Mgmt
// Copyright (C) 2013-2024+ James Shubin and the project contributors
// Written by James Shubin <james@shubin.ca> and the project contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// Additional permission under GNU GPL version 3 section 7
//
// If you modify this program, or any covered work, by linking or combining it
// with embedded mcl code and modules (and that the embedded mcl code and
// modules which link with this program, contain a copy of their source code in
// the authoritative form) containing parts covered by the terms of any other
// license, the licensors of this program grant you additional permission to
// convey the resulting work. Furthermore, the licensors of this program grant
// the original author, James Shubin, additional permission to update this
// additional permission if he deems it necessary to achieve the goals of this
// additional permission.
package coregolang
const (
// ModuleName is the prefix given to all the functions in this module.
ModuleName = "golang"
)

View File

@@ -27,7 +27,7 @@
// additional permission if he deems it necessary to achieve the goals of this
// additional permission.
package core // TODO: should this be in its own individual package?
package coregolang
import (
"bytes"
@@ -64,7 +64,7 @@ var (
)
func init() {
funcs.Register(TemplateFuncName, func() interfaces.Func { return &TemplateFunc{} })
funcs.ModuleRegister(ModuleName, TemplateFuncName, func() interfaces.Func { return &TemplateFunc{} })
}
var _ interfaces.InferableFunc = &TemplateFunc{} // ensure it meets this expectation

View File

@@ -76,7 +76,7 @@ type Init struct {
// nodes, and when it is used, it should be used carefully.
Txn Txn
// TODO: should we pass in a *Scope here for functions like template() ?
// TODO: should we pass in a *Scope here for functions like golang.template() ?
Local *local.API
World engine.World

View File

@@ -1,4 +1,5 @@
-- main.mcl --
import "golang"
import "iter"
$fn = func($x) { # notable because concrete type is fn(t1) t2, where t1 != t2
@@ -9,7 +10,7 @@ $ins = ["a", "bb", "ccc", "dddd", "eeeee",]
$out = iter.map($ins, $fn)
$t = template("out: {{ . }}", $out)
$t = golang.template("out: {{ . }}", $out)
test "${t}" {}
-- OUTPUT --

View File

@@ -1,4 +1,5 @@
-- main.mcl --
import "golang"
import "iter"
func itermap($a, $b) {
@@ -29,14 +30,14 @@ $out6 = iter.map($in4, func($x) { $x + $x })
$out7 = itermap($in3, func($x) { $x + $x })
$out8 = itermap($in4, func($x) { $x + $x })
$t1 = template("out1: {{ . }}", $out1)
$t2 = template("out2: {{ . }}", $out2)
$t3 = template("out3: {{ . }}", $out3)
$t4 = template("out4: {{ . }}", $out4)
$t5 = template("out5: {{ . }}", $out5)
$t6 = template("out6: {{ . }}", $out6)
$t7 = template("out7: {{ . }}", $out7)
$t8 = template("out8: {{ . }}", $out8)
$t1 = golang.template("out1: {{ . }}", $out1)
$t2 = golang.template("out2: {{ . }}", $out2)
$t3 = golang.template("out3: {{ . }}", $out3)
$t4 = golang.template("out4: {{ . }}", $out4)
$t5 = golang.template("out5: {{ . }}", $out5)
$t6 = golang.template("out6: {{ . }}", $out6)
$t7 = golang.template("out7: {{ . }}", $out7)
$t8 = golang.template("out8: {{ . }}", $out8)
test "${t1}" {}
test "${t2}" {}

View File

@@ -1,4 +1,5 @@
-- main.mcl --
import "golang"
import "iter"
$fn = func($x) { # ignore arg
@@ -9,7 +10,7 @@ $ins = [5, 4, 3, 2, 1,]
$out = iter.map($ins, $fn)
$t = template("out: {{ . }}", $out)
$t = golang.template("out: {{ . }}", $out)
test "${t}" {}
-- OUTPUT --

View File

@@ -1,4 +1,5 @@
-- main.mcl --
import "golang"
import "iter"
$fn = func($x) { # type changes from str to int
@@ -9,7 +10,7 @@ $ins = ["a", "bb", "ccc", "dddd", "eeeee",]
$out = iter.map($ins, $fn)
$t = template("out: {{ . }}", $out)
$t = golang.template("out: {{ . }}", $out)
test "${t}" {}
-- OUTPUT --

View File

@@ -1,4 +1,5 @@
-- main.mcl --
import "golang"
import "iter"
$ins = ["a", "bb", "ccc", "dddd", "eeeee",]
@@ -9,7 +10,7 @@ $out = iter.map($ins, func($x) {
})
$t = template("out: {{ . }}", $out)
$t = golang.template("out: {{ . }}", $out)
test "${t}" {}
-- OUTPUT --

View File

@@ -1,5 +1,6 @@
-- main.mcl --
import "datetime"
import "golang"
$secplus42 = 42 + $ayear
@@ -9,7 +10,7 @@ $ayear = 60 * 60 * 24 * 365 # is a year in seconds (31536000)
$tmplvalues = struct{time => $secplus42, hello => "world",}
print "template-0" {
msg => template("Hello: {{ .hello }}, 42 sec + 1 year is: {{ .time }} seconds, aka: {{ datetime_print .time }}", $tmplvalues),
msg => golang.template("Hello: {{ .hello }}, 42 sec + 1 year is: {{ .time }} seconds, aka: {{ datetime_print .time }}", $tmplvalues),
}
-- OUTPUT --
Vertex: print[template-0]

View File

@@ -1,6 +1,8 @@
-- main.mcl --
import "golang"
$v = 42
$x = template("hello", $v) # redirect var for harder unification
$x = golang.template("hello", $v) # redirect var for harder unification
test "${x}" {
#anotherstr => $x,
}

View File

@@ -1,4 +1,5 @@
-- main.mcl --
import "golang"
import "sys"
$tmplvalues = struct{num => 42, load => $theload,}
@@ -7,7 +8,7 @@ $theload bool = sys.load()->x1 # wrong type, make sure the compiler catches it!
file "/tmp/datetime" {
state => $const.res.file.state.exists,
content => template("num: {{ .num }} seconds\nload average: {{ .load }}\n", $tmplvalues),
content => golang.template("num: {{ .num }} seconds\nload average: {{ .load }}\n", $tmplvalues),
}
-- OUTPUT --
# err: errUnify: error setting type: func() { <built-in:_struct_lookup> }, error: field x1 type error: base kind does not match (bool != float)

View File

@@ -422,12 +422,12 @@ func TestUnification1(t *testing.T) {
})
}
{
//$x = template("hello", 42)
//$x = golang.template("hello", 42)
//test "t1" {
// anotherstr => $x,
//}
innerFunc := &ast.ExprCall{
Name: "template",
Name: "golang.template",
Args: []interfaces.Expr{
&ast.ExprStr{
V: "hello",
@@ -439,6 +439,9 @@ func TestUnification1(t *testing.T) {
}
stmt := &ast.StmtProg{
Body: []interfaces.Stmt{
&ast.StmtImport{
Name: "golang",
},
&ast.StmtBind{
Ident: "x",
Value: innerFunc,

View File

@@ -6,6 +6,7 @@
# time ./mgmt run --hostname h4 --seeds=http://127.0.0.1:2379 --client-urls=http://127.0.0.1:2385 --server-urls=http://127.0.0.1:2386 --tmp-prefix --no-pgp empty
# time ./mgmt deploy --no-git --seeds=http://127.0.0.1:2379 lang examples/lang/exchange0.mcl
import "golang"
import "sys"
import "world"
@@ -15,5 +16,5 @@ $host = sys.hostname()
file "/tmp/mgmt/exchange-${host}" {
state => $const.res.file.state.exists,
content => template("Found: {{ . }}\n", $exchanged),
content => golang.template("Found: {{ . }}\n", $exchanged),
}