From a1646b1f0d1129fe0c126014919282f6489e58fe Mon Sep 17 00:00:00 2001 From: qydysky Date: Sun, 26 Jul 2020 06:43:33 +0800 Subject: [PATCH] 1 --- .gitignore | 6 ++ CheckFile.go | 251 ++++++++++++++++++++++++++++++++++++++++++++++ FileWR.go | 190 +++++++++++++++++++++++++++++++++++ Log.go | 67 +++++++++++++ Md5.go | 65 ++++++++++++ README.md | 6 ++ Reqf.go | 136 +++++++++++++++++++++++++ go.mod | 3 + linuxwin/linux.go | 65 ++++++++++++ linuxwin/win.go | 100 ++++++++++++++++++ part.go | 1 + part_test.go | 7 ++ 12 files changed, 897 insertions(+) create mode 100644 .gitignore create mode 100644 CheckFile.go create mode 100644 FileWR.go create mode 100644 Log.go create mode 100644 Md5.go create mode 100644 README.md create mode 100644 Reqf.go create mode 100644 go.mod create mode 100755 linuxwin/linux.go create mode 100755 linuxwin/win.go create mode 100644 part.go create mode 100644 part_test.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f3f520 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.directory +old/Md5.go +old/Part.go +old/Reqf.go +old/Zip.go +old/FileWR.go diff --git a/CheckFile.go b/CheckFile.go new file mode 100644 index 0000000..9a8842e --- /dev/null +++ b/CheckFile.go @@ -0,0 +1,251 @@ +package part + +import ( + "sync" + "fmt" + "time" + "runtime" + "strings" + "syscall" + "errors" + "os" + "io/ioutil" + + "github.com/qydysky/part/linuxwin" +) + +type checkfile struct {sync.Mutex} + +func Checkfile() *checkfile{ + return &checkfile{} +} + +func (this *checkfile) Build(checkFile,checkDir,SplitString string) { + v,_:=this.GetAllFile(checkDir) + + _checkFile := Filel { + File:checkFile, + Write:true, + Loc:0, + } + + fmt.Println("===checkFile Build===") + + for _,value := range v { + _checkFile.Context += string(value) + SplitString + fmt.Print(value,"\r") + } + + File().FileWR(_checkFile) + fmt.Println("===checkFile Build===") + +} + +func (this *checkfile) Check(checkFile,checkDir,SplitString string) bool { + + if checkFile == "" || SplitString == "" { + fmt.Println("[err]checkFile or SplitString has null.") + return false + } + fmt.Println("===checkFile Check===") + + var checkFileString string + var checkFileList []string + if strings.Contains(checkFile,"https://") { + fmt.Println("[wait]checkFile: Getting checkfile...") + + b,_,e := Reqf(ReqfVal { + Url:checkFile, + Timeout:6, + Retry:2, + }) + + if e != nil { + fmt.Println("[err]checkFile:",checkFile,e) + return false + }else{ + fmt.Println("[ok]checkFile: Get checkfile.") + checkFileString = string(b) + } + }else{ + + checkFileString = File().FileWR(Filel { + File:checkFile, + Write:false, + Loc:0, + }) + + } + + checkFileList = strings.Split(checkFileString,SplitString); + + var returnVal bool = true + for _,value := range checkFileList { + if value != "" && !this.IsExist(checkDir+value) { + fmt.Println("[err]checkFile:", checkDir+value, "not exist!") + returnVal = false + }else{ + if runtime.GOOS != "windows" && strings.Contains(value, ".run") { + var want os.FileMode = 0700 + if ! this.CheckFilePerm(value,want) { + fmt.Println("[err]checkFile:", checkDir+value, "no permission!") + returnVal = false + } + } + // fmt.Println("[ok]checkFile:",checkDir+value) + } + } + if returnVal {fmt.Println("[ok]checkFile: all file pass!")} + fmt.Println("===checkFile Check===") + + return returnVal +} + +func (this *checkfile) IsExist(f string) bool { + this.Lock() + defer this.Unlock() + + return Ppart.PIsExist(f) +} + +func (this *checkfile) IsOpen(f string) bool { + fi,e:=os.OpenFile(f, syscall.O_RDONLY|syscall.O_EXCL, 0) + if e!=nil {return true} + fi.Close() + return false +} + +func (this *checkfile) Checkfile(src string)(string,error){ + this.Lock() + defer this.Unlock() + + str,err:=this.GetAllFile(src) + + if err !=nil {return "",errors.New("获取文件列表错误!")} + + return Md5().Md5String(str),nil + +} + +func (this *checkfile) GetAllFile(pathname string) (string,error) { + this.Lock() + defer this.Unlock() + + var returnVal string = "" + + rd, err := ioutil.ReadDir(pathname) + + for _, fi := range rd { + if fi.IsDir() { + _returnVal,_:=this.GetAllFile(pathname + fi.Name() + "/") + returnVal+=_returnVal + } else { + returnVal+=pathname + fi.Name() + } + } + return returnVal,err +} + +func (this *checkfile) GetFileSize(path string) int64 { + this.Lock() + defer this.Unlock() + + if !this.IsExist(path) { + return 0 + } + fileInfo, err := os.Stat(path) + if err != nil { + return 0 + } + return fileInfo.Size() +} + +func (this *checkfile) CheckList(checkFile,checkDir,SplitString string)bool{ + + if checkFile == "" || SplitString == "" { + Logf().E("[err]checkFile or SplitString has null.") + return false + } + Logf().I("===checkFile Check===") + + var checkFileString string + var checkFileList []string + if strings.Contains(checkFile,"https://") { + Logf().I("[wait]checkFile: Getting checkfile...") + + var r = ReqfVal { + Url:checkFile, + Timeout:6, + Retry:2, + } + + b,_,e:=Reqf(r) + if e != nil { + Logf().E("[err]checkFile:",checkFile,e.Error()) + return false + }else{ + Logf().I("[ok]checkFile: Get checkfile.") + checkFileString=string(b) + } + }else{ + var _checkFile = Filel { + File:checkFile, + Write:false, + Loc:0, + } + + checkFileString=File().FileWR(_checkFile) + } + + checkFileList=strings.Split(checkFileString,SplitString); + + var returnVal bool = true + for _,value := range checkFileList { + if value!=""&&!this.IsExist(checkDir+value) { + Logf().E("[err]checkFile:",checkDir+value,"not exist!") + returnVal=false + }else{ + if runtime.GOOS!="windows" && strings.Contains(value,".run") { + var want os.FileMode = 0700 + if !this.CheckFilePerm(value,want) { + Logf().E("[err]checkFile:",checkDir+value,"no permission!") + returnVal=false + } + } + // fmt.Println("[ok]checkFile:",checkDir+value) + } + } + if returnVal {Logf().I("[ok]checkFile: all file pass!")} + Logf().I("===checkFile Check===") + + return returnVal +} + +func (this *checkfile) GetFileModTime(path string) (error,int64) { + this.Lock() + defer this.Unlock() + + f, err := os.Open(path) + if err != nil { + fmt.Println("open file error") + return err,time.Now().Unix() + } + defer f.Close() + + fi, err := f.Stat() + if err != nil { + fmt.Println("stat fileinfo error") + return err,time.Now().Unix() + } + + return nil,fi.ModTime().Unix() +} + +func (this *checkfile) CheckFilePerm(f string,want os.FileMode)bool{ + fi, err := os.Lstat(f) + if err != nil { + Logf().E("[err]cant get permission : ",f); + return false + } + return fi.Mode().Perm()>=want +} diff --git a/FileWR.go b/FileWR.go new file mode 100644 index 0000000..12df737 --- /dev/null +++ b/FileWR.go @@ -0,0 +1,190 @@ +package part + +import ( + "sync" + "strings" + "fmt" + "os" + "io/ioutil" + "syscall" +) + +type file struct {sync.Mutex} + +const ( + o_RDONLY int = syscall.O_RDONLY // 只读打开文件和os.Open()同义 + o_WRONLY int = syscall.O_WRONLY // 只写打开文件 + o_RDWR int = syscall.O_RDWR // 读写方式打开文件 + o_APPEND int = syscall.O_APPEND // 当写的时候使用追加模式到文件末尾 + o_CREATE int = syscall.O_CREAT // 如果文件不存在,此案创建 + o_EXCL int = syscall.O_EXCL // 和o_CREATE一起使用, 只有当文件不存在时才创建 + o_SYNC int = syscall.O_SYNC // 以同步I/O方式打开文件,直接写入硬盘. + o_TRUNC int = syscall.O_TRUNC // 如果可以的话,当打开文件时先清空文件 +) + +type Filel struct { + File string //src + Write bool //false:read + Loc int64 //WriteOrRead loc ;0:rewrite Or read all;-1 write on end + ReadNum int64 + Context string //Write string +} + +func File() *file{ + return &file{} +} + +func (this *file) FileWR(C Filel) string { + this.Lock() + defer this.Unlock() + + var returnVal string + + if C.File == "" {returnVal="";return returnVal} + + if C.Write { + returnVal=this.write(C) + }else{ + returnVal=this.read(C) + } + + return returnVal +} + +func (this *file) write(C Filel) string { + + var ( + File string=C.File + Loc int64=C.Loc + Context string=C.Context + ) + + var Kind int + switch Loc { + case 0:Kind=os.O_RDWR|os.O_EXCL|os.O_TRUNC + default:Kind=os.O_RDWR|os.O_EXCL + } + + fileObj,err := os.OpenFile(File,Kind,0644) + if err != nil { + fmt.Println("Err:cant open file:",File,err); + return "" + } + defer fileObj.Close() + + Loc=this.locfix(Loc,File,fileObj) + + _, err = fileObj.WriteAt([]byte(Context), Loc) + if err != nil { + fmt.Println("Err:cant write file:",File,err); + return "" + } + + return "ok" +} + +func (this *file) read(C Filel) string { + + var ( + File string=C.File + Loc int64=C.Loc + ReadNum int64=C.ReadNum + ) + + fileObj,err := os.OpenFile(File,os.O_RDONLY,0644) + if err != nil { + fmt.Println("Err:cant open file:",File,err); + return "" + } + defer fileObj.Close() + + Loc=this.locfix(Loc,File,fileObj) + + if ReadNum == 0 { + returnVal,err := ioutil.ReadAll(fileObj) + + if err != nil { + fmt.Println("Err:cant read file:",File,err); + return "" + } + + return string(returnVal[Loc:]) + + } + + buf := make([]byte, ReadNum) + + _, err=fileObj.ReadAt(buf,Loc) + if err != nil { + fmt.Println("Err:cant read file:",File,err); + return "" + } + + return string(buf) +} + +func (this *file) locfix(Loc int64,File string,fileObj *os.File)int64{ + + var returnVal int64 + + FileInfo,err:=fileObj.Stat() + if err != nil { + fmt.Println("Err:cant read file lenght",File,err) + return 0 + } + + if Loc<0 { + returnVal=FileInfo.Size()+1+Loc + } + + if returnVal<0 || returnVal>FileInfo.Size() { + fmt.Println("Err:outrage of file lenght",File,Loc,"out of 0 ~",FileInfo.Size()) + return 0 + } + + return returnVal +} + +func (this *file) NewPath(filename string) error{ + this.Lock() + defer this.Unlock() + + /* + 如果filename路径不存在,就新建它 + */ + var exist func(string) bool = func (s string) bool { + _, err := os.Stat(s) + return err == nil || os.IsExist(err) + } + + for i:=0;true;{ + a := strings.Index(filename[i:],"/") + if a == -1 {break} + if a == 0 {a = 1}//bug fix 当绝对路径时开头的/导致问题 + i=i+a+1 + if !exist(filename[:i-1]) { + err := os.Mkdir(filename[:i-1], os.ModePerm) + if err != nil {return err} + } + } + + if _, err := os.Stat(filename); os.IsNotExist(err) { + fd,err:=os.Create(filename) + if err != nil { + return err + }else{ + fd.Close() + } + } + return nil +} + +// func main(){ +// var u File +// u.File="a.txt" +// u.Write=false +// u.Loc=0 +// u.ReadNum=2 +// u.Context="ad" +// fmt.Println(FileWR(u)) +// } diff --git a/Log.go b/Log.go new file mode 100644 index 0000000..236914c --- /dev/null +++ b/Log.go @@ -0,0 +1,67 @@ +package part + +import ( + "io" + "os" + "log" +) + +type logl struct {} + +func Logf() (*logl) { + return &logl{} +} + +var ( + isinit bool + tracef *log.Logger // 记录所有日志 + infof *log.Logger // 重要的信息 + warningf *log.Logger // 需要注意的信息 + errorf *log.Logger // 非常严重的问题 +) + +func (*logl) New(fileP string) { + + File().NewPath(fileP) + + file, err := os.OpenFile(fileP, + os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) + if err != nil { + log.Fatalln("Failed to open error log file:", err) + } + + tracef = log.New(io.MultiWriter(file, os.Stdout), + "TRACE: ", + log.Ldate|log.Ltime|log.Lshortfile) + + infof = log.New(io.MultiWriter(file, os.Stdout), + "INFO: ", + log.Ldate|log.Ltime|log.Lshortfile) + + warningf = log.New(io.MultiWriter(file, os.Stdout), + "WARNING: ", + log.Ldate|log.Ltime|log.Lshortfile) + + errorf = log.New(io.MultiWriter(file, os.Stderr), + "ERROR: ", + log.Ldate|log.Ltime|log.Lshortfile) + + isinit = true +} + +func (*logl) T(l ...string){ + if !isinit {log.Fatalln("not New(file string) yet!");return} + tracef.Println(l) +} +func (*logl) I(l ...string){ + if !isinit {log.Fatalln("not New(file string) yet!");return} + infof.Println(l) +} +func (*logl) W(l ...string){ + if !isinit {log.Fatalln("not New(file string) yet!");return} + warningf.Println(l) +} +func (*logl) E(l ...string){ + if !isinit {log.Fatalln("not New(file string) yet!");return} + errorf.Println(l) +} \ No newline at end of file diff --git a/Md5.go b/Md5.go new file mode 100644 index 0000000..b9fb0ef --- /dev/null +++ b/Md5.go @@ -0,0 +1,65 @@ +package part + +import ( + "sync" + "io" + "crypto/md5" + "fmt" + "os" + "time" +) + +type md5l struct{sync.Mutex} + +func Md5() *md5l{ + return &md5l{} +} + +func (this *md5l) Md5String(str string) string { + this.Lock() + defer this.Unlock() + + w := md5.New() + io.WriteString(w, str) + md5str := fmt.Sprintf("%x", w.Sum(nil)) + return md5str +} + +func (this *md5l) Md5File(path string) (string, error) { + this.Lock() + defer this.Unlock() + + file, err := os.Open(path) + defer file.Close() + if err != nil { + return "",err + } + + h := md5.New() + _, err = io.Copy(h,file) + if err != nil { + return "",err + } + + return fmt.Sprintf("%x",h.Sum(nil)), nil +} + +func (this *md5l) GetFileModTime(path string) (error,int64) { + this.Lock() + defer this.Unlock() + + f, err := os.Open(path) + if err != nil { + fmt.Println("open file error") + return err,time.Now().Unix() + } + defer f.Close() + + fi, err := f.Stat() + if err != nil { + fmt.Println("stat fileinfo error") + return err,time.Now().Unix() + } + + return nil,fi.ModTime().Unix() +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..3bf2e3b --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# part + +#### 介绍 +自己编写的一些go组件,增加复用 + +使用代理 `export GOPROXY="https://goproxy.io"` \ No newline at end of file diff --git a/Reqf.go b/Reqf.go new file mode 100644 index 0000000..87f4f24 --- /dev/null +++ b/Reqf.go @@ -0,0 +1,136 @@ +package part + +import ( + "io" + "os" + "time" + "net/http" + "errors" + "io/ioutil" + "net/url" + "encoding/binary" +) + +type ReqfVal struct { + Url string + Timeout int + Referer string + Cookie string + Proxy string + Accept string + AcceptLanguage string + Connection string + Retry int + JustResponseCode bool + SaveToPath string +} + +// func main(){ +// var _ReqfVal = ReqfVal{ +// Url:url, +// Proxy:proxy, +// Timeout:10, +// Retry:2, +// } +// Reqf(_ReqfVal) +// } + +func Reqf(val ReqfVal) ([]byte,time.Duration,error) { + var ( + returnVal []byte + returnTime time.Duration + returnErr error + ) + + var _val ReqfVal = val; + + if _val.Timeout==0{_val.Timeout=3} + + for ;_val.Retry>=0;_val.Retry-- { + returnVal,returnTime,returnErr=Reqf_1(_val) + if returnErr==nil {break} + } + return returnVal,returnTime,returnErr +} + +func Reqf_1(val ReqfVal) ([]byte,time.Duration,error){ + var ( + Url string = val.Url + Referer string = val.Referer + Cookie string = val.Cookie + Proxy string = val.Proxy + Accept string = val.Accept + Connection string = val.Connection + AcceptLanguage string = val.AcceptLanguage + Timeout int = val.Timeout + JustResponseCode bool =val.JustResponseCode + SaveToPath string =val.SaveToPath + ) + + var ( + usedTime time.Duration = 0 + beginTime time.Time = time.Now() + ) + + var _Timeout time.Duration = time.Duration(Timeout)*time.Second + + var client http.Client + if Proxy!="" { + proxy := func(_ *http.Request) (*url.URL, error) { + return url.Parse(Proxy) + } + transport := &http.Transport{Proxy: proxy} + client = http.Client{Timeout: _Timeout,Transport: transport} + }else{ + client = http.Client{Timeout: _Timeout} + } + + if Url==""{return nil,0,errors.New("Url is \"\"")} + req,_ := http.NewRequest("GET", Url, nil) + + if Cookie!=""{req.Header.Add("Cookie",Cookie)} + if Referer!=""{req.Header.Add("Referer",Referer)} + if Connection!=""{req.Header.Set("Connection",Connection)} + if AcceptLanguage!=""{req.Header.Set("Accept-Language",AcceptLanguage)} + if Referer!=""{req.Header.Add("Accept",Accept)} + + req.Header.Add("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36") + resp, err := client.Do(req) + + if err != nil { + return nil,0,err + } + + var saveToFile func(io.Reader,string)error = func (Body io.Reader,filepath string) error { + out, err := os.Create(filepath + ".dtmp") + if err != nil {out.Close();return err} + + // resp, err := http.Get(url) + // if err != nil {out.Close();return err} + // defer resp.Body.Close() + + if _, err = io.Copy(out, Body); err != nil {out.Close();return err} + out.Close() + + if err = os.Rename(filepath+".dtmp", filepath); err != nil {return err} + return nil + } + var b []byte + if !JustResponseCode { + defer resp.Body.Close() + if SaveToPath != "" { + if err := saveToFile(resp.Body, SaveToPath); err != nil { + return b,0,err + } + }else{ + b, _ = ioutil.ReadAll(resp.Body) + } + }else{ + b = make([]byte, 4) + binary.LittleEndian.PutUint32(b, uint32(resp.StatusCode)) + } + + usedTime=time.Since(beginTime) + + return b,usedTime,nil +} \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..8bf44a4 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/qydysky/part + +go 1.14 diff --git a/linuxwin/linux.go b/linuxwin/linux.go new file mode 100755 index 0000000..8aea6e7 --- /dev/null +++ b/linuxwin/linux.go @@ -0,0 +1,65 @@ +// +build linux + +package Ppart + +import ( + "os" + "os/exec" + "strings" + "strconv" + "path/filepath" +) + +func PCheck(pros []string) []int{ + res:=[]int{} + _pros:=[][]byte{} + + for _,v:= range pros{ + if v=="" {return res} + _pros=append(_pros,[]byte(v)) + res=append(res,0) + } + + for j,i :=range _pros{ + cmd := exec.Command("pgrep","-c",string(i)) + output, _ := cmd.Output() + outputt:=strings.Replace(string(output), "\n", "", -1) + res[j],_=strconv.Atoi(outputt) + } + return res +} + +func PStartf(pro []*exec.Cmd){ + for i := range pro { + pro[i].Start() + } +} + +func PRun(hide bool,prog string,cmd ...string){ + p:=exec.Command(prog,cmd...) + if hide {} + p.Run() +} + +func Cdir()string{ + dir, _ := os.Executable() + exPath := filepath.Dir(dir) + return exPath +} + +func PProxy(s, pacUrl string){ + if s=="off" { + PRun(true,"gsettings","set","org.gnome.system.proxy","mode","none"); + PRun(true,"kwriteconfig5","--file","kioslaverc","--group","'Proxy Settings'","--key","ProxyType","\"0\""); + }else{ + PRun(true,"gsettings","set","org.gnome.system.proxy","autoconfig-url",pacUrl); + PRun(true,"gsettings","set","org.gnome.system.proxy","mode","auto"); + PRun(true,"kwriteconfig5","--file","kioslaverc","--group","'Proxy Settings'","--key","ProxyType","\"2\""); + PRun(true,"kwriteconfig5","--file","kioslaverc","--group","'Proxy Settings'","--key","Proxy Config Script","\""+pacUrl+"\""); + } +} + +func PIsExist(f string) bool{ + _, err := os.Stat(f) + return err == nil || os.IsExist(err) +} \ No newline at end of file diff --git a/linuxwin/win.go b/linuxwin/win.go new file mode 100755 index 0000000..baaf17a --- /dev/null +++ b/linuxwin/win.go @@ -0,0 +1,100 @@ +// +build !linux + + +package Ppart + +import ( + "syscall" + "unsafe" + "os" + "os/exec" + "path/filepath" +) + +type ulong int32 +type ulong_ptr uintptr + +type PROCESSENTRY32 struct { + dwSize ulong + cntUsage ulong + th32ProcessID ulong + th32DefaultHeapID ulong_ptr + th32ModuleID ulong + cntThreads ulong + th32ParentProcessID ulong + pcPriClassBase ulong + dwFlags ulong + szExeFile [260]byte +} + +func PCheck(pros []string) []int{ + kernel32 := syscall.NewLazyDLL("kernel32.dll") + pHandle,_,_ := kernel32.NewProc("CreateToolhelp32Snapshot").Call(uintptr(0x2),uintptr(0x0)) + res:=[]int{} + _pros:=[][]byte{} + if int(pHandle)==-1 {return res} + for _,v:= range pros{ + if v=="" {return res} + _pros=append(_pros,[]byte(v)) + res=append(res,0) + } + // fmt.Println(string(_pros[0])) + pp:= kernel32.NewProc("Process32Next") + var proc PROCESSENTRY32; + var a [260]byte; + proc.dwSize = ulong(unsafe.Sizeof(proc)); + + for { + proc.szExeFile=a; + rt,_,_ := pp.Call(uintptr(pHandle),uintptr(unsafe.Pointer(&proc))) + + if int(rt)!=1 {break} + // fmt.Println(string(proc.szExeFile[0:])) + for j,i :=range _pros{ + // fmt.Println(string(proc.szExeFile[0:len(_pros[i])])) + // if len(_pros[i])!=len(proc.szExeFile){continue} + + for q,v:=range i{ + if proc.szExeFile[q]!=v {break} + if q+1==len(i) {res[j]+=1} + } + // fmt.Println("") + + + // if proc.szExeFile[:len(_pros[i])]==_pros[i] {res[i]+=1} + } + } + kernel32.NewProc("CloseHandle").Call(pHandle); + // fmt.Println(time.Since(t)) + return res +} + +func PStartf(pro []*exec.Cmd){ + for i := range pro { + pro[i].SysProcAttr = &syscall.SysProcAttr{HideWindow: true} + pro[i].Start() + } +} + +func PRun(hide bool,prog string,cmd ...string){ + p:=exec.Command(prog,cmd...) + if hide {p.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}} + p.Run() +} + +func PProxy(s, pacUrl string){ + if s=="off"{ + PRun(true,Cdir()+"/ref/sysproxy64.exe","off") + }else{ + PRun(true,Cdir()+"/ref/sysproxy64.exe","pac",pacUrl) + } +} +func Cdir()string{ + dir, _ := os.Executable() + exPath := filepath.Dir(dir) + return exPath +} +func PIsExist(f string) bool{ + _, err := os.Stat(f) + return err == nil || os.IsExist(err) +} \ No newline at end of file diff --git a/part.go b/part.go new file mode 100644 index 0000000..6e8550c --- /dev/null +++ b/part.go @@ -0,0 +1 @@ +package part diff --git a/part_test.go b/part_test.go new file mode 100644 index 0000000..a2ca98a --- /dev/null +++ b/part_test.go @@ -0,0 +1,7 @@ +package part + +import "testing" + +func TestHello(t *testing.T) { + t.Log("ok") +} -- 2.39.2