file.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. package pathx
  2. import (
  3. "bufio"
  4. "crypto/md5"
  5. "encoding/hex"
  6. "fmt"
  7. "io"
  8. "io/fs"
  9. "log"
  10. "os"
  11. "path/filepath"
  12. "strings"
  13. "github.com/gookit/color"
  14. "github.com/zeromicro/go-zero/tools/goctl/internal/version"
  15. )
  16. // NL defines a new line.
  17. const (
  18. NL = "\n"
  19. goctlDir = ".goctl"
  20. gitDir = ".git"
  21. autoCompleteDir = ".auto_complete"
  22. cacheDir = "cache"
  23. )
  24. var goctlHome string
  25. // RegisterGoctlHome register goctl home path.
  26. func RegisterGoctlHome(home string) {
  27. goctlHome = home
  28. }
  29. // CreateIfNotExist creates a file if it is not exists.
  30. func CreateIfNotExist(file string) (*os.File, error) {
  31. _, err := os.Stat(file)
  32. if !os.IsNotExist(err) {
  33. return nil, fmt.Errorf("%s already exist", file)
  34. }
  35. return os.Create(file)
  36. }
  37. // RemoveIfExist deletes the specified file if it is exists.
  38. func RemoveIfExist(filename string) error {
  39. if !FileExists(filename) {
  40. return nil
  41. }
  42. return os.Remove(filename)
  43. }
  44. // RemoveOrQuit deletes the specified file if read a permit command from stdin.
  45. func RemoveOrQuit(filename string) error {
  46. if !FileExists(filename) {
  47. return nil
  48. }
  49. fmt.Printf("%s exists, overwrite it?\nEnter to overwrite or Ctrl-C to cancel...",
  50. color.New(color.BgRed, color.Bold).Render(filename))
  51. bufio.NewReader(os.Stdin).ReadBytes('\n')
  52. return os.Remove(filename)
  53. }
  54. // FileExists returns true if the specified file is exists.
  55. func FileExists(file string) bool {
  56. _, err := os.Stat(file)
  57. return err == nil
  58. }
  59. // FileNameWithoutExt returns a file name without suffix.
  60. func FileNameWithoutExt(file string) string {
  61. return strings.TrimSuffix(file, filepath.Ext(file))
  62. }
  63. // GetGoctlHome returns the path value of the goctl, the default path is ~/.goctl, if the path has
  64. // been set by calling the RegisterGoctlHome method, the user-defined path refers to.
  65. func GetGoctlHome() (home string, err error) {
  66. defer func() {
  67. if err != nil {
  68. return
  69. }
  70. info, err := os.Stat(home)
  71. if err == nil && !info.IsDir() {
  72. os.Rename(home, home+".old")
  73. MkdirIfNotExist(home)
  74. }
  75. }()
  76. if len(goctlHome) != 0 {
  77. home = goctlHome
  78. return
  79. }
  80. home, err = GetDefaultGoctlHome()
  81. return
  82. }
  83. // GetDefaultGoctlHome returns the path value of the goctl home where Join $HOME with .goctl.
  84. func GetDefaultGoctlHome() (string, error) {
  85. home, err := os.UserHomeDir()
  86. if err != nil {
  87. return "", err
  88. }
  89. return filepath.Join(home, goctlDir), nil
  90. }
  91. // GetGitHome returns the git home of goctl.
  92. func GetGitHome() (string, error) {
  93. goctlH, err := GetGoctlHome()
  94. if err != nil {
  95. return "", err
  96. }
  97. return filepath.Join(goctlH, gitDir), nil
  98. }
  99. // GetAutoCompleteHome returns the auto_complete home of goctl.
  100. func GetAutoCompleteHome() (string, error) {
  101. goctlH, err := GetGoctlHome()
  102. if err != nil {
  103. return "", err
  104. }
  105. return filepath.Join(goctlH, autoCompleteDir), nil
  106. }
  107. // GetCacheDir returns the cache dit of goctl.
  108. func GetCacheDir() (string, error) {
  109. goctlH, err := GetGoctlHome()
  110. if err != nil {
  111. return "", err
  112. }
  113. return filepath.Join(goctlH, cacheDir), nil
  114. }
  115. // GetTemplateDir returns the category path value in GoctlHome where could get it by GetGoctlHome.
  116. func GetTemplateDir(category string) (string, error) {
  117. home, err := GetGoctlHome()
  118. if err != nil {
  119. return "", err
  120. }
  121. if home == goctlHome {
  122. // backward compatible, it will be removed in the feature
  123. // backward compatible start.
  124. beforeTemplateDir := filepath.Join(home, version.GetGoctlVersion(), category)
  125. entries, err := os.ReadDir(beforeTemplateDir)
  126. if err != nil {
  127. return "", err
  128. }
  129. infos := make([]fs.FileInfo, 0, len(entries))
  130. for _, entry := range entries {
  131. info, err := entry.Info()
  132. if err != nil {
  133. return "", err
  134. }
  135. infos = append(infos, info)
  136. }
  137. var hasContent bool
  138. for _, e := range infos {
  139. if e.Size() > 0 {
  140. hasContent = true
  141. }
  142. }
  143. if hasContent {
  144. return beforeTemplateDir, nil
  145. }
  146. // backward compatible end.
  147. return filepath.Join(home, category), nil
  148. }
  149. return filepath.Join(home, version.GetGoctlVersion(), category), nil
  150. }
  151. // InitTemplates creates template files GoctlHome where could get it by GetGoctlHome.
  152. func InitTemplates(category string, templates map[string]string) error {
  153. dir, err := GetTemplateDir(category)
  154. if err != nil {
  155. return err
  156. }
  157. if err := MkdirIfNotExist(dir); err != nil {
  158. return err
  159. }
  160. for k, v := range templates {
  161. if err := createTemplate(filepath.Join(dir, k), v, false); err != nil {
  162. return err
  163. }
  164. }
  165. return nil
  166. }
  167. // CreateTemplate writes template into file even it is exists.
  168. func CreateTemplate(category, name, content string) error {
  169. dir, err := GetTemplateDir(category)
  170. if err != nil {
  171. return err
  172. }
  173. return createTemplate(filepath.Join(dir, name), content, true)
  174. }
  175. // Clean deletes all templates and removes the parent directory.
  176. func Clean(category string) error {
  177. dir, err := GetTemplateDir(category)
  178. if err != nil {
  179. return err
  180. }
  181. return os.RemoveAll(dir)
  182. }
  183. // LoadTemplate gets template content by the specified file.
  184. func LoadTemplate(category, file, builtin string) (string, error) {
  185. dir, err := GetTemplateDir(category)
  186. if err != nil {
  187. return "", err
  188. }
  189. file = filepath.Join(dir, file)
  190. if !FileExists(file) {
  191. return builtin, nil
  192. }
  193. content, err := os.ReadFile(file)
  194. if err != nil {
  195. return "", err
  196. }
  197. return string(content), nil
  198. }
  199. // SameFile compares the between path if the same path,
  200. // it maybe the same path in case case-ignore, such as:
  201. // /Users/go_zero and /Users/Go_zero, as far as we know,
  202. // this case maybe appear on macOS and Windows.
  203. func SameFile(path1, path2 string) (bool, error) {
  204. stat1, err := os.Stat(path1)
  205. if err != nil {
  206. return false, err
  207. }
  208. stat2, err := os.Stat(path2)
  209. if err != nil {
  210. return false, err
  211. }
  212. return os.SameFile(stat1, stat2), nil
  213. }
  214. func createTemplate(file, content string, force bool) error {
  215. if FileExists(file) && !force {
  216. return nil
  217. }
  218. f, err := os.Create(file)
  219. if err != nil {
  220. return err
  221. }
  222. defer f.Close()
  223. _, err = f.WriteString(content)
  224. return err
  225. }
  226. // MustTempDir creates a temporary directory.
  227. func MustTempDir() string {
  228. dir, err := os.MkdirTemp("", "")
  229. if err != nil {
  230. log.Fatalln(err)
  231. }
  232. return dir
  233. }
  234. func Copy(src, dest string) error {
  235. f, err := os.Open(src)
  236. if err != nil {
  237. return err
  238. }
  239. defer f.Close()
  240. dir := filepath.Dir(dest)
  241. err = MkdirIfNotExist(dir)
  242. if err != nil {
  243. return err
  244. }
  245. w, err := os.Create(dest)
  246. if err != nil {
  247. return err
  248. }
  249. w.Chmod(os.ModePerm)
  250. defer w.Close()
  251. _, err = io.Copy(w, f)
  252. return err
  253. }
  254. func Hash(file string) (string, error) {
  255. f, err := os.Open(file)
  256. if err != nil {
  257. return "", err
  258. }
  259. defer func() {
  260. _ = f.Close()
  261. }()
  262. hash := md5.New()
  263. _, err = io.Copy(hash, f)
  264. if err != nil {
  265. return "", err
  266. }
  267. return hex.EncodeToString(hash.Sum(nil)), nil
  268. }