github.com/GGP1/kure@v0.8.4/commands/file/touch/touch.go (about) 1 package touch 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "strings" 8 9 "github.com/GGP1/kure/auth" 10 cmdutil "github.com/GGP1/kure/commands" 11 "github.com/GGP1/kure/db/file" 12 "github.com/GGP1/kure/pb" 13 14 "github.com/pkg/errors" 15 "github.com/spf13/cobra" 16 bolt "go.etcd.io/bbolt" 17 ) 18 19 const example = ` 20 * Create a file and overwrite if it already exists 21 kure file touch fileName -p path/to/folder -o 22 23 * Create multiple files in the current directory 24 kure file touch file1 file2 file3 25 26 * Create all the files (includes folders and subfolders) 27 kure file touch -p path/to/folder` 28 29 type touchOptions struct { 30 path string 31 overwrite bool 32 } 33 34 // NewCmd returns a new command. 35 func NewCmd(db *bolt.DB) *cobra.Command { 36 opts := touchOptions{} 37 cmd := &cobra.Command{ 38 Use: "touch <name>", 39 Short: "Create stored files", 40 Long: `Create one, multiple, all the files or an specific directory. 41 42 For creating an specific file the extension must be included in the arguments, if not, Kure will consider that the user is trying to create a directory and will create all the files in it. 43 44 In case any of the paths contains spaces within it, it must be enclosed by double quotes. 45 46 In case a path is passed, Kure will create any missing folders for you.`, 47 Aliases: []string{"th"}, 48 Example: example, 49 Args: func(cmd *cobra.Command, args []string) error { 50 if len(args) == 0 { 51 return nil 52 } 53 return cmdutil.MustExist(db, cmdutil.File, true)(cmd, args) 54 }, 55 PreRunE: auth.Login(db), 56 RunE: runTouch(db, &opts), 57 PostRun: func(cmd *cobra.Command, args []string) { 58 // Reset variables (session) 59 opts = touchOptions{} 60 }, 61 } 62 63 f := cmd.Flags() 64 f.BoolVarP(&opts.overwrite, "overwrite", "o", false, "overwrite files if they already exist") 65 f.StringVarP(&opts.path, "path", "p", "", "destination folder path") 66 67 return cmd 68 } 69 70 func runTouch(db *bolt.DB, opts *touchOptions) cmdutil.RunEFunc { 71 return func(cmd *cobra.Command, args []string) error { 72 absolute, err := filepath.Abs(opts.path) 73 if err != nil { 74 return cmdutil.ErrInvalidPath 75 } 76 opts.path = absolute 77 78 if err := os.MkdirAll(opts.path, 0o700); err != nil { 79 return errors.Wrap(err, "making directory") 80 } 81 82 if err := os.Chdir(opts.path); err != nil { 83 return errors.Wrap(err, "changing directory") 84 } 85 86 // Create all 87 if len(args) == 0 { 88 files, err := file.List(db) 89 if err != nil { 90 return err 91 } 92 93 fmt.Println("Creating files at", opts.path) 94 95 for _, f := range files { 96 // Log errors, do not return 97 if err := createFiles(f, opts.path, opts.overwrite); err != nil { 98 fmt.Fprintln(os.Stderr, "error:", err) 99 } 100 } 101 102 return nil 103 } 104 105 // Create one or more, files or directories 106 // Log errors to avoid stopping the whole operation 107 fmt.Println("Creating files at", opts.path) 108 for _, name := range args { 109 name = strings.ToLower(strings.TrimSpace(name)) 110 111 // Assume the user wants to recreate an entire directory 112 if strings.HasSuffix(name, "/") { 113 if err := createDirectory(db, name, opts.path, opts.overwrite); err != nil { 114 fmt.Fprintln(os.Stderr, "error:", err) 115 } 116 continue 117 } 118 119 // Create single file 120 f, err := file.Get(db, name) 121 if err != nil { 122 fmt.Fprintln(os.Stderr, "error:", err) 123 continue 124 } 125 126 if err := createFile(f, opts.overwrite); err != nil { 127 fmt.Fprintln(os.Stderr, "error:", err) 128 continue 129 } 130 } 131 132 return nil 133 } 134 } 135 136 func createDirectory(db *bolt.DB, name, path string, overwrite bool) error { 137 files, err := file.List(db) 138 if err != nil { 139 return err 140 } 141 142 // If the last rune of name is not a slash, 143 // add it to make sure to create items under that folder only 144 if name[len(name)-1] != '/' { 145 name += "/" 146 } 147 148 var dir []*pb.File 149 for _, f := range files { 150 if strings.HasPrefix(f.Name, name) { 151 dir = append(dir, f) 152 } 153 } 154 155 if len(dir) == 0 { 156 return errors.Errorf("there is no folder named %q", name) 157 } 158 159 for _, f := range dir { 160 if err := createFiles(f, path, overwrite); err != nil { 161 return err 162 } 163 } 164 165 return nil 166 } 167 168 func createFile(file *pb.File, overwrite bool) error { 169 filename := filepath.Base(file.Name) 170 171 // Create if it doesn't exist or if we are allowed to overwrite it 172 if _, err := os.Stat(filename); os.IsExist(err) && !overwrite { 173 return errors.Errorf("%q already exists, use -o to overwrite files", filename) 174 } 175 176 if err := os.WriteFile(filename, file.Content, 0o600); err != nil { 177 return errors.Wrapf(err, "writing %q", filename) 178 } 179 fmt.Println("Create:", file.Name) 180 return nil 181 } 182 183 // createFiles takes care of recreating folders and files as they were stored in the database. 184 // 185 // This function works synchronously only, running it concurrently messes up os.Chdir(). 186 // 187 // The path is used only to return to the root folder. 188 func createFiles(file *pb.File, path string, overwrite bool) error { 189 // "the shire/frodo/ring.png" would be [the shire, frodo, ring.png] 190 parts := strings.Split(file.Name, "/") 191 192 for i, p := range parts { 193 // If it's the last element, create the file 194 if i == len(parts)-1 { 195 if err := createFile(file, overwrite); err != nil { 196 return err 197 } 198 // Go back to the root folder 199 if err := os.Chdir(path); err != nil { 200 return errors.Wrap(err, "changing to root directory") 201 } 202 break 203 } 204 205 // If it's not the last element, create a folder 206 // Use MkdirAll to avoid errors when the folder already exists 207 if err := os.MkdirAll(p, 0o700); err != nil { 208 return errors.Wrapf(err, "making %q directory", p) 209 } 210 if err := os.Chdir(p); err != nil { 211 return errors.Wrapf(err, "changing to %q directory", p) 212 } 213 } 214 215 return nil 216 }