sip2/
client.rs

1use super::connection::Connection;
2use super::error::Error;
3use super::params::*;
4use super::{spec, util, Field, FixedField, Message};
5use std::str;
6
7/// Wrapper for Connection which provides a simpler interface for some
8/// common SIP2 actions.
9///
10/// This is not a complete set of friendly-ified requests.  Just a start.
11///
12/// ```no_run
13/// use sip2::{Client, ParamSet};
14/// let mut client = Client::new("127.0.0.1:6001").expect("Cannot Connect");
15///
16/// let mut params = ParamSet::new();
17/// params.set_sip_user("sip-server-login");
18/// params.set_sip_pass("sip-server-password");
19///
20/// // Login to the SIP server
21/// match client.login(&params).expect("Login Error").ok() {
22///     true => println!("Login OK"),
23///     false => eprintln!("Login Failed"),
24/// }
25/// ```
26pub struct Client {
27    connection: Connection,
28}
29
30impl Client {
31    /// Creates a new SIP client and opens the TCP connection to the server.
32    pub fn new(host: &str) -> Result<Self, Error> {
33        Ok(Client {
34            connection: Connection::new(host)?,
35        })
36    }
37
38    /// Shutdown the TCP connection with the SIP server.
39    pub fn disconnect(&self) -> Result<(), Error> {
40        self.connection.disconnect()
41    }
42
43    /// Login to the SIP server
44    ///
45    /// Sets ok=true if the OK fixed field is true.
46    pub fn login(&mut self, params: &ParamSet) -> Result<SipResponse, Error> {
47        let user = match params.sip_user() {
48            Some(u) => u,
49            _ => return Err(Error::MissingParamsError),
50        };
51
52        let pass = match params.sip_pass() {
53            Some(u) => u,
54            _ => return Err(Error::MissingParamsError),
55        };
56
57        let mut req = Message::new(
58            &spec::M_LOGIN,
59            vec![
60                FixedField::new(&spec::FF_UID_ALGO, "0").unwrap(),
61                FixedField::new(&spec::FF_PWD_ALGO, "0").unwrap(),
62            ],
63            vec![
64                Field::new(spec::F_LOGIN_UID.code, user),
65                Field::new(spec::F_LOGIN_PWD.code, pass),
66            ],
67        );
68
69        req.maybe_add_field(spec::F_LOCATION_CODE.code, params.location());
70
71        let resp = self.connection.sendrecv(&req)?;
72
73        if resp.spec().code == spec::M_LOGIN_RESP.code
74            && resp.fixed_fields().len() == 1
75            && resp.fixed_fields()[0].value() == "1"
76        {
77            Ok(SipResponse::new(resp, true))
78        } else {
79            Ok(SipResponse::new(resp, false))
80        }
81    }
82
83    /// Send the SC status message
84    ///
85    /// Sets ok=true if the server reports that it's online.
86    pub fn sc_status(&mut self) -> Result<SipResponse, Error> {
87        let req = Message::new(
88            &spec::M_SC_STATUS,
89            vec![
90                FixedField::new(&spec::FF_STATUS_CODE, "0").unwrap(),
91                FixedField::new(&spec::FF_MAX_PRINT_WIDTH, "999").unwrap(),
92                FixedField::new(&spec::FF_PROTOCOL_VERSION, spec::SIP_PROTOCOL_VERSION).unwrap(),
93            ],
94            vec![],
95        );
96
97        let resp = self.connection.sendrecv(&req)?;
98
99        if !resp.fixed_fields().is_empty() && resp.fixed_fields()[0].value() == "Y" {
100            Ok(SipResponse::new(resp, true))
101        } else {
102            Ok(SipResponse::new(resp, false))
103        }
104    }
105
106    /// Send a patron status request
107    ///
108    /// Sets ok=true if the "valid patron" (BL) field is "Y"
109    pub fn patron_status(&mut self, params: &ParamSet) -> Result<SipResponse, Error> {
110        let patron_id = match params.patron_id() {
111            Some(p) => p,
112            _ => return Err(Error::MissingParamsError),
113        };
114
115        let mut req = Message::new(
116            &spec::M_PATRON_STATUS,
117            vec![
118                FixedField::new(&spec::FF_LANGUAGE, "000").unwrap(),
119                FixedField::new(&spec::FF_DATE, &util::sip_date_now()).unwrap(),
120            ],
121            vec![Field::new(spec::F_PATRON_ID.code, patron_id)],
122        );
123
124        req.maybe_add_field(spec::F_INSTITUTION_ID.code, params.institution());
125        req.maybe_add_field(spec::F_PATRON_PWD.code, params.patron_pwd());
126        req.maybe_add_field(spec::F_TERMINAL_PWD.code, params.terminal_pwd());
127
128        let resp = self.connection.sendrecv(&req)?;
129
130        if let Some(bl_val) = resp.get_field_value(spec::F_VALID_PATRON.code) {
131            if bl_val == "Y" {
132                return Ok(SipResponse::new(resp, true));
133            }
134        }
135
136        Ok(SipResponse::new(resp, false))
137    }
138
139    /// Send a patron information request
140    ///
141    /// Sets ok=true if the "valid patron" (BL) field is "Y"
142    pub fn patron_info(&mut self, params: &ParamSet) -> Result<SipResponse, Error> {
143        let patron_id = match params.patron_id() {
144            Some(p) => p,
145            None => return Err(Error::MissingParamsError),
146        };
147
148        let mut summary: [char; 10] = [' '; 10];
149
150        if let Some(idx) = params.summary() {
151            if idx < 10 {
152                summary[idx] = 'Y';
153            }
154        }
155
156        let sum_str: String = summary.iter().collect::<String>();
157
158        let mut req = Message::new(
159            &spec::M_PATRON_INFO,
160            vec![
161                FixedField::new(&spec::FF_LANGUAGE, "000").unwrap(),
162                FixedField::new(&spec::FF_DATE, &util::sip_date_now()).unwrap(),
163                FixedField::new(&spec::FF_SUMMARY, &sum_str).unwrap(),
164            ],
165            vec![Field::new(spec::F_PATRON_ID.code, patron_id)],
166        );
167
168        req.maybe_add_field(spec::F_INSTITUTION_ID.code, params.institution());
169        req.maybe_add_field(spec::F_PATRON_PWD.code, params.patron_pwd());
170        req.maybe_add_field(spec::F_TERMINAL_PWD.code, params.terminal_pwd());
171
172        if let Some(v) = params.start_item() {
173            req.add_field(spec::F_START_ITEM.code, &v.to_string());
174        }
175
176        if let Some(v) = params.end_item() {
177            req.add_field(spec::F_END_ITEM.code, &v.to_string());
178        }
179
180        let resp = self.connection.sendrecv(&req)?;
181
182        if let Some(bl_val) = resp.get_field_value(spec::F_VALID_PATRON.code) {
183            if bl_val == "Y" {
184                return Ok(SipResponse::new(resp, true));
185            }
186        }
187
188        Ok(SipResponse::new(resp, false))
189    }
190
191    /// Send a item information request
192    ///
193    /// Sets ok=true if a title (AJ) value is present.  Oddly, there's no
194    /// specific "item does not exist" value in the Item Info Response.
195    pub fn item_info(&mut self, params: &ParamSet) -> Result<SipResponse, Error> {
196        let item_id = match params.item_id() {
197            Some(id) => id,
198            None => return Err(Error::MissingParamsError),
199        };
200
201        let mut req = Message::new(
202            &spec::M_ITEM_INFO,
203            vec![FixedField::new(&spec::FF_DATE, &util::sip_date_now()).unwrap()],
204            vec![Field::new(spec::F_ITEM_IDENT.code, item_id)],
205        );
206
207        req.maybe_add_field(spec::F_INSTITUTION_ID.code, params.institution());
208        req.maybe_add_field(spec::F_TERMINAL_PWD.code, params.terminal_pwd());
209
210        let resp = self.connection.sendrecv(&req)?;
211
212        if let Some(title_val) = resp.get_field_value(spec::F_TITLE_IDENT.code) {
213            if !title_val.is_empty() {
214                return Ok(SipResponse::new(resp, true));
215            }
216        }
217
218        Ok(SipResponse::new(resp, false))
219    }
220
221    /// Send a CHECKOUT request
222    pub fn checkout(&mut self, params: &ParamSet) -> Result<SipResponse, Error> {
223        let item_id = params.item_id().ok_or(Error::MissingParamsError)?;
224        let patron_id = params.patron_id().ok_or(Error::MissingParamsError)?;
225
226        let mut req = Message::from_values(
227            spec::M_CHECKOUT.code,
228            &[
229                "N",                   // renewal policy
230                "N",                   // no block
231                &util::sip_date_now(), // transaction date
232                &util::sip_date_now(), // no block due date
233            ],
234            &[
235                (spec::F_ITEM_IDENT.code, item_id),
236                (spec::F_PATRON_IDENT.code, patron_id),
237            ],
238        )?;
239
240        req.maybe_add_field(spec::F_INSTITUTION_ID.code, params.institution());
241        req.maybe_add_field(spec::F_TERMINAL_PWD.code, params.terminal_pwd());
242        req.maybe_add_field(spec::F_PATRON_PWD.code, params.patron_pwd());
243
244        let resp = self.connection.sendrecv(&req)?;
245
246        if let Some(status) = resp.fixed_fields().first() {
247            if status.value() == "1" {
248                return Ok(SipResponse::new(resp, true));
249            }
250        }
251
252        Ok(SipResponse::new(resp, false))
253    }
254
255    /// Send a CHECKIN request
256    pub fn checkin(&mut self, params: &ParamSet) -> Result<SipResponse, Error> {
257        let item_id = params.item_id().ok_or(Error::MissingParamsError)?;
258
259        let mut req = Message::from_values(
260            spec::M_CHECKIN.code,
261            &[
262                "N",                   // no block
263                &util::sip_date_now(), // transaction date
264                &util::sip_date_now(), // no block due date
265            ],
266            &[(spec::F_ITEM_IDENT.code, item_id)],
267        )?;
268
269        req.maybe_add_field(spec::F_INSTITUTION_ID.code, params.institution());
270        req.maybe_add_field(spec::F_TERMINAL_PWD.code, params.terminal_pwd());
271
272        let resp = self.connection.sendrecv(&req)?;
273
274        if let Some(status) = resp.fixed_fields().first() {
275            if status.value() == "1" {
276                return Ok(SipResponse::new(resp, true));
277            }
278        }
279
280        Ok(SipResponse::new(resp, false))
281    }
282
283    pub fn fee_paid(&mut self, params: &ParamSet) -> Result<SipResponse, Error> {
284        let patron_id = params.patron_id().ok_or(Error::MissingParamsError)?;
285        let pay_amount = params.pay_amount().ok_or(Error::MissingParamsError)?;
286
287        let pay_amount = pay_amount.to_string();
288
289        let fee_type = params.fee_type().unwrap_or(spec::FeeType::OtherUnknown);
290        let pay_type = params.pay_type().unwrap_or(spec::PayType::Cash);
291
292        let mut req = Message::from_values(
293            spec::M_FEE_PAID.code,
294            &[
295                &util::sip_date_now(), // transaction date
296                fee_type.into(),
297                pay_type.into(),
298                "USD", // TODO
299            ],
300            &[
301                (spec::F_PATRON_ID.code, patron_id),
302                (spec::F_FEE_AMOUNT.code, &pay_amount),
303            ],
304        )?;
305
306        req.maybe_add_field(spec::F_INSTITUTION_ID.code, params.institution());
307        req.maybe_add_field(spec::F_TERMINAL_PWD.code, params.terminal_pwd());
308        req.maybe_add_field(spec::F_TRANSACTION_ID.code, params.transaction_id());
309        req.maybe_add_field(spec::F_FEE_IDENTIFIER.code, params.fee_id());
310
311        let resp = self.connection.sendrecv(&req)?;
312
313        if let Some(status) = resp.fixed_fields().first() {
314            if status.value() == "1" {
315                return Ok(SipResponse::new(resp, true));
316            }
317        }
318
319        Ok(SipResponse::new(resp, false))
320    }
321}
322
323/// Wrapper for holding the SIP response message and a simplistic
324/// "OK" flag.
325pub struct SipResponse {
326    /// The response message.
327    msg: Message,
328
329    /// True if the message response indicates a success.
330    ///
331    /// The definition of success varies per request type and may not
332    /// match the caller's requirements.  See the full message in
333    /// 'msg' to inspect the entire response.
334    ok: bool,
335}
336
337impl SipResponse {
338    pub fn new(msg: Message, ok: bool) -> Self {
339        SipResponse { msg, ok }
340    }
341
342    pub fn ok(&self) -> bool {
343        self.ok
344    }
345    pub fn msg(&self) -> &Message {
346        &self.msg
347    }
348    pub fn msg_mut(&mut self) -> &mut Message {
349        &mut self.msg
350    }
351
352    /// Shortcut for this.resp.msg().get_field_value(code)
353    pub fn value(&self, code: &str) -> Option<&str> {
354        self.msg().get_field_value(code)
355    }
356}