scuffle_rtmp/command_messages/
writer.rs1use std::fmt::Display;
4use std::io;
5
6use bytes::{BufMut, Bytes, BytesMut};
7
8use super::error::CommandError;
9use super::{Command, CommandResultLevel, CommandType};
10use crate::chunk::writer::ChunkWriter;
11use crate::chunk::{CHUNK_STREAM_ID_COMMAND, Chunk};
12use crate::error::RtmpError;
13use crate::messages::MessageType;
14
15impl AsRef<str> for CommandResultLevel {
16 fn as_ref(&self) -> &str {
17 match self {
18 CommandResultLevel::Warning => "warning",
19 CommandResultLevel::Status => "status",
20 CommandResultLevel::Error => "error",
21 CommandResultLevel::Unknown(s) => s,
22 }
23 }
24}
25
26impl Display for CommandResultLevel {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 match self {
29 CommandResultLevel::Warning => write!(f, "warning"),
30 CommandResultLevel::Status => write!(f, "status"),
31 CommandResultLevel::Error => write!(f, "error"),
32 CommandResultLevel::Unknown(s) => write!(f, "{s}"),
33 }
34 }
35}
36
37impl Command<'_> {
38 fn write_amf0_chunk(io: &mut impl io::Write, writer: &ChunkWriter, payload: Bytes) -> io::Result<()> {
39 writer.write_chunk(
40 io,
41 Chunk::new(CHUNK_STREAM_ID_COMMAND, 0, MessageType::CommandAMF0, 0, payload),
42 )
43 }
44
45 pub fn write(self, io: &mut impl io::Write, writer: &ChunkWriter) -> Result<(), RtmpError> {
49 let mut buf = BytesMut::new();
50 let mut buf_writer = (&mut buf).writer();
51
52 match self.command_type {
53 CommandType::NetConnection(command) => {
54 command.write(&mut buf_writer, self.transaction_id)?;
55 }
56 CommandType::NetStream(_) => {
57 return Err(RtmpError::from(CommandError::NoClientImplementation));
58 }
59 CommandType::OnStatus(command) => {
60 command.write(&mut buf_writer, self.transaction_id)?;
61 }
62 CommandType::Unknown { .. } => {}
64 }
65
66 Self::write_amf0_chunk(io, writer, buf.freeze())?;
67
68 Ok(())
69 }
70}
71
72#[cfg(test)]
73#[cfg_attr(all(test, coverage_nightly), coverage(off))]
74mod tests {
75 use super::super::{Command, CommandResultLevel};
76 use crate::chunk::writer::ChunkWriter;
77 use crate::command_messages::CommandType;
78 use crate::command_messages::error::CommandError;
79 use crate::command_messages::netstream::NetStreamCommand;
80 use crate::error::RtmpError;
81
82 #[test]
83 fn command_result_level_to_str() {
84 assert_eq!(CommandResultLevel::Warning.as_ref(), "warning");
85 assert_eq!(CommandResultLevel::Status.as_ref(), "status");
86 assert_eq!(CommandResultLevel::Error.as_ref(), "error");
87 assert_eq!(CommandResultLevel::Unknown("custom".to_string()).as_ref(), "custom");
88 }
89
90 #[test]
91 fn command_result_level_into_string() {
92 assert_eq!(CommandResultLevel::Warning.to_string(), "warning");
93 assert_eq!(CommandResultLevel::Status.to_string(), "status");
94 assert_eq!(CommandResultLevel::Error.to_string(), "error");
95 assert_eq!(CommandResultLevel::Unknown("custom".to_string()).to_string(), "custom");
96 }
97
98 #[test]
99 fn netstream_command_write() {
100 let mut buf = Vec::new();
101 let writer = ChunkWriter::default();
102
103 let err = Command {
104 command_type: CommandType::NetStream(NetStreamCommand::CloseStream),
105 transaction_id: 1.0,
106 }
107 .write(&mut buf, &writer)
108 .unwrap_err();
109
110 assert!(matches!(err, RtmpError::Command(CommandError::NoClientImplementation)));
111 }
112}