Skip to content

Commit 3c70b44

Browse files
committed
[USAGE.md] Init ; [acquire/{acquire_net_common.h,cli/main.c,tests/{test_download.h,test_libcurl_backend.h}}] Percolate up async/sync API
1 parent c230d51 commit 3c70b44

File tree

5 files changed

+370
-93
lines changed

5 files changed

+370
-93
lines changed

USAGE.md

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
libacquire Usage Guide
2+
======================
3+
4+
`libacquire` provides a flexible, handle-based API for downloading, verifying, and extracting files. This guide demonstrates how to use the core features of the library.
5+
6+
## Core Concepts
7+
8+
### The `acquire_handle`
9+
10+
The `struct acquire_handle` is the heart of the library. It's an opaque state container that you create once per logical operation. It holds internal state for downloads, checksums, and extractions, and most importantly, it's how you retrieve progress and detailed error information.
11+
12+
- **Create a handle:** `struct acquire_handle *handle = acquire_handle_init();`
13+
- **Clean up a handle:** `acquire_handle_free(handle);`
14+
15+
You should reuse the same handle for a chained operation (e.g., download -> verify -> extract).
16+
17+
### API Pattern: Synchronous vs. Asynchronous
18+
19+
Every major feature offers two ways to operate:
20+
21+
1. **Synchronous (`_sync`)**: These are simple, blocking functions. They are easy to use but will freeze your application until the operation is complete.
22+
```c
23+
int result = acquire_feature_sync(handle, ...);
24+
```
25+
26+
2. **Asynchronous (`_async_...`)**: These non-blocking functions allow your application to remain responsive. The pattern is always:
27+
- `acquire_feature_async_start()`: Kicks off the operation.
28+
- `acquire_feature_async_poll()`: Called in a loop to perform a piece of work and check the status.
29+
- `acquire_feature_async_cancel()`: Can be called from another thread or signal handler to request cancellation.
30+
31+
### Error Handling
32+
33+
When a function returns `-1` (failure), the `acquire_handle` contains the reason.
34+
35+
```c
36+
if (result != 0) {
37+
fprintf(stderr, "Operation failed: [%d] %s\n",
38+
acquire_handle_get_error_code(handle),
39+
acquire_handle_get_error_string(handle));
40+
}
41+
```
42+
43+
---
44+
45+
## 0. Downloading a File
46+
47+
### a) Synchronous Download
48+
49+
This is the simplest way to download a file. The function blocks until the download is complete or an error occurs.
50+
51+
```c
52+
#include <stdio.h>
53+
#include <acquire_handle.h>
54+
#include <acquire_download.h>
55+
56+
void simple_blocking_download(void) {
57+
struct acquire_handle *handle = acquire_handle_init();
58+
const char *url = "https://httpbin.org/get";
59+
const char *destination = "./httpbin.json";
60+
int result;
61+
62+
printf("Starting synchronous download of '%s'...\n", url);
63+
64+
result = acquire_download_sync(handle, url, destination);
65+
66+
if (result == 0) {
67+
printf("Download successful! Saved to '%s'.\n", destination);
68+
} else {
69+
fprintf(stderr, "Download failed: %s\n",
70+
acquire_handle_get_error_string(handle));
71+
}
72+
73+
acquire_handle_free(handle);
74+
}
75+
```
76+
77+
### b) Asynchronous Download with Progress
78+
79+
This example shows a non-blocking download with a progress indicator, perfect for GUI applications or services that need to remain responsive.
80+
81+
```c
82+
#include <stdio.h>
83+
#include <acquire_handle.h>
84+
#include <acquire_download.h>
85+
86+
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
87+
#include <synchapi.h>
88+
#else
89+
#include <unistd.h>
90+
#endif
91+
92+
void non_blocking_download_with_progress(void) {
93+
struct acquire_handle *handle = acquire_handle_init();
94+
const char *url = "http://ipv4.download.thinkbroadband.com/5MB.zip";
95+
const char *destination = "./5MB.zip";
96+
enum acquire_status status;
97+
98+
puts("Starting asynchronous download of a 5MB file...");
99+
if (acquire_download_async_start(handle, url, destination) != 0) {
100+
fprintf(stderr, "Failed to start async download: %s\n",
101+
acquire_handle_get_error_string(handle));
102+
acquire_handle_free(handle);
103+
return;
104+
}
105+
106+
puts("Download in progress. Polling for updates...");
107+
do {
108+
status = acquire_download_async_poll(handle);
109+
110+
if (handle->total_size > 0) {
111+
double percent = 100.0 * handle->bytes_processed / handle->total_size;
112+
printf("\rProgress: %lld / %lld bytes (%.0f%%)",
113+
(long long)handle->bytes_processed, (long long)handle->total_size, percent);
114+
} else {
115+
printf("\rProgress: %lld bytes downloaded", (long long)handle->bytes_processed);
116+
}
117+
fflush(stdout);
118+
119+
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
120+
Sleep(100); /* 100ms */
121+
#else
122+
usleep(100000); /* 100ms */
123+
#endif
124+
} while (status == ACQUIRE_IN_PROGRESS);
125+
126+
putchar('\n');
127+
128+
switch (status) {
129+
case ACQUIRE_COMPLETE:
130+
puts("Async download complete!");
131+
break;
132+
case ACQUIRE_ERROR:
133+
fprintf(stderr, "Async download failed: [%d] %s\n",
134+
acquire_handle_get_error_code(handle),
135+
acquire_handle_get_error_string(handle));
136+
break;
137+
default:
138+
fprintf(stderr, "Download finished with unexpected status: %d\n", status);
139+
break;
140+
}
141+
142+
acquire_handle_free(handle);
143+
}
144+
```
145+
146+
---
147+
148+
## 1. Verifying a File Checksum
149+
150+
Verification follows the same sync/async pattern. This is useful for ensuring the integrity of a downloaded file.
151+
152+
### a) Synchronous Verification
153+
154+
```c
155+
#include <stdio.h>
156+
#include <acquire_handle.h>
157+
#include <acquire_checksums.h>
158+
159+
void verify_file_integrity(const char *filepath, const char *expected_hash) {
160+
struct acquire_handle *handle = acquire_handle_init();
161+
int result;
162+
163+
printf("Verifying SHA256 of '%s'...\n", filepath);
164+
165+
result = acquire_verify_sync(handle, filepath, LIBACQUIRE_SHA256, expected_hash);
166+
167+
if (result == 0) {
168+
puts("Verification successful: Checksum matches.");
169+
} else {
170+
fprintf(stderr, "Verification failed: %s\n",
171+
acquire_handle_get_error_string(handle));
172+
}
173+
174+
acquire_handle_free(handle);
175+
}
176+
```
177+
178+
---
179+
180+
## 2. Extracting an Archive
181+
182+
### a) Synchronous Extraction
183+
184+
```c
185+
#include <stdio.h>
186+
187+
#include <acquire_handle.h>
188+
#include <acquire_extract.h>
189+
190+
void extract_an_archive(const char *archive_path, const char *destination_dir) {
191+
struct acquire_handle *handle = acquire_handle_init();
192+
int result;
193+
194+
printf("Extracting '%s' to '%s'...\n", archive_path, destination_dir);
195+
196+
result = acquire_extract_sync(handle, archive_path, destination_dir);
197+
198+
if (result == 0) {
199+
printf("Extraction successful.\n");
200+
} else {
201+
fprintf(stderr, "Extraction failed: %s\n",
202+
acquire_handle_get_error_string(handle));
203+
}
204+
205+
acquire_handle_free(handle);
206+
}
207+
```
208+
209+
### b) Asynchronous Extraction
210+
211+
This is useful for showing progress as each file is extracted from a large archive.
212+
213+
```c
214+
#include <stdio.h>
215+
216+
#include <acquire_handle.h>
217+
#include <acquire_extract.h>
218+
219+
/* For usleep/Sleep */
220+
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
221+
#include <synchapi.h>
222+
#else
223+
#include <unistd.h>
224+
#endif
225+
226+
void non_blocking_extraction(const char *archive_path, const char *destination_dir) {
227+
struct acquire_handle *handle = acquire_handle_init();
228+
enum acquire_status status;
229+
230+
printf("Starting asynchronous extraction...\n");
231+
if (acquire_extract_async_start(handle, archive_path, destination_dir) != 0) {
232+
fprintf(stderr, "Failed to start async extraction: %s\n",
233+
acquire_handle_get_error_string(handle));
234+
acquire_handle_free(handle);
235+
return;
236+
}
237+
238+
do {
239+
status = acquire_extract_async_poll(handle);
240+
if (status == ACQUIRE_IN_PROGRESS) {
241+
printf("Extracting: %s\n", handle->current_file);
242+
}
243+
} while (status == ACQUIRE_IN_PROGRESS);
244+
245+
if (status == ACQUIRE_COMPLETE) {
246+
printf("Extraction complete.\n");
247+
} else {
248+
fprintf(stderr, "Extraction failed: [%d] %s\n",
249+
acquire_handle_get_error_code(handle),
250+
acquire_handle_get_error_string(handle));
251+
}
252+
253+
acquire_handle_free(handle);
254+
}
255+
256+
```
257+
258+
---
259+
260+
## 3. Putting It All Together: A Complete Workflow
261+
262+
This example shows the power of the unified handle API by performing a complete download, verify, and extract sequence for a Node.js headers package.
263+
264+
```c
265+
#include <stdio.h>
266+
#include <stdlib.h>
267+
268+
#include <acquire_handle.h>
269+
#include <acquire_download.h>
270+
#include <acquire_checksums.h>
271+
#include <acquire_extract.h>
272+
273+
int download_verify_and_extract(void) {
274+
/* Define our target and its properties */
275+
const char *const url = "https://nodejs.org/dist/v18.17.1/node-v18.17.1-headers.tar.gz";
276+
const char *const expected_sha256 = "b6a7a13d2f9758783a3c942d5940e4f26484a95697d8b5847b3b3a7f64243a75";
277+
const char *const download_path = "./node-headers.tar.gz";
278+
const char *const extract_dir = "./node-headers-extracted";
279+
280+
int result;
281+
282+
/* 1. Create a single handle for the entire operation. */
283+
struct acquire_handle *handle = acquire_handle_init();
284+
if (!handle) {
285+
fprintf(stderr, "Failed to initialize acquire handle.\n");
286+
return 1;
287+
}
288+
289+
/* 2. Download the file synchronously. */
290+
printf("--- Step 1: Downloading '%s' ---\n", url);
291+
result = acquire_download_sync(handle, url, download_path);
292+
if (result != 0) {
293+
fprintf(stderr, "Download failed: %s\n", acquire_handle_get_error_string(handle));
294+
acquire_handle_free(handle);
295+
return 1;
296+
}
297+
printf("Download complete.\n\n");
298+
299+
/* 3. Verify the checksum (reusing the same handle). */
300+
printf("--- Step 2: Verifying checksum ---\n");
301+
result = acquire_verify_sync(handle, download_path, LIBACQUIRE_SHA256, expected_sha256);
302+
if (result != 0) {
303+
fprintf(stderr, "Verification failed: %s\n", acquire_handle_get_error_string(handle));
304+
acquire_handle_free(handle);
305+
return 1;
306+
}
307+
printf("Verification successful.\n\n");
308+
309+
/* 4. Extract the archive (reusing the same handle). */
310+
printf("--- Step 3: Extracting archive ---\n");
311+
result = acquire_extract_sync(handle, download_path, extract_dir);
312+
if (result != 0) {
313+
fprintf(stderr, "Extraction failed: %s\n", acquire_handle_get_error_string(handle));
314+
acquire_handle_free(handle);
315+
return 1;
316+
}
317+
printf("Extraction complete.\n\n");
318+
319+
/* 5. Clean up. */
320+
acquire_handle_free(handle);
321+
printf("--- Workflow successful! ---\n");
322+
return 0;
323+
}
324+
325+
int main(void) {
326+
if (download_verify_and_extract() != 0) {
327+
fputs("The operation failed.", stderr);
328+
return EXIT_FAILURE;
329+
}
330+
return EXIT_SUCCESS;
331+
}
332+
```

0 commit comments

Comments
 (0)