1use crate::osrf::logging;
2use crate::util;
3use crate::{EgResult, EgValue};
4use json::JsonValue;
5use std::cell::RefCell;
6use std::fmt;
7
8const DEFAULT_TIMEZONE: &str = "America/New_York";
9const DEFAULT_API_LEVEL: u8 = 1;
10const DEFAULT_INGRESS: &str = "opensrf";
11const OSRF_MESSAGE_CLASS: &str = "osrfMessage";
12const EG_NULL: EgValue = EgValue::Null;
13const DEFAULT_LOCALE: &str = "en-US";
14const MAX_LOCALE_LEN: usize = 16;
16
17thread_local! {
22 static THREAD_LOCALE: RefCell<String> = RefCell::new(DEFAULT_LOCALE.to_string());
23 static THREAD_INGRESS: RefCell<String> = RefCell::new(DEFAULT_INGRESS.to_string());
24}
25
26pub fn set_thread_locale(locale: &str) {
28 THREAD_LOCALE.with(|lc| {
29 if lc.borrow().as_str() == locale {
31 return;
32 }
33
34 if locale.len() > MAX_LOCALE_LEN {
37 log::error!("Invalid locale: '{locale}'");
38 return;
39 }
40
41 if locale
42 .chars()
43 .any(|b| !b.is_ascii_alphabetic() && b != '-' && b != '.')
44 {
45 log::error!("Invalid locale: '{locale}'");
46 return;
47 }
48
49 *lc.borrow_mut() = locale.to_string();
50 });
51}
52
53pub fn set_thread_ingress(ingress: &str) {
55 THREAD_INGRESS.with(|lc| {
56 if lc.borrow().as_str() == ingress {
57 return;
58 }
59 *lc.borrow_mut() = ingress.to_string();
60 });
61}
62
63pub fn reset_thread_locale() {
65 set_thread_locale(DEFAULT_LOCALE);
66}
67
68pub fn thread_locale() -> String {
72 let mut locale = None;
73 THREAD_LOCALE.with(|lc| locale = Some((*lc.borrow()).to_string()));
74 locale.unwrap()
75}
76
77#[derive(Debug, Copy, Clone, PartialEq)]
78pub enum MessageType {
79 Connect,
80 Request,
81 Result,
82 Status,
83 Disconnect,
84 Unknown,
85}
86
87#[rustfmt::skip]
94impl From<&str> for MessageType {
95 fn from(s: &str) -> Self {
96 match s {
97 "CONNECT" => MessageType::Connect,
98 "REQUEST" => MessageType::Request,
99 "RESULT" => MessageType::Result,
100 "STATUS" => MessageType::Status,
101 "DISCONNECT" => MessageType::Disconnect,
102 _ => MessageType::Unknown,
103 }
104 }
105}
106
107impl From<MessageType> for &'static str {
115 fn from(mt: MessageType) -> &'static str {
116 match mt {
117 MessageType::Connect => "CONNECT",
118 MessageType::Request => "REQUEST",
119 MessageType::Result => "RESULT",
120 MessageType::Status => "STATUS",
121 MessageType::Disconnect => "DISCONNECT",
122 _ => "UNKNOWN",
123 }
124 }
125}
126
127impl fmt::Display for MessageType {
128 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
129 let s: &str = (*self).into();
130 write!(f, "{}", s)
131 }
132}
133
134#[derive(Debug, Copy, Clone, PartialEq)]
136#[rustfmt::skip]
137pub enum MessageStatus {
138 Continue = 100,
139 Ok = 200,
140 Accepted = 202,
141 PartialComplete = 204,
142 Complete = 205,
143 Partial = 206,
144 Redirected = 307,
145 BadRequest = 400,
146 Unauthorized = 401,
147 Forbidden = 403,
148 MethodNotFound = 404,
149 NotAllowed = 405,
150 ServiceNotFound = 406,
151 Timeout = 408,
152 Expfailed = 417,
153 InternalServerError = 500,
154 NotImplemented = 501,
155 ServiceUnavailable = 503,
156 VersionNotSupported = 505,
157 Unknown,
158}
159
160#[rustfmt::skip]
167impl From<isize> for MessageStatus {
168 fn from(num: isize) -> Self {
169 match num {
170 100 => MessageStatus::Continue,
171 200 => MessageStatus::Ok,
172 202 => MessageStatus::Accepted,
173 204 => MessageStatus::PartialComplete,
174 205 => MessageStatus::Complete,
175 206 => MessageStatus::Partial,
176 307 => MessageStatus::Redirected,
177 400 => MessageStatus::BadRequest,
178 401 => MessageStatus::Unauthorized,
179 403 => MessageStatus::Forbidden,
180 404 => MessageStatus::MethodNotFound,
181 405 => MessageStatus::NotAllowed,
182 406 => MessageStatus::ServiceNotFound,
183 408 => MessageStatus::Timeout,
184 417 => MessageStatus::Expfailed,
185 500 => MessageStatus::InternalServerError,
186 501 => MessageStatus::NotImplemented,
187 503 => MessageStatus::ServiceUnavailable,
188 505 => MessageStatus::VersionNotSupported,
189 _ => MessageStatus::Unknown,
190 }
191 }
192}
193
194#[rustfmt::skip]
201impl From<MessageStatus> for &'static str {
202 fn from(ms: MessageStatus) -> &'static str {
203 match ms {
204 MessageStatus::Ok => "OK",
205 MessageStatus::Continue => "Continue",
206 MessageStatus::Complete => "Request Complete",
207 MessageStatus::BadRequest => "Bad Request",
208 MessageStatus::Timeout => "Timeout",
209 MessageStatus::MethodNotFound => "Method Not Found",
210 MessageStatus::NotAllowed => "Not Allowed",
211 MessageStatus::ServiceNotFound => "Service Not Found",
212 MessageStatus::InternalServerError => "Internal Server Error",
213 _ => "See Status Code",
214 }
215 }
216}
217
218impl fmt::Display for MessageStatus {
219 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
220 write!(f, "({}) {:?}", *self as isize, self)
221 }
222}
223
224impl MessageStatus {
225 pub fn is_4xx(&self) -> bool {
226 let num = *self as isize;
227 (400..500).contains(&num)
228 }
229 pub fn is_5xx(&self) -> bool {
230 let num = *self as isize;
231 num >= 500
232 }
233}
234
235#[derive(Debug, Clone, PartialEq)]
237pub enum Payload {
238 Method(MethodCall),
239 Result(Result),
240 Status(Status),
241 NoPayload,
242}
243
244impl Payload {
245 pub fn into_json_value(self) -> JsonValue {
246 match self {
247 Payload::Method(pl) => pl.into_json_value(),
248 Payload::Result(pl) => pl.into_json_value(),
249 Payload::Status(pl) => pl.into_json_value(),
250 Payload::NoPayload => JsonValue::Null,
251 }
252 }
253}
254
255#[derive(Debug, PartialEq, Clone)]
258pub struct TransportMessage {
259 to: String,
260 from: String,
261 thread: String,
262 osrf_xid: String,
263 router_command: Option<String>,
264 router_class: Option<String>,
265 router_reply: Option<String>,
266 body: Vec<Message>,
267}
268
269impl TransportMessage {
270 pub fn new(to: &str, from: &str, thread: &str) -> Self {
271 TransportMessage {
272 to: to.to_string(),
273 from: from.to_string(),
274 thread: thread.to_string(),
275 osrf_xid: logging::Logger::get_log_trace(),
276 router_command: None,
277 router_class: None,
278 router_reply: None,
279 body: Vec::new(),
280 }
281 }
282
283 pub fn with_body(to: &str, from: &str, thread: &str, msg: Message) -> Self {
284 let mut tm = TransportMessage::new(to, from, thread);
285 tm.body.push(msg);
286 tm
287 }
288
289 pub fn with_body_vec(to: &str, from: &str, thread: &str, msgs: Vec<Message>) -> Self {
290 let mut tm = TransportMessage::new(to, from, thread);
291 tm.body = msgs;
292 tm
293 }
294
295 pub fn to(&self) -> &str {
296 &self.to
297 }
298
299 pub fn set_to(&mut self, to: &str) {
300 self.to = to.to_string();
301 }
302
303 pub fn from(&self) -> &str {
304 &self.from
305 }
306
307 pub fn set_from(&mut self, from: &str) {
308 self.from = from.to_string();
309 }
310
311 pub fn thread(&self) -> &str {
312 &self.thread
313 }
314
315 pub fn body(&self) -> &Vec<Message> {
316 &self.body
317 }
318
319 pub fn body_mut(&mut self) -> &mut Vec<Message> {
320 &mut self.body
321 }
322
323 pub fn take_body(&mut self) -> Vec<Message> {
324 std::mem::take(&mut self.body)
325 }
326
327 pub fn osrf_xid(&self) -> &str {
328 &self.osrf_xid
329 }
330
331 pub fn set_osrf_xid(&mut self, xid: &str) {
332 self.osrf_xid = xid.to_string()
333 }
334
335 pub fn router_command(&self) -> Option<&str> {
336 self.router_command.as_deref()
337 }
338
339 pub fn set_router_command(&mut self, command: &str) {
340 self.router_command = Some(command.to_string());
341 }
342
343 pub fn router_class(&self) -> Option<&str> {
344 self.router_class.as_deref()
345 }
346
347 pub fn set_router_class(&mut self, class: &str) {
348 self.router_class = Some(class.to_string());
349 }
350
351 pub fn router_reply(&self) -> Option<&str> {
352 self.router_reply.as_deref()
353 }
354
355 pub fn set_router_reply(&mut self, reply: &str) {
356 self.router_reply = Some(reply.to_string());
357 }
358
359 pub fn from_json_value(mut json_obj: JsonValue, raw_data_mode: bool) -> EgResult<Self> {
363 let err = || "Invalid TransportMessage".to_string();
364
365 let to = json_obj["to"].as_str().ok_or_else(err)?;
366 let from = json_obj["from"].as_str().ok_or_else(err)?;
367 let thread = json_obj["thread"].as_str().ok_or_else(err)?;
368
369 let mut tmsg = TransportMessage::new(to, from, thread);
370
371 if let Some(xid) = json_obj["osrf_xid"].as_str() {
372 logging::Logger::set_log_trace(xid);
373 tmsg.set_osrf_xid(xid);
374 };
375
376 if let Some(rc) = json_obj["router_command"].as_str() {
377 tmsg.set_router_command(rc);
378 }
379
380 if let Some(rc) = json_obj["router_class"].as_str() {
381 tmsg.set_router_class(rc);
382 }
383
384 if let Some(rc) = json_obj["router_reply"].as_str() {
385 tmsg.set_router_reply(rc);
386 }
387
388 let body = json_obj["body"].take();
389
390 if let JsonValue::Array(arr) = body {
391 for body in arr {
392 tmsg.body_mut()
393 .push(Message::from_json_value(body, raw_data_mode)?);
394 }
395 } else if body.is_object() {
396 tmsg.body_mut()
398 .push(Message::from_json_value(body, raw_data_mode)?);
399 }
400
401 Ok(tmsg)
402 }
403
404 pub fn into_json_value(mut self) -> JsonValue {
405 let mut body: Vec<JsonValue> = Vec::new();
406
407 while !self.body.is_empty() {
408 body.push(self.body.remove(0).into_json_value());
409 }
410
411 let mut obj = json::object! {
412 to: self.to(),
413 from: self.from(),
414 thread: self.thread(),
415 osrf_xid: self.osrf_xid(),
416 body: body,
417 };
418
419 if let Some(rc) = self.router_command() {
420 obj["router_command"] = rc.into();
421 }
422
423 if let Some(rc) = self.router_class() {
424 obj["router_class"] = rc.into();
425 }
426
427 if let Some(rc) = self.router_reply() {
428 obj["router_reply"] = rc.into();
429 }
430
431 obj
432 }
433}
434
435#[derive(Debug, Clone, PartialEq)]
436pub struct Message {
437 mtype: MessageType,
438 thread_trace: usize,
439 timezone: Option<String>,
440 api_level: u8,
441 ingress: Option<String>,
442 payload: Payload,
443}
444
445impl Message {
446 pub fn new(mtype: MessageType, thread_trace: usize, payload: Payload) -> Self {
447 Message {
448 mtype,
449 thread_trace,
450 payload,
451 api_level: DEFAULT_API_LEVEL,
452 timezone: None,
453 ingress: None,
454 }
455 }
456
457 pub fn mtype(&self) -> &MessageType {
458 &self.mtype
459 }
460
461 pub fn thread_trace(&self) -> usize {
462 self.thread_trace
463 }
464
465 pub fn payload(&self) -> &Payload {
466 &self.payload
467 }
468 pub fn payload_mut(&mut self) -> &mut Payload {
469 &mut self.payload
470 }
471 pub fn take_payload(&mut self) -> Payload {
474 std::mem::replace(&mut self.payload, Payload::NoPayload)
475 }
476
477 pub fn api_level(&self) -> u8 {
478 self.api_level
479 }
480
481 pub fn set_api_level(&mut self, level: u8) {
482 self.api_level = level;
483 }
484
485 pub fn timezone(&self) -> &str {
486 self.timezone.as_deref().unwrap_or(DEFAULT_TIMEZONE)
487 }
488
489 pub fn set_timezone(&mut self, timezone: &str) {
490 self.timezone = Some(timezone.to_string())
491 }
492
493 pub fn ingress(&self) -> Option<&str> {
494 self.ingress.as_deref()
495 }
496
497 pub fn set_ingress(&mut self, ingress: &str) {
498 self.ingress = Some(ingress.to_string())
499 }
500
501 pub fn from_json_value(json_obj: JsonValue, raw_data_mode: bool) -> EgResult<Self> {
505 let err = || "Invalid JSON Message".to_string();
506
507 let (msg_class, mut msg_hash) = EgValue::remove_class_wrapper(json_obj).ok_or_else(err)?;
508
509 if msg_class != "osrfMessage" {
510 return Err(format!("Unknown message class {msg_class}").into());
511 }
512
513 let thread_trace = util::json_usize(&msg_hash["threadTrace"]).ok_or_else(err)?;
514
515 let mtype_str = msg_hash["type"].as_str().ok_or_else(err)?;
516
517 let mtype: MessageType = mtype_str.into();
518 let payload = msg_hash["payload"].take();
519
520 let payload = Message::payload_from_json_value(mtype, payload, raw_data_mode)?;
521
522 let mut msg = Message::new(mtype, thread_trace, payload);
523
524 if let Some(tz) = msg_hash["tz"].as_str() {
525 msg.set_timezone(tz);
526 }
527
528 if let Some(lc) = msg_hash["locale"].as_str() {
531 set_thread_locale(lc);
532 }
533
534 if let Some(ing) = msg_hash["ingress"].as_str() {
535 set_thread_ingress(ing);
536 msg.set_ingress(ing);
537 }
538
539 if let Some(al) = msg_hash["api_level"].as_u8() {
540 msg.set_api_level(al);
541 }
542
543 Ok(msg)
544 }
545
546 fn payload_from_json_value(
547 mtype: MessageType,
548 payload_obj: JsonValue,
549 raw_data_mode: bool,
550 ) -> EgResult<Payload> {
551 match mtype {
552 MessageType::Request => {
553 let method = MethodCall::from_json_value(payload_obj, raw_data_mode)?;
554 Ok(Payload::Method(method))
555 }
556
557 MessageType::Result => {
558 let result = Result::from_json_value(payload_obj, raw_data_mode)?;
559 Ok(Payload::Result(result))
560 }
561
562 MessageType::Status => {
563 let stat = Status::from_json_value(payload_obj)?;
566 Ok(Payload::Status(stat))
567 }
568
569 _ => Ok(Payload::NoPayload),
570 }
571 }
572
573 pub fn into_json_value(self) -> JsonValue {
574 let mtype: &str = self.mtype.into();
575
576 let mut obj = json::object! {
577 threadTrace: self.thread_trace,
578 type: mtype,
579 locale: thread_locale(),
580 timezone: self.timezone(),
581 api_level: self.api_level(),
582 };
583
584 if let Some(ing) = self.ingress() {
585 obj["ingress"] = ing.into();
586 } else {
587 THREAD_INGRESS.with(|lc| obj["ingress"] = lc.borrow().as_str().into());
590 }
591
592 match self.payload {
593 Payload::NoPayload => {}
595 _ => obj["payload"] = self.payload.into_json_value(),
596 }
597
598 EgValue::add_class_wrapper(obj, OSRF_MESSAGE_CLASS)
599 }
600}
601
602#[derive(Debug, Clone, PartialEq)]
606pub struct Result {
607 status: MessageStatus,
608
609 status_label: String,
610
611 msg_class: String,
612
613 content: EgValue,
615}
616
617impl Result {
618 pub fn new(
619 status: MessageStatus,
620 status_label: &str,
621 msg_class: &str,
622 content: EgValue,
623 ) -> Self {
624 Result {
625 status,
626 content,
627 msg_class: msg_class.to_string(),
628 status_label: status_label.to_string(),
629 }
630 }
631
632 pub fn content(&self) -> &EgValue {
633 &self.content
634 }
635
636 pub fn content_mut(&mut self) -> &mut EgValue {
637 &mut self.content
638 }
639
640 pub fn take_content(&mut self) -> EgValue {
641 self.content.take()
642 }
643
644 pub fn set_content(&mut self, v: EgValue) {
645 self.content = v
646 }
647
648 pub fn status(&self) -> &MessageStatus {
649 &self.status
650 }
651
652 pub fn status_label(&self) -> &str {
653 &self.status_label
654 }
655
656 pub fn from_json_value(json_obj: JsonValue, raw_data_mode: bool) -> EgResult<Self> {
657 let err = || "Invalid Result message".to_string();
658
659 let (msg_class, mut msg_hash) = EgValue::remove_class_wrapper(json_obj).ok_or_else(err)?;
660
661 let content = if raw_data_mode {
662 EgValue::from_json_value_plain(msg_hash["content"].take())
663 } else {
664 EgValue::from_json_value(msg_hash["content"].take())?
665 };
666
667 let code = util::json_isize(&msg_hash["statusCode"]).ok_or_else(err)?;
668 let stat: MessageStatus = code.into();
669
670 let stat_str: &str = msg_hash["status"].as_str().unwrap_or(stat.into());
673
674 Ok(Result::new(stat, stat_str, &msg_class, content))
675 }
676
677 pub fn into_json_value(mut self) -> JsonValue {
678 let obj = json::object! {
679 status: self.status_label(),
680 statusCode: self.status as isize,
681 content: self.content.take().into_json_value(),
682 };
683
684 EgValue::add_class_wrapper(obj, &self.msg_class)
685 }
686}
687
688#[derive(Debug, Clone, PartialEq)]
689pub struct Status {
690 status: MessageStatus,
691 status_label: String,
692 msg_class: String,
693}
694
695impl Status {
696 pub fn new(status: MessageStatus, status_label: &str, msg_class: &str) -> Self {
697 Status {
698 status,
699 status_label: status_label.to_string(),
700 msg_class: msg_class.to_string(),
701 }
702 }
703
704 pub fn status(&self) -> &MessageStatus {
705 &self.status
706 }
707
708 pub fn status_label(&self) -> &str {
709 &self.status_label
710 }
711
712 pub fn from_json_value(json_obj: JsonValue) -> EgResult<Self> {
713 let err = || "Invalid Status message".to_string();
714
715 let (msg_class, msg_hash) = EgValue::remove_class_wrapper(json_obj).ok_or_else(err)?;
716
717 let code = util::json_isize(&msg_hash["statusCode"]).ok_or_else(err)?;
718 let stat: MessageStatus = code.into();
719
720 let stat_str: &str = msg_hash["status"].as_str().unwrap_or(stat.into());
723
724 Ok(Status::new(stat, stat_str, &msg_class))
725 }
726
727 pub fn into_json_value(self) -> JsonValue {
728 let obj = json::object! {
729 "status": self.status_label(),
730 "statusCode": self.status as isize,
731 };
732
733 EgValue::add_class_wrapper(obj, &self.msg_class)
734 }
735}
736
737impl fmt::Display for Status {
738 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
739 write!(
740 f,
741 "stat={} class={} label={}",
742 self.status, self.msg_class, self.status_label
743 )
744 }
745}
746
747#[derive(Debug, Clone, PartialEq)]
749pub struct MethodCall {
750 method: String,
751 params: Vec<EgValue>,
752 msg_class: String,
753}
754
755impl MethodCall {
756 pub fn new(method: &str, params: Vec<EgValue>) -> Self {
757 MethodCall {
758 params,
759 method: String::from(method),
760 msg_class: String::from("osrfMethod"), }
762 }
763
764 pub fn from_json_value(json_obj: JsonValue, raw_data_mode: bool) -> EgResult<Self> {
766 let err = || "Invalid MethodCall message".to_string();
767
768 let (msg_class, mut msg_hash) = EgValue::remove_class_wrapper(json_obj).ok_or_else(err)?;
769
770 let method = msg_hash["method"].as_str().ok_or_else(err)?.to_string();
771
772 let mut params = Vec::new();
773 if let JsonValue::Array(mut vec) = msg_hash["params"].take() {
774 while !vec.is_empty() {
775 if raw_data_mode {
776 params.push(EgValue::from_json_value_plain(vec.remove(0)));
777 } else {
778 params.push(EgValue::from_json_value(vec.remove(0))?);
779 }
780 }
781 }
782
783 Ok(MethodCall {
784 method,
785 params,
786 msg_class,
787 })
788 }
789
790 pub fn method(&self) -> &str {
791 &self.method
792 }
793
794 pub fn params(&self) -> &Vec<EgValue> {
795 &self.params
796 }
797
798 pub fn params_mut(&mut self) -> &mut Vec<EgValue> {
799 &mut self.params
800 }
801
802 pub fn take_params(&mut self) -> Vec<EgValue> {
803 std::mem::take(&mut self.params)
804 }
805
806 pub fn set_params(&mut self, params: Vec<EgValue>) {
807 self.params = params
808 }
809
810 pub fn param(&self, index: usize) -> &EgValue {
814 self.params.get(index).unwrap_or(&EG_NULL)
815 }
816
817 pub fn into_json_value(mut self) -> JsonValue {
818 let mut params: Vec<JsonValue> = Vec::new();
819
820 while !self.params.is_empty() {
821 params.push(self.params.remove(0).into_json_value());
822 }
823
824 let obj = json::object! {
825 "method": self.method(),
826 "params": params
827 };
828
829 EgValue::add_class_wrapper(obj, &self.msg_class)
830 }
831}