diff --git a/cmd/cmd.go b/cmd/cmd.go index 5f2345d..670f342 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -8,5 +8,6 @@ const ( Last Subcommand = "last" Sort Subcommand = "sort" Cat Subcommand = "cat" + Mv Subcommand = "mv" Help Subcommand = "help" ) diff --git a/mv/mv.go b/mv/mv.go new file mode 100644 index 0000000..de901b8 --- /dev/null +++ b/mv/mv.go @@ -0,0 +1,97 @@ +package mv + +import ( + "fmt" + "log" + "os" + "strings" + + "github.com/brtholomy/um/go/cat" + "github.com/brtholomy/um/go/cmd" + "github.com/brtholomy/um/go/flags" + "github.com/brtholomy/um/go/next" + "github.com/brtholomy/um/go/pipe" +) + +const ( + CMD = cmd.Mv + SUMMARY = "rename an um file descriptor field while updating its header" +) + +type options struct { + Filename flags.Arg + Descriptor flags.Arg + Help flags.Flag[bool] +} + +func initOpts() options { + return options{ + flags.Arg{"", "old filename"}, + flags.Arg{"", "new descriptor"}, + flags.Flag[bool]{"--help", "-h", false, "show help"}, + } +} + +// generates new filename and its content with updated H1 header +func newNameAndContent(olds string, opts options) (name string, content string, err error) { + // name and title + num, err := next.NumFromLast(opts.Filename.Val) + if err != nil { + return "", "", err + } + name = fmt.Sprintf("%s.%s.md", num, opts.Descriptor.Val) + title := fmt.Sprintf("# %s", name) + + // content + head, tail, ok := strings.Cut(olds, pipe.Newline) + // the file has no newline at all? + if !ok { + tail = head + } + // there was no title in the original + if !strings.HasPrefix(head, cat.H1) { + tail = olds + } + return name, fmt.Sprintf("%s%s%s", title, pipe.Newline, tail), nil +} + +// effectively does a mv to the new filename, while updating the H1 header to match. +func mv(opts options) error { + oldb, err := os.ReadFile(opts.Filename.Val) + if err != nil { + return err + } + olds := string(oldb) + + name, content, err := newNameAndContent(olds, opts) + if err != nil { + return err + } + + // create + newf, err := os.Create(name) + if err != nil { + return err + } + defer newf.Close() + _, err = newf.WriteString(content) + if err != nil { + return err + } + + // destroy + err = os.Remove(opts.Filename.Val) + if err != nil { + return err + } + return nil +} + +func Mv(args []string) { + opts := initOpts() + flags.ParseArgs(CMD, SUMMARY, args, &opts) + err := mv(opts) + if err != nil { + log.Fatalf("um %s: %v", CMD, err) + } +} diff --git a/next/next.go b/next/next.go index ccab0dc..f012758 100644 --- a/next/next.go +++ b/next/next.go @@ -38,7 +38,7 @@ func initOpts() options { // takes the complete last file string // returns the number as string -func numFromLast(l string) (string, error) { +func NumFromLast(l string) (string, error) { res := fileRegexp.FindStringSubmatch(l) num := "" if len(res) < 2 { @@ -51,7 +51,7 @@ func numFromLast(l string) (string, error) { // takes the complete last file string and new descriptor // returns the complete next file string func next(last string, desc string) (string, error) { - num, err := numFromLast(last) + num, err := NumFromLast(last) if err != nil { return "", err } diff --git a/um.go b/um.go index 3f055ae..c593c83 100644 --- a/um.go +++ b/um.go @@ -8,12 +8,13 @@ import ( "github.com/brtholomy/um/go/cat" "github.com/brtholomy/um/go/cmd" "github.com/brtholomy/um/go/last" + "github.com/brtholomy/um/go/mv" "github.com/brtholomy/um/go/next" "github.com/brtholomy/um/go/sort" "github.com/brtholomy/um/go/tag" ) -var helpShort string = fmt.Sprintf("um [%s | %s | %s | %s | %s | %s]", cmd.Next, cmd.Last, cmd.Tag, cmd.Cat, cmd.Sort, cmd.Help) +var helpShort string = fmt.Sprintf("um [%s | %s | %s | %s | %s | %s | %s]", cmd.Next, cmd.Last, cmd.Tag, cmd.Cat, cmd.Sort, cmd.Mv, cmd.Help) var helpLong string = fmt.Sprintf(`%s An (U)ltralight database for (M)arkdown composition. @@ -47,6 +48,8 @@ func main() { cat.Cat(args) case cmd.Sort: sort.Sort(args) + case cmd.Mv: + mv.Mv(args) case cmd.Help: log.Fatalln(helpLong) default: