#![doc = include_str!("../README.md")]
#![deny(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]
#![doc(html_root_url = "https://docs.smix.dev/smix-runner-wire")]
use serde::{Deserialize, Serialize};
use smix_selector::Selector;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum RunnerTransportErrorKind {
#[error("runner {endpoint} fetch failed")]
FetchFailed {
endpoint: String,
},
#[error("runner {endpoint} returned status {status}: {body}")]
NonSuccessStatus {
endpoint: String,
status: u16,
body: String,
},
#[error("runner {endpoint} returned non-JSON body")]
NonJsonBody {
endpoint: String,
},
#[error("runner {endpoint} returned malformed body: {detail}")]
MalformedBody {
endpoint: String,
detail: String,
},
#[error("runner {endpoint} unreachable: {message}")]
Unreachable {
endpoint: String,
message: String,
},
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RunnerIncludeOpts {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub include: Option<IncludeScope>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum IncludeScope {
AllWindows,
}
impl IncludeScope {
pub fn query_value(self) -> &'static str {
match self {
IncludeScope::AllWindows => "all-windows",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum TapMode {
Resolve,
ResolveAndTap,
DaemonProxySynthesize,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TapStages {
#[serde(rename = "resolveMs", default)]
pub resolve_ms: f64,
#[serde(rename = "tapCallMs", default)]
pub tap_call_ms: f64,
#[serde(rename = "totalMs", default)]
pub total_ms: f64,
#[serde(rename = "waitExistenceMs", default)]
pub wait_existence_ms: f64,
#[serde(rename = "frameReadMs", default)]
pub frame_read_ms: f64,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TapResult {
#[serde(default)]
pub stages: Option<TapStages>,
#[serde(default)]
pub frame: Option<smix_screen::Rect>,
#[serde(rename = "appFrame", default)]
pub app_frame: Option<smix_screen::Rect>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TapRequest {
pub selector: Selector,
pub mode: TapMode,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TapAtNormCoordRequest {
pub nx: f64,
pub ny: f64,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct KeyboardStages {
#[serde(rename = "resolveMs", default)]
pub resolve_ms: f64,
#[serde(rename = "keyboardWaitMs", default)]
pub keyboard_wait_ms: f64,
#[serde(rename = "typingMs", default)]
pub typing_ms: f64,
#[serde(rename = "totalMs", default)]
pub total_ms: f64,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RunnerKeyboardResult {
#[serde(default)]
pub tree: Option<smix_screen::A11yNode>,
#[serde(default)]
pub stages: Option<KeyboardStages>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FindRequest {
pub selector: Selector,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FindResponse {
#[serde(default)]
pub exists: bool,
#[serde(default)]
pub ok: bool,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RunnerScrollSelector {
Text {
text: String,
},
Id {
id: String,
},
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ScrollResponse {
#[serde(default)]
pub matched: Option<bool>,
#[serde(default)]
pub swipes: Option<u32>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SystemPopup {
pub id: String,
#[serde(rename = "type")]
pub kind: String,
pub source: String,
#[serde(default)]
pub title: String,
#[serde(default)]
pub body: String,
#[serde(default)]
pub buttons: Vec<SystemPopupButton>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SystemPopupButton {
pub id: String,
pub label: String,
pub role: String,
#[serde(default)]
pub dangerous: bool,
#[serde(rename = "outcomeHint", default)]
pub outcome_hint: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SystemPopupActionRequest {
pub popup_id: String,
pub button_id: String,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SystemPopupActionResponse {
#[serde(default)]
pub ok: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SystemPopupsResponse {
#[serde(default)]
pub popups: Vec<SystemPopup>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RecordedEvent {
#[serde(default)]
pub raw_code: i32,
#[serde(default)]
pub timestamp_ms: f64,
#[serde(flatten, default)]
pub extra: serde_json::Map<String, serde_json::Value>,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RecordEventsResponse {
#[serde(default)]
pub events: Vec<RecordedEvent>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionOpenRequest {
#[serde(default)]
pub bundle_id: String,
#[serde(default)]
pub activate: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionOpenResponse {
pub session_id: String,
#[serde(default)]
pub activated_once: bool,
#[serde(default)]
pub server_time_ms: u64,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionCloseRequest {
pub session_id: String,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionCloseResponse {
pub ok: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionRenewActivationRequest {
pub session_id: String,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionRenewActivationResponse {
pub ok: bool,
#[serde(default)]
pub activated: bool,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct HealthResponse {
pub ok: bool,
#[serde(default)]
pub runner_version: String,
#[serde(default)]
pub uptime_ms: u64,
#[serde(default)]
pub last_request_at_ms: u64,
#[serde(default)]
pub sessions_open: u32,
#[serde(default)]
pub activations_total: u64,
#[serde(default)]
pub sim_render_server: HealthProcessInfo,
#[serde(default)]
pub xcodebuild_test_host: HealthTestHostInfo,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct HealthProcessInfo {
#[serde(default)]
pub alive: bool,
#[serde(default)]
pub pid: u32,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct HealthTestHostInfo {
#[serde(default)]
pub alive: bool,
#[serde(default)]
pub pid: u32,
#[serde(default)]
pub restart_count: u32,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionCloseAllResponse {
pub ok: bool,
#[serde(default)]
pub closed: u32,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionRelaunchAppRequest {
pub session_id: String,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionRelaunchAppResponse {
pub ok: bool,
#[serde(default)]
pub wall_ms: u64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum SimHealthWireState {
#[serde(rename = "healthy")]
Healthy,
#[serde(rename = "degraded")]
Degraded,
#[serde(rename = "cycling")]
Cycling,
#[serde(rename = "dead")]
Dead,
}
impl SimHealthWireState {
pub fn from_header(value: &str) -> Option<Self> {
match value.trim().to_ascii_lowercase().as_str() {
"healthy" => Some(Self::Healthy),
"degraded" => Some(Self::Degraded),
"cycling" => Some(Self::Cycling),
"dead" => Some(Self::Dead),
_ => None,
}
}
pub fn as_header(&self) -> &'static str {
match self {
Self::Healthy => "healthy",
Self::Degraded => "degraded",
Self::Cycling => "cycling",
Self::Dead => "dead",
}
}
}