Skip to content

Commit 326fff3

Browse files
author
Threepwood-7
committed
RUST-REF-005 Extract core application API
1 parent 5c990e1 commit 326fff3

3 files changed

Lines changed: 157 additions & 153 deletions

File tree

crates/emulebb-core/src/app_api.rs

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
use super::*;
2+
3+
impl EmulebbCore {
4+
pub fn app_info(&self) -> AppInfo {
5+
AppInfo {
6+
name: "eMuleBB".to_string(),
7+
version: self.version.clone(),
8+
api_version: "v1".to_string(),
9+
lifecycle: AppLifecycle {
10+
state: self.lifecycle_state_name().to_string(),
11+
},
12+
capabilities: vec![
13+
"transfers".to_string(),
14+
"searches".to_string(),
15+
"servers".to_string(),
16+
"sharedFiles".to_string(),
17+
"sharedDirectories".to_string(),
18+
"uploads".to_string(),
19+
"logs".to_string(),
20+
"categoriesRead".to_string(),
21+
"categoryAssignment".to_string(),
22+
"categoryCrud".to_string(),
23+
"renameFile".to_string(),
24+
"transferDetails".to_string(),
25+
"fileRatingComment".to_string(),
26+
"friends".to_string(),
27+
"peerControls".to_string(),
28+
],
29+
}
30+
}
31+
32+
pub async fn capture_diagnostic_dump(&self, full_memory: bool) -> Result<DiagnosticDumpResult> {
33+
let dump_dir = self
34+
.transfer_root
35+
.parent()
36+
.unwrap_or(self.transfer_root.as_path())
37+
.join("diagnostics");
38+
fs::create_dir_all(&dump_dir).with_context(|| {
39+
format!(
40+
"failed to create diagnostics directory {}",
41+
dump_dir.display()
42+
)
43+
})?;
44+
45+
let stamp = Utc::now().format("%Y%m%dT%H%M%SZ");
46+
let path = dump_dir.join(format!(
47+
"emulebb-rust-diagnostic-dump-{stamp}-{}.json",
48+
Uuid::new_v4()
49+
));
50+
let payload = serde_json::to_vec_pretty(&json!({
51+
"app": self.app_info(),
52+
"status": self.status().await,
53+
"fullMemory": full_memory,
54+
"kind": "json",
55+
"capturedAt": Utc::now(),
56+
}))?;
57+
fs::write(&path, &payload)
58+
.with_context(|| format!("failed to write diagnostic dump {}", path.display()))?;
59+
Ok(DiagnosticDumpResult {
60+
ok: true,
61+
path: path.display().to_string(),
62+
full_memory,
63+
})
64+
}
65+
66+
pub async fn preferences(&self) -> Preferences {
67+
self.state.lock().await.preferences.clone()
68+
}
69+
70+
pub async fn update_preferences(&self, request: PreferencesUpdate) -> Result<Preferences> {
71+
ensure!(
72+
!preferences_update_is_empty(&request),
73+
"preferences PATCH requires at least one preference"
74+
);
75+
let preferences = {
76+
let mut state = self.state.lock().await;
77+
let mut preferences = state.preferences.clone();
78+
apply_preferences_update(&mut preferences, request)?;
79+
profile_state::persist_preferences(&self.metadata_store, &preferences)?;
80+
state.preferences = preferences.clone();
81+
preferences
82+
};
83+
self.ed2k_transfers
84+
.apply_upload_queue_policy(&ed2k_upload_queue_policy_from_preferences(
85+
self.ed2k_network
86+
.as_ref()
87+
.map(|network| &network.config.upload_queue),
88+
&preferences,
89+
))
90+
.await;
91+
self.ed2k_transfers
92+
.apply_download_limit(ed2k_download_limit_bytes_per_sec_from_preferences(
93+
&preferences,
94+
))
95+
.await;
96+
// Apply the global connection budget + per-file source caps live, like
97+
// the download limit (eMule GetMaxConnections / GetMaxConperFive /
98+
// GetConfiguredMaxSourcesPerFile preference changes take effect at once).
99+
self.ed2k_transfers.apply_download_coordinator_config(
100+
ed2k_download_coordinator_config_from_preferences(&preferences),
101+
);
102+
// Apply the credit-system toggle live (eMule thePrefs.GetCreditSystem()):
103+
// when off, upload scoring uses the neutral 1.0 credit ratio for everyone.
104+
self.ed2k_transfers
105+
.set_credit_system_enabled(preferences.credit_system);
106+
Ok(preferences)
107+
}
108+
109+
pub async fn status(&self) -> Status {
110+
let transfer_counts = self.ed2k_transfers.try_transfer_counts();
111+
let state = self.state.lock().await;
112+
let kad_running = state.kad_running;
113+
let transfer_counts = transfer_counts.unwrap_or_else(|error| {
114+
tracing::warn!("failed to read persisted ED2K transfer counts: {error}");
115+
None
116+
});
117+
let transfer_counts = transfer_counts.unwrap_or_else(|| {
118+
tracing::debug!("metadata transfer counts are busy; using in-memory status counts");
119+
let total = state.transfers.len();
120+
let mut active = 0;
121+
let mut completed = 0;
122+
for transfer in state.transfers.values() {
123+
match transfer.state.as_str() {
124+
"downloading" | "queued" => active += 1,
125+
"completed" => completed += 1,
126+
_ => {}
127+
}
128+
}
129+
MetadataTransferCounts {
130+
active,
131+
completed,
132+
total,
133+
}
134+
});
135+
drop(state);
136+
137+
Status {
138+
lifecycle: AppLifecycle {
139+
state: "running".to_string(),
140+
},
141+
uptime_secs: self.started_at.elapsed().as_secs(),
142+
kad: self.kad_status(kad_running).await,
143+
ed2k: self.ed2k_status().await,
144+
indexing: IndexingStatus {
145+
enabled: true,
146+
backend: "sqlite-fts5".to_string(),
147+
},
148+
transfers: TransferStats {
149+
active: transfer_counts.active,
150+
completed: transfer_counts.completed,
151+
total: transfer_counts.total,
152+
},
153+
}
154+
}
155+
}

