github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/pkg/query/queryhistory/history.go (about)

     1  package queryhistory
     2  
     3  import (
     4  	"encoding/json"
     5  	"io"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  
    10  	"github.com/turbot/steampipe/pkg/constants"
    11  	"github.com/turbot/steampipe/pkg/filepaths"
    12  )
    13  
    14  // QueryHistory :: struct for working with history in the interactive mode
    15  type QueryHistory struct {
    16  	history []string
    17  }
    18  
    19  // New creates a new QueryHistory object
    20  func New() (*QueryHistory, error) {
    21  	history := &QueryHistory{history: []string{}}
    22  	err := history.load()
    23  	if err != nil {
    24  		return nil, err
    25  	}
    26  	return history, nil
    27  }
    28  
    29  // Push adds a string to the history queue trimming to maxHistorySize if necessary
    30  func (q *QueryHistory) Push(query string) {
    31  	if len(strings.TrimSpace(query)) == 0 {
    32  		// do not store a blank query
    33  		return
    34  	}
    35  
    36  	// do a strict compare to see if we have this same exact query as the most recent history item
    37  	if lastElement := q.Peek(); lastElement != nil && (*lastElement) == query {
    38  		return
    39  	}
    40  
    41  	// limit the history length to HistorySize
    42  	historyLength := len(q.history)
    43  	if historyLength >= constants.HistorySize {
    44  		q.history = q.history[historyLength-constants.HistorySize+1:]
    45  	}
    46  
    47  	// append the new entry
    48  	q.history = append(q.history, query)
    49  }
    50  
    51  // Peek returns the last element of the history stack.
    52  // returns nil if there is no history
    53  func (q *QueryHistory) Peek() *string {
    54  	if len(q.history) == 0 {
    55  		return nil
    56  	}
    57  	return &q.history[len(q.history)-1]
    58  }
    59  
    60  // Persist writes the history to the filesystem
    61  func (q *QueryHistory) Persist() error {
    62  	var file *os.File
    63  	var err error
    64  	defer func() {
    65  		file.Close()
    66  	}()
    67  	path := filepath.Join(filepaths.EnsureInternalDir(), constants.HistoryFile)
    68  	file, err = os.Create(path)
    69  	if err != nil {
    70  		return err
    71  	}
    72  
    73  	jsonEncoder := json.NewEncoder(file)
    74  
    75  	// disable indentation
    76  	jsonEncoder.SetIndent("", "")
    77  
    78  	return jsonEncoder.Encode(q.history)
    79  }
    80  
    81  // Get returns the full history
    82  func (q *QueryHistory) Get() []string {
    83  	return q.history
    84  }
    85  
    86  // loads up the history from the file where it is persisted
    87  func (q *QueryHistory) load() error {
    88  	path := filepath.Join(filepaths.EnsureInternalDir(), constants.HistoryFile)
    89  	file, err := os.Open(path)
    90  	if err != nil {
    91  		// ignore not exists errors
    92  		if os.IsNotExist(err) {
    93  			return nil
    94  		}
    95  		return err
    96  
    97  	}
    98  	defer file.Close()
    99  
   100  	decoder := json.NewDecoder(file)
   101  	err = decoder.Decode(&q.history)
   102  	// ignore EOF (caused by empty file)
   103  	if err == io.EOF {
   104  		return nil
   105  	}
   106  	return err
   107  }