diff options
| author | tjp <tjp@ctrl-c.club> | 2024-01-03 12:17:37 -0700 |
|---|---|---|
| committer | tjp <tjp@ctrl-c.club> | 2024-01-03 12:17:37 -0700 |
| commit | 859f74231f2b48d2dcf6a29682e7651b504fda12 (patch) | |
| tree | e03126c48c8385d98ba81525e7b628d5ca2257ca /tour.go | |
| parent | 6c9558c0d2201d933b1d396febeb6e70ceaad058 (diff) | |
tours
Diffstat (limited to 'tour.go')
| -rw-r--r-- | tour.go | 214 |
1 files changed, 214 insertions, 0 deletions
@@ -0,0 +1,214 @@ +package main + +import ( + "bytes" + "errors" + "fmt" + "net/url" + "slices" + "strconv" + "strings" +) + +var ( + ErrEndOfTour = errors.New("you've hit the end of the tour") + ErrStartOfTour = errors.New("you're at the start of the tour") + ErrInvalidTourPos = errors.New("that's not a valid tour link") +) + +type Tour struct { + Index int + Links []*url.URL +} + +func parseURLs(state *BrowserState, defaultScheme, str string) ([]*url.URL, error) { + urls := []*url.URL{} + + if str == "*" { + for _, link := range state.Links { + urls = append(urls, state.Url.ResolveReference(link.Target)) + } + return urls, nil + } + + if i := strings.IndexByte(str, '-'); i > 0 { + start, e1 := strconv.Atoi(str[:i]) + end, e2 := strconv.Atoi(str[i+1:]) + if e1 == nil && e2 == nil && end >= start && start >= 0 && start < len(state.Links) && end < len(state.Links) { + for _, link := range state.Links[start : end+1] { + urls = append(urls, state.Url.ResolveReference(link.Target)) + } + return urls, nil + } + } + + u, _, err := parseURL(str, state, defaultScheme) + if err != nil { + return nil, err + } + return []*url.URL{u}, nil +} + +func TourAdd(state *BrowserState, conf *Config, targets []string) error { + newurls := []*url.URL{} + for _, target := range targets { + urls, err := parseURLs(state, conf.DefaultScheme, target) + if err != nil { + return err + } + newurls = append(newurls, urls...) + } + + state.CurrentTour.Links = append(state.CurrentTour.Links, newurls...) + + if state.CurrentTour != &state.DefaultTour { + return saveTours(state.NamedTours) + } + return nil +} + +func TourAddNext(state *BrowserState, conf *Config, targets []string) error { + newurls := []*url.URL{} + for _, target := range targets { + urls, err := parseURLs(state, conf.DefaultScheme, target) + if err != nil { + return err + } + newurls = append(newurls, urls...) + } + + state.CurrentTour.Links = slices.Insert( + state.CurrentTour.Links, + state.CurrentTour.Index, + newurls..., + ) + + if state.CurrentTour != &state.DefaultTour { + return saveTours(state.NamedTours) + } + return nil +} + +func TourShow(state *BrowserState) error { + tour := state.CurrentTour + buf := &bytes.Buffer{} + for i, link := range tour.Links { + mark := "" + if i == tour.Index-1 { + mark = "* " + } + if _, err := fmt.Fprintf(buf, "%s%d %s\n", mark, i, link.String()); err != nil { + return err + } + } + + state.Modal = buf.Bytes() + if len(state.Modal) == 0 { + state.Modal = []byte("(empty)\n") + } + return Print(state) +} + +func TourNext(state *BrowserState, conf *Config) error { + tour := state.CurrentTour + if tour.Index >= len(tour.Links) || len(tour.Links) == 0 { + return ErrEndOfTour + } + page := tour.Links[tour.Index] + tour.Index += 1 + + return Navigate(state, page, -1, conf) +} + +func TourPrevious(state *BrowserState, conf *Config) error { + tour := state.CurrentTour + if tour.Index <= 0 { + return ErrStartOfTour + } + tour.Index -= 1 + if tour.Index <= 0 { + return ErrStartOfTour + } + page := tour.Links[tour.Index-1] + + return Navigate(state, page, -1, conf) +} + +func TourClear(state *BrowserState) error { + state.CurrentTour.Index = 0 + state.CurrentTour.Links = nil + + if state.CurrentTour != &state.DefaultTour { + return saveTours(state.NamedTours) + } + return nil +} + +func TourList(state *BrowserState) error { + buf := &bytes.Buffer{} + mark := "" + if state.CurrentTour == &state.DefaultTour { + mark = "* " + } + if _, err := fmt.Fprintf(buf, "%s(default): %d links\n", mark, len(state.DefaultTour.Links)); err != nil { + return err + } + for name, tour := range state.NamedTours { + mark = "" + if tour == state.CurrentTour { + mark = "* " + } + if _, err := fmt.Fprintf(buf, "%s%s: %d links\n", mark, name, len(tour.Links)); err != nil { + return err + } + } + + state.Modal = buf.Bytes() + return Print(state) +} + +func TourGo(state *BrowserState, conf *Config, pos string) error { + tour := state.CurrentTour + + i, _ := strconv.Atoi(pos) + if i < 0 || i >= len(tour.Links) { + return ErrInvalidTourPos + } + + tour.Index = i + 1 + return Navigate(state, tour.Links[i], -1, conf) +} + +func TourSet(state *BrowserState, name string) error { + tour, err := findTour(state, name) + if err == nil { + state.CurrentTour = tour + } + return err +} + +func findTour(state *BrowserState, prefix string) (*Tour, error) { + if prefix == "" { + return &state.DefaultTour, nil + } + + found := 0 + var value *Tour + for name, tour := range state.NamedTours { + if strings.HasPrefix(name, prefix) { + found += 1 + value = tour + } + } + + switch found { + case 0: + tour := &Tour{} + state.NamedTours[prefix] = tour + return tour, nil + case 1: + return value, nil + default: + return nil, fmt.Errorf("too ambiguous - found %d matching tours", found) + } +} |
