file.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  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/wuntsong-org/go-zero-plus/tools/goctlwt/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, _ := os.ReadDir(beforeTemplateDir)
  126. infos := make([]fs.FileInfo, 0, len(entries))
  127. for _, entry := range entries {
  128. info, err := entry.Info()
  129. if err != nil {
  130. continue
  131. }
  132. infos = append(infos, info)
  133. }
  134. var hasContent bool
  135. for _, e := range infos {
  136. if e.Size() > 0 {
  137. hasContent = true
  138. }
  139. }
  140. if hasContent {
  141. return beforeTemplateDir, nil
  142. }
  143. // backward compatible end.
  144. return filepath.Join(home, category), nil
  145. }
  146. return filepath.Join(home, version.GetGoctlVersion(), category), nil
  147. }
  148. // InitTemplates creates template files GoctlHome where could get it by GetGoctlHome.
  149. func InitTemplates(category string, templates map[string]string) error {
  150. dir, err := GetTemplateDir(category)
  151. if err != nil {
  152. return err
  153. }
  154. if err := MkdirIfNotExist(dir); err != nil {
  155. return err
  156. }
  157. for k, v := range templates {
  158. if err := createTemplate(filepath.Join(dir, k), v, false); err != nil {
  159. return err
  160. }
  161. }
  162. return nil
  163. }
  164. // CreateTemplate writes template into file even it is exists.
  165. func CreateTemplate(category, name, content string) error {
  166. dir, err := GetTemplateDir(category)
  167. if err != nil {
  168. return err
  169. }
  170. return createTemplate(filepath.Join(dir, name), content, true)
  171. }
  172. // Clean deletes all templates and removes the parent directory.
  173. func Clean(category string) error {
  174. dir, err := GetTemplateDir(category)
  175. if err != nil {
  176. return err
  177. }
  178. return os.RemoveAll(dir)
  179. }
  180. // LoadTemplate gets template content by the specified file.
  181. func LoadTemplate(category, file, builtin string) (string, error) {
  182. dir, err := GetTemplateDir(category)
  183. if err != nil {
  184. return "", err
  185. }
  186. file = filepath.Join(dir, file)
  187. if !FileExists(file) {
  188. return builtin, nil
  189. }
  190. content, err := os.ReadFile(file)
  191. if err != nil {
  192. return "", err
  193. }
  194. return string(content), nil
  195. }
  196. // SameFile compares the between path if the same path,
  197. // it maybe the same path in case case-ignore, such as:
  198. // /Users/go_zero and /Users/Go_zero, as far as we know,
  199. // this case maybe appear on macOS and Windows.
  200. func SameFile(path1, path2 string) (bool, error) {
  201. stat1, err := os.Stat(path1)
  202. if err != nil {
  203. return false, err
  204. }
  205. stat2, err := os.Stat(path2)
  206. if err != nil {
  207. return false, err
  208. }
  209. return os.SameFile(stat1, stat2), nil
  210. }
  211. func createTemplate(file, content string, force bool) error {
  212. if FileExists(file) && !force {
  213. return nil
  214. }
  215. f, err := os.Create(file)
  216. if err != nil {
  217. return err
  218. }
  219. defer f.Close()
  220. _, err = f.WriteString(content)
  221. return err
  222. }
  223. // MustTempDir creates a temporary directory.
  224. func MustTempDir() string {
  225. dir, err := os.MkdirTemp("", "")
  226. if err != nil {
  227. log.Fatalln(err)
  228. }
  229. return dir
  230. }
  231. func Copy(src, dest string) error {
  232. f, err := os.Open(src)
  233. if err != nil {
  234. return err
  235. }
  236. defer f.Close()
  237. dir := filepath.Dir(dest)
  238. err = MkdirIfNotExist(dir)
  239. if err != nil {
  240. return err
  241. }
  242. w, err := os.Create(dest)
  243. if err != nil {
  244. return err
  245. }
  246. w.Chmod(os.ModePerm)
  247. defer w.Close()
  248. _, err = io.Copy(w, f)
  249. return err
  250. }
  251. func Hash(file string) (string, error) {
  252. f, err := os.Open(file)
  253. if err != nil {
  254. return "", err
  255. }
  256. defer func() {
  257. _ = f.Close()
  258. }()
  259. hash := md5.New()
  260. _, err = io.Copy(hash, f)
  261. if err != nil {
  262. return "", err
  263. }
  264. return hex.EncodeToString(hash.Sum(nil)), nil
  265. }