1use std::fmt;
2use std::io::{self, BufRead};
3use std::ops::{Deref, DerefMut};
4use std::str::FromStr;
5use std::string::FromUtf8Error;
6
7use derive_more::{Display, Error, From};
8use serde::{Deserialize, Serialize};
9use serde_json::{Map, Value};
10
11#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
13pub struct LspMsg {
14 header: LspHeader,
16
17 content: LspContent,
19}
20
21#[derive(Debug, Display, Error, From)]
22pub enum LspMsgParseError {
23 BadContent(LspContentParseError),
25
26 BadHeader(LspHeaderParseError),
28
29 BadHeaderTermination,
31
32 BadInput(FromUtf8Error),
34
35 IoError(io::Error),
37
38 UnexpectedEof,
40}
41
42impl From<LspMsgParseError> for io::Error {
43 fn from(x: LspMsgParseError) -> Self {
44 match x {
45 LspMsgParseError::BadContent(x) => x.into(),
46 LspMsgParseError::BadHeader(x) => x.into(),
47 LspMsgParseError::BadHeaderTermination => io::Error::new(
48 io::ErrorKind::InvalidData,
49 r"Received header line not terminated in \r\n",
50 ),
51 LspMsgParseError::BadInput(x) => io::Error::new(io::ErrorKind::InvalidData, x),
52 LspMsgParseError::IoError(x) => x,
53 LspMsgParseError::UnexpectedEof => io::Error::from(io::ErrorKind::UnexpectedEof),
54 }
55 }
56}
57
58impl LspMsg {
59 pub fn header(&self) -> &LspHeader {
61 &self.header
62 }
63
64 pub fn mut_header(&mut self) -> &mut LspHeader {
66 &mut self.header
67 }
68
69 pub fn content(&self) -> &LspContent {
71 &self.content
72 }
73
74 pub fn mut_content(&mut self) -> &mut LspContent {
76 &mut self.content
77 }
78
79 pub fn refresh_content_length(&mut self) {
81 self.header.content_length = self.content.to_string().len();
82 }
83
84 pub fn from_buf_reader<R: BufRead>(r: &mut R) -> Result<Self, LspMsgParseError> {
98 let mut buf = String::new();
100 loop {
101 let start = buf.len();
103
104 let len = r.read_line(&mut buf)?;
106 let end = start + len;
107
108 if len == 0 {
110 return Err(LspMsgParseError::UnexpectedEof);
111 }
112
113 let line = &buf[start..end];
114
115 if !line.ends_with("\r\n") {
117 return Err(LspMsgParseError::BadHeaderTermination);
118
119 } else if line == "\r\n" {
121 break;
122 }
123 }
124
125 let header = buf.parse::<LspHeader>()?;
127
128 let content = {
130 let mut buf = vec![0u8; header.content_length];
131 r.read_exact(&mut buf).map_err(|x| {
132 if x.kind() == io::ErrorKind::UnexpectedEof {
133 LspMsgParseError::UnexpectedEof
134 } else {
135 LspMsgParseError::IoError(x)
136 }
137 })?;
138 String::from_utf8(buf)?.parse::<LspContent>()?
139 };
140
141 Ok(Self { header, content })
142 }
143
144 pub fn to_bytes(&self) -> Vec<u8> {
146 self.to_string().into_bytes()
147 }
148}
149
150impl fmt::Display for LspMsg {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163 self.header.fmt(f)?;
164 self.content.fmt(f)
165 }
166}
167
168impl FromStr for LspMsg {
169 type Err = LspMsgParseError;
170
171 fn from_str(s: &str) -> Result<Self, Self::Err> {
183 let mut r = io::BufReader::new(io::Cursor::new(s));
184 Self::from_buf_reader(&mut r)
185 }
186}
187
188#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
190pub struct LspHeader {
191 pub content_length: usize,
193
194 pub content_type: Option<String>,
197}
198
199impl fmt::Display for LspHeader {
200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208 write!(f, "Content-Length: {}\r\n", self.content_length)?;
209
210 if let Some(ty) = self.content_type.as_ref() {
211 write!(f, "Content-Type: {ty}\r\n")?;
212 }
213
214 write!(f, "\r\n")
215 }
216}
217
218#[derive(Clone, Debug, PartialEq, Eq, Display, Error, From)]
219pub enum LspHeaderParseError {
220 MissingContentLength,
221 InvalidContentLength(std::num::ParseIntError),
222 BadHeaderField,
223}
224
225impl From<LspHeaderParseError> for io::Error {
226 fn from(x: LspHeaderParseError) -> Self {
227 io::Error::new(io::ErrorKind::InvalidData, x)
228 }
229}
230
231impl FromStr for LspHeader {
232 type Err = LspHeaderParseError;
233
234 fn from_str(s: &str) -> Result<Self, Self::Err> {
242 let lines = s.split("\r\n").map(str::trim).filter(|l| !l.is_empty());
243 let mut content_length = None;
244 let mut content_type = None;
245
246 for line in lines {
247 match line.find(':') {
248 Some(idx) if idx + 1 < line.len() => {
249 let name = &line[..idx];
250 let value = &line[(idx + 1)..];
251 match name {
252 "Content-Length" => content_length = Some(value.trim().parse()?),
253 "Content-Type" => content_type = Some(value.trim().to_string()),
254 _ => return Err(LspHeaderParseError::BadHeaderField),
255 }
256 }
257 _ => {
258 return Err(LspHeaderParseError::BadHeaderField);
259 }
260 }
261 }
262
263 match content_length {
264 Some(content_length) => Ok(Self {
265 content_length,
266 content_type,
267 }),
268 None => Err(LspHeaderParseError::MissingContentLength),
269 }
270 }
271}
272
273#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
275pub struct LspContent(Map<String, Value>);
276
277fn for_each_mut_string<F1, F2>(value: &mut Value, check: &F1, mutate: &mut F2)
278where
279 F1: Fn(&String) -> bool,
280 F2: FnMut(&mut String),
281{
282 match value {
283 Value::Object(obj) => {
284 obj.values_mut()
286 .for_each(|v| for_each_mut_string(v, check, mutate));
287
288 let keys: Vec<String> = obj
290 .keys()
291 .filter(|k| check(k))
292 .map(ToString::to_string)
293 .collect();
294 for key in keys {
295 if let Some((mut key, value)) = obj.remove_entry(&key) {
296 mutate(&mut key);
297 obj.insert(key, value);
298 }
299 }
300 }
301 Value::Array(items) => items
302 .iter_mut()
303 .for_each(|v| for_each_mut_string(v, check, mutate)),
304 Value::String(s) => mutate(s),
305 _ => {}
306 }
307}
308
309fn swap_prefix(obj: &mut Map<String, Value>, old: &str, new: &str) {
310 let check = |s: &String| s.starts_with(old);
311 let mut mutate = |s: &mut String| {
312 if let Some(pos) = s.find(old) {
313 s.replace_range(pos..pos + old.len(), new);
314 }
315 };
316
317 obj.values_mut()
319 .for_each(|v| for_each_mut_string(v, &check, &mut mutate));
320
321 let keys: Vec<String> = obj
323 .keys()
324 .filter(|k| check(k))
325 .map(ToString::to_string)
326 .collect();
327 for key in keys {
328 if let Some((mut key, value)) = obj.remove_entry(&key) {
329 mutate(&mut key);
330 obj.insert(key, value);
331 }
332 }
333}
334
335impl LspContent {
336 pub fn convert_local_scheme_to_distant(&mut self) {
338 self.convert_local_scheme_to("distant")
339 }
340
341 pub fn convert_local_scheme_to(&mut self, scheme: &str) {
343 swap_prefix(&mut self.0, "file:", &format!("{scheme}:"));
344 }
345
346 pub fn convert_distant_scheme_to_local(&mut self) {
348 self.convert_scheme_to_local("distant")
349 }
350
351 pub fn convert_scheme_to_local(&mut self, scheme: &str) {
353 swap_prefix(&mut self.0, &format!("{scheme}:"), "file:");
354 }
355}
356
357impl AsRef<Map<String, Value>> for LspContent {
358 fn as_ref(&self) -> &Map<String, Value> {
359 &self.0
360 }
361}
362
363impl Deref for LspContent {
364 type Target = Map<String, Value>;
365
366 fn deref(&self) -> &Self::Target {
367 &self.0
368 }
369}
370
371impl DerefMut for LspContent {
372 fn deref_mut(&mut self) -> &mut Self::Target {
373 &mut self.0
374 }
375}
376
377impl fmt::Display for LspContent {
378 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
380 write!(
381 f,
382 "{}",
383 serde_json::to_string_pretty(self).map_err(|_| fmt::Error)?
384 )
385 }
386}
387
388#[derive(Debug, Display, Error, From)]
389pub struct LspContentParseError(serde_json::Error);
390
391impl From<LspContentParseError> for io::Error {
392 fn from(x: LspContentParseError) -> Self {
393 io::Error::new(io::ErrorKind::InvalidData, x)
394 }
395}
396
397impl FromStr for LspContent {
398 type Err = LspContentParseError;
399
400 fn from_str(s: &str) -> Result<Self, Self::Err> {
402 serde_json::from_str(s).map_err(From::from)
403 }
404}
405
406#[cfg(test)]
407mod tests {
408 use test_log::test;
409
410 use super::*;
411
412 macro_rules! make_obj {
413 ($($tail:tt)*) => {
414 match serde_json::json!($($tail)*) {
415 serde_json::Value::Object(x) => x,
416 x => panic!("Got non-object: {:?}", x),
417 }
418 };
419 }
420
421 #[test]
422 fn msg_display_should_output_header_and_content() {
423 let msg = LspMsg {
424 header: LspHeader {
425 content_length: 123,
426 content_type: Some(String::from("some content type")),
427 },
428 content: LspContent(make_obj!({"hello": "world"})),
429 };
430
431 let output = msg.to_string();
432 assert_eq!(
433 output,
434 concat!(
435 "Content-Length: 123\r\n",
436 "Content-Type: some content type\r\n",
437 "\r\n",
438 "{\n",
439 " \"hello\": \"world\"\n",
440 "}",
441 )
442 );
443 }
444
445 #[test]
446 fn msg_from_buf_reader_should_be_successful_if_valid_msg_received() {
447 let mut input = io::Cursor::new(concat!(
448 "Content-Length: 22\r\n",
449 "Content-Type: some content type\r\n",
450 "\r\n",
451 "{\n",
452 " \"hello\": \"world\"\n",
453 "}",
454 ));
455 let msg = LspMsg::from_buf_reader(&mut input).unwrap();
456 assert_eq!(msg.header.content_length, 22);
457 assert_eq!(
458 msg.header.content_type.as_deref(),
459 Some("some content type")
460 );
461 assert_eq!(msg.content.as_ref(), &make_obj!({ "hello": "world" }));
462 }
463
464 #[test]
465 fn msg_from_buf_reader_should_fail_if_reach_eof_before_received_full_msg() {
466 let err = LspMsg::from_buf_reader(&mut io::Cursor::new("Content-Length: 22")).unwrap_err();
468 assert!(
469 matches!(err, LspMsgParseError::BadHeaderTermination),
470 "{:?}",
471 err
472 );
473
474 let err = LspMsg::from_buf_reader(&mut io::Cursor::new(concat!(
476 "Content-Length: 22\r\n",
477 "Content-Type: some content type\r\n",
478 )))
479 .unwrap_err();
480 assert!(matches!(err, LspMsgParseError::UnexpectedEof), "{:?}", err);
481
482 let err = LspMsg::from_buf_reader(&mut io::Cursor::new(concat!(
484 "Content-Length: 22\r\n",
485 "\r\n",
486 )))
487 .unwrap_err();
488 assert!(matches!(err, LspMsgParseError::UnexpectedEof), "{:?}", err);
489 }
490
491 #[test]
492 fn msg_from_buf_reader_should_fail_if_missing_proper_line_termination_for_header_field() {
493 let err = LspMsg::from_buf_reader(&mut io::Cursor::new(concat!(
494 "Content-Length: 22\n",
495 "\r\n",
496 "{\n",
497 " \"hello\": \"world\"\n",
498 "}",
499 )))
500 .unwrap_err();
501 assert!(
502 matches!(err, LspMsgParseError::BadHeaderTermination),
503 "{:?}",
504 err
505 );
506 }
507
508 #[test]
509 fn msg_from_buf_reader_should_fail_if_bad_header_provided() {
510 let err = LspMsg::from_buf_reader(&mut io::Cursor::new(concat!(
512 "Content-Length: -1\r\n",
513 "\r\n",
514 "{\n",
515 " \"hello\": \"world\"\n",
516 "}",
517 )))
518 .unwrap_err();
519 assert!(matches!(err, LspMsgParseError::BadHeader(_)), "{:?}", err);
520
521 let err = LspMsg::from_buf_reader(&mut io::Cursor::new(concat!(
523 "Content-Type: some content type\r\n",
524 "\r\n",
525 "{\n",
526 " \"hello\": \"world\"\n",
527 "}",
528 )))
529 .unwrap_err();
530 assert!(matches!(err, LspMsgParseError::BadHeader(_)), "{:?}", err);
531 }
532
533 #[test]
534 fn msg_from_buf_reader_should_fail_if_bad_content_provided() {
535 let err = LspMsg::from_buf_reader(&mut io::Cursor::new(concat!(
537 "Content-Length: 21\r\n",
538 "\r\n",
539 "{\n",
540 " \"hello\": \"world\"\n",
541 )))
542 .unwrap_err();
543 assert!(matches!(err, LspMsgParseError::BadContent(_)), "{:?}", err);
544 }
545
546 #[test]
547 fn msg_from_buf_reader_should_fail_if_non_utf8_msg_encountered_for_content() {
548 let mut raw = b"Content-Length: 2\r\n\r\n".to_vec();
550 raw.extend(vec![0, 159]);
551
552 let err = LspMsg::from_buf_reader(&mut io::Cursor::new(raw)).unwrap_err();
553 assert!(matches!(err, LspMsgParseError::BadInput(_)), "{:?}", err);
554 }
555
556 #[test]
557 fn header_parse_should_fail_if_missing_content_length() {
558 let err = "Content-Type: some type\r\n\r\n"
559 .parse::<LspHeader>()
560 .unwrap_err();
561 assert!(
562 matches!(err, LspHeaderParseError::MissingContentLength),
563 "{:?}",
564 err
565 );
566 }
567
568 #[test]
569 fn header_parse_should_fail_if_content_length_invalid() {
570 let err = "Content-Length: -1\r\n\r\n"
571 .parse::<LspHeader>()
572 .unwrap_err();
573 assert!(
574 matches!(err, LspHeaderParseError::InvalidContentLength(_)),
575 "{:?}",
576 err
577 );
578 }
579
580 #[test]
581 fn header_parse_should_fail_if_receive_an_unexpected_header_field() {
582 let err = "Content-Length: 123\r\nUnknown-Field: abc\r\n\r\n"
583 .parse::<LspHeader>()
584 .unwrap_err();
585 assert!(
586 matches!(err, LspHeaderParseError::BadHeaderField),
587 "{:?}",
588 err
589 );
590 }
591
592 #[test]
593 fn header_parse_should_succeed_if_given_valid_content_length() {
594 let header = "Content-Length: 123\r\n\r\n".parse::<LspHeader>().unwrap();
595 assert_eq!(header.content_length, 123);
596 assert_eq!(header.content_type, None);
597 }
598
599 #[test]
600 fn header_parse_should_support_optional_content_type() {
601 let header = "Content-Length: 123\r\nContent-Type: some content type\r\n\r\n"
603 .parse::<LspHeader>()
604 .unwrap();
605 assert_eq!(header.content_length, 123);
606 assert_eq!(header.content_type.as_deref(), Some("some content type"));
607
608 let header = "Content-Length: 123\r\nContent-Type: some:content:type\r\n\r\n"
610 .parse::<LspHeader>()
611 .unwrap();
612 assert_eq!(header.content_length, 123);
613 assert_eq!(header.content_type.as_deref(), Some("some:content:type"));
614 }
615
616 #[test]
617 fn header_display_should_output_header_fields_with_appropriate_line_terminations() {
618 let header = LspHeader {
620 content_length: 123,
621 content_type: None,
622 };
623 assert_eq!(header.to_string(), "Content-Length: 123\r\n\r\n");
624
625 let header = LspHeader {
627 content_length: 123,
628 content_type: Some(String::from("some type")),
629 };
630 assert_eq!(
631 header.to_string(),
632 "Content-Length: 123\r\nContent-Type: some type\r\n\r\n"
633 );
634 }
635
636 #[test]
637 fn content_parse_should_succeed_if_valid_json() {
638 let content = "{\"hello\": \"world\"}".parse::<LspContent>().unwrap();
639 assert_eq!(content.as_ref(), &make_obj!({"hello": "world"}));
640 }
641
642 #[test]
643 fn content_parse_should_fail_if_invalid_json() {
644 assert!(
645 "not json".parse::<LspContent>().is_err(),
646 "Unexpectedly succeeded"
647 );
648 }
649
650 #[test]
651 fn content_display_should_output_content_as_json() {
652 let content = LspContent(make_obj!({"hello": "world"}));
653 assert_eq!(content.to_string(), "{\n \"hello\": \"world\"\n}");
654 }
655
656 #[test]
657 fn content_convert_local_scheme_to_distant_should_convert_keys_and_values() {
658 let mut content = LspContent(make_obj!({
659 "distant://key1": "file://value1",
660 "file://key2": "distant://value2",
661 "key3": ["file://value3", "distant://value4"],
662 "key4": {
663 "distant://key5": "file://value5",
664 "file://key6": "distant://value6",
665 "key7": [
666 {
667 "distant://key8": "file://value8",
668 "file://key9": "distant://value9",
669 }
670 ]
671 },
672 "key10": null,
673 "key11": 123,
674 "key12": true,
675 }));
676
677 content.convert_local_scheme_to_distant();
678 assert_eq!(
679 content.0,
680 make_obj!({
681 "distant://key1": "distant://value1",
682 "distant://key2": "distant://value2",
683 "key3": ["distant://value3", "distant://value4"],
684 "key4": {
685 "distant://key5": "distant://value5",
686 "distant://key6": "distant://value6",
687 "key7": [
688 {
689 "distant://key8": "distant://value8",
690 "distant://key9": "distant://value9",
691 }
692 ]
693 },
694 "key10": null,
695 "key11": 123,
696 "key12": true,
697 })
698 );
699 }
700
701 #[test]
702 fn content_convert_local_scheme_to_should_convert_keys_and_values() {
703 let mut content = LspContent(make_obj!({
704 "distant://key1": "file://value1",
705 "file://key2": "distant://value2",
706 "key3": ["file://value3", "distant://value4"],
707 "key4": {
708 "distant://key5": "file://value5",
709 "file://key6": "distant://value6",
710 "key7": [
711 {
712 "distant://key8": "file://value8",
713 "file://key9": "distant://value9",
714 }
715 ]
716 },
717 "key10": null,
718 "key11": 123,
719 "key12": true,
720 }));
721
722 content.convert_local_scheme_to("custom");
723 assert_eq!(
724 content.0,
725 make_obj!({
726 "distant://key1": "custom://value1",
727 "custom://key2": "distant://value2",
728 "key3": ["custom://value3", "distant://value4"],
729 "key4": {
730 "distant://key5": "custom://value5",
731 "custom://key6": "distant://value6",
732 "key7": [
733 {
734 "distant://key8": "custom://value8",
735 "custom://key9": "distant://value9",
736 }
737 ]
738 },
739 "key10": null,
740 "key11": 123,
741 "key12": true,
742 })
743 );
744 }
745
746 #[test]
747 fn content_convert_distant_scheme_to_local_should_convert_keys_and_values() {
748 let mut content = LspContent(make_obj!({
749 "distant://key1": "file://value1",
750 "file://key2": "distant://value2",
751 "key3": ["file://value3", "distant://value4"],
752 "key4": {
753 "distant://key5": "file://value5",
754 "file://key6": "distant://value6",
755 "key7": [
756 {
757 "distant://key8": "file://value8",
758 "file://key9": "distant://value9",
759 }
760 ]
761 },
762 "key10": null,
763 "key11": 123,
764 "key12": true,
765 }));
766
767 content.convert_distant_scheme_to_local();
768 assert_eq!(
769 content.0,
770 make_obj!({
771 "file://key1": "file://value1",
772 "file://key2": "file://value2",
773 "key3": ["file://value3", "file://value4"],
774 "key4": {
775 "file://key5": "file://value5",
776 "file://key6": "file://value6",
777 "key7": [
778 {
779 "file://key8": "file://value8",
780 "file://key9": "file://value9",
781 }
782 ]
783 },
784 "key10": null,
785 "key11": 123,
786 "key12": true,
787 })
788 );
789 }
790
791 #[test]
792 fn content_convert_scheme_to_local_should_convert_keys_and_values() {
793 let mut content = LspContent(make_obj!({
794 "custom://key1": "file://value1",
795 "file://key2": "custom://value2",
796 "key3": ["file://value3", "custom://value4"],
797 "key4": {
798 "custom://key5": "file://value5",
799 "file://key6": "custom://value6",
800 "key7": [
801 {
802 "custom://key8": "file://value8",
803 "file://key9": "custom://value9",
804 }
805 ]
806 },
807 "key10": null,
808 "key11": 123,
809 "key12": true,
810 }));
811
812 content.convert_scheme_to_local("custom");
813 assert_eq!(
814 content.0,
815 make_obj!({
816 "file://key1": "file://value1",
817 "file://key2": "file://value2",
818 "key3": ["file://value3", "file://value4"],
819 "key4": {
820 "file://key5": "file://value5",
821 "file://key6": "file://value6",
822 "key7": [
823 {
824 "file://key8": "file://value8",
825 "file://key9": "file://value9",
826 }
827 ]
828 },
829 "key10": null,
830 "key11": 123,
831 "key12": true,
832 })
833 );
834 }
835}