chromiumoxide_types/
lib.rs

1use std::borrow::Cow;
2use std::fmt;
3use std::fmt::Debug;
4use std::ops::Deref;
5
6use serde::de::DeserializeOwned;
7use serde::{Deserialize, Serialize};
8
9pub type MethodId = Cow<'static, str>;
10
11/// A Request sent by the client, identified by the `id`
12#[derive(Serialize, Debug, PartialEq, Eq)]
13pub struct MethodCall {
14    /// Identifier for this method call
15    ///
16    /// [`MethodCall`] id's must be unique for every session
17    pub id: CallId,
18    /// The method identifier
19    pub method: MethodId,
20    /// The CDP session id of any
21    #[serde(rename = "sessionId", skip_serializing_if = "Option::is_none")]
22    pub session_id: Option<String>,
23    /// The payload of the request
24    pub params: serde_json::Value,
25}
26
27/// Identifier for a request send to the chromium server
28///
29/// All requests (`MethodCall`) must contain a unique identifier.
30#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
31pub struct CallId(usize);
32
33impl fmt::Display for CallId {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        write!(f, "CallId({})", self.0)
36    }
37}
38
39impl CallId {
40    /// Create a new id
41    pub fn new(id: usize) -> Self {
42        CallId(id)
43    }
44}
45
46/// Trait that all the request types have to implement.
47pub trait Command: serde::ser::Serialize + Method {
48    /// The type of the response this request triggers on the chromium server
49    type Response: serde::de::DeserializeOwned + fmt::Debug;
50
51    /// deserialize the response from json
52    fn response_from_value(response: serde_json::Value) -> serde_json::Result<Self::Response> {
53        serde_json::from_value(response)
54    }
55}
56
57/// A generic, successful,  response of a request where the `result` has been
58/// serialized into the `Command::Response` type.
59pub struct CommandResponse<T>
60where
61    T: fmt::Debug,
62{
63    pub id: CallId,
64    pub result: T,
65    pub method: MethodId,
66}
67
68/// Represents the successfully deserialization of an incoming response.
69///
70/// A response can either contain the result (`Command::Response`) are an error
71/// `Error`.
72pub type CommandResult<T> = Result<CommandResponse<T>, Error>;
73
74impl<T: fmt::Debug> Deref for CommandResponse<T> {
75    type Target = T;
76
77    fn deref(&self) -> &Self::Target {
78        &self.result
79    }
80}
81
82/// A received `Event` from the websocket where the `params` is deserialized as
83/// json
84#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
85pub struct CdpJsonEventMessage {
86    /// Name of the method
87    pub method: MethodId,
88    /// The session this event is meant for.
89    pub session_id: Option<String>,
90    /// Json payload of the event
91    pub params: serde_json::Value,
92}
93
94impl Method for CdpJsonEventMessage {
95    fn identifier(&self) -> MethodId {
96        self.method.clone()
97    }
98}
99
100impl EventMessage for CdpJsonEventMessage {
101    fn session_id(&self) -> Option<&str> {
102        self.params.get("sessionId").and_then(|x| x.as_str())
103    }
104}
105
106/// A trait that mark
107pub trait EventMessage: Method + DeserializeOwned + Debug {
108    /// The identifier of the session this event was meant for.
109    fn session_id(&self) -> Option<&str>;
110}
111
112/// `Method`s are message types that contain the field `method =
113/// Self::identifier()` in their json body.
114pub trait Method {
115    /// The whole string identifier for this method like: `DOM.removeNode`
116    fn identifier(&self) -> MethodId;
117
118    /// The name of the domain this method belongs to: `DOM`
119    fn domain_name(&self) -> MethodId {
120        self.split().0
121    }
122
123    /// The standalone identifier of the method inside the domain: `removeNode`
124    fn method_name(&self) -> MethodId {
125        self.split().1
126    }
127
128    /// Tuple of (`domain_name`, `method_name`) : (`DOM`, `removeNode`)
129    fn split(&self) -> (MethodId, MethodId) {
130        match self.identifier() {
131            Cow::Borrowed(id) => {
132                let mut iter = id.split('.');
133                (iter.next().unwrap().into(), iter.next().unwrap().into())
134            }
135            Cow::Owned(id) => {
136                let mut iter = id.split('.');
137                (
138                    Cow::Owned(iter.next().unwrap().into()),
139                    Cow::Owned(iter.next().unwrap().into()),
140                )
141            }
142        }
143    }
144}
145
146/// A trait that identifies a method on type level
147pub trait MethodType {
148    /// The identifier for this event's `method` field
149    fn method_id() -> MethodId
150    where
151        Self: Sized;
152}
153
154/// A Wrapper for json serialized requests
155#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
156pub struct Request {
157    /// The identifier for the type of this request.
158    pub method: MethodId,
159    /// The session this request targets
160    #[serde(rename = "sessionId", skip_serializing_if = "Option::is_none")]
161    pub session_id: Option<String>,
162    /// The serialized `Command` payload
163    pub params: serde_json::Value,
164}
165
166impl Request {
167    pub fn new(method: MethodId, params: serde_json::Value) -> Self {
168        Self {
169            method,
170            params,
171            session_id: None,
172        }
173    }
174
175    pub fn with_session(
176        method: MethodId,
177        params: serde_json::Value,
178        session_id: impl Into<String>,
179    ) -> Self {
180        Self {
181            method,
182            params,
183            session_id: Some(session_id.into()),
184        }
185    }
186}
187
188/// A response to a [`MethodCall`] from the chromium instance
189#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
190pub struct Response {
191    /// Numeric identifier for the exact request
192    pub id: CallId,
193    /// The response payload
194    pub result: Option<serde_json::Value>,
195    /// The Reason why the [`MethodCall`] failed.
196    pub error: Option<Error>,
197}
198
199/// An incoming message read from the web socket can either be a response to a
200/// previously submitted `Request`, identified by an identifier `id`, or an
201/// `Event` emitted by the server.
202#[derive(Deserialize, Debug, Clone)]
203#[serde(untagged)]
204#[allow(clippy::large_enum_variant)]
205pub enum Message<T = CdpJsonEventMessage> {
206    /// A response for a request
207    Response(Response),
208    /// An emitted event from the server
209    Event(T),
210}
211
212/// A response can either contain the `Command::Response` type in the `result`
213/// field of the payload or an `Error` in the `error` field if the request
214/// resulted in an error.
215#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
216pub struct ResponseError {
217    pub id: CallId,
218    /// Error code
219    pub code: usize,
220    /// Error Message
221    pub message: String,
222}
223
224/// Represents the error type emitted by the chromium server for failed
225/// requests.
226#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
227pub struct Error {
228    /// Error code
229    pub code: i64,
230    /// Error Message
231    pub message: String,
232}
233
234impl fmt::Display for Error {
235    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236        write!(f, "Error {}: {}", self.code, self.message)
237    }
238}
239
240impl std::error::Error for Error {}
241
242/// Represents a binary type as defined in the CDP protocol.
243#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
244pub struct Binary(String);
245
246impl AsRef<str> for Binary {
247    fn as_ref(&self) -> &str {
248        self.0.as_str()
249    }
250}
251
252impl AsRef<[u8]> for Binary {
253    fn as_ref(&self) -> &[u8] {
254        self.0.as_bytes()
255    }
256}
257
258impl From<Binary> for String {
259    fn from(b: Binary) -> String {
260        b.0
261    }
262}
263
264impl From<String> for Binary {
265    fn from(expr: String) -> Self {
266        Self(expr)
267    }
268}