1#![warn(missing_docs)]
46
47use serde::{Deserialize, Serialize};
48
49use std::convert::TryFrom;
50use std::error::Error;
51use std::fmt;
52use std::io::prelude::*;
53use std::io::{self, BufReader};
54use std::net::{SocketAddr, TcpStream, ToSocketAddrs};
55use std::num;
56use std::sync::mpsc::{self, Receiver};
57use std::thread;
58
59pub const HOST_VERSION: (u8, u8, u8) = (0, 9, 0);
61
62pub trait ReadWrite: Read + Write {}
64
65impl ReadWrite for TcpStream {}
66
67pub trait Connect: fmt::Debug {
69 fn connect(&self) -> io::Result<Box<dyn ReadWrite>>;
71}
72
73impl Connect for str {
74 fn connect(&self) -> io::Result<Box<dyn ReadWrite>> {
75 Ok(Box::new(TcpStream::connect(self)?))
76 }
77}
78
79impl Connect for &str {
80 fn connect(&self) -> io::Result<Box<dyn ReadWrite>> {
81 Ok(Box::new(TcpStream::connect(self)?))
82 }
83}
84
85#[derive(Debug)]
87pub struct TcpStreamConnector<T>
88where
89 T: fmt::Debug,
90{
91 addrs: T,
92}
93
94impl<T> TcpStreamConnector<T>
95where
96 T: ToSocketAddrs + fmt::Debug,
97{
98 pub fn new(addrs: T) -> TcpStreamConnector<T> {
100 TcpStreamConnector { addrs }
101 }
102}
103
104impl<T> Connect for TcpStreamConnector<T>
105where
106 T: ToSocketAddrs + fmt::Debug,
107{
108 fn connect(&self) -> io::Result<Box<dyn ReadWrite>> {
109 let stream = TcpStream::connect(&self.addrs)?;
110 Ok(Box::new(stream))
111 }
112}
113
114#[derive(Debug)]
128pub struct Client<T> {
129 connector: T,
130}
131
132impl<T> Client<T> {
133 pub fn new(connector: T) -> Client<T> {
135 Client { connector }
136 }
137
138 fn preamble(&self) -> String {
139 format!(
140 "FC v{}.{}.{}",
141 HOST_VERSION.0, HOST_VERSION.1, HOST_VERSION.2
142 )
143 }
144}
145
146impl<T> Client<T>
147where
148 T: Connect,
149{
150 pub fn crypto_shared(&self, pwd: &str) -> Result<String, ClientError> {
152 let mut stream = self.connector.connect()?;
153
154 writeln!(stream, "{} crypto shared", self.preamble())?;
155 writeln!(stream, "{}", pwd)?;
156
157 let r = BufReader::new(stream);
158 let line = read_utf8_line(r)?;
159 parse_server_message(&line)
160 }
161
162 pub fn crypto_pubpvt(&self, pwd: &str) -> Result<(String, String), ClientError> {
164 let mut stream = self.connector.connect()?;
165
166 writeln!(stream, "{} crypto pubpvt", self.preamble())?;
167 writeln!(stream, "{}", pwd)?;
168
169 let r = BufReader::new(stream);
170 let line = read_utf8_line(r)?;
171 let line = parse_server_message(&line)?;
172
173 let mut split_line = line.split(" ");
174 let pubkey = String::from(split_line.next().ok_or(ClientError::ExecutionError(
175 String::from("missing public key"),
176 ))?);
177 let prvkey = String::from(split_line.next().ok_or(ClientError::ExecutionError(
178 String::from("missing private key"),
179 ))?);
180
181 Ok((pubkey, prvkey))
182 }
183
184 pub fn chains(&self) -> Result<ChainsIds, ClientError> {
186 let mut stream = self.connector.connect()?;
187
188 writeln!(stream, "{} chains list", self.preamble())?;
189
190 let r = BufReader::new(stream);
191 let line = read_utf8_line(r)?;
192 let line = parse_server_message(&line)?;
193
194 let chains: Result<Vec<_>, _> = line.split(' ').map(ChainId::new).collect();
195 let chains = chains?;
196
197 Ok(chains)
198 }
199
200 pub fn join_chain(&self, chain: &ChainId, keys: &[&str]) -> Result<String, ClientError> {
204 let mut stream = self.connector.connect()?;
205
206 writeln!(
207 stream,
208 "{} chains join {} {}",
209 self.preamble(),
210 chain,
211 keys.join(" ")
212 )?;
213
214 let r = BufReader::new(stream);
215 let line = read_utf8_line(r)?;
216 let hash = parse_server_message(&line)?;
217
218 Ok(hash)
219 }
220
221 pub fn chain(&self, chain: &ChainId) -> ChainClient<T> {
223 ChainClient::new(self, &chain.to_string())
224 }
225
226 pub fn peer(&self, peer: impl ToSocketAddrs) -> io::Result<PeerClient<T>> {
228 PeerClient::new(self, peer)
229 }
230
231 pub fn host(&self) -> HostClient<T> {
233 HostClient::new(self)
234 }
235}
236
237impl<T> Client<T>
238where
239 T: 'static + Connect + Send + Clone,
240{
241 pub fn listen(&self) -> Receiver<Result<(usize, ChainId), ClientError>> {
282 let (tx, rx) = mpsc::channel();
283
284 let connector = self.connector.clone();
285 let preamble = self.preamble();
286 thread::spawn(move || match connector.connect() {
287 Err(e) => tx.send(Err(ClientError::IoError(e))),
288 Ok(mut stream) => match writeln!(stream, "{} chains listen", preamble) {
289 Err(e) => tx.send(Err(ClientError::IoError(e))),
290 Ok(_) => {
291 let r = BufReader::new(stream);
292 for line in r.lines() {
293 match line {
294 Err(e) => {
295 tx.send(Err(ClientError::IoError(e)))?;
296 break;
297 }
298 Ok(line) => {
299 let line = match parse_server_message(&line) {
300 Err(e) => {
301 tx.send(Err(e))?;
302 continue;
303 }
304 Ok(line) => line,
305 };
306
307 let mut line_split = line.split_whitespace();
308
309 let updated = match line_split.next() {
310 None => {
311 tx.send(Err(ClientError::InvalidServerResponseError(
312 String::from("empty updated number"),
313 )))?;
314 continue;
315 }
316 Some(updated) => updated,
317 };
318 let updated = match updated.parse() {
319 Err(e) => {
320 tx.send(Err(ClientError::InvalidServerResponseError(
321 format!("invalid updated number: {}", e),
322 )))?;
323 continue;
324 }
325 Ok(updated) => updated,
326 };
327
328 let chain_id = match line_split.next() {
329 None => {
330 tx.send(Err(ClientError::InvalidServerResponseError(
331 String::from("empty chain name"),
332 )))?;
333 continue;
334 }
335 Some(id) => id,
336 };
337 let chain_id = match ChainId::new(chain_id) {
338 Err(e) => {
339 tx.send(Err(ClientError::InvalidServerResponseError(
340 format!("invalid chain name: {}", e),
341 )))?;
342 continue;
343 }
344 Ok(chain_id) => chain_id,
345 };
346
347 tx.send(Ok((updated, chain_id)))?;
348 }
349 }
350 }
351
352 Ok(())
353 }
354 },
355 });
356
357 rx
358 }
359}
360
361pub struct ChainClient<'a, T> {
381 client: &'a Client<T>,
382 name: String,
383}
384
385impl<'a, T> ChainClient<'a, T> {
386 fn new(client: &'a Client<T>, name: &str) -> ChainClient<'a, T> {
387 let name = String::from(name);
388 ChainClient { client, name }
389 }
390
391 fn preamble(&self) -> String {
392 format!("{} chain {}", self.client.preamble(), self.name)
393 }
394}
395
396impl<'a, T> ChainClient<'a, T>
397where
398 T: Connect,
399{
400 pub fn name(&self) -> &str {
402 &self.name
403 }
404
405 pub fn leave(self) -> Result<bool, ClientError> {
407 let mut stream = self.client.connector.connect()?;
408
409 writeln!(
410 stream,
411 "{} chains leave {}",
412 self.client.preamble(),
413 self.name
414 )?;
415
416 let r = BufReader::new(stream);
417 let line = read_utf8_line(r)?;
418 let line = parse_server_message(&line)?;
419 let left = line == "true";
420
421 Ok(left)
422 }
423
424 pub fn genesis(&self) -> Result<String, ClientError> {
426 let mut stream = self.client.connector.connect()?;
427
428 writeln!(stream, "{} genesis", self.preamble())?;
429
430 let r = BufReader::new(stream);
431 let line = read_utf8_line(r)?;
432 parse_server_message(&line)
433 }
434
435 pub fn payload(&self, hash: &str, pvtkey: Option<&str>) -> Result<Vec<u8>, ClientError> {
438 let mut stream = self.client.connector.connect()?;
439
440 let pvtkey = pvtkey.unwrap_or("null");
441 writeln!(
442 stream,
443 "{} get payload {} {}",
444 self.preamble(),
445 hash,
446 pvtkey
447 )?;
448
449 let mut r = BufReader::new(stream);
450 let payload_size = read_utf8_line(&mut r)?;
451 let payload_size = parse_server_message(&payload_size)?;
452 let payload_size = payload_size.parse()?;
453
454 let mut buf = vec![0; payload_size];
455 r.read_exact(&mut buf)?;
456
457 Ok(buf.to_vec())
458 }
459
460 pub fn content(&self, hash: &str, pvtkey: Option<&str>) -> Result<ContentBlock, ClientError> {
462 let mut stream = self.client.connector.connect()?;
463
464 let pvtkey = pvtkey.unwrap_or("null");
465 writeln!(stream, "{} get block {} {}", self.preamble(), hash, pvtkey)?;
466
467 let mut r = BufReader::new(stream);
468 let block_size = read_utf8_line(&mut r)?;
469 let block_size = parse_server_message(&block_size)?;
470 let block_size = block_size.parse()?;
471
472 let mut buf = vec![0; block_size];
473 r.read_exact(&mut buf)?;
474
475 let content: ContentBlock = serde_json::from_slice(&buf)?;
476
477 Ok(content)
478 }
479
480 pub fn post(
484 &self,
485 signature: Option<&str>,
486 encrypt: bool,
487 payload: &[u8],
488 ) -> Result<String, ClientError> {
489 let mut stream = self.client.connector.connect()?;
490
491 let signature = signature.unwrap_or("anon");
492 writeln!(
493 stream,
494 "{} post {} {} {}",
495 self.preamble(),
496 signature,
497 encrypt,
498 payload.len()
499 )?;
500
501 stream.write_all(payload)?;
502
503 let r = BufReader::new(stream);
504 let line = read_utf8_line(r)?;
505 parse_server_message(&line)
506 }
507
508 pub fn heads(&self, blocked: bool) -> Result<Vec<String>, ClientError> {
510 let mut stream = self.client.connector.connect()?;
511
512 let mut msg = String::from("heads");
513 if blocked {
514 msg = format!("{} blocked", msg);
515 }
516 writeln!(stream, "{} {}", self.preamble(), &msg)?;
517
518 let r = BufReader::new(stream);
519 let line = read_utf8_line(r)?;
520 let response = parse_server_message(&line)?;
521 let hashes = response.split(' ').map(String::from).collect();
522
523 Ok(hashes)
524 }
525
526 pub fn consensus(&self) -> Result<Vec<String>, ClientError> {
528 let mut stream = self.client.connector.connect()?;
529
530 writeln!(stream, "{} consensus", self.preamble(),)?;
531
532 let r = BufReader::new(stream);
533 let line = read_utf8_line(r)?;
534 let response = parse_server_message(&line)?;
535 let hashes = response.split(' ').map(String::from).collect();
536
537 Ok(hashes)
538 }
539
540 pub fn reputation(&self, hash: &str) -> Result<isize, ClientError> {
544 let mut stream = self.client.connector.connect()?;
545
546 writeln!(stream, "{} reps {}", self.preamble(), hash)?;
547
548 let r = BufReader::new(stream);
549 let line = read_utf8_line(r)?;
550 let response = parse_server_message(&line)?;
551 let reputation = response.parse()?;
552
553 Ok(reputation)
554 }
555
556 pub fn like(&self, hash: &str, pvtkey: &str, reason: &[u8]) -> Result<String, ClientError> {
558 let mut stream = self.client.connector.connect()?;
559
560 writeln!(
561 stream,
562 "{} like 1 {} {} {}",
563 self.preamble(),
564 hash,
565 pvtkey,
566 reason.len()
567 )?;
568 stream.write_all(reason)?;
569
570 let r = BufReader::new(stream);
571 let line = read_utf8_line(r)?;
572 parse_server_message(&line)
573 }
574
575 pub fn dislike(&self, hash: &str, pvtkey: &str, reason: &[u8]) -> Result<String, ClientError> {
577 let mut stream = self.client.connector.connect()?;
578
579 writeln!(
580 stream,
581 "{} like -1 {} {} {}",
582 self.preamble(),
583 hash,
584 pvtkey,
585 reason.len()
586 )?;
587 stream.write_all(reason)?;
588
589 let r = BufReader::new(stream);
590 let line = read_utf8_line(r)?;
591 parse_server_message(&line)
592 }
593}
594
595impl<T> ChainClient<'_, T>
596where
597 T: 'static + Connect + Clone + Send,
598{
599 pub fn listen(&self) -> Receiver<Result<usize, ClientError>> {
642 let (tx, rx) = mpsc::channel();
643
644 let connector = self.client.connector.clone();
645 let preamble = self.preamble();
646 thread::spawn(move || match connector.connect() {
647 Err(e) => tx.send(Err(ClientError::IoError(e))),
648 Ok(mut stream) => match writeln!(stream, "{} listen", preamble) {
649 Err(e) => tx.send(Err(ClientError::IoError(e))),
650 Ok(_) => {
651 let r = BufReader::new(stream);
652 for line in r.lines() {
653 match line {
654 Err(e) => {
655 tx.send(Err(ClientError::IoError(e)))?;
656 break;
657 }
658 Ok(line) => {
659 let line = match parse_server_message(&line) {
660 Err(e) => {
661 tx.send(Err(e))?;
662 continue;
663 }
664 Ok(line) => line,
665 };
666
667 let updated = match line.parse() {
668 Err(e) => {
669 tx.send(Err(ClientError::InvalidServerResponseError(
670 format!("invalid updated number: {}", e),
671 )))?;
672 continue;
673 }
674 Ok(updated) => updated,
675 };
676
677 tx.send(Ok(updated))?;
678 }
679 }
680 }
681
682 Ok(())
683 }
684 },
685 });
686
687 rx
688 }
689}
690
691impl<T> fmt::Display for ChainClient<'_, T> {
692 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
693 self.name.fmt(f)
694 }
695}
696
697impl<T> fmt::Debug for ChainClient<'_, T> {
698 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
699 self.name.fmt(f)
700 }
701}
702
703pub struct PeerClient<'a, T> {
721 client: &'a Client<T>,
722 peer: SocketAddr,
723}
724
725impl<'a, T> PeerClient<'a, T>
726where
727 T: Connect,
728{
729 fn new(client: &'a Client<T>, peer: impl ToSocketAddrs) -> io::Result<PeerClient<'a, T>> {
730 let mut peer = peer.to_socket_addrs()?;
731 let peer = peer.next().unwrap_or("0.0.0.0:8330".parse().unwrap());
732
733 Ok(PeerClient { client, peer })
734 }
735
736 fn preamble(&self) -> String {
737 format!(
738 "{} peer {}:{}",
739 self.client.preamble(),
740 self.peer.ip(),
741 self.peer.port()
742 )
743 }
744
745 pub fn send_chain(&self, id: &ChainId) -> Result<(usize, usize), ClientError> {
747 let mut stream = self.client.connector.connect()?;
748
749 writeln!(stream, "{} send {}", self.preamble(), id)?;
750
751 let r = BufReader::new(stream);
752 let line = read_utf8_line(r)?;
753 let response = parse_server_message(&line)?;
754
755 let mut response_split = response.split('/');
756 let sent = response_split
757 .next()
758 .ok_or(ClientError::InvalidServerResponseError(response.clone()))?;
759 let sent = sent.trim().parse()?;
760 let total = response_split
761 .next()
762 .ok_or(ClientError::InvalidServerResponseError(response.clone()))?;
763 let total = total.trim().parse()?;
764
765 Ok((sent, total))
766 }
767
768 pub fn receive_chain(&self, id: &ChainId) -> Result<(usize, usize), ClientError> {
770 let mut stream = self.client.connector.connect()?;
771
772 writeln!(stream, "{} recv {}", self.preamble(), id)?;
773
774 let r = BufReader::new(stream);
775 let line = read_utf8_line(r)?;
776 let response = parse_server_message(&line)?;
777
778 let mut response_split = response.split('/');
779 let recv = response_split
780 .next()
781 .ok_or(ClientError::InvalidServerResponseError(response.clone()))?;
782 let recv = recv.trim().parse()?;
783 let total = response_split
784 .next()
785 .ok_or(ClientError::InvalidServerResponseError(response.clone()))?;
786 let total = total.trim().parse()?;
787
788 Ok((recv, total))
789 }
790
791 pub fn ping(&self) -> Result<usize, ClientError> {
795 let mut stream = self.client.connector.connect()?;
796
797 writeln!(stream, "{} ping", self.preamble())?;
798
799 let r = BufReader::new(stream);
800 let line = read_utf8_line(r)?;
801 let response = parse_server_message(&line)?;
802 let ping = response.parse()?;
803
804 Ok(ping)
805 }
806
807 pub fn chains(&self) -> Result<ChainsIds, ClientError> {
809 let mut stream = self.client.connector.connect()?;
810
811 writeln!(stream, "{} chains", self.preamble())?;
812
813 let r = BufReader::new(stream);
814 let line = read_utf8_line(r)?;
815 let response = parse_server_message(&line)?;
816 let chains: Result<Vec<_>, _> = response.split(' ').map(ChainId::new).collect();
817 let chains = chains?;
818
819 Ok(chains)
820 }
821}
822
823pub struct HostClient<'a, T> {
841 client: &'a Client<T>,
842}
843
844impl<'a, T> HostClient<'a, T>
845where
846 T: Connect,
847{
848 fn new(client: &'a Client<T>) -> HostClient<'a, T> {
849 HostClient { client }
850 }
851
852 fn preamble(&self) -> String {
853 format!("{} host", self.client.preamble())
854 }
855
856 pub fn time(&self) -> Result<usize, ClientError> {
860 let mut stream = self.client.connector.connect()?;
861
862 writeln!(stream, "{} now", self.preamble())?;
863
864 let r = BufReader::new(stream);
865 let line = read_utf8_line(r)?;
866 let response = parse_server_message(&line)?;
867 let time = response.parse()?;
868
869 Ok(time)
870 }
871
872 pub fn set_time(&self, milli: usize) -> Result<usize, ClientError> {
893 let mut stream = self.client.connector.connect()?;
894
895 writeln!(stream, "{} now {}", self.preamble(), milli)?;
896
897 let r = BufReader::new(stream);
898 let line = read_utf8_line(r)?;
899 let response = parse_server_message(&line)?;
900 let time = response.parse()?;
901
902 Ok(time)
903 }
904
905 pub fn path(&self) -> Result<String, ClientError> {
907 let mut stream = self.client.connector.connect()?;
908
909 writeln!(stream, "{} path", self.preamble())?;
910
911 let r = BufReader::new(stream);
912 let line = read_utf8_line(r)?;
913 let path = parse_server_message(&line)?;
914
915 Ok(path)
916 }
917
918 pub fn stop(self) -> Result<bool, ClientError> {
920 let mut stream = self.client.connector.connect()?;
921
922 writeln!(stream, "{} stop", self.preamble())?;
923
924 let r = BufReader::new(stream);
925 let line = read_utf8_line(r)?;
926 let stopped = parse_server_message(&line)?;
927 let stopped = stopped == "true";
928
929 Ok(stopped)
930 }
931}
932
933#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
935pub enum ChainType {
936 PrivateChain,
938
939 PublicChain,
941
942 PublicIdentityChain,
944}
945
946impl fmt::Display for ChainType {
947 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
948 let c: char = self.into();
949 write!(f, "{}", c)
950 }
951}
952
953impl TryFrom<char> for ChainType {
954 type Error = InvalidChainNameError;
955
956 fn try_from(c: char) -> Result<Self, Self::Error> {
957 match c {
958 '$' => Ok(ChainType::PrivateChain),
959 '#' => Ok(ChainType::PublicChain),
960 '@' => Ok(ChainType::PublicIdentityChain),
961 _ => Err(InvalidChainNameError),
962 }
963 }
964}
965
966impl From<&ChainType> for char {
967 fn from(t: &ChainType) -> char {
968 match t {
969 ChainType::PrivateChain => '$',
970 ChainType::PublicChain => '#',
971 ChainType::PublicIdentityChain => '@',
972 }
973 }
974}
975
976pub type ChainsIds = Vec<ChainId>;
978
979#[derive(Debug, PartialEq, Eq)]
981pub struct ChainId {
982 chain_type: ChainType,
983 name: String,
984}
985
986impl ChainId {
987 pub fn new(name: &str) -> Result<ChainId, InvalidChainNameError> {
989 let mut chars = name.chars();
990 let first_char = chars.next();
991 let first_char = first_char.ok_or(InvalidChainNameError)?;
992
993 let chain_type = ChainType::try_from(first_char)?;
994 let name = chars.collect();
995
996 Ok(ChainId { chain_type, name })
997 }
998}
999
1000impl fmt::Display for ChainId {
1001 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1002 write!(f, "{}{}", self.chain_type, self.name)
1003 }
1004}
1005
1006impl std::str::FromStr for ChainId {
1007 type Err = InvalidChainNameError;
1008
1009 fn from_str(s: &str) -> Result<Self, Self::Err> {
1010 ChainId::new(s)
1011 }
1012}
1013
1014#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
1016pub struct ContentBlock {
1017 pub hash: String,
1019 pub time: usize,
1021 #[serde(rename(serialize = "pay", deserialize = "pay"))]
1023 pub payload: Option<PayloadBlock>,
1024 pub like: Option<LikeBlock>,
1026 #[serde(rename(serialize = "sign", deserialize = "sign"))]
1028 pub signature: Option<SignatureBlock>,
1029 pub backs: Vec<String>,
1031}
1032
1033#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
1035pub struct PayloadBlock {
1036 pub hash: String,
1038 pub crypt: bool,
1040}
1041
1042#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
1044pub struct LikeBlock {
1045 pub hash: String,
1047 pub n: usize,
1049}
1050
1051#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
1053pub struct SignatureBlock {
1054 pub hash: String,
1056 #[serde(rename(serialize = "pub", deserialize = "pub"))]
1058 pub pubkey: String,
1059}
1060
1061#[derive(Debug)]
1063pub enum ClientError {
1064 EmptyResponseError,
1066
1067 ExecutionError(String),
1070
1071 InvalidServerResponseError(String),
1073
1074 IoError(io::Error),
1076}
1077
1078impl Error for ClientError {}
1079
1080impl fmt::Display for ClientError {
1081 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1082 match self {
1083 ClientError::EmptyResponseError => write!(f, "empty response"),
1084 ClientError::ExecutionError(e) => write!(f, "execution error: {}", e),
1085 ClientError::InvalidServerResponseError(e) => {
1086 write!(f, "invalid server response: {}", e)
1087 }
1088 ClientError::IoError(e) => e.fmt(f),
1089 }
1090 }
1091}
1092
1093impl From<io::Error> for ClientError {
1094 fn from(error: io::Error) -> Self {
1095 ClientError::IoError(error)
1096 }
1097}
1098
1099impl From<num::ParseIntError> for ClientError {
1100 fn from(error: num::ParseIntError) -> Self {
1101 ClientError::ExecutionError(error.to_string())
1102 }
1103}
1104
1105impl From<serde_json::Error> for ClientError {
1106 fn from(error: serde_json::Error) -> Self {
1107 ClientError::InvalidServerResponseError(error.to_string())
1108 }
1109}
1110
1111impl From<InvalidChainNameError> for ClientError {
1112 fn from(error: InvalidChainNameError) -> Self {
1113 ClientError::InvalidServerResponseError(error.to_string())
1114 }
1115}
1116
1117#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
1119pub struct InvalidChainNameError;
1120
1121impl Error for InvalidChainNameError {}
1122
1123impl fmt::Display for InvalidChainNameError {
1124 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1125 write!(f, "chain name must start with '$', '@' or '#'")
1126 }
1127}
1128
1129fn read_utf8_line(mut stream: impl BufRead) -> io::Result<String> {
1130 let mut line = String::new();
1131 let size = stream.read_line(&mut line)?;
1132 if size > 0 && line.chars().last().expect("must have last character") == '\n' {
1133 line = String::from(&line[..size - 1]);
1134 }
1135
1136 Ok(line)
1137}
1138
1139fn parse_server_message(msg: &str) -> Result<String, ClientError> {
1140 if msg.len() == 0 {
1141 return Err(ClientError::EmptyResponseError);
1142 }
1143
1144 let mut chars = msg.chars();
1145
1146 let first_char = chars.next();
1147 if let Some(c) = first_char {
1148 if c == '!' {
1149 chars.next();
1150 return Err(ClientError::ExecutionError(chars.collect()));
1151 }
1152 }
1153
1154 Ok(String::from(msg))
1155}
1156
1157#[cfg(test)]
1158mod test {
1159 use std::cell::RefCell;
1160 use std::rc::Rc;
1161 use std::time::Duration;
1162
1163 use super::*;
1164
1165 #[derive(Debug)]
1166 struct ConnectorMock(ConnectionMock);
1167
1168 impl ConnectorMock {
1169 fn new() -> ConnectorMock {
1170 ConnectorMock(ConnectionMock::new())
1171 }
1172
1173 fn read_stream(&self) -> Rc<RefCell<Vec<u8>>> {
1174 self.0.read_stream()
1175 }
1176 }
1177
1178 impl Connect for ConnectorMock {
1179 fn connect(&self) -> io::Result<Box<dyn ReadWrite>> {
1180 Ok(Box::new(self.0.clone()))
1181 }
1182 }
1183
1184 #[derive(Debug)]
1185 struct ConnectionMock {
1186 read_stream: Rc<RefCell<Vec<u8>>>,
1187 write_stream: Rc<RefCell<Vec<u8>>>,
1188 }
1189
1190 impl ConnectionMock {
1191 fn new() -> ConnectionMock {
1192 let read_stream = Rc::new(RefCell::new(Vec::new()));
1193 let write_stream = Rc::new(RefCell::new(Vec::new()));
1194
1195 ConnectionMock {
1196 read_stream,
1197 write_stream,
1198 }
1199 }
1200
1201 fn read_stream(&self) -> Rc<RefCell<Vec<u8>>> {
1202 Rc::clone(&self.read_stream)
1203 }
1204 }
1205
1206 impl ReadWrite for ConnectionMock {}
1207
1208 impl Read for ConnectionMock {
1209 fn read(&mut self, b: &mut [u8]) -> io::Result<usize> {
1210 let mut read_stream = self.read_stream.borrow_mut();
1211 let mut a: &[u8] = read_stream.as_ref();
1212
1213 let size = a.read(b)?;
1214 read_stream.drain(..size);
1215 Ok(size)
1216 }
1217 }
1218
1219 impl Write for ConnectionMock {
1220 fn write(&mut self, b: &[u8]) -> io::Result<usize> {
1221 let mut a = self.write_stream.borrow_mut();
1222 a.write(b)
1223 }
1224
1225 fn flush(&mut self) -> io::Result<()> {
1226 Ok(())
1227 }
1228 }
1229
1230 impl Clone for ConnectionMock {
1231 fn clone(&self) -> Self {
1232 let read_stream = Rc::clone(&self.read_stream);
1233 let write_stream = Rc::clone(&self.write_stream);
1234
1235 ConnectionMock {
1236 read_stream,
1237 write_stream,
1238 }
1239 }
1240 }
1241
1242 #[derive(Debug)]
1243 struct SyncConnectorMock(Vec<u8>);
1244
1245 impl SyncConnectorMock {
1246 fn new(r: &[u8]) -> SyncConnectorMock {
1247 SyncConnectorMock(r.to_vec())
1248 }
1249 }
1250
1251 impl Clone for SyncConnectorMock {
1252 fn clone(&self) -> Self {
1253 SyncConnectorMock(self.0.clone())
1254 }
1255 }
1256
1257 impl Connect for SyncConnectorMock {
1258 fn connect(&self) -> io::Result<Box<dyn ReadWrite>> {
1259 Ok(Box::new(SyncConnectionMock::new(&self.0)))
1260 }
1261 }
1262
1263 #[derive(Debug)]
1264 struct SyncConnectionMock {
1265 response: Vec<u8>,
1266 sent: Vec<u8>,
1267 }
1268
1269 impl SyncConnectionMock {
1270 fn new(response: &[u8]) -> SyncConnectionMock {
1271 let response = response.to_vec();
1272 let sent = Vec::new();
1273 SyncConnectionMock { response, sent }
1274 }
1275 }
1276
1277 impl ReadWrite for SyncConnectionMock {}
1278
1279 impl Read for SyncConnectionMock {
1280 fn read(&mut self, b: &mut [u8]) -> io::Result<usize> {
1281 let mut a: &[u8] = &self.response;
1282
1283 let size = a.read(b)?;
1284 self.response.drain(..size);
1285 Ok(size)
1286 }
1287 }
1288
1289 impl Write for SyncConnectionMock {
1290 fn write(&mut self, b: &[u8]) -> io::Result<usize> {
1291 self.sent.write(b)
1292 }
1293
1294 fn flush(&mut self) -> io::Result<()> {
1295 Ok(())
1296 }
1297 }
1298
1299 #[test]
1300 fn crypto_shared() -> Result<(), Box<dyn Error>> {
1301 let mock = ConnectorMock::new();
1302 let w = mock.read_stream();
1303 let client = Client::new(mock);
1304
1305 let response = b"707E8334560C3FB2852CCCE11F9221FA3594B7DE3919A8BAA5B6DB90FE432E53\n";
1306 w.replace(response.to_vec());
1307 let hash = client.crypto_shared("pwd")?;
1308
1309 assert_eq!(hash.as_bytes(), &response[..response.len() - 1]);
1310
1311 Ok(())
1312 }
1313
1314 #[test]
1315 fn crypto_pubpvt() -> Result<(), Box<dyn Error>> {
1316 let mock = ConnectorMock::new();
1317 let w = mock.read_stream();
1318 let client = Client::new(mock);
1319
1320 let response = b"646AABC0D78E87574FFCD2E98FF14B08C76D424C7A0ED783CB7B840117A403E3 \
1321 707E8334560C3FB2852CCCE11F9221FA3594B7DE3919A8BAA5B6DB90FE432E53646AABC0D78E87574FFCD2E98FF14B08C76D424C7A0ED783CB7B840117A403E3\n";
1322 w.replace(response.to_vec());
1323 let (pubkey, pvtkey) = client.crypto_pubpvt("pwd")?;
1324
1325 let exp_pubkey = "646AABC0D78E87574FFCD2E98FF14B08C76D424C7A0ED783CB7B840117A403E3";
1326 assert_eq!(pubkey, exp_pubkey);
1327
1328 let exp_pvtkey = "707E8334560C3FB2852CCCE11F9221FA3594B7DE3919A8BAA5B6DB90FE432E53646AABC0D78E87574FFCD2E98FF14B08C76D424C7A0ED783CB7B840117A403E3";
1329 assert_eq!(pvtkey, exp_pvtkey);
1330
1331 Ok(())
1332 }
1333
1334 #[test]
1335 fn listen() -> Result<(), Box<dyn Error>> {
1336 let response = b"5 #forum\n10 #chat\n2 $family\n";
1337 let mock = SyncConnectorMock::new(response);
1338 let client = Client::new(mock);
1339
1340 let listener = client.listen();
1341
1342 let (updates, chain) = listener.recv_timeout(Duration::new(1, 0))??;
1343 assert_eq!(updates, 5);
1344 assert_eq!(chain, ChainId::new("#forum").unwrap());
1345
1346 let (updates, chain) = listener.recv_timeout(Duration::new(1, 0))??;
1347 assert_eq!(updates, 10);
1348 assert_eq!(chain, ChainId::new("#chat").unwrap());
1349
1350 let (updates, chain) = listener.recv_timeout(Duration::new(1, 0))??;
1351 assert_eq!(updates, 2);
1352 assert_eq!(chain, ChainId::new("$family").unwrap());
1353
1354 Ok(())
1355 }
1356
1357 #[test]
1358 fn chains_join() -> Result<(), Box<dyn Error>> {
1359 let mock = ConnectorMock::new();
1360 let w = mock.read_stream();
1361 let client = Client::new(mock);
1362
1363 let chain_id = ChainId::new("$chat")?;
1364 let pwd_hash = "707E8334560C3FB2852CCCE11F9221FA3594B7DE3919A8BAA5B6DB90FE432E53";
1365
1366 let response = b"3E56AE4D17484398F7694C75EA6D3F9FD31C756574B4346D0BA40FB36DAB4501\n";
1367 w.replace(response.to_vec());
1368 let hash = client.join_chain(&chain_id, &[pwd_hash])?;
1369
1370 let exp_hash = &response[..response.len() - 1];
1371 assert_eq!(hash.as_bytes(), exp_hash);
1372
1373 Ok(())
1374 }
1375
1376 #[test]
1377 fn chains_list() -> Result<(), Box<dyn Error>> {
1378 let mock = ConnectorMock::new();
1379 let w = mock.read_stream();
1380 let client = Client::new(mock);
1381
1382 let response = b"$chain1 $chain2 #chain3 @chain4\n";
1383 w.replace(response.to_vec());
1384 let chains = client.chains()?;
1385
1386 let exp_chains: Result<Vec<_>, _> = std::str::from_utf8(&response[..response.len() - 1])
1387 .unwrap()
1388 .split(' ')
1389 .map(ChainId::new)
1390 .collect();
1391 let exp_chains = exp_chains?;
1392 assert_eq!(chains, exp_chains);
1393
1394 Ok(())
1395 }
1396
1397 #[test]
1398 fn chain_leave() -> Result<(), Box<dyn Error>> {
1399 let mock = ConnectorMock::new();
1400 let w = mock.read_stream();
1401 let client = Client::new(mock);
1402
1403 let chain_id = ChainId::new("$chat").unwrap();
1404
1405 let response = b"true\n";
1406 w.replace(response.to_vec());
1407 let left = client.chain(&chain_id).leave()?;
1408
1409 assert_eq!(left, true);
1410
1411 Ok(())
1412 }
1413
1414 #[test]
1415 fn chain_leave_failed() -> Result<(), Box<dyn Error>> {
1416 let mock = ConnectorMock::new();
1417 let w = mock.read_stream();
1418 let client = Client::new(mock);
1419
1420 let chain_id = ChainId::new("$chat").unwrap();
1421
1422 let response = b"false\n";
1423 w.replace(response.to_vec());
1424 let left = client.chain(&chain_id).leave()?;
1425
1426 assert_eq!(left, false);
1427
1428 Ok(())
1429 }
1430
1431 #[test]
1432 fn chain_genesis() -> Result<(), Box<dyn Error>> {
1433 let mock = ConnectorMock::new();
1434 let w = mock.read_stream();
1435 let client = Client::new(mock);
1436
1437 let chain_id = ChainId::new("$chat").unwrap();
1438
1439 let response = b"0_3E56AE4D17484398F7694C75EA6D3F9FD31C756574B4346D0BA40FB36DAB4501\n";
1440 w.replace(response.to_vec());
1441 let genesis = client.chain(&chain_id).genesis()?;
1442
1443 let exp_genesis = &response[..response.len() - 1];
1444 assert_eq!(genesis.as_bytes(), exp_genesis);
1445
1446 Ok(())
1447 }
1448
1449 #[test]
1450 fn chain_heads() -> Result<(), Box<dyn Error>> {
1451 let mock = ConnectorMock::new();
1452 let w = mock.read_stream();
1453 let client = Client::new(mock);
1454
1455 let chain_id = ChainId::new("$chat").unwrap();
1456
1457 let response = b"1_B8AAB63B4CC2129443F0BEA3F1A7FB16C193FE92C3DB2245EB9062EB07A47159 \
1458 2_30F9ABD1FDB2DF44CAF47743AE01FD768B1C2B1952B74A761F32E13D5483BE0E\n";
1459 w.replace(response.to_vec());
1460 let heads = client.chain(&chain_id).heads(false)?;
1461
1462 let exp_heads: Vec<_> = std::str::from_utf8(&response[..response.len() - 1])
1463 .unwrap()
1464 .split(' ')
1465 .map(String::from)
1466 .collect();
1467 assert_eq!(heads, exp_heads);
1468
1469 Ok(())
1470 }
1471
1472 #[test]
1473 fn chain_payload() -> Result<(), Box<dyn Error>> {
1474 let mock = ConnectorMock::new();
1475 let w = mock.read_stream();
1476 let client = Client::new(mock);
1477
1478 let chain_id = ChainId::new("$chat").unwrap();
1479
1480 let response = b"17\nhello,\nok message";
1481 w.replace(response.to_vec());
1482 let payload = client.chain(&chain_id).payload("some_hash", None)?;
1483 let payload = std::str::from_utf8(&payload)?;
1484
1485 let exp_payload = std::str::from_utf8(response)
1486 .unwrap()
1487 .split_once('\n')
1488 .unwrap()
1489 .1;
1490 assert_eq!(payload, exp_payload);
1491
1492 Ok(())
1493 }
1494
1495 #[test]
1496 fn chain_content_block() -> Result<(), Box<dyn Error>> {
1497 let mock = ConnectorMock::new();
1498 let w = mock.read_stream();
1499 let client = Client::new(mock);
1500
1501 let chain_id = ChainId::new("$chat").unwrap();
1502 let response = br#"710
1503{
1504 "hash": "2_3D9997F5D8A57B26B7DEC3BCDFD3A12EAE48C585DFC287FE9DE522E44A96DB46",
1505 "time": 1635174995341,
1506 "pay": {
1507 "crypt": false,
1508 "hash": "FD7B3E075AB7E245506323F1D1B55330CCD606FB3301F4CABA25022A1DE50B47"
1509 },
1510 "like": {
1511 "n": 1,
1512 "hash": "1_59C6AA0E3B3650D44D27EB8EF3645ECFC69A1B78F0DAA74A2B89167538749103"
1513 },
1514 "sign": {
1515 "hash": "9EB2D4CB0AC67B35B78B545E4AB3410DC82E6D0778DECFC80C83A13BEAD05F7B83D5B8B6CE67AB7EF9E0CA09FA489AE88FB0D126C522A0ED4E2C5BF939901205",
1516 "pub": "197154707DAF7953BE0EBB7BBE29FA1AECA402505E9ED00BCAF189EB6A32FCE8"
1517 },
1518 "backs": [
1519 "1_59C6AA0E3B3650D44D27EB8EF3645ECFC69A1B78F0DAA74A2B89167538749103"
1520 ]
1521}"#;
1522
1523 w.replace(response.to_vec());
1524 let content = client.chain(&chain_id).content("some_hash", None)?;
1525
1526 let exp_content = ContentBlock {
1527 hash: "2_3D9997F5D8A57B26B7DEC3BCDFD3A12EAE48C585DFC287FE9DE522E44A96DB46".to_string(),
1528 time: 1635174995341,
1529 payload: Some(PayloadBlock {
1530 crypt: false,
1531 hash: "FD7B3E075AB7E245506323F1D1B55330CCD606FB3301F4CABA25022A1DE50B47"
1532 .to_string(),
1533 }),
1534 like: Some(LikeBlock {
1535 n: 1,
1536 hash: "1_59C6AA0E3B3650D44D27EB8EF3645ECFC69A1B78F0DAA74A2B89167538749103"
1537 .to_string(),
1538 }),
1539 signature: Some(SignatureBlock {
1540 hash:"9EB2D4CB0AC67B35B78B545E4AB3410DC82E6D0778DECFC80C83A13BEAD05F7B83D5B8B6CE67AB7EF9E0CA09FA489AE88FB0D126C522A0ED4E2C5BF939901205".to_string(),
1541 pubkey: "197154707DAF7953BE0EBB7BBE29FA1AECA402505E9ED00BCAF189EB6A32FCE8".to_string(),
1542 }),
1543 backs: vec![
1544 "1_59C6AA0E3B3650D44D27EB8EF3645ECFC69A1B78F0DAA74A2B89167538749103".to_string()],
1545 };
1546 assert_eq!(content, exp_content);
1547
1548 Ok(())
1549 }
1550
1551 #[test]
1552 fn chain_post() -> Result<(), Box<dyn Error>> {
1553 let mock = ConnectorMock::new();
1554 let w = mock.read_stream();
1555 let client = Client::new(mock);
1556
1557 let chain_id = ChainId::new("$chat").unwrap();
1558
1559 let response = b"3_9B144EE0518E5DE01D4C1AABD469D85315715CB15F77AB4B3D87D7802EE970E6\n";
1560 w.replace(response.to_vec());
1561 let hash = client.chain(&chain_id).post(None, false, b"payload")?;
1562
1563 let exp_hash = std::str::from_utf8(&response[..response.len() - 1]).unwrap();
1564 assert_eq!(hash, exp_hash);
1565
1566 Ok(())
1567 }
1568
1569 #[test]
1570 fn chain_like() -> Result<(), Box<dyn Error>> {
1571 let mock = ConnectorMock::new();
1572 let w = mock.read_stream();
1573 let client = Client::new(mock);
1574
1575 let chain_id = ChainId::new("$chat").unwrap();
1576
1577 let response = b"2_3D9997F5D8A57B26B7DEC3BCDFD3A12EAE48C585DFC287FE9DE522E44A96DB46\n";
1578 w.replace(response.to_vec());
1579
1580 let fake_hash = "1_9B144EE0518E5DE01D4C1AABD469D85315715CB15F77AB4B3D87D7802EE970E6";
1581 let hash = client
1582 .chain(&chain_id)
1583 .like(&fake_hash, "pvt_key", b"i liked it")?;
1584
1585 let exp_hash = std::str::from_utf8(&response[..response.len() - 1]).unwrap();
1586 assert_eq!(hash, exp_hash);
1587
1588 Ok(())
1589 }
1590
1591 #[test]
1592 fn chain_dislike() -> Result<(), Box<dyn Error>> {
1593 let mock = ConnectorMock::new();
1594 let w = mock.read_stream();
1595 let client = Client::new(mock);
1596
1597 let chain_id = ChainId::new("$chat").unwrap();
1598
1599 let response = b"2_3D9997F5D8A57B26B7DEC3BCDFD3A12EAE48C585DFC287FE9DE522E44A96DB46\n";
1600 w.replace(response.to_vec());
1601
1602 let fake_hash = "1_9B144EE0518E5DE01D4C1AABD469D85315715CB15F77AB4B3D87D7802EE970E6";
1603 let hash = client
1604 .chain(&chain_id)
1605 .dislike(&fake_hash, "pvt_key", b"i liked it")?;
1606
1607 let exp_hash = std::str::from_utf8(&response[..response.len() - 1]).unwrap();
1608 assert_eq!(hash, exp_hash);
1609
1610 Ok(())
1611 }
1612
1613 #[test]
1614 fn chain_reputation() -> Result<(), Box<dyn Error>> {
1615 let mock = ConnectorMock::new();
1616 let w = mock.read_stream();
1617 let client = Client::new(mock);
1618
1619 let chain_id = ChainId::new("$chat").unwrap();
1620
1621 let response = b"18\n";
1622 w.replace(response.to_vec());
1623
1624 let fake_hash = "1_9B144EE0518E5DE01D4C1AABD469D85315715CB15F77AB4B3D87D7802EE970E6";
1625 let reps = client.chain(&chain_id).reputation(&fake_hash)?;
1626
1627 let exp_reps = 18;
1628 assert_eq!(reps, exp_reps);
1629
1630 Ok(())
1631 }
1632
1633 #[test]
1634 fn chain_consensus() -> Result<(), Box<dyn Error>> {
1635 let mock = ConnectorMock::new();
1636 let w = mock.read_stream();
1637 let client = Client::new(mock);
1638
1639 let chain_id = ChainId::new("$chat").unwrap();
1640
1641 let response = b"1_B8AAB63B4CC2129443F0BEA3F1A7FB16C193FE92C3DB2245EB9062EB07A47159 \
1642 2_30F9ABD1FDB2DF44CAF47743AE01FD768B1C2B1952B74A761F32E13D5483BE0E \
1643 3_3D9997F5D8A57B26B7DEC3BCDFD3A12EAE48C585DFC287FE9DE522E44A96DB46\n";
1644 w.replace(response.to_vec());
1645
1646 let hashes = client.chain(&chain_id).consensus()?;
1647
1648 let exp_hahses: Vec<_> = std::str::from_utf8(&response[..response.len() - 1])
1649 .unwrap()
1650 .split(' ')
1651 .map(String::from)
1652 .collect();
1653 assert_eq!(hashes, exp_hahses);
1654
1655 Ok(())
1656 }
1657
1658 #[test]
1659 fn chain_listen() -> Result<(), Box<dyn Error>> {
1660 let response = b"9\n6\n7\n9\n";
1661 let mock = SyncConnectorMock::new(response);
1662 let client = Client::new(mock);
1663
1664 let chain_id = ChainId::new("#forum").unwrap();
1665
1666 let listener = client.chain(&chain_id).listen();
1667
1668 let updates = listener.recv_timeout(Duration::new(1, 0))??;
1669 assert_eq!(updates, 9);
1670
1671 let updates = listener.recv_timeout(Duration::new(1, 0))??;
1672 assert_eq!(updates, 6);
1673
1674 let updates = listener.recv_timeout(Duration::new(1, 0))??;
1675 assert_eq!(updates, 7);
1676
1677 let updates = listener.recv_timeout(Duration::new(1, 0))??;
1678 assert_eq!(updates, 9);
1679
1680 Ok(())
1681 }
1682
1683 #[test]
1684 fn peer_ping() -> Result<(), Box<dyn Error>> {
1685 let mock = ConnectorMock::new();
1686 let w = mock.read_stream();
1687 let client = Client::new(mock);
1688
1689 let fake_peer = "1.2.3.4:8330";
1690
1691 let response = b"10\n";
1692 w.replace(response.to_vec());
1693
1694 let ping = client.peer(&fake_peer)?.ping()?;
1695
1696 let exp_ping = 10;
1697 assert_eq!(ping, exp_ping);
1698
1699 Ok(())
1700 }
1701
1702 #[test]
1703 fn peer_chains_list() -> Result<(), Box<dyn Error>> {
1704 let mock = ConnectorMock::new();
1705 let w = mock.read_stream();
1706 let client = Client::new(mock);
1707
1708 let fake_peer = "1.2.3.4:8330";
1709
1710 let response = b"$chain1 $chain2 #chain3 @chain4\n";
1711 w.replace(response.to_vec());
1712 let chains = client.peer(&fake_peer)?.chains()?;
1713
1714 let exp_chains: Result<Vec<_>, _> = std::str::from_utf8(&response[..response.len() - 1])
1715 .unwrap()
1716 .split(' ')
1717 .map(ChainId::new)
1718 .collect();
1719 let exp_chains = exp_chains?;
1720 assert_eq!(chains, exp_chains);
1721
1722 Ok(())
1723 }
1724
1725 #[test]
1726 fn peer_send() -> Result<(), Box<dyn Error>> {
1727 let mock = ConnectorMock::new();
1728 let w = mock.read_stream();
1729 let client = Client::new(mock);
1730
1731 let fake_peer = "1.2.3.4:8330";
1732 let chain_id = ChainId::new("#forum").unwrap();
1733
1734 let response = b"5 / 8\n";
1735 w.replace(response.to_vec());
1736
1737 let sent = client.peer(&fake_peer)?.send_chain(&chain_id)?;
1738
1739 let exp_sent = (5, 8);
1740 assert_eq!(sent, exp_sent);
1741
1742 Ok(())
1743 }
1744
1745 #[test]
1746 fn peer_receive() -> Result<(), Box<dyn Error>> {
1747 let mock = ConnectorMock::new();
1748 let w = mock.read_stream();
1749 let client = Client::new(mock);
1750
1751 let fake_peer = "1.2.3.4:8330";
1752 let chain_id = ChainId::new("#forum").unwrap();
1753
1754 let response = b"5 / 8\n";
1755 w.replace(response.to_vec());
1756
1757 let received = client.peer(&fake_peer)?.receive_chain(&chain_id)?;
1758
1759 let exp_received = (5, 8);
1760 assert_eq!(received, exp_received);
1761
1762 Ok(())
1763 }
1764
1765 #[test]
1766 fn host_get_time() -> Result<(), Box<dyn Error>> {
1767 let mock = ConnectorMock::new();
1768 let w = mock.read_stream();
1769 let client = Client::new(mock);
1770
1771 let response = b"1635310954\n";
1772 w.replace(response.to_vec());
1773
1774 let time = client.host().time()?;
1775
1776 let exp_time = 1635310954;
1777 assert_eq!(time, exp_time);
1778
1779 Ok(())
1780 }
1781
1782 #[test]
1783 fn host_set_time() -> Result<(), Box<dyn Error>> {
1784 let mock = ConnectorMock::new();
1785 let w = mock.read_stream();
1786 let client = Client::new(mock);
1787
1788 let response = b"1635310954\n";
1789 w.replace(response.to_vec());
1790
1791 let time = client.host().set_time(1635310954)?;
1792
1793 let exp_time = 1635310954;
1794 assert_eq!(time, exp_time);
1795
1796 Ok(())
1797 }
1798
1799 #[test]
1800 fn host_path() -> Result<(), Box<dyn Error>> {
1801 let mock = ConnectorMock::new();
1802 let w = mock.read_stream();
1803 let client = Client::new(mock);
1804
1805 let response = b"/tmp/freechains\n";
1806 w.replace(response.to_vec());
1807
1808 let path = client.host().path()?;
1809
1810 let exp_path = std::str::from_utf8(&response[..response.len() - 1]).unwrap();
1811 assert_eq!(path, exp_path);
1812
1813 Ok(())
1814 }
1815
1816 #[test]
1817 fn host_stop() -> Result<(), Box<dyn Error>> {
1818 let mock = ConnectorMock::new();
1819 let w = mock.read_stream();
1820 let client = Client::new(mock);
1821
1822 let response = b"true\n";
1823 w.replace(response.to_vec());
1824
1825 let stopped = client.host().stop()?;
1826
1827 let exp_stopped = true;
1828 assert_eq!(stopped, exp_stopped);
1829
1830 Ok(())
1831 }
1832}