Skip to content

Commit 4ba8be8

Browse files
refactor: reduce complexity of codebase (#44)
* gnhf #2: Reduced code complexity by extracting a `tool_def()` helper in agent/tools.rs (eliminating 8 duplicated ToolDefinition blocks) and removing an unnecessary wrapper function in commands/audio.rs * gnhf #3: Extracted tauriInvoke() helper in shared.js and refactored all 10 API modules to eliminate ~800 lines of duplicated try/catch/if(invoke) boilerplate * refactor(agent): throttle model pull progress logging to 10s intervals * fix(audio): gate tracing imports behind cfg(not(test)) The `debug!`/`warn!` macros are only used in `safe_list_output_devices_subprocess`, which is `#[cfg(not(test))]`. Without the matching gate on the import, `cargo test` fails with `-D warnings` due to unused imports. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 354c6a8 commit 4ba8be8

16 files changed

Lines changed: 375 additions & 860 deletions

File tree

app/frontend/__tests__/lyrics.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ describe('lyrics API', () => {
9898

9999
await lyrics.clearCache();
100100

101-
expect(mockInvoke).toHaveBeenCalledWith('lyrics_clear_cache');
101+
expect(mockInvoke).toHaveBeenCalledWith('lyrics_clear_cache', {});
102102
});
103103
});
104104
});

app/frontend/js/api/agent.js

Lines changed: 16 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
* Agent API
33
*
44
* Conversational playlist generation via local LLM (Ollama + Rig).
5-
* All commands return graceful fallbacks when the agent feature is disabled.
5+
* All operations return graceful fallbacks when not running in Tauri.
66
*/
77

8-
import { ApiError, invoke } from './shared.js';
8+
import { tauriInvoke } from './shared.js';
99

