mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-05 03:17:50 +08:00
Minor fixup for path:temp-{dir file}.
* Remove `MatchesRegexp` from eval/vals since it is not part of the Elvish value protocol. * Simplify implementation of value matching in `eval/evaltest`. * Documentation wording tweaks.
This commit is contained in:
parent
bc37099c92
commit
90acb4a242
|
@ -49,8 +49,8 @@ New features in the standard library:
|
|||
|
||||
- A new `file:` module contains utilities for manipulating files.
|
||||
|
||||
- Commands for creating, what are usually meant to be temporary, unique
|
||||
directories, path:temp-dir, and regular files, path:temp-file
|
||||
- Commands for creating temporary files and directories, `path:temp-file`
|
||||
and `path:temp-dir`
|
||||
([#1255](https://b.elv.sh/1255)).
|
||||
|
||||
New features in the interactive editor:
|
||||
|
|
|
@ -43,10 +43,6 @@ type Result struct {
|
|||
Exception error
|
||||
}
|
||||
|
||||
// MatchingRegexp is used in a `Puts()` call when the value being matched should be a string
|
||||
// matching a regexp rather than a literal string.
|
||||
type MatchingRegexp struct{ Pattern string }
|
||||
|
||||
// The following functions and methods are used to build Test structs. They are
|
||||
// supposed to read like English, so a test that "put x" should put "x" reads:
|
||||
//
|
||||
|
@ -212,45 +208,37 @@ func capturePort() (*eval.Port, func() ([]interface{}, []byte)) {
|
|||
}
|
||||
|
||||
func matchOut(want, got []interface{}) bool {
|
||||
if len(got) == 0 && len(want) == 0 {
|
||||
return true
|
||||
}
|
||||
if len(got) != len(want) {
|
||||
return false
|
||||
}
|
||||
for i := range got {
|
||||
switch g := got[i].(type) {
|
||||
case float64:
|
||||
// Special-case float64 to correctly handle NaN and support
|
||||
// approximate comparison.
|
||||
switch w := want[i].(type) {
|
||||
case float64:
|
||||
if !matchFloat64(g, w, 0) {
|
||||
return false
|
||||
}
|
||||
case Approximately:
|
||||
if !matchFloat64(g, w.F, ApproximatelyThreshold) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
default:
|
||||
switch w := want[i].(type) {
|
||||
case MatchingRegexp:
|
||||
if !vals.MatchesRegexp(got[i], w.Pattern) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !vals.Equal(got[i], want[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !match(got[i], want[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func match(got, want interface{}) bool {
|
||||
switch got := got.(type) {
|
||||
case float64:
|
||||
// Special-case float64 to correctly handle NaN and support
|
||||
// approximate comparison.
|
||||
switch want := want.(type) {
|
||||
case float64:
|
||||
return matchFloat64(got, want, 0)
|
||||
case Approximately:
|
||||
return matchFloat64(got, want.F, ApproximatelyThreshold)
|
||||
}
|
||||
case string:
|
||||
switch want := want.(type) {
|
||||
case MatchingRegexp:
|
||||
return matchRegexp(want.Pattern, got)
|
||||
}
|
||||
}
|
||||
return vals.Equal(got, want)
|
||||
}
|
||||
|
||||
func reprs(values []interface{}) []string {
|
||||
s := make([]string, len(values))
|
||||
for i, v := range values {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"regexp"
|
||||
|
||||
"src.elv.sh/pkg/eval"
|
||||
)
|
||||
|
@ -27,6 +28,19 @@ func matchFloat64(a, b, threshold float64) bool {
|
|||
return math.Abs(a-b) <= threshold
|
||||
}
|
||||
|
||||
// MatchingRegexp can be passed to TestCase.Puts to match a any string that
|
||||
// matches a regexp pattern. If the pattern is not a valid regexp, the test will
|
||||
// panic.
|
||||
type MatchingRegexp struct{ Pattern string }
|
||||
|
||||
func matchRegexp(p, s string) bool {
|
||||
matched, err := regexp.MatchString(p, s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return matched
|
||||
}
|
||||
|
||||
type errorMatcher interface{ matchError(error) bool }
|
||||
|
||||
// AnyError is an error that can be passed to TestCase.Throws to match any
|
||||
|
|
|
@ -184,13 +184,19 @@ func isRegular(path string) bool {
|
|||
//elvdoc:fn temp-dir
|
||||
//
|
||||
// ```elvish
|
||||
// temp-dir &dir=$dir $pattern?
|
||||
// temp-dir &dir='' $pattern?
|
||||
// ```
|
||||
//
|
||||
// Create a unique directory and output its name. The &dir option determines where the directory
|
||||
// will be created, and its default value is appropriate for your system. The `$pattern` value is
|
||||
// optional. If omitted it defaults to `elvish-*`. The last star in the pattern is replaced by a
|
||||
// random string. It is your responsibility to remove the (presumably) temporary directory.
|
||||
// Creates a new directory and outputs its name.
|
||||
//
|
||||
// The &dir option determines where the directory will be created; if it is an
|
||||
// empty string (the default), a system-dependent directory suitable for storing
|
||||
// temporary files will be used. The `$pattern` argument determins the name of
|
||||
// the directory, where the last star will be replaced by a random string; it
|
||||
// defaults to `elvish-*`.
|
||||
//
|
||||
// It is the caller's responsibility to remove the directory if it is intended
|
||||
// to be temporary.
|
||||
//
|
||||
// ```elvish-transcript
|
||||
// ~> path:temp-dir
|
||||
|
@ -228,17 +234,21 @@ func tempDir(opts mktempOpt, args ...string) (string, error) {
|
|||
//elvdoc:fn temp-file
|
||||
//
|
||||
// ```elvish
|
||||
// temp-file [&dir=$dir] [$pattern]
|
||||
// temp-file &dir='' $pattern?
|
||||
// ```
|
||||
//
|
||||
// Create a unique file and output a [file](language.html#file) object opened for reading and
|
||||
// writing. The &dir option determines where the directory will be created, and its default value is
|
||||
// appropriate for your system. The `$pattern` value is optional. If omitted it defaults to
|
||||
// `elvish-*`. The last star in the pattern is replaced by a random string. It is your
|
||||
// responsibility to remove the (presumably) temporary file.
|
||||
// Creates a new file and outputs a [file](language.html#file) object opened
|
||||
// for reading and writing.
|
||||
//
|
||||
// You can use [`fclose`](builtin.html#fclose) to close the file. You can use `$f[name]` to extract
|
||||
// the name of the file so it can be used as an argument for another command; e.g., `rm`.
|
||||
// The &dir option determines where the file will be created; if it is an
|
||||
// empty string (the default), a system-dependent directory suitable for storing
|
||||
// temporary files will be used. The `$pattern` argument determins the name of
|
||||
// the file, where the last star will be replaced by a random string; it
|
||||
// defaults to `elvish-*`.
|
||||
//
|
||||
// It is the caller's responsibility to close the file with
|
||||
// [`file:close`](file.html#close). The caller should also remove the file if it
|
||||
// is intended to be temporary (with `rm $f[name]`).
|
||||
//
|
||||
// ```elvish-transcript
|
||||
// ~> f = path:temp-file
|
||||
|
|
|
@ -35,36 +35,16 @@ func (err noSuchKeyError) Error() string {
|
|||
return "no such key: " + Repr(err.key, NoPretty)
|
||||
}
|
||||
|
||||
// TODO: Replace this with a a generalized introspection mechanism based on PseudoStructMap for
|
||||
// *os.File objects so that commands like `keys` also work on those objects.
|
||||
var errInvalidOsFileIndex = errors.New("invalid index for a File object")
|
||||
|
||||
func indexOsFile(f *os.File, k interface{}) (interface{}, error) {
|
||||
switch k := k.(type) {
|
||||
case string:
|
||||
switch {
|
||||
case k == "fd":
|
||||
return int(f.Fd()), nil
|
||||
case k == "name":
|
||||
return f.Name(), nil
|
||||
default:
|
||||
return nil, errInvalidOsFileIndex
|
||||
}
|
||||
default:
|
||||
return nil, errInvalidOsFileIndex
|
||||
}
|
||||
}
|
||||
|
||||
// Index indexes a value with the given key. It is implemented for the builtin
|
||||
// type string, the List type, StructMap types, and types satisfying the
|
||||
// ErrIndexer or Indexer interface (the Map type satisfies Indexer). For other
|
||||
// types, it returns a nil value and a non-nil error.
|
||||
// type string, *os.File, List, StructMap and PseudoStructMap types, and types
|
||||
// satisfying the ErrIndexer or Indexer interface (the Map type satisfies
|
||||
// Indexer). For other types, it returns a nil value and a non-nil error.
|
||||
func Index(a, k interface{}) (interface{}, error) {
|
||||
switch a := a.(type) {
|
||||
case string:
|
||||
return indexString(a, k)
|
||||
case *os.File:
|
||||
return indexOsFile(a, k)
|
||||
return indexFile(a, k)
|
||||
case ErrIndexer:
|
||||
return a.Index(k)
|
||||
case Indexer:
|
||||
|
@ -84,6 +64,16 @@ func Index(a, k interface{}) (interface{}, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func indexFile(f *os.File, k interface{}) (interface{}, error) {
|
||||
switch k {
|
||||
case "fd":
|
||||
return int(f.Fd()), nil
|
||||
case "name":
|
||||
return f.Name(), nil
|
||||
}
|
||||
return nil, NoSuchKey(k)
|
||||
}
|
||||
|
||||
func indexStructMap(a StructMap, k interface{}) (interface{}, error) {
|
||||
fieldName, ok := k.(string)
|
||||
if !ok || fieldName == "" {
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
package vals
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// MatchesRegexp returns whether The first value matches the second value interpreted as a regexp.
|
||||
// Both bytes must be a string.
|
||||
func MatchesRegexp(x, y interface{}) bool {
|
||||
val, ok := x.(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
pat, ok := y.(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
matched, err := regexp.MatchString(pat, val)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return matched
|
||||
}
|
Loading…
Reference in New Issue
Block a user