From 3011e57f29c4a1c0b13a8d59876c13cb0796881d Mon Sep 17 00:00:00 2001 From: Wudong Liu Date: Wed, 29 Jul 2020 15:43:13 +0100 Subject: [PATCH 1/5] add history file --- cache/cache.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/cache/cache.go b/cache/cache.go index 959f6e36..c5107750 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -19,6 +19,7 @@ import ( const ( indexJSON = "index.json" pagesDirectory = "pages" + historyPath = "/history" pageSuffix = ".md" zipPath = "/tldr.zip" ) @@ -200,7 +201,20 @@ 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 { + fmt.Errorf("ERROR: creating directory %s: %s", r.directory, err) + return err + } + // touching the history file. + historyFile := r.directory + historyPath + file, err := os.Create(historyPath) + if err != nil { + fmt.Errorf("ERROR: creating file %s: %s", historyFile, err) + return err + } else { + file.Close() + return nil + } } func (r *Repository) unzip() error { From 0f7199da7c9162ef041f81ed71af19fbcc8645bd Mon Sep 17 00:00:00 2001 From: Wudong Liu Date: Thu, 30 Jul 2020 20:14:42 +0100 Subject: [PATCH 2/5] add support for saving search history --- cache/cache.go | 119 ++++++++++++++++++++++++++++++++++++++++++++--- cmd/tldr/main.go | 3 ++ 2 files changed, 116 insertions(+), 6 deletions(-) diff --git a/cache/cache.go b/cache/cache.go index 4112280d..ea19204c 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -12,8 +12,10 @@ import ( "os/user" "path" "path/filepath" + "strconv" "strings" "time" + "bufio" ) const ( @@ -33,6 +35,12 @@ type Repository struct { ttl time.Duration } +// HistoryRecord represent the search history of certain page +type HistoryRecord struct { + page string + count int +} + // 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) { @@ -210,15 +218,18 @@ func (r *Repository) makeCacheDir() error { return err } // touching the history file. - historyFile := r.directory + historyPath - file, err := os.Create(historyPath) + historyFile := path.Join(r.directory, historyPath) + return touchFile(historyFile) +} + +func touchFile(fileName string) error { + file, err := os.Create(fileName) + defer file.Close() if err != nil { - fmt.Errorf("ERROR: creating file %s: %s", historyFile, err) + fmt.Errorf("ERROR: creating file %s: %s", fileName, err) return err - } else { - file.Close() - return nil } + return nil } func (r *Repository) unzip() error { @@ -273,3 +284,99 @@ func (r Repository) isReachable() bool { _, err = net.DialTimeout("tcp", u.Hostname()+":"+u.Port(), timeout) return err == nil } + + + +func (r Repository) RecordHistory(page string) error { + records, err := r.loadHistory() + if err!=nil { + fmt.Errorf("ERROR: loading history failed %s", err) + return 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 { + fmt.Errorf("ERROR: opening history file %s", hisFile) + return err + } + 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 { + fmt.Errorf("ERROR: cannot create the history file %s, %s", history, err) + return nil, err + } + } + + inFile, err := os.Open(history) + if err != nil { + fmt.Errorf("ERROR: opening history file %s", history) + return nil, err + } + + 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 +} + +func (r Repository) cleanHistory() { + history := path.Join(r.directory, historyPath) + touchFile(history) +} diff --git a/cmd/tldr/main.go b/cmd/tldr/main.go index 2f99d930..a618eb14 100644 --- a/cmd/tldr/main.go +++ b/cmd/tldr/main.go @@ -105,7 +105,10 @@ func printPage(page string) { err = tldr.Write(markdown, os.Stdout) if err != nil { log.Fatalf("ERROR: writing markdown: %s", err) + }else { + repository.RecordHistory(page) } + } func printPageForPlatform(page string, platform string) { From 75b585b230630f1b8b93d53eeef4ee0c618438e7 Mon Sep 17 00:00:00 2001 From: Wudong Liu Date: Thu, 30 Jul 2020 23:26:14 +0100 Subject: [PATCH 3/5] Add test for history --- cache/cache.go | 34 ++++++++++------------- cache/cache_test.go | 68 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 79 insertions(+), 23 deletions(-) diff --git a/cache/cache.go b/cache/cache.go index ea19204c..23f0c3c5 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -2,6 +2,7 @@ package cache import ( "archive/zip" + "bufio" "fmt" "io" "io/ioutil" @@ -15,13 +16,12 @@ import ( "strconv" "strings" "time" - "bufio" ) const ( indexJSON = "index.json" pagesDirectory = "pages" - historyPath = "/history" + historyPath = "/history" pageSuffix = ".md" zipPath = "/tldr.zip" ) @@ -37,7 +37,7 @@ type Repository struct { // HistoryRecord represent the search history of certain page type HistoryRecord struct { - page string + page string count int } @@ -285,25 +285,23 @@ func (r Repository) isReachable() bool { return err == nil } - - func (r Repository) RecordHistory(page string) error { records, err := r.loadHistory() - if err!=nil { + if err != nil { fmt.Errorf("ERROR: loading history failed %s", err) return err } - newRecord:= HistoryRecord{ - page: page, + newRecord := HistoryRecord{ + page: page, count: 1, } - foundIdx:=-1 + foundIdx := -1 for idx, r := range *records { if r.page == page { - newRecord.count = r.count+1 - foundIdx=idx + newRecord.count = r.count + 1 + foundIdx = idx break } } @@ -320,7 +318,7 @@ func (r Repository) RecordHistory(page string) error { func (r Repository) saveHistory(history *[]HistoryRecord) error { hisFile := path.Join(r.directory, historyPath) inFile, err := os.Create(hisFile) - if err!=nil { + if err != nil { fmt.Errorf("ERROR: opening history file %s", hisFile) return err } @@ -332,8 +330,6 @@ func (r Repository) saveHistory(history *[]HistoryRecord) error { return nil } - - func (r Repository) loadHistory() (*[]HistoryRecord, error) { // read the history file line by line, into a map. history := path.Join(r.directory, historyPath) @@ -341,7 +337,7 @@ func (r Repository) loadHistory() (*[]HistoryRecord, error) { _, err := os.Stat(history) if os.IsNotExist(err) { - if err := touchFile(history);err!=nil { + if err := touchFile(history); err != nil { fmt.Errorf("ERROR: cannot create the history file %s, %s", history, err) return nil, err } @@ -361,14 +357,14 @@ func (r Repository) loadHistory() (*[]HistoryRecord, error) { for scanner.Scan() { line := scanner.Text() lineParts := strings.Split(line, ",") - count, err := strconv.Atoi(lineParts[1]) + count, err := strconv.Atoi(lineParts[1]) - if err!=nil { - return nil, err + if err != nil { + return nil, err } historyRecords = append(historyRecords, HistoryRecord{ - page: lineParts[0], + page: lineParts[0], count: count, }) } diff --git a/cache/cache_test.go b/cache/cache_test.go index 58c78aee..fb05245b 100644 --- a/cache/cache_test.go +++ b/cache/cache_test.go @@ -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") } } @@ -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) + } + +} From 629574db4d309ddf038a56edf5db5a568b77a986 Mon Sep 17 00:00:00 2001 From: Wudong Liu Date: Fri, 31 Jul 2020 10:41:46 +0100 Subject: [PATCH 4/5] add history to main --- cache/cache.go | 12 ++++++++---- cache/cache_test.go | 2 +- cmd/tldr/main.go | 30 +++++++++++++++++++++++++++++- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/cache/cache.go b/cache/cache.go index 23f0c3c5..76b22020 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -41,6 +41,10 @@ type HistoryRecord struct { 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) { @@ -286,7 +290,7 @@ func (r Repository) isReachable() bool { } func (r Repository) RecordHistory(page string) error { - records, err := r.loadHistory() + records, err := r.LoadHistory() if err != nil { fmt.Errorf("ERROR: loading history failed %s", err) return err @@ -330,7 +334,7 @@ func (r Repository) saveHistory(history *[]HistoryRecord) error { return nil } -func (r Repository) loadHistory() (*[]HistoryRecord, error) { +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. @@ -372,7 +376,7 @@ func (r Repository) loadHistory() (*[]HistoryRecord, error) { return &historyRecords, nil } -func (r Repository) cleanHistory() { +func (r Repository) cleanHistory() error { history := path.Join(r.directory, historyPath) - touchFile(history) + return touchFile(history) } diff --git a/cache/cache_test.go b/cache/cache_test.go index fb05245b..a0e2f30c 100644 --- a/cache/cache_test.go +++ b/cache/cache_test.go @@ -156,7 +156,7 @@ func TestHistory(t *testing.T) { repo.RecordHistory("git-fetch") repo.RecordHistory("git-pull") - history, err := repo.loadHistory() + history, err := repo.LoadHistory() if err != nil { t.Error("Expected to load history successful.") } diff --git a/cmd/tldr/main.go b/cmd/tldr/main.go index a618eb14..1d653d86 100644 --- a/cmd/tldr/main.go +++ b/cmd/tldr/main.go @@ -22,6 +22,7 @@ const ( updateUsage = "update local database" versionUsage = "print version and exit" randomUsage = "prints a random page" + historyUsage = "show the latest search history" ) const ( @@ -105,7 +106,7 @@ func printPage(page string) { err = tldr.Write(markdown, os.Stdout) if err != nil { log.Fatalf("ERROR: writing markdown: %s", err) - }else { + } else { repository.RecordHistory(page) } @@ -159,6 +160,28 @@ 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. + for i := 1; i <= 10; i++ { + record := (*history)[hisLen-i] + fmt.Printf("%s\n", record) + } + } +} + func main() { version := flag.Bool("version", false, versionUsage) flag.BoolVar(version, "v", false, versionUsage) @@ -179,6 +202,9 @@ func main() { random := flag.Bool("random", false, randomUsage) flag.BoolVar(random, "r", false, randomUsage) + history := flag.Bool("history", false, historyUsage) + flag.BoolVar(history, "h", false, historyUsage) + flag.Parse() if *version { @@ -194,6 +220,8 @@ func main() { printPageForPlatform(page, *platform) } else if *random { printRandomPage() + } else if *history { + printHistory() } else { page := flag.Arg(0) printPage(page) From 6486b189713c685fac5d69eb1bb54de58cddd24e Mon Sep 17 00:00:00 2001 From: Wudong Liu Date: Sun, 4 Oct 2020 19:59:01 +0100 Subject: [PATCH 5/5] Addressing PR comment. --- cache/cache.go | 26 ++++++++------------------ cmd/tldr/main.go | 12 ++++++++---- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/cache/cache.go b/cache/cache.go index 76b22020..340f8ca7 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -218,8 +218,7 @@ func (r *Repository) loadFromRemote() error { func (r *Repository) makeCacheDir() error { if err := os.MkdirAll(r.directory, 0755); err != nil { - fmt.Errorf("ERROR: creating directory %s: %s", r.directory, err) - return err + return fmt.Errorf("ERROR: creating directory %s: %s", r.directory, err) } // touching the history file. historyFile := path.Join(r.directory, historyPath) @@ -228,11 +227,10 @@ func (r *Repository) makeCacheDir() error { func touchFile(fileName string) error { file, err := os.Create(fileName) - defer file.Close() if err != nil { - fmt.Errorf("ERROR: creating file %s: %s", fileName, err) - return err + return fmt.Errorf("ERROR: creating file %s: %s", fileName, err) } + defer file.Close() return nil } @@ -292,8 +290,7 @@ func (r Repository) isReachable() bool { func (r Repository) RecordHistory(page string) error { records, err := r.LoadHistory() if err != nil { - fmt.Errorf("ERROR: loading history failed %s", err) - return err + return fmt.Errorf("ERROR: loading history failed %s", err) } newRecord := HistoryRecord{ @@ -323,8 +320,7 @@ func (r Repository) saveHistory(history *[]HistoryRecord) error { hisFile := path.Join(r.directory, historyPath) inFile, err := os.Create(hisFile) if err != nil { - fmt.Errorf("ERROR: opening history file %s", hisFile) - return err + return fmt.Errorf("ERROR: opening history file %s", hisFile) } defer inFile.Close() @@ -342,15 +338,14 @@ func (r Repository) LoadHistory() (*[]HistoryRecord, error) { if os.IsNotExist(err) { if err := touchFile(history); err != nil { - fmt.Errorf("ERROR: cannot create the history file %s, %s", history, err) - return nil, err + return nil, fmt.Errorf("ERROR: cannot create the history file %s, %s", history, err) } } inFile, err := os.Open(history) if err != nil { - fmt.Errorf("ERROR: opening history file %s", history) - return nil, err + return nil, fmt.Errorf("ERROR: opening history file %s", history) + } defer inFile.Close() @@ -375,8 +370,3 @@ func (r Repository) LoadHistory() (*[]HistoryRecord, error) { return &historyRecords, nil } - -func (r Repository) cleanHistory() error { - history := path.Join(r.directory, historyPath) - return touchFile(history) -} diff --git a/cmd/tldr/main.go b/cmd/tldr/main.go index 1d653d86..7b2fbda5 100644 --- a/cmd/tldr/main.go +++ b/cmd/tldr/main.go @@ -5,6 +5,7 @@ import ( "flag" "fmt" "log" + "math" "math/rand" "os" "runtime" @@ -106,10 +107,12 @@ func printPage(page string) { err = tldr.Write(markdown, os.Stdout) if err != nil { log.Fatalf("ERROR: writing markdown: %s", err) - } else { - repository.RecordHistory(page) } + err2 := repository.RecordHistory(page) + if err2 != nil { + log.Fatalf("ERROR: saving history: %s", err2) + } } func printPageForPlatform(page string, platform string) { @@ -175,7 +178,8 @@ func printHistory() { if hisLen == 0 { fmt.Println("No history is available yet") } else { //default print last 10. - for i := 1; i <= 10; i++ { + size := int(math.Min(10, float64(hisLen))) + for i := 1; i <= size; i++ { record := (*history)[hisLen-i] fmt.Printf("%s\n", record) } @@ -203,7 +207,7 @@ func main() { flag.BoolVar(random, "r", false, randomUsage) history := flag.Bool("history", false, historyUsage) - flag.BoolVar(history, "h", false, historyUsage) + flag.BoolVar(history, "t", false, historyUsage) flag.Parse()