bulletin_board_client/
lib.rs

1//! # Bulletin Board Client
2//! A rust client for the bulletin board.
3//!
4//! # Examples
5//! To post and read the bulletins,
6//! ```
7//! use bulletin_board_client as bbclient;
8//! use bbclient::*;
9//!
10//! let data: ArrayObject = vec![1f32, 2., -3., 5.].try_into().unwrap();
11//! bbclient::post("x", "tag", data.clone()).unwrap();
12//!
13//! let recv = bbclient::read("x", Some("tag"), vec![]).unwrap().pop().unwrap();
14//! let restored = recv.try_into().unwrap();
15//! assert_eq!(data, restored);
16//! ```
17//!
18//! Make the data persistent.
19//! ```
20//! use bulletin_board_client as bbclient;
21//!
22//! bbclient::archive("acv", "x", Some("tag")).unwrap();
23//! bbclient::reset_server().unwrap(); // Delete all temporary data.
24//!
25//! bbclient::load("acv").unwrap();
26//! dbg!(bbclient::view_board().unwrap());
27//! ```
28//!
29
30/// Low-level functions that isolate the opening and closing functions of a socket. These can be used to speed up commucation with the server when you do many operations at the same time.
31pub mod low_level;
32
33pub use array_object::{ArrayObject, DataType, Pack, TryConcat, Unpack, adaptor};
34
35use low_level::*;
36use std::{
37    sync::{LazyLock, Mutex},
38    time::Duration,
39};
40
41static ADDR: LazyLock<Mutex<String>> = LazyLock::new(|| {
42    let addr = std::env::var("BB_ADDR").unwrap_or("127.0.0.1:7578".to_string());
43    Mutex::new(addr)
44});
45
46static TIMEOUT: LazyLock<Mutex<Option<Duration>>> = LazyLock::new(|| {
47    let timeout = std::env::var("BB_TIMEOUT").unwrap_or("".to_string());
48    if timeout == "" {
49        Mutex::new(None)
50    } else {
51        Mutex::new(Some(Duration::from_millis(timeout.parse().unwrap())))
52    }
53});
54
55/// Sets the server address.
56///
57/// Valid formats are "address:port" and "path/to/socket".
58pub fn set_addr(new_addr: &str) {
59    let mut addr = ADDR.lock().unwrap();
60    *addr = new_addr.to_string();
61}
62
63/// Sets timeout for TCP stream connect. Setting it to None disable timeout.
64pub fn set_timeout(new_timeout: Option<Duration>) {
65    let mut timeout = TIMEOUT.lock().unwrap();
66    *timeout = new_timeout;
67}
68
69/// Posts an ArrayObject.
70pub fn post(title: &str, tag: &str, obj: ArrayObject) -> Result<(), Box<dyn std::error::Error>> {
71    let mut stream = TcpOrUnixStream::connect()?;
72    stream.post_raw(title, tag, obj.pack())?;
73    stream.shutdown()?;
74    Ok(())
75}
76
77/// Posts an ArrayObject without compression.
78pub fn post_as_it_is(
79    title: &str,
80    tag: &str,
81    obj: ArrayObject,
82) -> Result<(), Box<dyn std::error::Error>> {
83    let mut stream = TcpOrUnixStream::connect()?;
84    stream.post_raw(title, tag, obj.pack_as_it_is())?;
85    stream.shutdown()?;
86    Ok(())
87}
88
89/// Reads ArrayObjects.
90///
91/// Tag can be None if there is only one tag exists for the title.
92/// When revisions is empty, the latest revision is returned.
93pub fn read(
94    title: &str,
95    tag: Option<&str>,
96    revisions: Vec<u64>,
97) -> Result<Vec<ArrayObject>, Box<dyn std::error::Error>> {
98    let mut stream = TcpOrUnixStream::connect()?;
99    let list = stream.read_raw(title, tag, revisions)?;
100    stream.shutdown()?;
101    let mut objs = vec![];
102    for data in list {
103        objs.push(ArrayObject::unpack(data)?);
104    }
105    Ok(objs)
106}
107
108/// Relabels a bulletin.
109pub fn relabel(
110    title_from: &str,
111    tag_from: Option<&str>,
112    title_to: Option<&str>,
113    tag_to: Option<&str>,
114) -> Result<(), Box<dyn std::error::Error>> {
115    let mut stream = TcpOrUnixStream::connect()?;
116    stream.relabel(title_from, tag_from, title_to, tag_to)?;
117    stream.shutdown()?;
118    Ok(())
119}
120
121/// Returns the version of the server.
122pub fn server_version() -> Result<String, Box<dyn std::error::Error>> {
123    let mut stream = TcpOrUnixStream::connect()?;
124    let version = stream.server_version()?;
125    stream.shutdown()?;
126    Ok(version)
127}
128
129/// Returns the version of the client.
130pub fn client_version() -> String {
131    let client_version = env!("CARGO_PKG_VERSION").to_string();
132    client_version
133}
134
135/// Returns the status of the server.
136///
137/// The return values are (total datasize (bytes), memory used (bytes), memory used (%), the number of objects, the number of objects backed by files, the number of archived objects)
138///
139/// The total datasize does not include the size of metadata such as timestamp.
140pub fn status() -> Result<(u64, u64, f64, u64, u64, u64), Box<dyn std::error::Error>> {
141    let mut stream = TcpOrUnixStream::connect()?;
142    let status = stream.status()?;
143    stream.shutdown()?;
144    Ok(status)
145}
146
147/// Returns the log of the server.
148pub fn log() -> Result<String, Box<dyn std::error::Error>> {
149    let mut stream = TcpOrUnixStream::connect()?;
150    let log = stream.log()?;
151    stream.shutdown()?;
152    Ok(log)
153}
154
155/// Returns the list of the bulletins.
156pub fn view_board() -> Result<Vec<(String, String, u64)>, Box<dyn std::error::Error>> {
157    let mut stream = TcpOrUnixStream::connect()?;
158    let list = stream.view_board()?;
159    stream.shutdown()?;
160    Ok(list)
161}
162
163/// Returns the details of a bulletin. The return values are a vector of (revision number, datasize (bytes), timestamp, backend).
164pub fn get_info(
165    title: &str,
166    tag: Option<&str>,
167) -> Result<Vec<(u64, u64, String, String)>, Box<dyn std::error::Error>> {
168    let mut stream = TcpOrUnixStream::connect()?;
169    let list = stream.get_info(title, tag)?;
170    stream.shutdown()?;
171    Ok(list)
172}
173
174/// Deletes specific revisions from a bulletin.
175pub fn clear_revisions(
176    title: &str,
177    tag: Option<&str>,
178    revisions: Vec<u64>,
179) -> Result<(), Box<dyn std::error::Error>> {
180    let mut stream = TcpOrUnixStream::connect()?;
181    stream.clear_revisions(title, tag, revisions)?;
182    stream.shutdown()?;
183    Ok(())
184}
185
186/// Removes all the revisions and the database entry of a bulletin.
187pub fn remove(title: &str, tag: Option<&str>) -> Result<(), Box<dyn std::error::Error>> {
188    let mut stream = TcpOrUnixStream::connect()?;
189    stream.remove(title, tag)?;
190    stream.shutdown()?;
191    Ok(())
192}
193
194/// Moves a bulletin to a persistent archive.
195pub fn archive(
196    acv_name: &str,
197    title: &str,
198    tag: Option<&str>,
199) -> Result<(), Box<dyn std::error::Error>> {
200    let mut stream = TcpOrUnixStream::connect()?;
201    stream.archive(acv_name, title, tag)?;
202    stream.shutdown()?;
203    Ok(())
204}
205
206/// Loads or reloads an archive. The data is directly read from the archive file and a suffix "acv_name:" is added to the tag.
207pub fn load(acv_name: &str) -> Result<(), Box<dyn std::error::Error>> {
208    let mut stream = TcpOrUnixStream::connect()?;
209    stream.load(acv_name)?;
210    stream.shutdown()?;
211    Ok(())
212}
213
214/// Shows the list of archive.
215pub fn list_archive() -> Result<Vec<String>, Box<dyn std::error::Error>> {
216    let mut stream = TcpOrUnixStream::connect()?;
217    let list = stream.list_archive()?;
218    stream.shutdown()?;
219    Ok(list)
220}
221
222/// Renames an archive. This will be applied after after calling reset_server.
223pub fn rename_archive(name_from: &str, name_to: &str) -> Result<(), Box<dyn std::error::Error>> {
224    let mut stream = TcpOrUnixStream::connect()?;
225    stream.rename_archive(name_from, name_to)?;
226    stream.shutdown()?;
227    Ok(())
228}
229
230/// Deletes an archive. This will be applied after after calling reset_server.
231pub fn delete_archive(acv_name: &str) -> Result<(), Box<dyn std::error::Error>> {
232    let mut stream = TcpOrUnixStream::connect()?;
233    stream.delete_archive(acv_name)?;
234    stream.shutdown()?;
235    Ok(())
236}
237
238/// Dumps all the unarchived data into an archive.
239pub fn dump(acv_name: &str) -> Result<(), Box<dyn std::error::Error>> {
240    let mut stream = TcpOrUnixStream::connect()?;
241    stream.dump(acv_name)?;
242    stream.shutdown()?;
243    Ok(())
244}
245
246/// Delete all the temporary data and restores data from an archive. Each data is copied to memory or a separate file. No suffix is added to the tag.
247pub fn restore(acv_name: &str) -> Result<(), Box<dyn std::error::Error>> {
248    let mut stream = TcpOrUnixStream::connect()?;
249    stream.restore(acv_name)?;
250    stream.shutdown()?;
251    Ok(())
252}
253
254/// Clears the log file of the server.
255pub fn clear_log() -> Result<(), Box<dyn std::error::Error>> {
256    let mut stream = TcpOrUnixStream::connect()?;
257    stream.clear_log()?;
258    stream.shutdown()?;
259    Ok(())
260}
261
262/// Resets and clears the data. The archived data is not affected, but must be loaded before use.
263pub fn reset_server() -> Result<(), Box<dyn std::error::Error>> {
264    let mut stream = TcpOrUnixStream::connect()?;
265    stream.reset_server()?;
266    stream.shutdown()?;
267    Ok(())
268}
269
270/// Terminates the server.
271pub fn terminate_server() -> Result<(), Box<dyn std::error::Error>> {
272    let mut stream = TcpOrUnixStream::connect()?;
273    stream.terminate_server()?;
274    stream.shutdown()?;
275    Ok(())
276}