nuts_tool/
backend.rs

1// MIT License
2//
3// Copyright (c) 2024 Robin Doer
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to
7// deal in the Software without restriction, including without limitation the
8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9// sell copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in
13// all copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21// IN THE SOFTWARE.
22
23use log::error;
24use nuts_backend::{Backend, Binary, Create, IdSize, Open, ReceiveHeader, HEADER_MAX_SIZE};
25use nuts_tool_api::tool::{Plugin, PluginConnection, PluginError};
26use std::cell::RefCell;
27use std::collections::HashMap;
28use std::str::FromStr;
29use std::{cmp, fmt};
30
31thread_local! {
32    static ID_SIZE: RefCell<usize> = RefCell::new(0);
33    static CONN: RefCell<Option<PluginConnection>> = RefCell::new(None);
34}
35
36fn setup_connection(mut connection: PluginConnection) -> Result<(), PluginError> {
37    let id_size = connection.id_size()?;
38
39    ID_SIZE.with(|size| *size.borrow_mut() = id_size);
40    CONN.with(|cell| *cell.borrow_mut() = Some(connection));
41
42    Ok(())
43}
44
45fn with_connection<T, F: FnOnce(&mut PluginConnection) -> Result<T, PluginError>>(
46    f: F,
47) -> Result<T, PluginError> {
48    CONN.with_borrow_mut(|opt| match opt.as_mut() {
49        Some(conn) => f(conn),
50        None => Err(PluginError::NotConnected),
51    })
52}
53
54#[derive(Clone, Debug)]
55pub struct PluginSettings(Vec<u8>);
56
57impl Binary for PluginSettings {
58    fn from_bytes(bytes: &[u8]) -> Option<PluginSettings> {
59        Some(PluginSettings(bytes.to_vec()))
60    }
61
62    fn as_bytes(&self) -> Vec<u8> {
63        self.0.clone()
64    }
65}
66
67#[derive(Clone, Debug, PartialEq)]
68pub struct PluginId(Vec<u8>);
69
70impl Binary for PluginId {
71    fn from_bytes(bytes: &[u8]) -> Option<PluginId> {
72        Some(PluginId(bytes.to_vec()))
73    }
74
75    fn as_bytes(&self) -> Vec<u8> {
76        self.0.clone()
77    }
78}
79
80impl IdSize for PluginId {
81    fn size() -> usize {
82        ID_SIZE.with(|n| *n.borrow())
83    }
84}
85
86impl FromStr for PluginId {
87    type Err = PluginError;
88
89    fn from_str(s: &str) -> Result<PluginId, PluginError> {
90        let bytes = with_connection(|conn| conn.id_string_to_bytes(s.to_string()))?;
91
92        Ok(PluginId(bytes))
93    }
94}
95
96impl fmt::Display for PluginId {
97    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
98        match with_connection(|conn| conn.id_bytes_to_string(self.0.clone())) {
99            Ok(s) => fmt.write_str(&s),
100            Err(_) => fmt.write_str("???"),
101        }
102    }
103}
104
105pub struct PluginBackendOpenBuilder;
106
107impl PluginBackendOpenBuilder {
108    pub fn new(
109        plugin: Plugin,
110        name: &str,
111        verbose: u8,
112    ) -> Result<PluginBackendOpenBuilder, PluginError> {
113        setup_connection(plugin.open(name, verbose)?)?;
114
115        Ok(PluginBackendOpenBuilder)
116    }
117}
118
119impl ReceiveHeader<PluginBackend> for PluginBackendOpenBuilder {
120    fn get_header_bytes(&mut self, bytes: &mut [u8; HEADER_MAX_SIZE]) -> Result<(), PluginError> {
121        let header = with_connection(|conn| conn.read_header())?;
122
123        bytes.copy_from_slice(&header[..HEADER_MAX_SIZE]);
124
125        Ok(())
126    }
127}
128
129impl Open<PluginBackend> for PluginBackendOpenBuilder {
130    fn build(self, settings: PluginSettings) -> Result<PluginBackend, PluginError> {
131        with_connection(|conn| conn.open(settings.0.clone()))?;
132
133        PluginBackend::new()
134    }
135}
136
137pub struct PluginBackendCreateBuilder {
138    settings: Vec<u8>,
139}
140
141impl PluginBackendCreateBuilder {
142    pub fn new(
143        plugin: Plugin,
144        name: &str,
145        verbose: u8,
146        extra_args: &[String],
147    ) -> Result<PluginBackendCreateBuilder, PluginError> {
148        setup_connection(plugin.create(name, verbose, extra_args)?)?;
149
150        let settings = with_connection(|conn| conn.settings())?;
151
152        Ok(PluginBackendCreateBuilder { settings })
153    }
154}
155
156impl Create<PluginBackend> for PluginBackendCreateBuilder {
157    fn settings(&self) -> PluginSettings {
158        PluginSettings(self.settings.clone())
159    }
160
161    fn build(
162        self,
163        header: [u8; HEADER_MAX_SIZE],
164        overwrite: bool,
165    ) -> Result<PluginBackend, PluginError> {
166        with_connection(|conn| conn.create(header.to_vec(), overwrite))?;
167
168        PluginBackend::new()
169    }
170}
171
172#[derive(Debug)]
173pub struct PluginBackend {
174    block_size: u32,
175}
176
177impl PluginBackend {
178    fn new() -> Result<PluginBackend, PluginError> {
179        let block_size = with_connection(|conn| conn.block_size())?;
180
181        Ok(PluginBackend { block_size })
182    }
183}
184
185impl ReceiveHeader<PluginBackend> for PluginBackend {
186    fn get_header_bytes(&mut self, bytes: &mut [u8; HEADER_MAX_SIZE]) -> Result<(), PluginError> {
187        let header = with_connection(|conn| conn.read_header())?;
188
189        bytes.copy_from_slice(&header[..HEADER_MAX_SIZE]);
190
191        Ok(())
192    }
193}
194
195impl Backend for PluginBackend {
196    type Settings = PluginSettings;
197    type Err = PluginError;
198    type Id = PluginId;
199    type Info = HashMap<String, String>;
200
201    fn info(&self) -> Result<Self::Info, PluginError> {
202        with_connection(|conn| conn.info())
203    }
204
205    fn block_size(&self) -> u32 {
206        self.block_size
207    }
208
209    fn aquire(&mut self, buf: &[u8]) -> Result<PluginId, PluginError> {
210        let id = with_connection(|conn| conn.aquire(buf.to_vec()))?;
211
212        Ok(PluginId(id))
213    }
214
215    fn release(&mut self, id: PluginId) -> Result<(), PluginError> {
216        with_connection(|conn| conn.release(id.0))
217    }
218
219    fn read(&mut self, id: &PluginId, buf: &mut [u8]) -> Result<usize, PluginError> {
220        let bytes = with_connection(|conn| conn.read(id.0.clone()))?;
221
222        let n = cmp::min(bytes.len(), buf.len());
223        buf.copy_from_slice(&bytes);
224
225        Ok(n)
226    }
227
228    fn write(&mut self, id: &PluginId, buf: &[u8]) -> Result<usize, PluginError> {
229        with_connection(|conn| conn.write(id.0.clone(), buf.to_vec()))
230    }
231
232    fn write_header(&mut self, buf: &[u8; HEADER_MAX_SIZE]) -> Result<(), PluginError> {
233        with_connection(|conn| conn.write_header(buf.to_vec()))
234    }
235
236    fn delete(self) {
237        if let Err(err) = with_connection(|conn| conn.delete()) {
238            error!("failed to delete backend instance: {}", err);
239        }
240    }
241}
242
243impl Drop for PluginBackend {
244    fn drop(&mut self) {
245        if let Err(err) = with_connection(|conn| conn.quit()) {
246            error!("failed to quit connection to plugin: {}", err);
247        };
248    }
249}