Skip to content

Commit

Permalink
Merge pull request #53 from wudong/master
Browse files Browse the repository at this point in the history
Add history function for the past page view.
  • Loading branch information
mstruebing committed Oct 5, 2020
2 parents 2039581 + 6486b18 commit a91ffbe
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 5 deletions.
113 changes: 112 additions & 1 deletion cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cache

import (
"archive/zip"
"bufio"
"fmt"
"io"
"io/ioutil"
Expand All @@ -12,13 +13,15 @@ import (
"os/user"
"path"
"path/filepath"
"strconv"
"strings"
"time"
)

const (
indexJSON = "index.json"
pagesDirectory = "pages"
historyPath = "/history"
pageSuffix = ".md"
zipPath = "/tldr.zip"
)
Expand All @@ -32,6 +35,16 @@ type Repository struct {
ttl time.Duration
}

// HistoryRecord represent the search history of certain page
type HistoryRecord struct {
page string
count int
}

func (h HistoryRecord) String() string {
return fmt.Sprintf("%s %d", h.page, h.count)
}

// NewRepository returns a new cache repository. The data is loaded from the
// remote if missing or stale.
func NewRepository(remote string, ttl time.Duration) (*Repository, error) {
Expand Down Expand Up @@ -204,7 +217,21 @@ func (r *Repository) loadFromRemote() error {
}

func (r *Repository) makeCacheDir() error {
return os.MkdirAll(r.directory, 0755)
if err := os.MkdirAll(r.directory, 0755); err != nil {
return fmt.Errorf("ERROR: creating directory %s: %s", r.directory, err)
}
// touching the history file.
historyFile := path.Join(r.directory, historyPath)
return touchFile(historyFile)
}

func touchFile(fileName string) error {
file, err := os.Create(fileName)
if err != nil {
return fmt.Errorf("ERROR: creating file %s: %s", fileName, err)
}
defer file.Close()
return nil
}

func (r *Repository) unzip() error {
Expand Down Expand Up @@ -266,3 +293,87 @@ func (r Repository) isReachable() bool {
_, err = net.DialTimeout("tcp", u.Hostname()+":"+port, timeout)
return err == nil
}

func (r Repository) RecordHistory(page string) error {
records, err := r.LoadHistory()
if err != nil {
return fmt.Errorf("ERROR: loading history failed %s", err)
}

newRecord := HistoryRecord{
page: page,
count: 1,
}

foundIdx := -1
for idx, r := range *records {
if r.page == page {
newRecord.count = r.count + 1
foundIdx = idx
break
}
}

if foundIdx != -1 { //found in history, we want to put the last search at the end of the history.
newRecords := append((*records)[:foundIdx], (*records)[foundIdx+1:]...)
records = &newRecords
}

newRecords := append(*records, newRecord)
return r.saveHistory(&newRecords)
}

func (r Repository) saveHistory(history *[]HistoryRecord) error {
hisFile := path.Join(r.directory, historyPath)
inFile, err := os.Create(hisFile)
if err != nil {
return fmt.Errorf("ERROR: opening history file %s", hisFile)
}
defer inFile.Close()

for _, his := range *history {
fmt.Fprintln(inFile, fmt.Sprintf("%s,%d", his.page, his.count))
}
return nil
}

func (r Repository) LoadHistory() (*[]HistoryRecord, error) {
// read the history file line by line, into a map.
history := path.Join(r.directory, historyPath)
//if it is not exist, touch it.
_, err := os.Stat(history)

if os.IsNotExist(err) {
if err := touchFile(history); err != nil {
return nil, fmt.Errorf("ERROR: cannot create the history file %s, %s", history, err)
}
}

inFile, err := os.Open(history)
if err != nil {
return nil, fmt.Errorf("ERROR: opening history file %s", history)

}

defer inFile.Close()

scanner := bufio.NewScanner(inFile)
historyRecords := make([]HistoryRecord, 0, 10)

for scanner.Scan() {
line := scanner.Text()
lineParts := strings.Split(line, ",")
count, err := strconv.Atoi(lineParts[1])

if err != nil {
return nil, err
}

historyRecords = append(historyRecords, HistoryRecord{
page: lineParts[0],
count: count,
})
}

return &historyRecords, nil
}
68 changes: 64 additions & 4 deletions cache/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,13 @@ func TestMarkdown(t *testing.T) {
_, err := r.Markdown("linux", "hostname")

if err != nil {
t.Error("Exptected to successfully pull a page from the cache")
t.Error("Expected to successfully pull a page from the cache")
}

_, err = r.Markdown("linux", "hostnamee")

if err == nil {
t.Error("Exptected to return an error for non existing pages")
t.Error("Expected to return an error for non existing pages")
}
}

Expand All @@ -127,10 +127,70 @@ func TestPages(t *testing.T) {
pages, err := r.Pages()

if err != nil {
t.Error("Exptected to successfully retrieve all pages.")
t.Error("Expected to successfully retrieve all pages.")
}

if len(pages) == 0 {
t.Error("Exptected to find some pages")
t.Error("Expected to find some pages")
}
}

func TestHistory(t *testing.T) {

repo := Repository{
directory: "/tmp/.cache/tldr",
remote: "https://tldr.sh/assets/tldr.zip",
ttl: time.Hour * 24 * 7,
}

repo.makeCacheDir()

if err2 := repo.RecordHistory("git-pull"); err2 != nil {
t.Error("Expected to record history successful.")
}

repo.RecordHistory("git-pull")
repo.RecordHistory("git-pull")
repo.RecordHistory("git-push")
repo.RecordHistory("git-push")
repo.RecordHistory("git-fetch")
repo.RecordHistory("git-pull")

history, err := repo.LoadHistory()
if err != nil {
t.Error("Expected to load history successful.")
}

length := len(*history)
if length != 3 {
t.Error("Expected to have 3 history records.")
}

rec1 := HistoryRecord{
page: "git-push",
count: 2,
}

if (*history)[0] != rec1 {
t.Errorf("Expected first record to be %+v", rec1)
}

rec2 := HistoryRecord{
page: "git-fetch",
count: 1,
}

if (*history)[1] != rec2 {
t.Errorf("Expected second record to be %+v", rec2)
}

rec3 := HistoryRecord{
page: "git-pull",
count: 4,
}

if (*history)[2] != rec3 {
t.Errorf("Expected third record to be %+v", rec3)
}

}
35 changes: 35 additions & 0 deletions cmd/tldr/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"flag"
"fmt"
"log"
"math"
"math/rand"
"os"
"runtime"
Expand All @@ -22,6 +23,7 @@ const (
updateUsage = "update local database"
versionUsage = "print version and exit"
randomUsage = "prints a random page"
historyUsage = "show the latest search history"
)

const (
Expand Down Expand Up @@ -106,6 +108,11 @@ func printPage(page string) {
if err != nil {
log.Fatalf("ERROR: writing markdown: %s", err)
}

err2 := repository.RecordHistory(page)
if err2 != nil {
log.Fatalf("ERROR: saving history: %s", err2)
}
}

func printPageForPlatform(page string, platform string) {
Expand Down Expand Up @@ -156,6 +163,29 @@ func updatePages() {
}
}

func printHistory() {
repository, err := cache.NewRepository(remoteURL, ttl)
if err != nil {
log.Fatalf("ERROR: creating cache repository: %s", err)
}

history, err := repository.LoadHistory()
if err != nil {
log.Fatalf("ERROR: error loading history: %s", err)
}

hisLen := len(*history)
if hisLen == 0 {
fmt.Println("No history is available yet")
} else { //default print last 10.
size := int(math.Min(10, float64(hisLen)))
for i := 1; i <= size; i++ {
record := (*history)[hisLen-i]
fmt.Printf("%s\n", record)
}
}
}

func main() {
version := flag.Bool("version", false, versionUsage)
flag.BoolVar(version, "v", false, versionUsage)
Expand All @@ -176,6 +206,9 @@ func main() {
random := flag.Bool("random", false, randomUsage)
flag.BoolVar(random, "r", false, randomUsage)

history := flag.Bool("history", false, historyUsage)
flag.BoolVar(history, "t", false, historyUsage)

flag.Parse()

if *version {
Expand All @@ -191,6 +224,8 @@ func main() {
printPageForPlatform(page, *platform)
} else if *random {
printRandomPage()
} else if *history {
printHistory()
} else {
page := flag.Arg(0)
printPage(page)
Expand Down

0 comments on commit a91ffbe

Please sign in to comment.