|
1 | 1 | use super::*; |
| 2 | +use crate::{ |
| 3 | + nvim::BufferDirection, |
| 4 | + types::{BuildConfiguration, Platform}, |
| 5 | +}; |
| 6 | + |
| 7 | +#[cfg(feature = "daemon")] |
| 8 | +use {crate::constants::DAEMON_STATE, crate::types::SimDevice, anyhow::anyhow as err}; |
| 9 | + |
| 10 | +#[derive(Debug, Serialize, Deserialize)] |
| 11 | +pub struct DeviceLookup { |
| 12 | + name: String, |
| 13 | + udid: String, |
| 14 | + platform: Platform, |
| 15 | +} |
2 | 16 |
|
3 | 17 | /// Run a project. |
4 | 18 | #[derive(Debug, Serialize, Deserialize)] |
5 | 19 | pub struct Run { |
6 | | - simulator: bool, |
7 | | - client: Client, |
| 20 | + pub client: Client, |
| 21 | + pub config: BuildConfiguration, |
| 22 | + pub device: Option<DeviceLookup>, |
| 23 | + pub direction: Option<BufferDirection>, |
8 | 24 | } |
9 | 25 |
|
10 | | -#[cfg(feature = "lua")] |
11 | | -impl<'a> Requester<'a, Run> for Run {} |
12 | | - |
13 | | -// TODO: Implement run command |
14 | | -// |
15 | | -// Also, it might be important to pick which target/paltform to run under. This is currently just |
16 | | -// either with a simulator or not assuming only the use case won't include |
17 | | -// macos apps, which is wrong |
18 | 26 | #[cfg(feature = "daemon")] |
19 | 27 | #[async_trait::async_trait] |
20 | 28 | impl Handler for Run { |
21 | 29 | async fn handle(self) -> Result<()> { |
22 | | - tracing::info!("Run command"); |
| 30 | + tracing::info!("⚙️ Running command: {:#?}", self); |
| 31 | + |
| 32 | + let Self { |
| 33 | + client, |
| 34 | + config, |
| 35 | + device, |
| 36 | + .. |
| 37 | + } = self; |
| 38 | + let Client { pid, root } = client; |
| 39 | + |
| 40 | + let direction = self.direction.clone(); |
| 41 | + let state = DAEMON_STATE.clone().lock_owned().await; |
| 42 | + let platform = if let Some(d) = device.as_ref() { |
| 43 | + Some(d.platform.clone()) |
| 44 | + } else { |
| 45 | + None |
| 46 | + }; |
| 47 | + |
| 48 | + let nvim = state |
| 49 | + .clients |
| 50 | + .get(&pid) |
| 51 | + .ok_or_else(|| err!("no client found with {}", pid))?; |
| 52 | + |
| 53 | + let args = { |
| 54 | + let mut args = config.as_args(); |
| 55 | + if let Some(platform) = platform { |
| 56 | + args.extend(platform.sdk_simulator_args()) |
| 57 | + } |
| 58 | + args |
| 59 | + }; |
| 60 | + |
| 61 | + let build_settings = xcodebuild::runner::build_settings(&root, &args).await?; |
| 62 | + let ref app_id = build_settings.product_bundle_identifier; |
| 63 | + |
| 64 | + // FIX(run): When running with release path_to_app is incorrect |
| 65 | + // |
| 66 | + // Err: application bundle was not found at the provided path.\nProvide a valid path to the desired application bundle. |
| 67 | + // |
| 68 | + // Path doesn't point to local directory build |
| 69 | + let ref path_to_app = build_settings.metal_library_output_dir; |
| 70 | + |
| 71 | + tracing::debug!("{app_id}: {:?}", path_to_app); |
| 72 | + let (success, ref win) = nvim |
| 73 | + .new_logger("Build", &config.target, &direction) |
| 74 | + .log_build_stream(&root, &args, false, true) |
| 75 | + .await?; |
| 76 | + |
| 77 | + if !success { |
| 78 | + let msg = format!("Failed: {} ", config.to_string()); |
| 79 | + nvim.echo_err(&msg).await?; |
| 80 | + anyhow::bail!("{msg}"); |
| 81 | + } |
| 82 | + |
| 83 | + let ref mut logger = nvim.new_logger("Run", &config.target, &direction); |
| 84 | + |
| 85 | + logger.set_running().await?; |
| 86 | + |
| 87 | + if let Some(mut device) = get_device(&state, device) { |
| 88 | + device.try_boot(logger, win).await?; |
| 89 | + device.try_install(path_to_app, app_id, logger, win).await?; |
| 90 | + device.try_launch(app_id, logger, win).await?; |
| 91 | + |
| 92 | + logger.set_status_end(true, true).await?; |
| 93 | + |
| 94 | + tokio::spawn(async move { |
| 95 | + let mut state = DAEMON_STATE.clone().lock_owned().await; |
| 96 | + state.devices.insert(device); |
| 97 | + state.sync_client_state().await |
| 98 | + }); |
| 99 | + } else { |
| 100 | + // TODO: check if macOS is the platform and run it |
| 101 | + } |
| 102 | + |
23 | 103 | Ok(()) |
24 | 104 | } |
25 | 105 | } |
26 | 106 |
|
| 107 | +// let target = project.get_target(&config.target, ,)?; |
| 108 | +#[cfg(feature = "daemon")] |
| 109 | +fn get_device<'a>( |
| 110 | + state: &'a tokio::sync::OwnedMutexGuard<crate::state::State>, |
| 111 | + device: Option<DeviceLookup>, |
| 112 | +) -> Option<SimDevice> { |
| 113 | + if let Some(device) = device { |
| 114 | + state |
| 115 | + .devices |
| 116 | + .iter() |
| 117 | + .find(|d| d.name == device.name && d.udid == device.udid) |
| 118 | + .cloned() |
| 119 | + } else { |
| 120 | + None |
| 121 | + } |
| 122 | +} |
| 123 | + |
27 | 124 | #[cfg(feature = "lua")] |
28 | | -impl<'a> FromLua<'a> for Run { |
29 | | - fn from_lua(lua_value: LuaValue<'a>, _lua: &'a Lua) -> LuaResult<Self> { |
30 | | - if let LuaValue::Table(table) = lua_value { |
31 | | - Ok(Self { |
32 | | - simulator: table.get("simulator")?, |
33 | | - client: table.get("client")?, |
34 | | - }) |
| 125 | +impl<'a> Requester<'a, Run> for Run { |
| 126 | + fn pre(lua: &Lua, msg: &Run) -> LuaResult<()> { |
| 127 | + lua.print(&msg.to_string()); |
| 128 | + Ok(()) |
| 129 | + } |
| 130 | +} |
| 131 | + |
| 132 | +impl ToString for Run { |
| 133 | + fn to_string(&self) -> String { |
| 134 | + if let Some(ref device) = self.device { |
| 135 | + format!("run [{}] with {}", device.name, self.config.to_string()) |
35 | 136 | } else { |
36 | | - Err(LuaError::external("Fail to deserialize Run")) |
| 137 | + format!("run with {}", self.config.to_string()) |
37 | 138 | } |
38 | 139 | } |
39 | 140 | } |
| 141 | + |
| 142 | +#[cfg(feature = "lua")] |
| 143 | +impl<'a> FromLua<'a> for Run { |
| 144 | + fn from_lua(lua_value: LuaValue<'a>, _lua: &'a Lua) -> LuaResult<Self> { |
| 145 | + let table = match lua_value { |
| 146 | + LuaValue::Table(t) => Ok(t), |
| 147 | + _ => Err(LuaError::external("Fail to deserialize Run")), |
| 148 | + }?; |
| 149 | + |
| 150 | + let device: Option<LuaTable> = table.get("device")?; |
| 151 | + |
| 152 | + Ok(Self { |
| 153 | + client: table.get("client")?, |
| 154 | + config: table.get("config")?, |
| 155 | + direction: table.get("direction").ok(), |
| 156 | + device: device |
| 157 | + .map(|d| { |
| 158 | + let name = d.get("name").ok()?; |
| 159 | + let udid = d.get("udid").ok()?; |
| 160 | + let platform = d.get("platform").ok()?; |
| 161 | + Some(DeviceLookup { |
| 162 | + name, |
| 163 | + udid, |
| 164 | + platform, |
| 165 | + }) |
| 166 | + }) |
| 167 | + .flatten(), |
| 168 | + }) |
| 169 | + } |
| 170 | +} |
0 commit comments