1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-02-05 05:12:26 +08:00

Compare commits

...

10 Commits

Author SHA1 Message Date
dudaodong
c7e961704d Merge branch 'main' of github.com:duke-git/lancet into main 2022-01-06 17:09:43 +08:00
dudaodong
cb7df1b57d update: add some new feature comments for file and slice 2022-01-06 17:09:01 +08:00
dudaodong
eeff28606e feat: add IsLink, FileMode, MiMeType funcs for file 2022-01-06 16:53:32 +08:00
dudaodong
86d4b25a2b feat: and Zip and UnZip func for file operation 2022-01-06 15:15:59 +08:00
Ahmad Alfy
ad287ed99a doc: minor spelling mistak (#14) 2022-01-05 21:25:21 +08:00
dudaodong
df9de3065b feat: add ForEach func 2022-01-05 20:17:16 +08:00
dudaodong
71a2ea3f20 update: change case name for TestNone func 2022-01-05 19:46:51 +08:00
dudaodong
955f2e6de6 update func GetElapsedTime and None comment 2022-01-05 19:44:22 +08:00
donutloop
4aef9d6d22 Slice: Add none func (#13)
Returns true whether no elements of this slice match the provided predicate
func. Negated form of Every func
2022-01-05 19:38:14 +08:00
dudaodong
4752725dd6 add Wrap and Unwrap func comment 2022-01-04 11:22:54 +08:00
7 changed files with 347 additions and 17 deletions

3
.gitignore vendored
View File

@@ -3,4 +3,7 @@
.DS_Store
cryptor/*.txt
fileutil/*.txt
fileutil/*.zip
fileutil/*.link
fileutil/unzip/*
cryptor/*.pem

View File

@@ -6,7 +6,7 @@
<div align="center" style="text-align: center;">
![Go version](https://img.shields.io/badge/go-%3E%3D1.16<recommend>-9cf)
[![Release](https://img.shields.io/badge/release-1.1.7-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-1.1.8-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com//duke-git/lancet?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet)](https://goreportcard.com/report/github.com/duke-git/lancet)
[![codecov](https://codecov.io/gh/duke-git/lancet/branch/main/graph/badge.svg?token=FC48T1F078)](https://codecov.io/gh/duke-git/lancet)
@@ -23,7 +23,7 @@ English | [简体中文](./README_zh-CN.md)
- 👏 Comprehensive, efficient and reusable.
- 💪 140+ common go util functions, support string, slice, datetime, net, crypt...
- 💅 Only depend on the go standard library.
- 🌍 Unit test for exery exported function.
- 🌍 Unit test for every exported function.
### Installation
@@ -215,12 +215,17 @@ func main() {
func ClearFile(path string) error //write empty string to path file
func CreateFile(path string) bool // create a file in path
func CopyFile(srcFilePath string, dstFilePath string) error //copy src file to dst file
func FileMode(path string) (fs.FileMode, error) //return file's mode and permission
func MiMeType(file interface{}) string //return file mime type, file should be string or *os.File
func IsExist(path string) bool //checks if a file or directory exists
func IsLink(path string) bool //checks if a file is symbol link or not
func IsDir(path string) bool //checks if the path is directy or not
func ListFileNames(path string) ([]string, error) //return all file names in the path
func RemoveFile(path string) error //remove the path file
func ReadFileToString(path string) (string, error) //return string of file content
func ReadFileByLine(path string)([]string, error) //read file content by line
func Zip(fpath string, destPath string) error //create zip file, fpath could be a single file or a directory
func UnZip(zipFile string, destPath string) error //unzip the file and save it to destPath
```
#### 5. formatter is for data format
@@ -281,7 +286,7 @@ func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool //
func (w *Watcher) Start() //start the watch timer.
func (w *Watcher) Stop() //stop the watch timer
func (w *Watcher) Reset() {} //reset the watch timer.
func (w *Watcher) GetElapsedTime() time.Duration //获取代码段运行时间
func (w *Watcher) GetElapsedTime() time.Duration //return time duration from watcher start to end.
```
#### 7. netutil is for net process
@@ -394,9 +399,11 @@ func Difference(slice1, slice2 interface{}) interface{} //creates an slice of wh
func DeleteByIndex(slice interface{}, start int, end ...int) (interface{}, error) //delete the element of slice from start index to end index - 1
func Drop(slice interface{}, n int) interface{} //creates a slice with `n` elements dropped from the beginning when n > 0, or `n` elements dropped from the ending when n < 0
func Every(slice, function interface{}) bool //return true if all of the values in the slice pass the predicate function, function signature should be func(index int, value interface{}) bool
func None(slice, function interface{}) bool // return true if all the values in the slice mismatch the criteria
func Filter(slice, function interface{}) interface{} //filter slice, function signature should be func(index int, value interface{}) bool
func Find(slice, function interface{}) (interface{}, bool) //iterates over elements of slice, returning the first one that passes a truth test on function.function signature should be func(index int, value interface{}) bool .
func FlattenDeep(slice interface{}) interface{} //flattens slice recursive
func ForEach(slice, function interface{}) //iterates over elements of slice and invokes function for each element, function signature should be func(index int, value interface{})
func IntSlice(slice interface{}) ([]int, error) //convert value to int slice
func InterfaceSlice(slice interface{}) []interface{} //convert value to interface{} slice
func Intersection(slices ...interface{}) interface{} //creates a slice of unique values that included by all slices.
@@ -453,6 +460,8 @@ func PadEnd(source string, size int, padStr string) string //pads string on the
func PadStart(source string, size int, padStr string) string//pads string on the left side if it's shorter than size
func ReverseStr(s string) string //return string whose char order is reversed to the given string
func SnakeCase(s string) string //covert string to snake_case "fooBar" -> "foo_bar"
func Wrap(str string, wrapWith string) string //wrap a string with another string.
func Unwrap(str string, wrapToken string) string //unwrap a given string from anther string. will change str value
```
#### 11. validator is for data validation

View File

@@ -6,7 +6,7 @@
<div align="center" style="text-align: center;">
![Go version](https://img.shields.io/badge/go-%3E%3D1.16<recommend>-9cf)
[![Release](https://img.shields.io/badge/release-1.1.7-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-1.1.8-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com//duke-git/lancet?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet)](https://goreportcard.com/report/github.com/duke-git/lancet)
[![codecov](https://codecov.io/gh/duke-git/lancet/branch/main/graph/badge.svg?token=FC48T1F078)](https://codecov.io/gh/duke-git/lancet)
@@ -214,14 +214,19 @@ func main() {
```go
func ClearFile(path string) error //清空文件内容
func IsExist(path string) bool //判断文件/目录是否存在
func CreateFile(path string) bool //创建文件
func FileMode(path string) (fs.FileMode, error) //返回文件mode信息
func MiMeType(file interface{}) string //返回文件mime类型
func IsExist(path string) bool //判断文件/目录是否存在
func IsDir(path string) bool //判断是否为目录
func IsLink(path string) bool //检查文件是否为符号链接文件
func RemoveFile(path string) error //删除文件
func CopyFile(srcFilePath string, dstFilePath string) error //复制文件
func ListFileNames(path string) ([]string, error) //列出目录下所有文件名称
func ReadFileToString(path string) (string, error) //读取文件内容为字符串
func ReadFileByLine(path string)([]string, error) //按行读取文件内容
func Zip(fpath string, destPath string) error //压缩文件fpath参数可以是文件或目录destPath是压缩后目标文件
func UnZip(zipFile string, destPath string) error //解压文件并将文件存储在destPath目录中
```
#### 5. formatter格式化处理包
@@ -395,9 +400,11 @@ func Difference(slice1, slice2 interface{}) interface{} //返回
func DeleteByIndex(slice interface{}, start int, end ...int) (interface{}, error) //删除切片中start到end位置的值
func Drop(slice interface{}, n int) interface{} //创建一个新切片当n大于0时删除原切片前n个元素当n小于0时删除原切片后n个元素
func Every(slice, function interface{}) bool //slice中所有元素都符合函数条件时返回true, 否则返回false. 函数签名func(index int, value interface{}) bool
func None(slice, function interface{}) bool //slice中所有元素都不符合函数条件时返回true, 否则返回false. 函数签名func(index int, value interface{}) bool
func Find(slice, function interface{}) (interface{}, bool)//查找slice中第一个符合条件的元素函数签名func(index int, value interface{}) bool
func Filter(slice, function interface{}) interface{} //过滤slice, 函数签名func(index int, value interface{}) bool
func FlattenDeep(slice interface{}) interface{} //将slice递归为一维切片。
func ForEach(slice, function interface{}) //遍历切片在每个元素上执行函数函数签名func(index int, value interface{})
func IntSlice(slice interface{}) ([]int, error) //转成int切片
func InterfaceSlice(slice interface{}) []interface{} //转成interface{}切片
func Intersection(slices ...interface{}) interface{} //slice交集去重
@@ -452,7 +459,9 @@ func KebabCase(s string) string //字符串转为KebabCase, "foo_Bar" -> "foo-ba
func LowerFirst(s string) string //字符串的第一个字母转为小写字母
func PadEnd(source string, size int, padStr string) string //字符串末尾填充size个字符
func PadStart(source string, size int, padStr string) string//字符串开头填充size个字符
func ReverseStr(s string) string //字符串逆
func ReverseStr(s string) string //字符串逆
func Wrap(str string, wrapWith string) string //包裹字符串 Wrap("abc", "*") -> *abc*.
func Unwrap(str string, wrapToken string) string //解包裹字符串 Wrap("*abc*", "*") -> abc.
func SnakeCase(s string) string //字符串转为SnakeCase, "fooBar" -> "foo_bar"
```

View File

@@ -5,11 +5,16 @@
package fileutil
import (
"archive/zip"
"bufio"
"errors"
"io"
"io/fs"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"strings"
)
// IsExist checks if a file or directory exists
@@ -148,3 +153,146 @@ func ListFileNames(path string) ([]string, error) {
return res, nil
}
// Zip create zip file, fpath could be a single file or a directory
func Zip(fpath string, destPath string) error {
zipFile, err := os.Create(destPath)
if err != nil {
return err
}
defer zipFile.Close()
archive := zip.NewWriter(zipFile)
defer archive.Close()
filepath.Walk(fpath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
header.Name = strings.TrimPrefix(path, filepath.Dir(fpath)+"/")
if info.IsDir() {
header.Name += "/"
} else {
header.Method = zip.Deflate
}
writer, err := archive.CreateHeader(header)
if err != nil {
return err
}
if !info.IsDir() {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(writer, file)
if err != nil {
return err
}
}
return nil
})
return nil
}
// UnZip unzip the file and save it to destPath
func UnZip(zipFile string, destPath string) error {
zipReader, err := zip.OpenReader(zipFile)
if err != nil {
return err
}
defer zipReader.Close()
for _, f := range zipReader.File {
path := filepath.Join(destPath, f.Name)
if f.FileInfo().IsDir() {
os.MkdirAll(path, os.ModePerm)
} else {
if err = os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
return err
}
inFile, err := f.Open()
if err != nil {
return err
}
defer inFile.Close()
outFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return err
}
defer outFile.Close()
_, err = io.Copy(outFile, inFile)
if err != nil {
return err
}
}
}
return nil
}
// IsLink checks if a file is symbol link or not
func IsLink(path string) bool {
fi, err := os.Lstat(path)
if err != nil {
return false
}
return fi.Mode()&os.ModeSymlink != 0
}
// FileMode return file's mode and permission
func FileMode(path string) (fs.FileMode, error) {
fi, err := os.Lstat(path)
if err != nil {
return 0, err
}
return fi.Mode(), nil
}
// MiMeType return file mime type
// file should be string or *os.File
func MiMeType(file interface{}) string {
var mediatype string
readBuffer := func(f *os.File) ([]byte, error) {
buffer := make([]byte, 512)
_, err := f.Read(buffer)
if err != nil {
return nil, err
}
return buffer, nil
}
if filePath, ok := file.(string); ok {
f, err := os.Open(filePath)
if err != nil {
return mediatype
}
buffer, err := readBuffer(f)
if err != nil {
return mediatype
}
return http.DetectContentType(buffer)
}
if f, ok := file.(*os.File); ok {
buffer, err := readBuffer(f)
if err != nil {
return mediatype
}
return http.DetectContentType(buffer)
}
return mediatype
}

View File

@@ -37,6 +37,7 @@ func TestCreateFile(t *testing.T) {
internal.LogFailedTestInfo(t, "CreateFile", f, f, "create file error")
t.FailNow()
}
os.Remove(f)
}
func TestIsDir(t *testing.T) {
@@ -70,20 +71,22 @@ func TestCopyFile(t *testing.T) {
srcFile := "./text.txt"
CreateFile(srcFile)
dstFile := "./text_copy.txt"
destFile := "./text_copy.txt"
err := CopyFile(srcFile, dstFile)
err := CopyFile(srcFile, destFile)
if err != nil {
file, err := os.Open(dstFile)
file, err := os.Open(destFile)
if err != nil {
internal.LogFailedTestInfo(t, "CopyFile", srcFile, dstFile, "create file error: "+err.Error())
internal.LogFailedTestInfo(t, "CopyFile", srcFile, destFile, "create file error: "+err.Error())
t.FailNow()
}
if file.Name() != dstFile {
internal.LogFailedTestInfo(t, "CopyFile", srcFile, dstFile, file.Name())
if file.Name() != destFile {
internal.LogFailedTestInfo(t, "CopyFile", srcFile, destFile, file.Name())
t.FailNow()
}
}
os.Remove(srcFile)
os.Remove(destFile)
}
func TestListFileNames(t *testing.T) {
@@ -101,32 +104,41 @@ func TestListFileNames(t *testing.T) {
func TestReadFileToString(t *testing.T) {
path := "./text.txt"
CreateFile(path)
f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777)
f.WriteString("hello world")
res, _ := ReadFileToString(path)
if res != "hello world" {
internal.LogFailedTestInfo(t, "ReadFileToString", path, "hello world", res)
t.FailNow()
}
os.Remove(path)
}
func TestClearFile(t *testing.T) {
path := "./text.txt"
CreateFile(path)
f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777)
f.WriteString("hello world")
CreateFile(path)
res, _ := ReadFileToString(path)
if res != "" {
internal.LogFailedTestInfo(t, "CreateFile", path, "", res)
err := ClearFile(path)
if err != nil {
t.Error("Clear file error: ", err)
}
fileContent, _ := ReadFileToString(path)
if fileContent != "" {
internal.LogFailedTestInfo(t, "ClearFile", path, "", fileContent)
t.FailNow()
}
os.Remove(path)
}
func TestReadFileByLine(t *testing.T) {
path := "./text.txt"
CreateFile(path)
f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777)
f.WriteString("hello\nworld")
@@ -134,5 +146,88 @@ func TestReadFileByLine(t *testing.T) {
res, _ := ReadFileByLine(path)
if !reflect.DeepEqual(res, expected) {
internal.LogFailedTestInfo(t, "ReadFileByLine", path, expected, res)
t.FailNow()
}
os.Remove(path)
}
func TestZipAndUnZip(t *testing.T) {
srcFile := "./text.txt"
CreateFile(srcFile)
file, _ := os.OpenFile(srcFile, os.O_WRONLY|os.O_TRUNC, 0777)
file.WriteString("hello\nworld")
zipFile := "./text.zip"
err := Zip(srcFile, zipFile)
if err != nil {
internal.LogFailedTestInfo(t, "Zip", srcFile, zipFile, err)
t.FailNow()
}
unZipPath := "./unzip"
err = UnZip(zipFile, unZipPath)
if err != nil {
internal.LogFailedTestInfo(t, "UnZip", srcFile, unZipPath, err)
t.FailNow()
}
unZipFile := "./unzip/text.txt"
if !IsExist(unZipFile) {
internal.LogFailedTestInfo(t, "UnZip", zipFile, zipFile, err)
t.FailNow()
}
os.Remove(srcFile)
os.Remove(zipFile)
os.RemoveAll(unZipPath)
}
func TestFileMode(t *testing.T) {
srcFile := "./text.txt"
CreateFile(srcFile)
mode, err := FileMode(srcFile)
if err != nil {
t.Fail()
}
t.Log(mode)
os.Remove(srcFile)
}
func TestIsLink(t *testing.T) {
srcFile := "./text.txt"
CreateFile(srcFile)
linkFile := "./text.link"
if !IsExist(linkFile) {
_ = os.Symlink(srcFile, linkFile)
}
if !IsLink(linkFile) {
internal.LogFailedTestInfo(t, "IsLink", linkFile, "true", "false")
t.FailNow()
}
if IsLink("./file.go") {
internal.LogFailedTestInfo(t, "IsLink", "./file.go", "false", "true")
t.FailNow()
}
os.Remove(srcFile)
os.Remove(linkFile)
}
func TestMiMeType(t *testing.T) {
mt1 := MiMeType("./file.go")
expected := "text/plain; charset=utf-8"
if mt1 != expected {
internal.LogFailedTestInfo(t, "MiMeType", "./file.go", expected, mt1)
t.FailNow()
}
f, _ := os.Open("./file.go")
mt2 := MiMeType(f)
if mt2 != expected {
internal.LogFailedTestInfo(t, "MiMeType", "./file.go", expected, mt2)
t.FailNow()
}
}

View File

@@ -121,6 +121,28 @@ func Every(slice, function interface{}) bool {
return currentLength == sv.Len()
}
// None return true if all the values in the slice mismatch the criteria
// The function signature should be func(index int, value interface{}) bool .
func None(slice, function interface{}) bool {
sv := sliceValue(slice)
fn := functionValue(function)
elemType := sv.Type().Elem()
if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) {
panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
}
var currentLength int
for i := 0; i < sv.Len(); i++ {
flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0]
if !flag.Bool() {
currentLength++
}
}
return currentLength == sv.Len()
}
// Some return true if any of the values in the list pass the predicate function.
// The function signature should be func(index int, value interface{}) bool .
func Some(slice, function interface{}) bool {
@@ -243,6 +265,22 @@ func flattenRecursive(value reflect.Value, result reflect.Value) reflect.Value {
return result
}
// ForEach iterates over elements of slice and invokes function for each element
// The function signature should be func(index int, value interface{}).
func ForEach(slice, function interface{}) {
sv := sliceValue(slice)
fn := functionValue(function)
elemType := sv.Type().Elem()
if checkSliceCallbackFuncSignature(fn, elemType, nil) {
panic("function param should be of type func(int, " + elemType.String() + ")" + elemType.String())
}
for i := 0; i < sv.Len(); i++ {
fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})
}
}
// Map creates an slice of values by running each element of `slice` thru `function`.
// The function signature should be func(index int, value interface{}) interface{}.
func Map(slice, function interface{}) interface{} {

View File

@@ -108,6 +108,18 @@ func TestEvery(t *testing.T) {
}
}
func TestNone(t *testing.T) {
nums := []int{1, 2, 3, 5}
check := func(i, num int) bool {
return num%2 == 1
}
res := None(nums, check)
if res != false {
internal.LogFailedTestInfo(t, "None", nums, false, res)
t.FailNow()
}
}
func TestSome(t *testing.T) {
nums := []int{1, 2, 3, 5}
isEven := func(i, num int) bool {
@@ -223,6 +235,22 @@ func TestFlattenDeep(t *testing.T) {
}
}
func TestForEach(t *testing.T) {
numbers := []int{1, 2, 3, 4, 5}
expected := []int{3, 4, 5, 6, 7}
var numbersAddTwo []int
ForEach(numbers, func(index int, value int) {
numbersAddTwo = append(numbersAddTwo, value+2)
})
if !reflect.DeepEqual(numbersAddTwo, expected) {
internal.LogFailedTestInfo(t, "ForEach", numbers, expected, numbersAddTwo)
t.FailNow()
}
}
func TestMap(t *testing.T) {
s1 := []int{1, 2, 3, 4}
multiplyTwo := func(i, num int) int {