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  }