1
0
mirror of https://github.com/MontFerret/ferret.git synced 2025-03-05 15:16:07 +02:00

Add Path functions (#505)

* Add Path functions

* Add unit tests
This commit is contained in:
Aleksandr Yakimenko 2020-05-13 05:19:41 +03:00 committed by GitHub
parent 311d07ad40
commit ed8e43e4c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 676 additions and 0 deletions

View File

@ -9,6 +9,7 @@ import (
"github.com/MontFerret/ferret/pkg/stdlib/io"
"github.com/MontFerret/ferret/pkg/stdlib/math"
"github.com/MontFerret/ferret/pkg/stdlib/objects"
"github.com/MontFerret/ferret/pkg/stdlib/path"
"github.com/MontFerret/ferret/pkg/stdlib/strings"
"github.com/MontFerret/ferret/pkg/stdlib/types"
"github.com/MontFerret/ferret/pkg/stdlib/utils"
@ -51,5 +52,9 @@ func RegisterLib(ns core.Namespace) error {
return err
}
if err := path.RegisterLib(ns); err != nil {
return err
}
return utils.RegisterLib(ns)
}

32
pkg/stdlib/path/base.go Normal file
View File

@ -0,0 +1,32 @@
package path
import (
"context"
"path"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
// Base returns the last component of the path.
// or the path itself if it does not contain any directory separators.
// @params path (String) - The path.
// @returns (String) - The last component of the path.
func Base(_ context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 1, 1)
if err != nil {
return values.EmptyString, err
}
err = core.ValidateType(args[0], types.String)
if err != nil {
return values.None, err
}
pathText := args[0].String()
return values.NewString(path.Base(pathText)), nil
}

View File

@ -0,0 +1,55 @@
package path_test
import (
"context"
"testing"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/stdlib/path"
. "github.com/smartystreets/goconvey/convey"
)
func TestBase(t *testing.T) {
Convey("When arg is not passed", t, func() {
Convey("It should return an error", func() {
var err error
_, err = path.Base(context.Background())
So(err, ShouldBeError)
})
})
Convey("Wrong argument", t, func() {
var err error
_, err = path.Base(context.Background(), values.NewInt(0))
So(err, ShouldBeError)
})
Convey("Base('') should return '.'", t, func() {
out, _ := path.Base(
context.Background(),
values.NewString(""),
)
So(out, ShouldEqual, ".")
})
Convey("Base('.') should return '.'", t, func() {
out, _ := path.Base(
context.Background(),
values.NewString("."),
)
So(out, ShouldEqual, ".")
})
Convey("Base('pkg/path/base.go') should return 'base.go'", t, func() {
out, _ := path.Base(
context.Background(),
values.NewString("pkg/path/base.go"),
)
So(out, ShouldEqual, "base.go")
})
}

31
pkg/stdlib/path/clean.go Normal file
View File

@ -0,0 +1,31 @@
package path
import (
"context"
"path"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
// Clean returns the shortest path name equivalent to path.
// @params path (String) - The path.
// @returns (String) - The shortest path name equivalent to path
func Clean(_ context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 1, 1)
if err != nil {
return values.EmptyString, err
}
err = core.ValidateType(args[0], types.String)
if err != nil {
return values.None, err
}
pathText := args[0].String()
return values.NewString(path.Clean(pathText)), nil
}

View File

@ -0,0 +1,46 @@
package path_test
import (
"context"
"testing"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/stdlib/path"
. "github.com/smartystreets/goconvey/convey"
)
func TestClean(t *testing.T) {
Convey("When arg is not passed", t, func() {
Convey("It should return an error", func() {
var err error
_, err = path.Clean(context.Background())
So(err, ShouldBeError)
})
})
Convey("Wrong argument", t, func() {
var err error
_, err = path.Clean(context.Background(), values.NewInt(0))
So(err, ShouldBeError)
})
Convey("Clean('pkg//path//clean.go') should return 'pkg/path/clean.go'", t, func() {
out, _ := path.Clean(
context.Background(),
values.NewString("pkg//path//clean.go"),
)
So(out, ShouldEqual, "pkg/path/clean.go")
})
Convey("Clean('/cmd/main/../../..') should return '/'", t, func() {
out, _ := path.Clean(
context.Background(),
values.NewString("/cmd/main/../../.."),
)
So(out, ShouldEqual, "/")
})
}

31
pkg/stdlib/path/dir.go Normal file
View File

@ -0,0 +1,31 @@
package path
import (
"context"
"path"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
// Dir returns the directory component of path.
// @params path (String) - The path.
// @returns (String) - The directory component of path.
func Dir(_ context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 1, 1)
if err != nil {
return values.EmptyString, err
}
err = core.ValidateType(args[0], types.String)
if err != nil {
return values.None, err
}
pathText := args[0].String()
return values.NewString(path.Dir(pathText)), nil
}

View File

@ -0,0 +1,37 @@
package path_test
import (
"context"
"testing"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/stdlib/path"
. "github.com/smartystreets/goconvey/convey"
)
func TestDir(t *testing.T) {
Convey("When arg is not passed", t, func() {
Convey("It should return an error", func() {
var err error
_, err = path.Dir(context.Background())
So(err, ShouldBeError)
})
})
Convey("Wrong argument", t, func() {
var err error
_, err = path.Dir(context.Background(), values.NewInt(0))
So(err, ShouldBeError)
})
Convey("Dir('pkg/path/dir.go') should return 'pkg/path'", t, func() {
out, _ := path.Dir(
context.Background(),
values.NewString("pkg/path/dir.go"),
)
So(out, ShouldEqual, "pkg/path")
})
}

31
pkg/stdlib/path/ext.go Normal file
View File

@ -0,0 +1,31 @@
package path
import (
"context"
"path"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
// Ext returns the extension of the last component of path.
// @params path (String) - The path.
// @returns (String) - The extension of the last component of path.
func Ext(_ context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 1, 1)
if err != nil {
return values.EmptyString, err
}
err = core.ValidateType(args[0], types.String)
if err != nil {
return values.None, err
}
pathText := args[0].String()
return values.NewString(path.Ext(pathText)), nil
}

View File

@ -0,0 +1,37 @@
package path_test
import (
"context"
"testing"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/stdlib/path"
. "github.com/smartystreets/goconvey/convey"
)
func TestExt(t *testing.T) {
Convey("When arg is not passed", t, func() {
Convey("It should return an error", func() {
var err error
_, err = path.Ext(context.Background())
So(err, ShouldBeError)
})
})
Convey("Wrong argument", t, func() {
var err error
_, err = path.Ext(context.Background(), values.NewInt(0))
So(err, ShouldBeError)
})
Convey("Ext('dir/main.go') should return '.go'", t, func() {
out, _ := path.Ext(
context.Background(),
values.NewString("dir/main.go"),
)
So(out, ShouldEqual, ".go")
})
}

31
pkg/stdlib/path/is_abs.go Normal file
View File

@ -0,0 +1,31 @@
package path
import (
"context"
"path"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
// IsAbs reports whether the path is absolute.
// @params path (String) - The path.
// @returns (Boolean) - True if the path is absolute.
func IsAbs(_ context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 1, 1)
if err != nil {
return values.False, err
}
err = core.ValidateType(args[0], types.String)
if err != nil {
return values.None, err
}
pathText := args[0].String()
return values.NewBoolean(path.IsAbs(pathText)), nil
}

View File

@ -0,0 +1,46 @@
package path_test
import (
"context"
"testing"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/stdlib/path"
. "github.com/smartystreets/goconvey/convey"
)
func TestIsAbs(t *testing.T) {
Convey("When arg is not passed", t, func() {
Convey("It should return an error", func() {
var err error
_, err = path.IsAbs(context.Background())
So(err, ShouldBeError)
})
})
Convey("Wrong argument", t, func() {
var err error
_, err = path.IsAbs(context.Background(), values.NewInt(0))
So(err, ShouldBeError)
})
Convey("IsAbs('/ferret/bin/ferret') should return true", t, func() {
out, _ := path.IsAbs(
context.Background(),
values.NewString("/ferret/bin/ferret"),
)
So(out, ShouldEqual, values.True)
})
Convey("IsAbs('..') should return false", t, func() {
out, _ := path.IsAbs(
context.Background(),
values.NewString(".."),
)
So(out, ShouldEqual, values.False)
})
}

44
pkg/stdlib/path/join.go Normal file
View File

@ -0,0 +1,44 @@
package path
import (
"context"
"path"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
// Join joins any number of path elements into a single path, separating them with slashes.
// @param elem (String...|Array<String>) - The path elements
// @returns (String) - Single path from the given elements.
func Join(_ context.Context, args ...core.Value) (core.Value, error) {
argsCount := len(args)
if argsCount == 0 {
return values.EmptyString, nil
}
arr := &values.Array{}
if argsCount != 1 && args[0].Type() != types.Array {
arr = values.NewArrayWith(args...)
} else {
arr = args[0].(*values.Array)
}
elems := make([]string, arr.Length())
for idx := values.NewInt(0); idx < arr.Length(); idx++ {
arrElem := arr.Get(idx)
err := core.ValidateType(arrElem, types.String)
if err != nil {
return values.None, err
}
elems[idx] = arrElem.String()
}
return values.NewString(path.Join(elems...)), nil
}

View File

@ -0,0 +1,56 @@
package path_test
import (
"context"
"testing"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/stdlib/path"
. "github.com/smartystreets/goconvey/convey"
)
func TestJoin(t *testing.T) {
Convey("When arg is not passed", t, func() {
Convey("It should return an empty string without error", func() {
out, err := path.Join(context.Background())
So(out, ShouldEqual, "")
So(err, ShouldBeNil)
})
})
Convey("Wrong argument", t, func() {
var err error
_, err = path.Join(context.Background(), values.NewString("/"), values.NewInt(0))
So(err, ShouldBeError)
})
Convey("Wrong argument within an array", t, func() {
var err error
_, err = path.Join(
context.Background(),
values.NewArrayWith(values.NewString("/"), values.NewInt(0)),
)
So(err, ShouldBeError)
})
Convey("Join(['pkg', 'path']) should return 'pkg/path'", t, func() {
out, _ := path.Join(
context.Background(),
values.NewArrayWith(values.NewString("pkg"), values.NewString("path")),
)
So(out, ShouldEqual, "pkg/path")
})
Convey("Join('pkg', 'path') should return 'pkg/path'", t, func() {
out, _ := path.Join(
context.Background(),
values.NewString("pkg"), values.NewString("path"),
)
So(out, ShouldEqual, "pkg/path")
})
}

18
pkg/stdlib/path/lib.go Normal file
View File

@ -0,0 +1,18 @@
package path
import "github.com/MontFerret/ferret/pkg/runtime/core"
// RegisterLib register `PATH` namespace functions.
func RegisterLib(ns core.Namespace) error {
return ns.RegisterFunctions(
core.NewFunctionsFromMap(map[string]core.Function{
"BASE": Base,
"CLEAN": Clean,
"DIR": Dir,
"EXT": Ext,
"IS_ABS": IsAbs,
"JOIN": Join,
"MATCH": Match,
"SEPARATE": Separate,
}))
}

45
pkg/stdlib/path/match.go Normal file
View File

@ -0,0 +1,45 @@
package path
import (
"context"
"path"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
// Match reports whether name matches the pattern.
// @param pattern (String) - The pattern.
// @param name (String) - The name.
// @returns (Boolean) - True if the name mathes the pattern.
func Match(_ context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 2, 2)
if err != nil {
return values.False, err
}
err = core.ValidateType(args[0], types.String)
if err != nil {
return values.False, err
}
err = core.ValidateType(args[1], types.String)
if err != nil {
return values.False, err
}
pattern := args[0].String()
name := args[1].String()
matched, err := path.Match(pattern, name)
if err != nil {
return values.False, core.Error(err, "match")
}
return values.NewBoolean(matched), nil
}

View File

@ -0,0 +1,61 @@
package path_test
import (
"context"
"testing"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/stdlib/path"
. "github.com/smartystreets/goconvey/convey"
)
func TestMatch(t *testing.T) {
Convey("When arg is not passed", t, func() {
Convey("It should return an error", func() {
_, err := path.Match(context.Background())
So(err, ShouldBeError)
})
})
Convey("First argument is wrong", t, func() {
var err error
_, err = path.Match(context.Background(), values.NewInt(0), values.NewString("/"))
So(err, ShouldBeError)
})
Convey("Second argument is wrong", t, func() {
var err error
_, err = path.Match(context.Background(), values.NewString("/"), values.NewInt(0))
So(err, ShouldBeError)
})
Convey("Match('http://site.com/*.csv', 'http://site.com/goods.csv') should return true", t, func() {
out, _ := path.Match(
context.Background(),
values.NewString("http://site.com/*.csv"), values.NewString("http://site.com/goods.csv"),
)
So(out, ShouldEqual, values.True)
})
Convey("Match('ferret*/ferret', 'ferret/bin/ferret') should return false", t, func() {
out, _ := path.Match(
context.Background(),
values.NewString("ferret*/ferret"), values.NewString("ferret/bin/ferret"),
)
So(out, ShouldEqual, values.False)
})
Convey("Match('[x-]', 'x') should return ad error", t, func() {
_, err := path.Match(
context.Background(),
values.NewString("[x-]"), values.NewString("x"),
)
So(err, ShouldBeError)
})
}

View File

@ -0,0 +1,33 @@
package path
import (
"context"
"path"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
// Separate separates the path into a directory and filename component.
// @param path (String) - The path
// @returns (Array) - First item is a directory component, and second is a filename component.
func Separate(_ context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 1, 1)
if err != nil {
return values.None, err
}
err = core.ValidateType(args[0], types.String)
if err != nil {
return values.None, err
}
pattern, name := path.Split(args[0].String())
arr := values.NewArrayWith(values.NewString(pattern), values.NewString(name))
return arr, nil
}

View File

@ -0,0 +1,37 @@
package path_test
import (
"context"
"testing"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/stdlib/path"
. "github.com/smartystreets/goconvey/convey"
)
func TestSeparate(t *testing.T) {
Convey("When arg is not passed", t, func() {
Convey("It should return an error", func() {
_, err := path.Separate(context.Background())
So(err, ShouldBeError)
})
})
Convey("Wrong argument", t, func() {
var err error
_, err = path.Separate(context.Background(), values.NewInt(0))
So(err, ShouldBeError)
})
Convey("Separate('http://site.com/logo.png') should return ['http://site.com/', 'logo.png']", t, func() {
out, _ := path.Separate(
context.Background(),
values.NewString("http://site.com/logo.png"),
)
expected := values.NewArrayWith(values.NewString("http://site.com/"), values.NewString("logo.png"))
So(out, ShouldResemble, expected)
})
}