~swflint/track-calorie-values

track-calorie-values/track-calorie-values.el -rw-r--r-- 3.7 KiB
ca10957dSamuel W. Flint Fix value setting 5 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
;;; track-calorie-values.el --- Track calorie values for various foods

;; Copyright (C) 2020 Samuel W. Flint <swflint@flintfam.org>

;; author: Samuel W. Flint <swflint@flintfam.org>
;; Version: 0.1
;; Keywords: diet, tracking
;; URL: https://git.sr.ht/~swflint/track-calorie-values

;; This file is NOT part of Emacs.
;;
;;  This file is licensed under the GNU GPLv3 or later.

;;; Commentary:
;;

;;; Code:

(require 'cl-lib)


;;; Customizeable values

(defgroup track-calorie-values nil
  "Track calorie values for various foods"
  :tag "Track Calorie Values"
  :group 'org
  :link '(url-link "https://git.sr.ht/~swflint/track-calorie-values")
  :prefix "track-calorie-values-")

(defcustom track-calorie-values-save-file
  (locate-user-emacs-file "track-calorie-values-data.el")
  "File to store calorie values of food items."
  :type '(file :must-match t))


;;; Hashing

;; Taken from emacs documentation
(defun case-fold-string= (a b)
  (eq t (compare-strings a nil nil b nil nil t)))
(defun case-fold-string-hash (a)
  (sxhash-equal (upcase a)))

(define-hash-table-test 'case-fold
  'case-fold-string= 'case-fold-string-hash)


;;; Internal Data
(defvar track-calorie-values-data (make-hash-table :weakness nil :test 'case-fold)
  "Storage for calorie values.

Keys are names, values are calories.")

(defvar track-calorie-values-foods nil
  "Storage for food names.")

(defvar track-calorie-values-loaded-p nil
  "Have calorie values been loaded?")

(defvar track-calorie-values-modified-p t
  "Have calorie values been modified?")

(defvar track-calorie-values-current-food nil
  "Current food being recorded.")


;;; I/O functions

(defun track-calorie-values-load-data ()
  "Load calorie data."
  (when (file-exists-p track-calorie-values-save-file)
    (load track-calorie-values-save-file t t t)
    (setf track-calorie-values-loaded-p t
          track-calorie-values-foods (hash-table-keys track-calorie-values-data)
          track-calorie-values-modified-p nil)))

(defun track-calorie-values-save-data ()
  "Write calorie data."
  (when track-calorie-values-modified-p
    (with-temp-buffer
      (insert ";; generated by track-calorie-values.el        -*- mode: lisp-data -*-\n"
              ";; do not modify by hand\n\n")
      (let ((standard-output (current-buffer)))
        (print `(setq track-calorie-values-data ,track-calorie-values-data)))
      (write-region (point-min) (point-max) track-calorie-values-save-file)
      (setf track-calorie-values-modified-p nil))))


;;; Primary interaction functions

(defun track-calorie-values-food ()
  "Get a food item to retrieve or prompt for calories."
  (unless track-calorie-values-loaded-p
    (track-calorie-values-load-data))
  (let ((food (completing-read "Food item: " track-calorie-values-foods
                               #'identity nil nil nil nil t)))
    (if food
        (setf track-calorie-values-current-food (downcase food))
      (user-error "A food name must be provided"))))

(defun track-calorie-values-calories ()
  "Get or prompt for the number of calories in the current food item."
  (let ((calories (gethash track-calorie-values-current-food track-calorie-values-data)))
    (if calories
        (progn
          (setf track-calorie-values-current-food nil)
          (format "%d" calories))
      (let ((calories (read-number (format "Calories in \"%s\": " track-calorie-values-current-food))))
        (puthash track-calorie-values-current-food calories track-calorie-values-data)
        (cl-pushnew track-calorie-values-current-food track-calorie-values-foods :test #'string=)
        (setf track-calorie-values-current-food nil
              track-calorie-values-modified-p t)
        (format "%d" calories)))))

(provide 'track-calorie-values)

;;; track-calorie-values.el ends here