From edfbee2da241372271d2a32cff8ab7409e910727 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Thu, 31 Oct 2024 22:50:00 +0200 Subject: restructure --- internal/entry/entry.go | 20 +++++++- internal/entry/sharetags.go | 45 +++++++++++++++++ internal/entry/sharetags_test.go | 102 +++++++++++++++++++++++++++++++++++++++ internal/queue/queue.go | 37 ++++++-------- internal/queue/sharetags.go | 54 --------------------- internal/queue/sharetags_test.go | 102 --------------------------------------- 6 files changed, 182 insertions(+), 178 deletions(-) create mode 100644 internal/entry/sharetags.go create mode 100644 internal/entry/sharetags_test.go delete mode 100644 internal/queue/sharetags.go delete mode 100644 internal/queue/sharetags_test.go diff --git a/internal/entry/entry.go b/internal/entry/entry.go index be3bb80..d757632 100644 --- a/internal/entry/entry.go +++ b/internal/entry/entry.go @@ -9,6 +9,7 @@ import ( "strings" "time" + "codeberg.org/snonux/gos/internal/config" "codeberg.org/snonux/gos/internal/oi" "codeberg.org/snonux/gos/internal/prompt" "codeberg.org/snonux/gos/internal/timestamp" @@ -18,7 +19,7 @@ type State int const ( Unknown State = iota - Inboxed // TODO: Implement + Inboxed Queued Posted ) @@ -152,6 +153,15 @@ func (e Entry) HasTag(tag string) bool { return slices.Contains(e.simpleTags, tag) } +// Valid tags are: share:foo[,...] +// whereas foo can be a supported plutform such as linkedin, mastodon, etc. +// foo can also be prefixed with - to exclude it. See unit tests for examples. +func (e Entry) ExcludedByTags(args config.Args, platform string) (bool, error) { + s, err := newShareTags(args, e.Path) + return slices.Contains(s.excludes, strings.ToLower(platform)) || + !slices.Contains(s.includes, strings.ToLower(platform)), err +} + func (e Entry) Edit() error { if err := prompt.EditFile(e.Path); err != nil { return err @@ -163,6 +173,14 @@ func (e Entry) Remove() error { return os.Remove(e.Path) } +func (e Entry) FileAction(question string) error { + content, _, err := e.Content() + if err != nil { + return err + } + return prompt.FileAction(question, content, e.Path) +} + func extractURLs(input string) []string { urlPattern := `(http://|https://|ftp://)[^\s]+` re := regexp.MustCompile(urlPattern) diff --git a/internal/entry/sharetags.go b/internal/entry/sharetags.go new file mode 100644 index 0000000..4a7f0cf --- /dev/null +++ b/internal/entry/sharetags.go @@ -0,0 +1,45 @@ +package entry + +import ( + "fmt" + "slices" + "strings" + + "codeberg.org/snonux/gos/internal/config" +) + +type shareTags struct { + includes []string // The platforms to include + excludes []string // The platforms to exclude +} + +func newShareTags(args config.Args, filePath string) (shareTags, error) { + var s shareTags + + parts := strings.Split(filePath, ".") + if len(parts) < 4 { + return s, fmt.Errorf("invalid file path: %s", filePath) + } + tagStr := parts[len(parts)-4] + + if len(parts) > 2 && strings.HasPrefix(tagStr, "share:") { + for _, tag := range strings.Split(tagStr[6:], ":") { + if strings.HasPrefix(tag, "-") { + s.excludes = append(s.excludes, strings.ToLower(tag[1:])) + } else { + s.includes = append(s.includes, strings.ToLower(tag)) + } + } + } + + if len(s.includes) == 0 { + for platform := range args.Platforms { + if slices.Contains(s.excludes, strings.ToLower(platform)) { + continue + } + s.includes = append(s.includes, strings.ToLower(platform)) + } + } + + return s, nil +} diff --git a/internal/entry/sharetags_test.go b/internal/entry/sharetags_test.go new file mode 100644 index 0000000..ba97842 --- /dev/null +++ b/internal/entry/sharetags_test.go @@ -0,0 +1,102 @@ +package entry + +import ( + "slices" + "testing" + + "codeberg.org/snonux/gos/internal/config" +) + +func TestShareTagsPositive(t *testing.T) { + args := config.Args{Platforms: map[string]int{"mastodon": 100, "linkedin": 100}} + testTable := map[string]shareTags{ + "./foo/bar.without.tags.txt.20240101-010101.queued": { + includes: []string{"mastodon", "linkedin"}, + }, + "./foo/bar.share:linkeDin.txt.20240101-010101.queued": { + includes: []string{"linkedin"}, + }, + "./foo/bar.share:-LinkedIn.txt.20240101-010101.queued": { + includes: []string{"mastodon"}, + excludes: []string{"linkedin"}, + }, + "./foo/bar.share:linkedin:mastOdon.txt.20240101-010101.queued": { + includes: []string{"linkedin", "mastodon"}, + }, + "./foo/bar.share:linkediN:-mastodon:XCOM.txt.20240101-010101.queued": { + includes: []string{"linkedin", "xcom"}, + excludes: []string{"mastodon"}, + }, + "./foo/bar/ql-e7657e8a1ab573f84ad0dbc55199e937.share:-mastodon.txt.20241018-105524.queued": { + includes: []string{"linkedin"}, + excludes: []string{"mastodon"}, + }, + } + + for filePath, expectedResult := range testTable { + t.Run(filePath, func(t *testing.T) { + shareTags, err := newShareTags(args, filePath) + if err != nil { + t.Error(err) + } + if !sameElements(shareTags.includes, expectedResult.includes) { + t.Errorf("Expected includes to be %v but got %v with %s", + expectedResult.includes, shareTags.includes, filePath) + } + if !sameElements(shareTags.excludes, expectedResult.excludes) { + t.Errorf("Expected excludes to be %v but got %v with %s", + expectedResult.excludes, shareTags.excludes, filePath) + } + }) + + } +} +func TestShareTagsNegative(t *testing.T) { + args := config.Args{Platforms: map[string]int{"mastodon": 100, "linkedin": 100}} + testTable := map[string]shareTags{ + "./foo/bar.without.tags.txt.20240101-010101.queued": { + includes: []string{"linkedin"}, + }, + "./foo/bar.share:linkedIn.txt.20240101-010101.queued": { + includes: []string{"mastodon"}, + }, + "./foo/bar.share:-liNkedin.txt.20240101-010101.queued": { + includes: []string{"linkedin"}, + }, + "./foo/bar.share:linkedin:mastodon.txt.20240101-010101.queued": { + includes: []string{"oups", "mastodon"}, + }, + "./foo/bar.share:linkedin:-MASTODON:xcom.txt.20240101-010101.queued": { + includes: []string{"linkedin", "xcom"}, + excludes: []string{"mastodon", "xcom"}, + }, + } + + for filePath, unexpectedResult := range testTable { + t.Run(filePath, func(t *testing.T) { + shareTags, err := newShareTags(args, filePath) + if err != nil { + t.Error(err) + } + if sameElements(shareTags.includes, unexpectedResult.includes) && + sameElements(shareTags.excludes, unexpectedResult.excludes) { + t.Errorf("expected %v not to be the actual result with %s", + unexpectedResult, filePath) + } + }) + + } +} + +// Can't use slices.Equal as order of elements may be different. +func sameElements(a, b []string) bool { + if len(a) != len(b) { + return false + } + for _, elem := range a { + if !slices.Contains(b, elem) { + return false + } + } + return true +} diff --git a/internal/queue/queue.go b/internal/queue/queue.go index 74ba00f..6df59df 100644 --- a/internal/queue/queue.go +++ b/internal/queue/queue.go @@ -12,7 +12,6 @@ import ( "codeberg.org/snonux/gos/internal/config" "codeberg.org/snonux/gos/internal/entry" "codeberg.org/snonux/gos/internal/oi" - "codeberg.org/snonux/gos/internal/prompt" "codeberg.org/snonux/gos/internal/timestamp" ) @@ -48,20 +47,16 @@ func queueEntries(args config.Args) error { return err } if ent.HasTag("ask") { - content, _, err := ent.Content() - if err != nil { - return err - } - if err := prompt.FileAction("Do you want to queue this content", content, ent.Path); err != nil { + if err := ent.FileAction("Do you want to queue this content"); err != nil { return err } } - destPath := fmt.Sprintf("%s/db/%s.%s.queued", args.GosDir, filepath.Base(filePath), timestamp.Now()) + destPath := fmt.Sprintf("%s/db/%s.%s.queued", args.GosDir, filepath.Base(ent.Path), timestamp.Now()) if args.DryRun { - log.Println("Not queueing entry", filePath, "to", destPath, "as dry-run mode enabled") + log.Println("Not queueing entry", ent.Path, "to", destPath, "as dry-run mode enabled") continue } - if err := oi.Rename(filePath, destPath); err != nil { + if err := oi.Rename(ent.Path, destPath); err != nil { return err } } @@ -80,21 +75,20 @@ func queuePlatforms(args config.Args) error { trashDir := filepath.Join(args.GosDir, "db", "trashbin") for filePath := range ch { - _, err := entry.New(filePath) + ent, err := entry.New(filePath) if err != nil { return err } for platform := range args.Platforms { - // TODO: Move excludedByTags to entry.Entry.IsSared(args, platform) - excluded, err := excludedByTags(args, filePath, platform) + excluded, err := ent.ExcludedByTags(args, platform) if err != nil { return err } if excluded { - log.Println("Not queueing entry", filePath, "to platform", platform, "as it is excluded") + log.Println("Not queueing entry", ent, "to platform", platform, "as it is excluded") continue } - if err := queuePlatform(filePath, args.GosDir, platform); err != nil { + if err := queuePlatform(ent, args.GosDir, platform); err != nil { return err } } @@ -103,12 +97,12 @@ func queuePlatforms(args config.Args) error { } // Keep queued items in trash for a while. - trashPath := filepath.Join(trashDir, strings.TrimSuffix(filepath.Base(filePath), ".queued")+".trash") - log.Printf("Trashing %s -> %s", filePath, trashPath) + trashPath := filepath.Join(trashDir, strings.TrimSuffix(filepath.Base(ent.Path), ".queued")+".trash") + log.Printf("Trashing %s -> %s", ent.Path, trashPath) if err := oi.EnsureParentDir(trashPath); err != nil { return err } - if err := os.Rename(filePath, trashPath); err != nil { + if err := os.Rename(ent.Path, trashPath); err != nil { return err } } @@ -118,9 +112,10 @@ func queuePlatforms(args config.Args) error { } // Queue ./db/queued/*.txt.STAMP.queued to ./db/platforms/PLATFORM/*.txt.STAMP.queued -func queuePlatform(entryPath, gosDir, platform string) error { +// TODO: Rename all ent to en +func queuePlatform(ent entry.Entry, gosDir, platform string) error { destDir := filepath.Join(gosDir, "db/platforms", strings.ToLower(platform)) - destPath := filepath.Join(destDir, filepath.Base(entryPath)) + destPath := filepath.Join(destDir, filepath.Base(ent.Path)) postedFile := fmt.Sprintf("%s.posted", strings.TrimSuffix(destPath, ".queued")) // Entry already posted platform? @@ -129,8 +124,8 @@ func queuePlatform(entryPath, gosDir, platform string) error { return nil } - log.Println("Queuing", entryPath, "->", destPath) - return oi.CopyFile(entryPath, destPath) + log.Println("Queuing", ent.Path, "->", destPath) + return oi.CopyFile(ent.Path, destPath) } func deleteFiles(path, suffix string, olderThan time.Time) error { diff --git a/internal/queue/sharetags.go b/internal/queue/sharetags.go deleted file mode 100644 index 044d6bd..0000000 --- a/internal/queue/sharetags.go +++ /dev/null @@ -1,54 +0,0 @@ -package queue - -import ( - "fmt" - "slices" - "strings" - - "codeberg.org/snonux/gos/internal/config" -) - -type shareTags struct { - includes []string // The platforms to include - excludes []string // The platforms to exclude -} - -func newShareTags(args config.Args, filePath string) (shareTags, error) { - var s shareTags - - parts := strings.Split(filePath, ".") - if len(parts) < 4 { - return s, fmt.Errorf("invalid file path: %s", filePath) - } - tagStr := parts[len(parts)-4] - - if len(parts) > 2 && strings.HasPrefix(tagStr, "share:") { - for _, tag := range strings.Split(tagStr[6:], ":") { - if strings.HasPrefix(tag, "-") { - s.excludes = append(s.excludes, strings.ToLower(tag[1:])) - } else { - s.includes = append(s.includes, strings.ToLower(tag)) - } - } - } - - if len(s.includes) == 0 { - for platform := range args.Platforms { - if slices.Contains(s.excludes, strings.ToLower(platform)) { - continue - } - s.includes = append(s.includes, strings.ToLower(platform)) - } - } - - return s, nil -} - -// Valid tags are: share:foo[,...] -// whereas foo can be a supported plutform such as linkedin, mastodon, etc. -// foo can also be prefixed with - to exclude it. See unit tests for examples. -func excludedByTags(args config.Args, filePath, platform string) (bool, error) { - s, err := newShareTags(args, filePath) - return slices.Contains(s.excludes, strings.ToLower(platform)) || - !slices.Contains(s.includes, strings.ToLower(platform)), err -} diff --git a/internal/queue/sharetags_test.go b/internal/queue/sharetags_test.go deleted file mode 100644 index d13c79e..0000000 --- a/internal/queue/sharetags_test.go +++ /dev/null @@ -1,102 +0,0 @@ -package queue - -import ( - "slices" - "testing" - - "codeberg.org/snonux/gos/internal/config" -) - -func TestShareTagsPositive(t *testing.T) { - args := config.Args{Platforms: map[string]int{"mastodon": 100, "linkedin": 100}} - testTable := map[string]shareTags{ - "./foo/bar.without.tags.txt.20240101-010101.queued": { - includes: []string{"mastodon", "linkedin"}, - }, - "./foo/bar.share:linkeDin.txt.20240101-010101.queued": { - includes: []string{"linkedin"}, - }, - "./foo/bar.share:-LinkedIn.txt.20240101-010101.queued": { - includes: []string{"mastodon"}, - excludes: []string{"linkedin"}, - }, - "./foo/bar.share:linkedin:mastOdon.txt.20240101-010101.queued": { - includes: []string{"linkedin", "mastodon"}, - }, - "./foo/bar.share:linkediN:-mastodon:XCOM.txt.20240101-010101.queued": { - includes: []string{"linkedin", "xcom"}, - excludes: []string{"mastodon"}, - }, - "./foo/bar/ql-e7657e8a1ab573f84ad0dbc55199e937.share:-mastodon.txt.20241018-105524.queued": { - includes: []string{"linkedin"}, - excludes: []string{"mastodon"}, - }, - } - - for filePath, expectedResult := range testTable { - t.Run(filePath, func(t *testing.T) { - shareTags, err := newShareTags(args, filePath) - if err != nil { - t.Error(err) - } - if !sameElements(shareTags.includes, expectedResult.includes) { - t.Errorf("Expected includes to be %v but got %v with %s", - expectedResult.includes, shareTags.includes, filePath) - } - if !sameElements(shareTags.excludes, expectedResult.excludes) { - t.Errorf("Expected excludes to be %v but got %v with %s", - expectedResult.excludes, shareTags.excludes, filePath) - } - }) - - } -} -func TestShareTagsNegative(t *testing.T) { - args := config.Args{Platforms: map[string]int{"mastodon": 100, "linkedin": 100}} - testTable := map[string]shareTags{ - "./foo/bar.without.tags.txt.20240101-010101.queued": { - includes: []string{"linkedin"}, - }, - "./foo/bar.share:linkedIn.txt.20240101-010101.queued": { - includes: []string{"mastodon"}, - }, - "./foo/bar.share:-liNkedin.txt.20240101-010101.queued": { - includes: []string{"linkedin"}, - }, - "./foo/bar.share:linkedin:mastodon.txt.20240101-010101.queued": { - includes: []string{"oups", "mastodon"}, - }, - "./foo/bar.share:linkedin:-MASTODON:xcom.txt.20240101-010101.queued": { - includes: []string{"linkedin", "xcom"}, - excludes: []string{"mastodon", "xcom"}, - }, - } - - for filePath, unexpectedResult := range testTable { - t.Run(filePath, func(t *testing.T) { - shareTags, err := newShareTags(args, filePath) - if err != nil { - t.Error(err) - } - if sameElements(shareTags.includes, unexpectedResult.includes) && - sameElements(shareTags.excludes, unexpectedResult.excludes) { - t.Errorf("expected %v not to be the actual result with %s", - unexpectedResult, filePath) - } - }) - - } -} - -// Can't use slices.Equal as order of elements may be different. -func sameElements(a, b []string) bool { - if len(a) != len(b) { - return false - } - for _, elem := range a { - if !slices.Contains(b, elem) { - return false - } - } - return true -} -- cgit v1.2.3