-
-
Notifications
You must be signed in to change notification settings - Fork 8
/
mentor-data.el
149 lines (119 loc) · 5.4 KB
/
mentor-data.el
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
;;; mentor-data.el --- Mentor data structures -*- lexical-binding: t -*-
;; Copyright (C) 2016-2023 Stefan Kangas
;; Author: Stefan Kangas <[email protected]>
;; This file is NOT part of GNU Emacs.
;; Mentor is free software; you can redistribute it and/or modify it
;; under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;;
;; Mentor is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
;; License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with Mentor. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; This library contains internal data structures used by Mentor.
;;; Code:
;;; Mentor items
(require 'cl-lib)
(declare-function mentor-view-torrent-list-add "mentor")
(defconst mentor-rpc-t-multicall-sep "#")
(cl-defstruct mentor-item
"Struct containing an item that can be displayed in a buffer.
This could be a torrent, file, directory, peer, etc."
id data marked type)
(defvar-local mentor-items nil
"Hash table containing all items for the current buffer.
This can be torrents, files, peers etc. All values should be made
using `make-mentor-item'.")
(defun mentor-get-item (id)
(gethash id mentor-items))
(defun mentor-item-id-at-point ()
(get-text-property (point) 'field))
(defun mentor-get-item-at-point ()
(mentor-get-item (mentor-item-id-at-point)))
(defun mentor-item-set-property (property value &optional item must-exist)
"Set data PROPERTY to given VALUE of an item.
If ITEM is nil, use torrent at point.
If MUST-EXIST is non-nil, give a warning if the property does not
already exist."
(let ((it (or item
(mentor-get-item-at-point)
(error "There is no item here"))))
(let ((prop (assq property (mentor-item-data it))))
(if prop
(setcdr prop value)
(if must-exist
(error "Tried updating non-existent property")
(push (cons property value) (mentor-item-data it)))))))
(defun mentor-item-property (property &optional item)
"Get PROPERTY for item at point or ITEM."
(let ((it (or item
(mentor-get-item-at-point)
(error "There is no item here"))))
(cdr (assq property (mentor-item-data it)))))
;;; Download data structure
(defun mentor-download-create (data)
(make-mentor-item
:id (cdr (assq 'local_id data))
:type 'torrent
:marked nil
:data data))
(defun mentor-download-update (new &optional is-init)
"Add or update a torrent using data in NEW."
(let* ((id (mentor-item-property 'local_id new))
(old (mentor-get-item id)))
(when (and (null old)
(not is-init))
(signal 'mentor-need-init `("No such torrent" ,id)))
(if is-init
(progn (setf (mentor-item-marked new) nil)
(puthash id new mentor-items))
(dolist (row (mentor-item-data new))
(let* ((p (car row))
(v (cdr row)))
(mentor-item-set-property p v old 'must-exist))))
(mentor-view-torrent-list-add new)))
(defun mentor-data-download-update-from (d-methods t-methods values &optional is-init)
"Parse results from `mentor-rpc-d.multicall' and update download data.
If T-METHODS is nil, do not handle tracker data.
Assumes that VALUES is of the form (dval_0 dval_1 ... tvals), where dval_i
corresponds to (nth i D-METHODS) and tvals is a string of the form
\"tval_t0_p0#tval_t0_p1#...#tval_t1_p0#tval_t1p1#...#\", where tval_ti_pj
corresponds to (nth j T-METHODS) for the ith tracker. The # in this example
is the value of mentor-rpc-t-multicall-sep."
(let ((result ()))
(if t-methods
(let ((d-values (butlast values))
(t-values (butlast (split-string (car (last values)) mentor-rpc-t-multicall-sep)))
(t-methods-len (length t-methods))
(t-accum ()))
(cl-mapc (lambda (m v) (push (cons m v) result)) d-methods d-values)
;; Group the values by tracker by chopping the list of length
;; num_trackers*num_t_methods into num_trackers lists of length
;; num_t_methods.
(while t-values
(push (cl-subseq t-values 0 t-methods-len) t-accum)
(setq t-values (nthcdr t-methods-len t-values)))
(setq t-accum (nreverse t-accum))
;; Group the values by property by transposing the list of values:
;; (tval_t0_p0 tval_t0_p1) (tval_t1_p0 tval_t1_p1) ->
;; (tval_t0_p0 tval_t1_p0) (tval_t0_p1 tval_t0_p1)
(setq t-accum (apply #'cl-mapcar #'list t-accum))
;; Zip value-lists with method names:
;; ((tmthd0 . (tval_t0_p0 tval_t1_p0)) (tmthd1 . (tval_t0_p1 tval_t1_p1)))
;; ==
;; ((tmthd0 tval_t0_p0 tval_t1_p0) (tmthd1 tval_t0_p1 tval_t1_p1))
;; When one of these t properties is retrieved, is retrieved, the returned
;; value will be a list.
(cl-mapc (lambda (m v) (push (cons m v) result)) t-methods t-accum))
(cl-mapc (lambda (m v) (push (cons m v) result)) d-methods values))
(mentor-download-update (mentor-download-create result) is-init)))
(put 'mentor-need-init
'error-conditions
'(error mentor-error mentor-need-init))
(provide 'mentor-data)
;;; mentor-data.el ends here