1use std::cell::{Cell, OnceCell, RefCell};
2
3use async_compat::CompatExt;
4use gio::prelude::FileExt;
5use glib::object::ObjectExt;
6use glib::subclass::prelude::{ObjectSubclass, *};
7use glib::{Properties, clone};
8use transmission_client::{Session, SessionMutator};
9
10use crate::{TrClient, TrEncryption};
11
12mod imp {
13 use super::*;
14
15 #[derive(Debug, Default, Properties)]
16 #[properties(wrapper_type = super::TrSession)]
17 pub struct TrSession {
18 #[property(get, set, construct_only)]
19 pub client: OnceCell<TrClient>,
20
21 #[property(get)]
22 pub version: RefCell<String>,
23 #[property(get, set = Self::set_download_dir)]
24 pub download_dir: RefCell<Option<gio::File>>,
25 #[property(get, set = Self::set_start_added_torrents)]
26 pub start_added_torrents: Cell<bool>,
27 #[property(get, set = Self::set_encryption, builder(Default::default()))]
28 pub encryption: Cell<TrEncryption>,
29 #[property(get, set = Self::set_incomplete_dir_enabled)]
30 pub incomplete_dir_enabled: Cell<bool>,
31 #[property(get, set = Self::set_incomplete_dir)]
32 pub incomplete_dir: RefCell<Option<gio::File>>,
33 #[property(get, set = Self::set_download_queue_enabled)]
34 pub download_queue_enabled: Cell<bool>,
35 #[property(get, set = Self::set_download_queue_size, minimum = 1, default_value = 1)]
36 pub download_queue_size: Cell<i32>,
37 #[property(get, set = Self::set_seed_queue_enabled)]
38 pub seed_queue_enabled: Cell<bool>,
39 #[property(get, set = Self::set_seed_queue_size, minimum = 1, default_value = 1)]
40 pub seed_queue_size: Cell<i32>,
41 #[property(get, set = Self::set_port_forwarding_enabled)]
42 pub port_forwarding_enabled: Cell<bool>,
43 #[property(get, set = Self::set_peer_port_random_on_start)]
44 pub peer_port_random_on_start: Cell<bool>,
45 #[property(get, set = Self::set_peer_port, minimum = 1, default_value = 1)]
46 pub peer_port: Cell<i32>,
47 #[property(get, set = Self::set_peer_limit_global, minimum = 1, default_value = 1)]
48 pub peer_limit_global: Cell<i32>,
49 #[property(get, set = Self::set_peer_limit_per_torrent, minimum = 1, default_value = 1)]
50 pub peer_limit_per_torrent: Cell<i32>,
51 }
52
53 #[glib::object_subclass]
54 impl ObjectSubclass for TrSession {
55 const NAME: &'static str = "TrSession";
56 type ParentType = glib::Object;
57 type Type = super::TrSession;
58 }
59
60 #[glib::derived_properties]
61 impl ObjectImpl for TrSession {}
62
63 impl TrSession {
64 pub fn set_download_dir(&self, value: gio::File) {
65 *self.download_dir.borrow_mut() = Some(value.clone());
66
67 let mutator = SessionMutator {
68 download_dir: Some(value.path().unwrap()),
69 ..Default::default()
70 };
71 self.mutate_session(mutator, "download_dir");
72 }
73
74 pub fn set_start_added_torrents(&self, value: bool) {
75 self.start_added_torrents.set(value);
76
77 let mutator = SessionMutator {
78 start_added_torrents: Some(value),
79 ..Default::default()
80 };
81 self.mutate_session(mutator, "start-added-torrents");
82 }
83
84 pub fn set_encryption(&self, value: TrEncryption) {
85 self.encryption.set(value);
86
87 let mutator = SessionMutator {
88 encryption: Some(value.into()),
89 ..Default::default()
90 };
91 self.mutate_session(mutator, "encryption");
92 }
93
94 pub fn set_incomplete_dir_enabled(&self, value: bool) {
95 self.incomplete_dir_enabled.set(value);
96
97 let mutator = SessionMutator {
98 incomplete_dir_enabled: Some(value),
99 ..Default::default()
100 };
101 self.mutate_session(mutator, "incomplete-dir-enabled");
102 }
103
104 pub fn set_incomplete_dir(&self, value: gio::File) {
105 *self.incomplete_dir.borrow_mut() = Some(value.clone());
106
107 let mutator = SessionMutator {
108 incomplete_dir: Some(value.path().unwrap()),
109 ..Default::default()
110 };
111 self.mutate_session(mutator, "incomplete-dir");
112 }
113
114 pub fn set_download_queue_enabled(&self, value: bool) {
115 self.download_queue_enabled.set(value);
116
117 let mutator = SessionMutator {
118 download_queue_enabled: Some(value),
119 ..Default::default()
120 };
121 self.mutate_session(mutator, "download-queue-enabled");
122 }
123
124 pub fn set_download_queue_size(&self, value: i32) {
125 self.download_queue_size.set(value);
126
127 let mutator = SessionMutator {
128 download_queue_size: Some(value),
129 ..Default::default()
130 };
131 self.mutate_session(mutator, "download-queue-size");
132 }
133
134 pub fn set_seed_queue_enabled(&self, value: bool) {
135 self.seed_queue_enabled.set(value);
136
137 let mutator = SessionMutator {
138 seed_queue_enabled: Some(value),
139 ..Default::default()
140 };
141 self.mutate_session(mutator, "seed-queue-enabled");
142 }
143
144 pub fn set_seed_queue_size(&self, value: i32) {
145 self.seed_queue_size.set(value);
146
147 let mutator = SessionMutator {
148 seed_queue_size: Some(value),
149 ..Default::default()
150 };
151 self.mutate_session(mutator, "seed-queue-size");
152 }
153
154 pub fn set_port_forwarding_enabled(&self, value: bool) {
155 self.port_forwarding_enabled.set(value);
156
157 let mutator = SessionMutator {
158 port_forwarding_enabled: Some(value),
159 ..Default::default()
160 };
161 self.mutate_session(mutator, "port-forwarding-enabled");
162 }
163
164 pub fn set_peer_port_random_on_start(&self, value: bool) {
165 self.peer_port_random_on_start.set(value);
166
167 let mutator = SessionMutator {
168 peer_port_random_on_start: Some(value),
169 ..Default::default()
170 };
171 self.mutate_session(mutator, "peer-port-random-on-start");
172 }
173
174 pub fn set_peer_port(&self, value: i32) {
175 self.peer_port.set(value);
176
177 let mutator = SessionMutator {
178 peer_port: Some(value),
179 ..Default::default()
180 };
181 self.mutate_session(mutator, "peer-port");
182 }
183
184 pub fn set_peer_limit_global(&self, value: i32) {
185 self.peer_limit_global.set(value);
186
187 let mutator = SessionMutator {
188 peer_limit_global: Some(value),
189 ..Default::default()
190 };
191 self.mutate_session(mutator, "peer-limit-global");
192 }
193
194 pub fn set_peer_limit_per_torrent(&self, value: i32) {
195 self.peer_limit_per_torrent.set(value);
196
197 let mutator = SessionMutator {
198 peer_limit_per_torrent: Some(value),
199 ..Default::default()
200 };
201 self.mutate_session(mutator, "peer-limit-per-torrent");
202 }
203
204 fn mutate_session(&self, mutator: SessionMutator, prop_name: &str) {
205 self.obj().notify(prop_name);
206
207 let fut = clone!(
208 #[weak(rename_to = this)]
209 self,
210 async move {
211 if let Some(rpc_client) = this.client.get().unwrap().rpc_client() {
212 rpc_client.session_set(mutator).compat().await.unwrap();
213 } else {
214 warn!("Unable set mutate session, no rpc connection.");
215 }
216 }
217 );
218 glib::spawn_future_local(fut);
219 }
220 }
221}
222
223glib::wrapper! {
224 pub struct TrSession(ObjectSubclass<imp::TrSession>);
225}
226
227impl TrSession {
228 pub(crate) fn new(client: &TrClient) -> Self {
229 glib::Object::builder().property("client", client).build()
230 }
231
232 pub(crate) fn refresh_values(&self, rpc_session: Session) {
233 let imp = self.imp();
234
235 if *imp.version.borrow() != rpc_session.version {
237 *imp.version.borrow_mut() = rpc_session.version;
238 self.notify_version();
239 }
240
241 let download_dir = gio::File::for_path(rpc_session.download_dir);
243 if download_dir.path() != imp.download_dir.borrow().as_ref().and_then(|f| f.path()) {
244 *imp.download_dir.borrow_mut() = Some(download_dir);
245 self.notify_download_dir();
246 }
247
248 if imp.start_added_torrents.get() != rpc_session.start_added_torrents {
250 imp.start_added_torrents
251 .set(rpc_session.start_added_torrents);
252 self.notify_start_added_torrents();
253 }
254
255 if imp.encryption.get() != rpc_session.encryption.clone().into() {
257 imp.encryption.set(rpc_session.encryption.into());
258 self.notify_encryption();
259 }
260
261 if imp.incomplete_dir_enabled.get() != rpc_session.incomplete_dir_enabled {
263 imp.incomplete_dir_enabled
264 .set(rpc_session.incomplete_dir_enabled);
265 self.notify_incomplete_dir_enabled();
266 }
267
268 let incomplete_dir = gio::File::for_path(rpc_session.incomplete_dir);
270 if incomplete_dir.path() != imp.incomplete_dir.borrow().as_ref().and_then(|f| f.path()) {
271 *imp.incomplete_dir.borrow_mut() = Some(incomplete_dir);
272 self.notify_incomplete_dir();
273 }
274
275 if imp.download_queue_enabled.get() != rpc_session.download_queue_enabled {
277 imp.download_queue_enabled
278 .set(rpc_session.download_queue_enabled);
279 self.notify_download_queue_enabled();
280 }
281
282 if imp.download_queue_size.get() != rpc_session.download_queue_size {
284 imp.download_queue_size.set(rpc_session.download_queue_size);
285 self.notify_download_queue_size();
286 }
287
288 if imp.seed_queue_enabled.get() != rpc_session.seed_queue_enabled {
290 imp.seed_queue_enabled.set(rpc_session.seed_queue_enabled);
291 self.notify_seed_queue_enabled();
292 }
293
294 if imp.seed_queue_size.get() != rpc_session.seed_queue_size {
296 imp.seed_queue_size.set(rpc_session.seed_queue_size);
297 self.notify_seed_queue_size();
298 }
299
300 if imp.port_forwarding_enabled.get() != rpc_session.port_forwarding_enabled {
302 imp.port_forwarding_enabled
303 .set(rpc_session.port_forwarding_enabled);
304 self.notify_port_forwarding_enabled();
305 }
306
307 if imp.peer_port_random_on_start.get() != rpc_session.peer_port_random_on_start {
309 imp.peer_port_random_on_start
310 .set(rpc_session.peer_port_random_on_start);
311 self.notify_peer_port_random_on_start();
312 }
313
314 if imp.peer_port.get() != rpc_session.peer_port {
316 imp.peer_port.set(rpc_session.peer_port);
317 self.notify_peer_port();
318 }
319
320 if imp.peer_limit_global.get() != rpc_session.peer_limit_global {
322 imp.peer_limit_global.set(rpc_session.peer_limit_global);
323 self.notify_peer_limit_global();
324 }
325
326 if imp.peer_limit_per_torrent.get() != rpc_session.peer_limit_per_torrent {
328 imp.peer_limit_per_torrent
329 .set(rpc_session.peer_limit_per_torrent);
330 self.notify_peer_limit_per_torrent();
331 }
332 }
333}