crates/emulebb-core/src/lib.rs

Lines changed: 1 addition & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ use tokio::{
8787
use tokio_util::sync::CancellationToken;
8888
use uuid::Uuid;
8989

90+
mod app_api;
9091
mod callback_tracker;
9192
mod categories;
9293
mod category_runtime;
@@ -532,158 +533,6 @@ impl EmulebbCore {
532533
Self::new(version, index, unique_runtime_dir("emulebb-core-transfers"))
533534
}
534535

535-
pub fn app_info(&self) -> AppInfo {
536-
AppInfo {
537-
name: "eMuleBB".to_string(),
538-
version: self.version.clone(),
539-
api_version: "v1".to_string(),
540-
lifecycle: AppLifecycle {
541-
state: self.lifecycle_state_name().to_string(),
542-
},
543-
capabilities: vec![
544-
"transfers".to_string(),
545-
"searches".to_string(),
546-
"servers".to_string(),
547-
"sharedFiles".to_string(),
548-
"sharedDirectories".to_string(),
549-
"uploads".to_string(),
550-
"logs".to_string(),
551-
"categoriesRead".to_string(),
552-
"categoryAssignment".to_string(),
553-
"categoryCrud".to_string(),
554-
"renameFile".to_string(),
555-
"transferDetails".to_string(),
556-
"fileRatingComment".to_string(),
557-
"friends".to_string(),
558-
"peerControls".to_string(),
559-
],
560-
}
561-
}
562-
563-
pub async fn capture_diagnostic_dump(&self, full_memory: bool) -> Result<DiagnosticDumpResult> {
564-
let dump_dir = self
565-
.transfer_root
566-
.parent()
567-
.unwrap_or(self.transfer_root.as_path())
568-
.join("diagnostics");
569-
fs::create_dir_all(&dump_dir).with_context(|| {
570-
format!(
571-
"failed to create diagnostics directory {}",
572-
dump_dir.display()
573-
)
574-
})?;
575-
576-
let stamp = Utc::now().format("%Y%m%dT%H%M%SZ");
577-
let path = dump_dir.join(format!(
578-
"emulebb-rust-diagnostic-dump-{stamp}-{}.json",
579-
Uuid::new_v4()
580-
));
581-
let payload = serde_json::to_vec_pretty(&json!({
582-
"app": self.app_info(),
583-
"status": self.status().await,
584-
"fullMemory": full_memory,
585-
"kind": "json",
586-
"capturedAt": Utc::now(),
587-
}))?;
588-
fs::write(&path, &payload)
589-
.with_context(|| format!("failed to write diagnostic dump {}", path.display()))?;
590-
Ok(DiagnosticDumpResult {
591-
ok: true,
592-
path: path.display().to_string(),
593-
full_memory,
594-
})
595-
}
596-
597-
pub async fn preferences(&self) -> Preferences {
598-
self.state.lock().await.preferences.clone()
599-
}
600-
601-
pub async fn update_preferences(&self, request: PreferencesUpdate) -> Result<Preferences> {
602-
ensure!(
603-
!preferences_update_is_empty(&request),
604-
"preferences PATCH requires at least one preference"
605-
);
606-
let preferences = {
607-
let mut state = self.state.lock().await;
608-
let mut preferences = state.preferences.clone();
609-
apply_preferences_update(&mut preferences, request)?;
610-
profile_state::persist_preferences(&self.metadata_store, &preferences)?;
611-
state.preferences = preferences.clone();
612-
preferences
613-
};
614-
self.ed2k_transfers
615-
.apply_upload_queue_policy(&ed2k_upload_queue_policy_from_preferences(
616-
self.ed2k_network
617-
.as_ref()
618-
.map(|network| &network.config.upload_queue),
619-
&preferences,
620-
))
621-
.await;
622-
self.ed2k_transfers
623-
.apply_download_limit(ed2k_download_limit_bytes_per_sec_from_preferences(
624-
&preferences,
625-
))
626-
.await;
627-
// Apply the global connection budget + per-file source caps live, like
628-
// the download limit (eMule GetMaxConnections / GetMaxConperFive /
629-
// GetConfiguredMaxSourcesPerFile preference changes take effect at once).
630-
self.ed2k_transfers.apply_download_coordinator_config(
631-
ed2k_download_coordinator_config_from_preferences(&preferences),
632-
);
633-
// Apply the credit-system toggle live (eMule thePrefs.GetCreditSystem()):
634-
// when off, upload scoring uses the neutral 1.0 credit ratio for everyone.
635-
self.ed2k_transfers
636-
.set_credit_system_enabled(preferences.credit_system);
637-
Ok(preferences)
638-
}
639-
640-
pub async fn status(&self) -> Status {
641-
let transfer_counts = self.ed2k_transfers.try_transfer_counts();
642-
let state = self.state.lock().await;
643-
let kad_running = state.kad_running;
644-
let transfer_counts = transfer_counts.unwrap_or_else(|error| {
645-
tracing::warn!("failed to read persisted ED2K transfer counts: {error}");
646-
None
647-
});
648-
let transfer_counts = transfer_counts.unwrap_or_else(|| {
649-
tracing::debug!("metadata transfer counts are busy; using in-memory status counts");
650-
let total = state.transfers.len();
651-
let mut active = 0;
652-
let mut completed = 0;
653-
for transfer in state.transfers.values() {
654-
match transfer.state.as_str() {
655-
"downloading" | "queued" => active += 1,
656-
"completed" => completed += 1,
657-
_ => {}
658-
}
659-
}
660-
MetadataTransferCounts {
661-
active,
662-
completed,
663-
total,
664-
}
665-
});
666-
drop(state);
667-
668-
Status {
669-
lifecycle: AppLifecycle {
670-
state: "running".to_string(),
671-
},
672-
uptime_secs: self.started_at.elapsed().as_secs(),
673-
kad: self.kad_status(kad_running).await,
674-
ed2k: self.ed2k_status().await,
675-
indexing: IndexingStatus {
676-
enabled: true,
677-
backend: "sqlite-fts5".to_string(),
678-
},
679-
transfers: TransferStats {
680-
active: transfer_counts.active,
681-
completed: transfer_counts.completed,
682-
total: transfer_counts.total,
683-
},
684-
}
685-
}
686-
687536
pub async fn import_kad_nodes_url(&self, url: &str) -> Result<bool> {
688537
let url = validate_url_import(url)?;
689538
match fetch_url_bytes(&url).await {

policy/rust-client.toml

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)