(&self, udid: &str)
| 88 | } |
| 89 | |
| 90 | pub async fn ensure_started(&self, udid: &str) -> Result<(), AppError> { |
| 91 | let state = { |
| 92 | let mut streams = self.streams.lock().await; |
| 93 | streams |
| 94 | .entry(udid.to_owned()) |
| 95 | .or_insert_with(|| Arc::new(LogStreamState::default())) |
| 96 | .clone() |
| 97 | }; |
| 98 | let spawn_log_stream = self.spawn_log_stream.clone(); |
| 99 | |
| 100 | let Some((child, stdout)) = run_start_if_idle(&state, || async move { |
| 101 | let mut child = spawn_log_stream(udid)?; |
| 102 | |
| 103 | let stdout = child |
| 104 | .stdout |
| 105 | .take() |
| 106 | .ok_or_else(|| AppError::native("Simulator log stream did not expose stdout."))?; |
| 107 | |
| 108 | Ok((child, stdout)) |
| 109 | }) |
| 110 | .await? |
| 111 | else { |
| 112 | return Ok(()); |
| 113 | }; |
| 114 | |
| 115 | let udid = udid.to_owned(); |
| 116 | tokio::spawn(async move { |
| 117 | let result = read_log_stream(child, stdout, state.clone()).await; |
| 118 | let mut status = state.status.lock().await; |
| 119 | status.phase = LogStreamPhase::Idle; |
| 120 | if let Err(error) = result { |
| 121 | tracing::warn!(%udid, %error, "simulator log stream stopped"); |
| 122 | } |
| 123 | }); |
| 124 | |
| 125 | Ok(()) |
| 126 | } |
| 127 | |
| 128 | pub async fn snapshot(&self, udid: &str, filters: &LogFilters, limit: usize) -> Vec<LogEntry> { |
| 129 | let state = { |
no test coverage detected