1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
use super::{Args, Numeric, ServerMsgKindRaw, SharedSource, Source, Tags};
use crate::{
error::{InvalidString, ParseError},
names::{Name, NameValued, ServerMsgKind},
string::{Cmd, Line, Nick},
};
use std::io::Write;
/// An IRC message sent by a server.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize))]
pub struct ServerMsg<'a> {
/// This message's tags, if any.
pub tags: Tags<'a>,
/// Where this message originated.
pub source: Option<SharedSource<'a>>,
/// What kind of message this is.
pub kind: ServerMsgKindRaw<'a>,
/// This message's arguments.
pub args: Args<'a>,
}
impl ServerMsg<'static> {
#[deprecated = "Moved to `ClientCodec` in 0.4."]
/// Reads a `'static` server message from `read`.
/// This function may block.
///
/// `buf` must either be empty or contain a partial message from
/// a previous call to this function that errored due to
/// non-blocking I/O or unexpected EOF.
/// Other errors may leave `buf` in an invalid state for future calls.
pub fn read_owning_from(
read: &mut (impl std::io::BufRead + ?Sized),
buf: &mut Vec<u8>,
) -> std::io::Result<Self> {
super::ClientCodec::read_owning_from(read, buf)
}
#[deprecated = "Moved to `ClientCodec` in 0.4."]
/// Asynchronously reads a `'static` server message from `read`.
///
/// `buf` must either be empty or contain a partial message from
/// a previous call to this function that errored due to
/// non-blocking I/O or unexpected EOF.
/// Other errors may leave `buf` in an invalid state for future calls.
#[cfg(feature = "tokio")]
pub async fn read_owning_from_tokio(
read: &mut (impl tokio::io::AsyncBufReadExt + ?Sized + Unpin),
buf: &mut Vec<u8>,
) -> std::io::Result<Self> {
super::ClientCodec::read_owning_from_tokio(read, buf).await
}
}
impl<'a> ServerMsg<'a> {
/// Creates a new `ServerMsg` with the provided message type and source.
pub fn new<T: Name<ServerMsgKind>>(kind: T, source: SharedSource<'a>) -> Self {
ServerMsg {
tags: Tags::new(),
source: Some(source),
kind: kind.as_raw().clone(),
args: Args::empty(),
}
}
/// Attempts to parse this message further into a higher-level message type.
///
/// Does not check if the message kind matches, assuming such a check has been done earlier.
pub fn parse_as<N>(&self, _kind: N) -> Result<N::Value<'a>, ParseError>
where
N: NameValued<ServerMsgKind>,
{
N::from_union(self)
}
#[deprecated = "Moved to `ClientCodec` in 0.4."]
/// Reads a server message from `read`.
/// This function may block.
///
/// Consider using [`ServerMsg::read_owning_from`] instead
/// unless minimizing memory allocations is very important.
///
/// `buf` must either be empty or contain a partial message from
/// a previous call to this function that errored due to
/// non-blocking I/O or unexpected EOF.
/// Other errors may leave `buf` in an invalid state for future calls.
pub fn read_borrowing_from(
read: &mut (impl std::io::BufRead + ?Sized),
buf: &'a mut Vec<u8>,
) -> std::io::Result<Self> {
super::ClientCodec::read_borrowing_from(read, buf)
}
#[deprecated = "Moved to `ClientCodec` in 0.4."]
/// Asynchronously reads a server message from `read`.
///
/// Consider using [`ServerMsg::read_owning_from_tokio`] instead
/// unless minimizing memory allocations is very important.
///
/// `buf` must either be empty or contain a partial message from
/// a previous call to this function that errored due to
/// non-blocking I/O or unexpected EOF.
/// Other errors may leave `buf` in an invalid state for future calls.
#[cfg(feature = "tokio")]
pub async fn read_borrowing_from_tokio(
read: &mut (impl tokio::io::AsyncBufReadExt + ?Sized + Unpin),
buf: &'a mut Vec<u8>,
) -> std::io::Result<ServerMsg<'a>> {
super::ClientCodec::read_borrowing_from_tokio(read, buf).await
}
/// The length of the longest permissible server message, including tags.
pub const MAX_LEN: usize = 8703;
/// Creates a new `ServerMsg` with the provided numeric reply code, source, and target.
pub fn new_num(num: Numeric, source: SharedSource<'a>, target: Nick<'a>) -> Self {
ServerMsg {
tags: Tags::new(),
source: Some(source),
kind: ServerMsgKindRaw::Numeric(num),
args: Args::new(vec![target.into()], None),
}
}
/// Creates a new `ServerMsg` with the provided command.
pub const fn new_cmd(cmd: Cmd<'a>) -> Self {
ServerMsg {
tags: Tags::new(),
source: None,
kind: ServerMsgKindRaw::Cmd(cmd),
args: Args::empty(),
}
}
/// Parses a message from a [`Line`].
pub fn parse(
msg: impl TryInto<Line<'a>, Error = impl Into<InvalidString>>,
) -> Result<ServerMsg<'a>, ParseError> {
let msg = msg.try_into().map_err(|e| ParseError::InvalidLine(e.into()))?;
let (tags, source, kind, args) = super::parse(msg, Source::parse, |kind| {
Ok(if let Some(num) = Numeric::from_bytes(&kind) {
num.into()
} else {
Cmd::from_word(kind).map_err(ParseError::InvalidKind)?.into()
})
})?;
let source = source.map(SharedSource::new);
Ok(ServerMsg { tags, source, kind, args })
}
/// The number of bytes of space remaining in this message, excluding tags.
///
/// If either of the returned values are negative, this message is too long
/// to guarantee that it will be delivered whole.
pub fn bytes_left(&self) -> isize {
super::bytes_left(&self.kind.as_arg(), self.source.as_deref(), &self.args)
}
#[deprecated = "Moved to `ServerCodec` in 0.4."]
/// Writes self to the provided [`Write`] WITHOUT a trailing CRLF.
///
/// This function makes many small writes. Buffering is strongly recommended.
pub fn write_to(&self, write: &mut (impl Write + ?Sized)) -> std::io::Result<()> {
super::ServerCodec::write_to(self, write)
}
#[deprecated = "Moved to `ServerCodec` in 0.4."]
/// Writes self to `write` WITH a trailing CRLF,
/// using the provided buffer to minimize the necessary number of writes to `write`.
///
/// The buffer will be cleared after successfully sending this message.
/// If the buffer is non-empty, message data will be appended to the buffer's contents.
pub fn send_to(
&self,
write: &mut (impl Write + ?Sized),
buf: &mut Vec<u8>,
) -> std::io::Result<()> {
super::ServerCodec::send_to(self, write, buf)
}
#[deprecated = "Moved to `ServerCodec` in 0.4."]
/// Asynchronously writes self to `write` WITH a trailing CRLF,
/// using the provided buffer to minimize the necessary number of writes to `write`.
///
/// The buffer will be cleared after successfully sending this message.
/// If the buffer is non-empty, message data will be appended to the buffer's contents.
#[cfg(feature = "tokio")]
pub async fn send_to_tokio(
&self,
write: &mut (impl tokio::io::AsyncWriteExt + ?Sized + Unpin),
buf: &mut Vec<u8>,
) -> std::io::Result<()> {
super::ServerCodec::send_to_tokio(self, write, buf).await
}
/// Converts `self` into a version that owns its data.
pub fn owning(self) -> ServerMsg<'static> {
let source = self.source.map(|src| SharedSource::new(src.owning()));
ServerMsg {
tags: self.tags.owning(),
source,
kind: self.kind.owning(),
args: self.args.owning(),
}
}
}
impl std::fmt::Display for ServerMsg<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if !self.tags.is_empty() {
// Tags' Display impl includes the leading @.
write!(f, "{} ", self.tags)?;
}
if let Some(ref src) = self.source {
write!(f, ":{} ", src)?;
}
write!(f, "{}", self.kind.as_arg())?;
if !self.args.is_empty() {
write!(f, " {}", self.args)?;
}
Ok(())
}
}