evergreen/osrf/
message.rs

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";
14/// The C code maxes this at 16 chars.
15const MAX_LOCALE_LEN: usize = 16;
16
17// Locale is tied to the current thread.
18// Initially the locale is set to the default value.
19// When parsing an opensrf message that contains a locale value,
20// adopt that value as our new thread-scoped locale.
21thread_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
26/// Set the locale for the current thread.
27pub fn set_thread_locale(locale: &str) {
28    THREAD_LOCALE.with(|lc| {
29        // Only verify and allocate if necessary.
30        if lc.borrow().as_str() == locale {
31            return;
32        }
33
34        // Make sure the requested locale is reasonable.
35
36        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
53/// Set the locale for the current thread.
54pub 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
63/// Reset the locale to our default.
64pub fn reset_thread_locale() {
65    set_thread_locale(DEFAULT_LOCALE);
66}
67
68/// Returns the locale for the current thread.
69///
70/// String clone is required here to escape the temp borrow.
71pub 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/// Create a MessageType from the string that would be found in a message.
88///
89/// ```
90/// let mt: evergreen::osrf::message::MessageType = "REQUEST".into();
91/// assert_eq!(mt, evergreen::osrf::message::MessageType::Request);
92/// ```
93#[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
107/// Create the string that will be used within the serialized message
108/// for a given MessageType
109///
110/// ```
111/// let s: &str = evergreen::osrf::message::MessageType::Request.into();
112/// assert_eq!(s, "REQUEST");
113/// ```
114impl 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/// OpenSRF messages have HTTP-like status codes.
135#[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/// Translate a code number into a MessageStatus
161///
162/// ```
163/// let ms: evergreen::osrf::message::MessageStatus = 205.into();
164/// assert_eq!(ms, evergreen::osrf::message::MessageStatus::Complete);
165/// ```
166#[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/// Translate a MessageStatus into its display label
195///
196/// ```
197/// let s: &str = evergreen::osrf::message::MessageStatus::Continue.into();
198/// assert_eq!(s, "Continue");
199/// ```
200#[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/// The message payload is the core of the message.
236#[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/// Message envelope containing one or more Messages, routing
256/// details, and other metadata.
257#[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    /// Create a TransportMessage from a JSON object, consuming the JSON value.
360    ///
361    /// Returns None if the JSON value cannot be coerced into a TransportMessage.
362    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            // Sometimes a transport message body is a single message.
397            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    /// Returns the message payload, replacing the value on the
472    /// message with Payload::NoPayload.
473    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    /// Creates a Message from a JSON value, consuming the JSON value.
502    ///
503    /// Returns Err if the JSON value cannot be coerced into a Message.
504    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        // Any time we receive (parse) a message the contains a locale
529        // value, adopt that value as our new thread-scoped locale.
530        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                // re: raw_data_mode, only Requests and Results contain
564                // IDL-classed data (to potentially ignore).
565                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            // Pull the ingress from the value stored in the thread.
588            // It will use the default if none is set.
589            THREAD_INGRESS.with(|lc| obj["ingress"] = lc.borrow().as_str().into());
590        }
591
592        match self.payload {
593            // Avoid adding the "payload" key for non-payload messages.
594            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/// Delivers a single API response.
603///
604/// Each Request will have zero or more associated Response messages.
605#[derive(Debug, Clone, PartialEq)]
606pub struct Result {
607    status: MessageStatus,
608
609    status_label: String,
610
611    msg_class: String,
612
613    /// API response value.
614    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        // If the message contains a status label, use it, otherwise
671        // use the label associated locally with the status code
672        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        // If the message contains a status label, use it, otherwise
721        // use the label associated locally with the status code
722        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/// A single API request with method name and parameters.
748#[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"), // only supported value
761        }
762    }
763
764    /// Create a Method from a JsonValue.
765    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    /// Return a ref to the param at the specififed index.
811    ///
812    /// Returns NULL if the param is not set.
813    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}