• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mattwparas / steel / 18461079395

13 Oct 2025 09:20AM UTC coverage: 42.731% (-0.9%) from 43.668%
18461079395

Pull #536

github

web-flow
Merge 6f55a8b56 into e378cba22
Pull Request #536: Initial proposal for no_std support

63 of 755 new or added lines in 38 files covered. (8.34%)

73 existing lines in 15 files now uncovered.

12324 of 28841 relevant lines covered (42.73%)

3215759.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/libs/steel-websockets/src/lib.rs
1
use std::net::TcpStream;
2

3
use abi_stable::std_types::{RBoxError, RResult};
4
use steel::steel_vm::ffi::{as_underlying_ffi_type, CustomRef, FFIArg, FFIValue};
5
use tungstenite::{
6
    connect, handshake::client::Response, stream::MaybeTlsStream, Message, WebSocket,
7
};
8

9
use steel::{rvals::Custom, steel_vm::ffi::IntoFFIVal};
10

11
use steel::{
12
    declare_module,
13
    steel_vm::ffi::{FFIModule, RegisterFFIFn},
14
};
15

16
struct SteelWebSocket(WebSocket<MaybeTlsStream<TcpStream>>);
17
impl Custom for SteelWebSocket {}
18

19
impl SteelWebSocket {
20
    fn read_message(&mut self) -> Result<WebSocketMessage, WebSocketError> {
×
21
        self.0
×
22
            .read_message()
23
            .map(WebSocketMessage)
×
24
            .map_err(WebSocketError)
×
25
    }
26

27
    fn write_message(&mut self, message: FFIArg) -> RResult<FFIValue, RBoxError> {
×
28
        if let FFIArg::CustomRef(CustomRef { mut custom, .. }) = message {
×
29
            if let Some(inner) = as_underlying_ffi_type::<WebSocketMessage>(custom.get_mut()) {
×
30
                let result = self.0.write_message(std::mem::replace(
31
                    &mut inner.0,
32
                    Message::Text(String::new()),
33
                ));
34

35
                match result {
36
                    Ok(_) => true.into_ffi_val(),
×
37
                    Err(e) => RResult::RErr(RBoxError::new(e)),
×
38
                }
39
            } else {
40
                false.into_ffi_val()
×
41
            }
42
        } else {
43
            false.into_ffi_val()
×
44
        }
45
    }
46
}
47

48
#[derive(Clone)]
49
struct WebSocketMessage(Message);
50

51
impl Custom for WebSocketMessage {
52
    fn fmt(&self) -> Option<std::result::Result<String, std::fmt::Error>> {
×
53
        Some(Ok(format!("{:?}", self.0)))
×
54
    }
55
}
56

57
impl WebSocketMessage {
58
    fn is_ping(&self) -> bool {
×
59
        self.0.is_ping()
×
60
    }
61

62
    fn is_pong(&self) -> bool {
×
63
        self.0.is_pong()
×
64
    }
65

66
    fn is_text(&self) -> bool {
×
67
        self.0.is_text()
×
68
    }
69
}
70

71
struct WebSocketResponse { _inner: Response }
72
impl Custom for WebSocketResponse {}
73

74
#[derive(Debug)]
75
struct WebSocketError(tungstenite::Error);
76

77
impl Custom for WebSocketError {
78
    fn fmt(&self) -> Option<std::result::Result<String, std::fmt::Error>> {
×
79
        Some(Ok(format!("{:?}", self.0)))
×
80
    }
81
}
82

83
/// In lieu of a direct bytes type in Steel, since we need to typically respond to a ping with a pong
84
/// containing the matching payload, we can skip the deserializing into the Steel type and directly
85
/// construct the pong here
86
fn ping_to_pong(message: &WebSocketMessage) -> Option<WebSocketMessage> {
×
87
    if let Message::Ping(payload) = &message.0 {
×
88
        Some(WebSocketMessage(Message::Pong(payload.clone())))
89
    } else {
90
        None
×
91
    }
92
}
93

94
/// Extracts the payload from a given message
95
fn text_payload(message: &WebSocketMessage) -> Option<String> {
×
96
    if let Message::Text(text) = &message.0 {
×
97
        Some(text.clone())
98
    } else {
99
        None
×
100
    }
101
}
102

103
// TODO: @Matt -> perhaps explore this and/or explore using tokio tungstenite for this
104
fn _make_non_blocking(socket: &mut SteelWebSocket) -> std::result::Result<(), std::io::Error> {
×
105
    match socket.0.get_mut() {
×
106
        MaybeTlsStream::Plain(s) => s.set_nonblocking(true),
×
107
        MaybeTlsStream::Rustls(s) => s.get_mut().set_nonblocking(true),
×
108
        _ => todo!(),
109
    }
110
}
111

112
declare_module!(create_module);
113

114
fn create_module() -> FFIModule {
×
115
    let mut module = FFIModule::new("steel/web/ws");
×
116

117
    module
×
118
        .register_fn("ws/message-ping?", WebSocketMessage::is_ping)
×
119
        .register_fn("ws/message-pong?", WebSocketMessage::is_pong)
×
120
        .register_fn("ws/message-text?", WebSocketMessage::is_text)
×
121
        .register_fn("ws/message-text", |text: String| {
×
122
            WebSocketMessage(Message::Text(text))
×
123
        })
124
        .register_fn("ws/message-ping->pong", ping_to_pong)
×
125
        .register_fn("ws/message->text-payload", text_payload)
×
126
        .register_fn("ws/connect", |url: String| {
×
127
            connect(url)
×
128
                .map(|(socket, resp)| {
×
129
                    vec![
×
130
                        SteelWebSocket(socket).into_ffi_val().unwrap(),
×
NEW
131
                        WebSocketResponse { _inner: resp }.into_ffi_val().unwrap(),
×
132
                    ]
133
                })
134
                .map_err(WebSocketError)
×
135
        })
136
        .register_fn("ws/read-message!", SteelWebSocket::read_message)
×
137
        .register_fn("ws/write-message!", SteelWebSocket::write_message);
×
138

139
    module
×
140
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc