Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1.3.0
v1.3.1
4 changes: 2 additions & 2 deletions cmd/book/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ func Main() {
Before: func(ctx context.Context, c *cli.Command) (context.Context, error) {
if c.Args().First() != "" && markURL != "" {
return ctx, cli.Exit(config.StyledError(
fmt.Errorf("cannot pass URL as both --url and a positional argument")), 1)
fmt.Errorf("cannot pass url as both --url and a positional argument")), 1)
}

// use positional argument to populate markURL if flag not used
Expand All @@ -397,7 +397,7 @@ func Main() {
}
if markURL == "" {
return ctx, cli.Exit(config.StyledError(
fmt.Errorf("must pass URL as --url or argument")), 1)
fmt.Errorf("must pass url as --url or argument")), 1)
}
return ctx, nil
},
Expand Down
17 changes: 5 additions & 12 deletions cmd/book/mark.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,7 @@ func marks(bs *book.BookShelves, shelfName string, collectionName string, format
}
switch format {
case "toml":
wrapped := struct {
Marks []catalog.SearchResult `toml:"marks"`
}{deleted}
return book.PrintCatalog(wrapped, format)
return book.PrintCatalog(catalog.SearchResultList{Marks: deleted}, format)
case "json":
return book.PrintCatalog(deleted, format)
default:
Expand Down Expand Up @@ -111,7 +108,7 @@ func editMark(bs *book.BookShelves, id, title, tags, url string, config *book.Co
return err
}
if err := bs.VerifyUniqueURL(book.GenerateID(url), target); err != nil {
return err
return fmt.Errorf("verify unique url: %w", err)
}
}

Expand Down Expand Up @@ -142,11 +139,7 @@ func searchMarks(query string, tags string, shelfName string, collectionName str
case "json":
return book.PrintCatalog(results, format)
case "toml":
// TOML requires a top-level map or struct, so wrap the slice.
wrapped := struct {
Marks []catalog.SearchResult `toml:"marks"`
}{results}
return book.PrintCatalog(wrapped, format)
return book.PrintCatalog(catalog.SearchResultList{Marks: results}, format)
default:
for _, r := range results {
fmt.Printf("%s %s\n", r.Title, r.URL)
Expand All @@ -163,7 +156,7 @@ func addMark(bs *book.BookShelves, URL string, tags string, shelfName string, co

// Ensure URL hash not in bookshelves
if err := bs.VerifyUniqueURL(mark.ID, nil); err != nil {
return err
return fmt.Errorf("verify unique url: %w", err)
}

// Use provided title or fetch from URL
Expand All @@ -172,7 +165,7 @@ func addMark(bs *book.BookShelves, URL string, tags string, shelfName string, co
fetchedTitle, err := web.LoadWebsite(mark.URL)
if err != nil {
if !errors.Is(err, web.ErrTitleUnavailable) {
return err
return fmt.Errorf("load website: %w", err)
}
fetched.Unavailable = true
} else {
Expand Down
30 changes: 0 additions & 30 deletions internal/book/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,33 +96,3 @@ var DefaultViewTemplates = map[string]ViewTemplate{
"mark-get": markTemplate("To open mark:"),
"mark-delete": markTemplate("To delete mark:"),
}

// UserViewTemplates holds user-defined template overrides loaded at runtime.
var UserViewTemplates = map[string]ViewTemplate{}

// GetTemplate merges user overrides over the default for a given key.
func GetTemplate(key string) ViewTemplate {
def, hasDef := DefaultViewTemplates[key]
user, hasUser := UserViewTemplates[key]

switch {
case !hasDef && !hasUser:
return ViewTemplate{}
case !hasUser:
return def
case !hasDef:
return user
}

// Overlay: user field non-empty → override default.
if user.PrimaryTitle != "" {
def.PrimaryTitle = user.PrimaryTitle
}
if user.SecondaryTitle != "" {
def.SecondaryTitle = user.SecondaryTitle
}
if user.ListTitle != "" {
def.ListTitle = user.ListTitle
}
return def
}
8 changes: 4 additions & 4 deletions internal/book/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,10 @@ func (bs *BookShelves) VerifyUniqueURL(id string, exclude *Mark) error {
continue
}
if m.IsDeleted() {
return fmt.Errorf("URL already trashed!\n\n%s\n\nrestore it with:\nbook mark restore --shelf %s --collection %s --url %s",
return fmt.Errorf("url already trashed\n\n%s\n\nrestore it with:\nbook mark restore --shelf %s --collection %s --url %s",
m.FullDetail(), m.Shelf.Name, m.Collection.Name, m.URL)
}
return fmt.Errorf("duplicate URL Found!\n\n%s", m.FullDetail())
return fmt.Errorf("duplicate url found\n\n%s", m.FullDetail())
}
}
}
Expand Down Expand Up @@ -582,10 +582,10 @@ func ResolveMarkTitle(providedTitle, url string, fetched TitleFetchResult, inter
// ValidateNewShelfName returns an error if name is empty or already in use.
func (bs *BookShelves) ValidateNewShelfName(name string) error {
if name == "" {
return fmt.Errorf("HARD requirement")
return fmt.Errorf("shelf name is required")
}
if slices.Contains(bs.ShelfNames(), name) {
return fmt.Errorf("womp womp, shelf already exists")
return fmt.Errorf("shelf already exists")
}
return nil
}
Expand Down
6 changes: 6 additions & 0 deletions internal/catalog/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,12 @@ type SearchResult struct {
Tags []string `json:"tags" toml:"tags"`
}

// SearchResultList wraps a slice of search results so it can be serialized as
// TOML, which requires a top-level map or struct.
type SearchResultList struct {
Marks []SearchResult `toml:"marks" json:"marks"`
}

// Search runs an FTS5 query over mark titles and URLs, excluding soft-deleted
// marks. The query string uses FTS5 match syntax and may be empty to search by
// filters alone. tagClauses filters results to marks matching every clause
Expand Down
Loading