taskchampion_lib/
server.rs

1use crate::traits::*;
2use crate::types::*;
3use crate::util::err_to_ruststring;
4use taskchampion::{Server, ServerConfig};
5
6#[ffizz_header::item]
7#[ffizz(order = 800)]
8/// ***** TCServer *****
9///
10/// TCServer represents an interface to a sync server.  Aside from new and free, a server
11/// has no C-accessible API, but is designed to be passed to `tc_replica_sync`.
12///
13/// ## Safety
14///
15/// TCServer are not threadsafe, and must not be used with multiple replicas simultaneously.
16///
17/// ```c
18/// typedef struct TCServer TCServer;
19/// ```
20pub struct TCServer(Box<dyn Server>);
21
22impl PassByPointer for TCServer {}
23
24impl From<Box<dyn Server>> for TCServer {
25    fn from(server: Box<dyn Server>) -> TCServer {
26        TCServer(server)
27    }
28}
29
30impl AsMut<Box<dyn Server>> for TCServer {
31    fn as_mut(&mut self) -> &mut Box<dyn Server> {
32        &mut self.0
33    }
34}
35
36/// Utility function to allow using `?` notation to return an error value.  
37fn wrap<T, F>(f: F, error_out: *mut TCString, err_value: T) -> T
38where
39    F: FnOnce() -> anyhow::Result<T>,
40{
41    if !error_out.is_null() {
42        // SAFETY:
43        //  - error_out is not NULL (just checked)
44        //  - properly aligned and valid (promised by caller)
45        unsafe { *error_out = TCString::default() };
46    }
47
48    match f() {
49        Ok(v) => v,
50        Err(e) => {
51            if !error_out.is_null() {
52                // SAFETY:
53                //  - error_out is not NULL (just checked)
54                //  - properly aligned and valid (promised by caller)
55                unsafe {
56                    TCString::val_to_arg_out(err_to_ruststring(e), error_out);
57                }
58            }
59            err_value
60        }
61    }
62}
63
64#[ffizz_header::item]
65#[ffizz(order = 801)]
66/// Create a new TCServer that operates locally (on-disk).  See the TaskChampion docs for the
67/// description of the arguments.
68///
69/// On error, a string is written to the error_out parameter (if it is not NULL) and NULL is
70/// returned.  The caller must free this string.
71///
72/// The server must be freed after it is used - tc_replica_sync does not automatically free it.
73///
74/// ```c
75/// EXTERN_C struct TCServer *tc_server_new_local(struct TCString server_dir, struct TCString *error_out);
76/// ```
77#[no_mangle]
78pub unsafe extern "C" fn tc_server_new_local(
79    server_dir: TCString,
80    error_out: *mut TCString,
81) -> *mut TCServer {
82    wrap(
83        || {
84            // SAFETY:
85            //  - server_dir is valid (promised by caller)
86            //  - caller will not use server_dir after this call (convention)
87            let mut server_dir = unsafe { TCString::val_from_arg(server_dir) };
88            let server_config = ServerConfig::Local {
89                server_dir: server_dir.to_path_buf_mut()?,
90            };
91            let server = server_config.into_server()?;
92            // SAFETY: caller promises to free this server.
93            Ok(unsafe { TCServer::return_ptr(server.into()) })
94        },
95        error_out,
96        std::ptr::null_mut(),
97    )
98}
99
100#[ffizz_header::item]
101#[ffizz(order = 801)]
102/// Create a new TCServer that connects to a remote server.  See the TaskChampion docs for the
103/// description of the arguments.
104///
105/// On error, a string is written to the error_out parameter (if it is not NULL) and NULL is
106/// returned.  The caller must free this string.
107///
108/// The server must be freed after it is used - tc_replica_sync does not automatically free it.
109///
110/// ```c
111/// EXTERN_C struct TCServer *tc_server_new_sync(struct TCString origin,
112///                                       struct TCUuid client_id,
113///                                       struct TCString encryption_secret,
114///                                       struct TCString *error_out);
115/// ```
116#[no_mangle]
117pub unsafe extern "C" fn tc_server_new_sync(
118    url: TCString,
119    client_id: TCUuid,
120    encryption_secret: TCString,
121    error_out: *mut TCString,
122) -> *mut TCServer {
123    wrap(
124        || {
125            // SAFETY:
126            //  - url is valid (promised by caller)
127            //  - url ownership is transferred to this function
128            let url = unsafe { TCString::val_from_arg(url) }.into_string()?;
129
130            // SAFETY:
131            //  - client_id is a valid Uuid (any 8-byte sequence counts)
132            let client_id = unsafe { TCUuid::val_from_arg(client_id) };
133
134            // SAFETY:
135            //  - encryption_secret is valid (promised by caller)
136            //  - encryption_secret ownership is transferred to this function
137            let encryption_secret = unsafe { TCString::val_from_arg(encryption_secret) }
138                .as_bytes()
139                .to_vec();
140
141            let server_config = ServerConfig::Remote {
142                url,
143                client_id,
144                encryption_secret,
145            };
146            let server = server_config.into_server()?;
147            // SAFETY: caller promises to free this server.
148            Ok(unsafe { TCServer::return_ptr(server.into()) })
149        },
150        error_out,
151        std::ptr::null_mut(),
152    )
153}
154
155#[ffizz_header::item]
156#[ffizz(order = 802)]
157/// Create a new TCServer that connects to the Google Cloud Platform.  See the TaskChampion docs
158/// for the description of the arguments.
159///
160/// On error, a string is written to the error_out parameter (if it is not NULL) and NULL is
161/// returned.  The caller must free this string.
162///
163/// The server must be freed after it is used - tc_replica_sync does not automatically free it.
164///
165/// ```c
166/// EXTERN_C struct TCServer *tc_server_new_gcp(struct TCString bucket,
167///                                       struct TCString credential_path,
168///                                       struct TCString encryption_secret,
169///                                       struct TCString *error_out);
170/// ```
171#[no_mangle]
172pub unsafe extern "C" fn tc_server_new_gcp(
173    bucket: TCString,
174    credential_path_argument: TCString,
175    encryption_secret: TCString,
176    error_out: *mut TCString,
177) -> *mut TCServer {
178    wrap(
179        || {
180            // SAFETY:
181            //  - bucket is valid (promised by caller)
182            //  - bucket ownership is transferred to this function
183            let bucket = unsafe { TCString::val_from_arg(bucket) }.into_string()?;
184
185            // SAFETY:
186            //  - credential_path is valid (promised by caller)
187            //  - credential_path ownership is transferred to this function
188
189            let credential_path =
190                unsafe { TCString::val_from_arg(credential_path_argument) }.into_string()?;
191            let credential_path = if credential_path.is_empty() {
192                None
193            } else {
194                Some(credential_path)
195            };
196
197            // SAFETY:
198            //  - encryption_secret is valid (promised by caller)
199            //  - encryption_secret ownership is transferred to this function
200            let encryption_secret = unsafe { TCString::val_from_arg(encryption_secret) }
201                .as_bytes()
202                .to_vec();
203            let server_config = ServerConfig::Gcp {
204                bucket,
205                credential_path,
206                encryption_secret,
207            };
208            let server = server_config.into_server()?;
209            // SAFETY: caller promises to free this server.
210            Ok(unsafe { TCServer::return_ptr(server.into()) })
211        },
212        error_out,
213        std::ptr::null_mut(),
214    )
215}
216
217#[ffizz_header::item]
218#[ffizz(order = 899)]
219/// Free a server.  The server may not be used after this function returns and must not be freed
220/// more than once.
221///
222/// ```c
223/// EXTERN_C void tc_server_free(struct TCServer *server);
224/// ```
225#[no_mangle]
226pub unsafe extern "C" fn tc_server_free(server: *mut TCServer) {
227    debug_assert!(!server.is_null());
228    // SAFETY:
229    //  - server is not NULL
230    //  - server came from tc_server_new_.., which used return_ptr
231    //  - server will not be used after (promised by caller)
232    let server = unsafe { TCServer::take_from_ptr_arg(server) };
233    drop(server);
234}