1010
export const agent = {
1111
/**
@@ -14,30 +14,18 @@ export const agent = {
1414
* @returns {Promise<{status: string, playlist_id?: number, playlist_name?: string, track_count?: number, message: string}>}
1515
*/
1616
async generatePlaylist(prompt) {
17-
if (invoke) {
18-
try {
19-
return await invoke('agent_generate_playlist', { prompt });
20-
} catch (error) {
21-
console.error('[api.agent.generatePlaylist] Tauri error:', error);
22-
throw new ApiError(500, error.toString());
23-
}
24-
}
25-
throw new ApiError(501, 'Agent requires Tauri runtime');
17+
const result = await tauriInvoke('agent_generate_playlist', { prompt });
18+
if (result !== null) return result;
19+
throw new Error('Agent requires Tauri runtime');
2620
},
2721

2822
/**
2923
* Check agent availability (Ollama running + model present).
3024
* @returns {Promise<{available: boolean, model: string, message: string}>}
3125
*/
3226
async checkStatus() {
33-
if (invoke) {
34-
try {
35-
return await invoke('agent_check_status');
36-
} catch (error) {
37-
console.error('[api.agent.checkStatus] Tauri error:', error);
38-
throw new ApiError(500, error.toString());
39-
}
40-
}
27+
const result = await tauriInvoke('agent_check_status');
28+
if (result !== null) return result;
4129
return { available: false, model: '', message: 'Agent requires Tauri runtime' };
4230
},
4331

@@ -46,14 +34,8 @@ export const agent = {
4634
* @returns {Promise<{connected: boolean, models: string[]}>}
4735
*/
4836
async checkOllama() {
49-
if (invoke) {
50-
try {
51-
return await invoke('agent_check_ollama');
52-
} catch (error) {
53-
console.error('[api.agent.checkOllama] Tauri error:', error);
54-
throw new ApiError(500, error.toString());
55-
}
56-
}
37+
const result = await tauriInvoke('agent_check_ollama');
38+
if (result !== null) return result;
5739
return { connected: false, models: [] };
5840
},
5941

@@ -63,30 +45,18 @@ export const agent = {
6345
* @returns {Promise<{success: boolean, model: string, message: string}>}
6446
*/
6547
async pullModel(model) {
66-
if (invoke) {
67-
try {
68-
return await invoke('agent_pull_model', { model });
69-
} catch (error) {
70-
console.error('[api.agent.pullModel] Tauri error:', error);
71-
throw new ApiError(500, error.toString());
72-
}
73-
}
74-
throw new ApiError(501, 'Agent requires Tauri runtime');
48+
const result = await tauriInvoke('agent_pull_model', { model });
49+
if (result !== null) return result;
50+
throw new Error('Agent requires Tauri runtime');
7551
},
7652

7753
/**
7854
* Get onboarding state from persistent store.
7955
* @returns {Promise<{completed: boolean, model?: string}>}
8056
*/
8157
async getOnboardingState() {
82-
if (invoke) {
83-
try {
84-
return await invoke('agent_get_onboarding_state');
85-
} catch (error) {
86-
console.error('[api.agent.getOnboardingState] Tauri error:', error);
87-
throw new ApiError(500, error.toString());
88-
}
89-
}
58+
const result = await tauriInvoke('agent_get_onboarding_state');
59+
if (result !== null) return result;
9060
return { completed: false, model: null };
9161
},
9262

@@ -95,14 +65,7 @@ export const agent = {
9565
* @param {string|null} model - Model name used
9666
* @returns {Promise<void>}
9767
*/
98-
async setOnboardingComplete(model = null) {
99-
if (invoke) {
100-
try {
101-
return await invoke('agent_set_onboarding_complete', { model });
102-
} catch (error) {
103-
console.error('[api.agent.setOnboardingComplete] Tauri error:', error);
104-
throw new ApiError(500, error.toString());
105-
}
106-
}
68+
setOnboardingComplete(model = null) {
69+
return tauriInvoke('agent_set_onboarding_complete', { model });
10770
},
10871
};

app/frontend/js/api/audio.js

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,16 @@
44
* Audio output device enumeration and selection via Tauri commands.
55
*/
66

7-
import { ApiError, invoke } from './shared.js';
7+
import { tauriInvoke } from './shared.js';
88

99
export const audio = {
1010
/**
1111
* List available audio output devices
1212
* @returns {Promise<{devices: string[]}>}
1313
*/
1414
async listDevices() {
15-
if (invoke) {
16-
try {
17-
return await invoke('audio_list_devices');
18-
} catch (error) {
19-
console.error('[api.audio.listDevices] Tauri error:', error);
20-
throw new ApiError(500, error.toString());
21-
}
22-
}
23-
// No HTTP fallback — audio device selection requires Tauri runtime
15+
const result = await tauriInvoke('audio_list_devices');
16+
if (result !== null) return result;
2417
return { devices: [] };
2518
},
2619

@@ -29,14 +22,7 @@ export const audio = {
2922
* @param {string|null} deviceName - Device name, or null for system default
3023
* @returns {Promise<void>}
3124
*/
32-
async setDevice(deviceName) {
33-
if (invoke) {
34-
try {
35-
return await invoke('audio_set_device', { deviceName });
36-
} catch (error) {
37-
console.error('[api.audio.setDevice] Tauri error:', error);
38-
throw new ApiError(500, error.toString());
39-
}
40-
}
25+
setDevice(deviceName) {
26+
return tauriInvoke('audio_set_device', { deviceName });
4127
},
4228
};

app/frontend/js/api/favorites.js

Lines changed: 37 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* Liked songs, top played, recently played/added operations.
55
*/
66

7-
import { ApiError, invoke, request } from './shared.js';
7+
import { ApiError, request, tauriInvoke } from './shared.js';
88

99
export const favorites = {
1010
/**
@@ -15,17 +15,11 @@ export const favorites = {
1515
* @returns {Promise<{tracks: Array, total: number, limit: number, offset: number}>}
1616
*/
1717
async get(params = {}) {
18-
if (invoke) {
19-
try {
20-
return await invoke('favorites_get', {
21-
limit: params.limit ?? null,
22-
offset: params.offset ?? null,
23-
});
24-
} catch (error) {
25-
console.error('[api.favorites.get] Tauri error:', error);
26-
throw new ApiError(500, error.toString());
27-
}
28-
}
18+
const result = await tauriInvoke('favorites_get', {
19+
limit: params.limit ?? null,
20+
offset: params.offset ?? null,
21+
});
22+
if (result !== null) return result;
2923
// Fallback to HTTP
3024
const query = new URLSearchParams();
3125
if (params.limit) query.set('limit', params.limit.toString());
@@ -40,14 +34,8 @@ export const favorites = {
4034
* @returns {Promise<{is_favorite: boolean, favorited_date: string|null}>}
4135
*/
4236
async check(trackId) {
43-
if (invoke) {
44-
try {
45-
return await invoke('favorites_check', { trackId });
46-
} catch (error) {
47-
console.error('[api.favorites.check] Tauri error:', error);
48-
throw new ApiError(500, error.toString());
49-
}
50-
}
37+
const result = await tauriInvoke('favorites_check', { trackId });
38+
if (result !== null) return result;
5139
return request(`/favorites/${encodeURIComponent(trackId)}`);
5240
},
5341

@@ -57,20 +45,17 @@ export const favorites = {
5745
* @returns {Promise<{success: boolean, favorited_date: string}>}
5846
*/
5947
async add(trackId) {
60-
if (invoke) {
61-
try {
62-
return await invoke('favorites_add', { trackId });
63-
} catch (error) {
64-
console.error('[api.favorites.add] Tauri error:', error);
65-
// Check for specific error messages
66-
if (error.toString().includes('already favorited')) {
67-
throw new ApiError(409, 'Track is already favorited');
68-
}
69-
if (error.toString().includes('not found')) {
70-
throw new ApiError(404, error.toString());
71-
}
72-
throw new ApiError(500, error.toString());
48+
try {
49+
const result = await tauriInvoke('favorites_add', { trackId });
50+
if (result !== null) return result;
51+
} catch (error) {
52+
if (error.message.includes('already favorited')) {
53+
throw new ApiError(409, 'Track is already favorited');
54+
}
55+
if (error.message.includes('not found')) {
56+
throw new ApiError(404, error.message);
7357
}
58+
throw error;
7459
}
7560
return request(`/favorites/${encodeURIComponent(trackId)}`, {
7661
method: 'POST',
@@ -83,16 +68,14 @@ export const favorites = {
8368
* @returns {Promise<void>}
8469
*/
8570
async remove(trackId) {
86-
if (invoke) {
87-
try {
88-
return await invoke('favorites_remove', { trackId });
89-
} catch (error) {
90-
console.error('[api.favorites.remove] Tauri error:', error);
91-
if (error.toString().includes('not in favorites')) {
92-
throw new ApiError(404, error.toString());
93-
}
94-
throw new ApiError(500, error.toString());
71+
try {
72+
const result = await tauriInvoke('favorites_remove', { trackId });
73+
if (result !== null) return result;
74+
} catch (error) {
75+
if (error.message.includes('not in favorites')) {
76+
throw new ApiError(404, error.message);
9577
}
78+
throw error;
9679
}
9780
return request(`/favorites/${encodeURIComponent(trackId)}`, {
9881
method: 'DELETE',
@@ -104,14 +87,8 @@ export const favorites = {
10487
* @returns {Promise<{tracks: Array}>}
10588
*/
10689
async getTop25() {
107-
if (invoke) {
108-
try {
109-
return await invoke('favorites_get_top25');
110-
} catch (error) {
111-
console.error('[api.favorites.getTop25] Tauri error:', error);
112-
throw new ApiError(500, error.toString());
113-
}
114-
}
90+
const result = await tauriInvoke('favorites_get_top25');
91+
if (result !== null) return result;
11592
return request('/favorites/top25');
11693
},
11794

@@ -123,17 +100,11 @@ export const favorites = {
123100
* @returns {Promise<{tracks: Array, days: number}>}
124101
*/
125102
async getRecentlyPlayed(params = {}) {
126-
if (invoke) {
127-
try {
128-
return await invoke('favorites_get_recently_played', {
129-
days: params.days ?? null,
130-
limit: params.limit ?? null,
131-
});
132-
} catch (error) {
133-
console.error('[api.favorites.getRecentlyPlayed] Tauri error:', error);
134-
throw new ApiError(500, error.toString());
135-
}
136-
}
103+
const result = await tauriInvoke('favorites_get_recently_played', {
104+
days: params.days ?? null,
105+
limit: params.limit ?? null,
106+
});
107+
if (result !== null) return result;
137108
// Fallback to HTTP
138109
const query = new URLSearchParams();
139110
if (params.days) query.set('days', params.days.toString());
@@ -150,17 +121,11 @@ export const favorites = {
150121
* @returns {Promise<{tracks: Array, days: number}>}
151122
*/
152123
async getRecentlyAdded(params = {}) {
153-
if (invoke) {
154-
try {
155-
return await invoke('favorites_get_recently_added', {
156-
days: params.days ?? null,
157-
limit: params.limit ?? null,
158-
});
159-
} catch (error) {
160-
console.error('[api.favorites.getRecentlyAdded] Tauri error:', error);
161-
throw new ApiError(500, error.toString());
162-
}
163-
}
124+
const result = await tauriInvoke('favorites_get_recently_added', {
125+
days: params.days ?? null,
126+
limit: params.limit ?? null,
127+
});
128+
if (result !== null) return result;
164129
// Fallback to HTTP
165130
const query = new URLSearchParams();
166131
if (params.days) query.set('days', params.days.toString());

0 commit comments

Comments
 (0)