From fc3baa28d6274cf8495881ad9f18cb2322c8777c Mon Sep 17 00:00:00 2001 From: James Shubin Date: Tue, 16 Apr 2019 21:42:32 -0400 Subject: [PATCH] lang: funcs: Add regexp package and match function This adds a simple regexp match function. This will be useful for regexp based name classification, if you're into that sort of thing. --- examples/lang/regexp0.mcl | 10 +++ lang/funcs/core/core.go | 1 + lang/funcs/core/regexp/match_func.go | 51 +++++++++++++++ lang/funcs/core/regexp/match_func_test.go | 75 +++++++++++++++++++++++ lang/funcs/core/regexp/regexp.go | 23 +++++++ 5 files changed, 160 insertions(+) create mode 100644 examples/lang/regexp0.mcl create mode 100644 lang/funcs/core/regexp/match_func.go create mode 100644 lang/funcs/core/regexp/match_func_test.go create mode 100644 lang/funcs/core/regexp/regexp.go diff --git a/examples/lang/regexp0.mcl b/examples/lang/regexp0.mcl new file mode 100644 index 00000000..4bbaa6c0 --- /dev/null +++ b/examples/lang/regexp0.mcl @@ -0,0 +1,10 @@ +import "fmt" +import "regexp" + +# test with: +# ./mgmt run --hostname foo.example.com --tmp-prefix lang --lang examples/lang/regexp0.mcl +# ./mgmt run --hostname db1.example.com --tmp-prefix lang --lang examples/lang/regexp0.mcl +print "regexp" { + # TODO: add a heredoc string to avoid needing to escape the \ chars + msg => fmt.printf("match: %t", regexp.match("^db\\d+\\.example\\.com$", $hostname)), +} diff --git a/lang/funcs/core/core.go b/lang/funcs/core/core.go index 3b272012..d962f616 100644 --- a/lang/funcs/core/core.go +++ b/lang/funcs/core/core.go @@ -24,6 +24,7 @@ import ( _ "github.com/purpleidea/mgmt/lang/funcs/core/fmt" _ "github.com/purpleidea/mgmt/lang/funcs/core/math" _ "github.com/purpleidea/mgmt/lang/funcs/core/os" + _ "github.com/purpleidea/mgmt/lang/funcs/core/regexp" _ "github.com/purpleidea/mgmt/lang/funcs/core/strings" _ "github.com/purpleidea/mgmt/lang/funcs/core/sys" _ "github.com/purpleidea/mgmt/lang/funcs/core/world" diff --git a/lang/funcs/core/regexp/match_func.go b/lang/funcs/core/regexp/match_func.go new file mode 100644 index 00000000..5f7a387f --- /dev/null +++ b/lang/funcs/core/regexp/match_func.go @@ -0,0 +1,51 @@ +// Mgmt +// Copyright (C) 2013-2019+ James Shubin and the project contributors +// Written by James Shubin 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 . + +package coreregexp + +import ( + "regexp" + + "github.com/purpleidea/mgmt/lang/funcs/simple" + "github.com/purpleidea/mgmt/lang/types" + "github.com/purpleidea/mgmt/util/errwrap" +) + +func init() { + simple.ModuleRegister(moduleName, "match", &types.FuncValue{ + T: types.NewType("func(pattern str, s str) bool"), + V: Match, + }) +} + +// Match matches whether a string matches the regexp pattern. +func Match(input []types.Value) (types.Value, error) { + pattern := input[0].Str() + s := input[1].Str() + + // TODO: We could make this more efficient with the regular function API + // by only compiling the pattern when it changes. + re, err := regexp.Compile(pattern) + if err != nil { + return nil, errwrap.Wrapf(err, "pattern did not compile") + } + + result := re.MatchString(s) + return &types.BoolValue{ + V: result, + }, nil +} diff --git a/lang/funcs/core/regexp/match_func_test.go b/lang/funcs/core/regexp/match_func_test.go new file mode 100644 index 00000000..447b30a1 --- /dev/null +++ b/lang/funcs/core/regexp/match_func_test.go @@ -0,0 +1,75 @@ +// Mgmt +// Copyright (C) 2013-2019+ James Shubin and the project contributors +// Written by James Shubin 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 . + +package coreregexp + +import ( + "testing" + + "github.com/purpleidea/mgmt/lang/types" +) + +func TestMatch0(t *testing.T) { + values := []struct { + pattern string + s string + expected bool + }{ + { + "(mgmt){2}", + "mgmt", + false, + }, + { + "(mgmt){2}", + "mgmtmgmt", + true, + }, + { + "(mgmt){2}", + "mgmtmgmtmgmt", + true, + }, + { + `^db\d+\.example\.com$`, + "db1.example.com", + true, + }, + { + `^db\d+\.example\.com$`, + "dbX.example.com", + false, + }, + { + `^db\d+\.example\.com$`, + "db1.exampleXcom", + false, + }, + } + + for i, x := range values { + pattern := &types.StrValue{V: x.pattern} + s := &types.StrValue{V: x.s} + val, err := Match([]types.Value{pattern, s}) + if err != nil { + t.Errorf("test index %d failed with: %+v", i, err) + } + if a, b := x.expected, val.Bool(); a != b { + t.Errorf("test index %d expected %t, got %t", i, a, b) + } + } +} diff --git a/lang/funcs/core/regexp/regexp.go b/lang/funcs/core/regexp/regexp.go new file mode 100644 index 00000000..c0b842d8 --- /dev/null +++ b/lang/funcs/core/regexp/regexp.go @@ -0,0 +1,23 @@ +// Mgmt +// Copyright (C) 2013-2019+ James Shubin and the project contributors +// Written by James Shubin 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 . + +package coreregexp + +const ( + // moduleName is the prefix given to all the functions in this module. + moduleName = "regexp" +)