You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: device.rs
    path of file: ./repos/dinghy/dinghy-lib/src/apple
    
    the code of the file until where you have to start completion: use super::{xcode, AppleSimulatorType};
use crate::apple::AppleDevicePlatform;
use crate::device::make_remote_app_with_name;
use crate::errors::*;
use crate::project::Project;
use crate::utils::LogCommandExt;
use crate::utils::{get_current_verbosity, user_facing_log};
use crate::Build;
use crate::BuildBundle;
use crate::Device;
use crate::DeviceCompatibility;
use crate::Runnable;
use colored::Colorize;
use itertools::Itertools;
use log::debug;
use std::fmt;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fs;
use std::io::{BufRead, BufReader};
use std::path::Path;
use std::process::{self, Stdio};
use std::time::Duration;

#[derive(Clone, Debug)]
pub struct IosDevice {
    pub id: String,
    pub name: String,
    pub arch_cpu: &'static str,
    rustc_triple: String,
    pub os: String,
}

#[derive(Clone, Debug)]
pub struct AppleSimDevice {
    pub id: String,
    pub name: String,
    pub os: String,
    pub sim_type: AppleSimulatorType,
}

unsafe impl Send for IosDevice {}

impl IosDevice {
    pub fn new(name: String, id: String, arch_cpu: &str, os: String) -> Result<IosDevice> {
        let cpu = match &*arch_cpu {
            "arm64" | "arm64e" => "aarch64",
            _ => "armv7",
        };
        Ok(IosDevice {
            name,
            id,
            os,
            arch_cpu: cpu.into(),
            rustc_triple: format!("{}-apple-ios", cpu),
        })
    }

    fn is_pre_ios_17(&self) -> Result<bool> {
        Ok(semver::Version::parse(&self.os)?.major < 17)
    }

    fn is_locked(&self) -> Result<bool> {
        let result = process::Command::new("xcrun")
            .args(
                "devicectl device info lockState --quiet --json-output /dev/stdout --device"
                    .split_whitespace(),
            )
            .arg(&self.id)
            .log_invocation(1)
            .output()
            .context("Failed to run devicectl device info lockState")?;
        if !result.status.success() {
            bail!("Device lock query failed\n",)
        }
        Ok(
            json::parse(std::str::from_utf8(&result.stdout)?)?["result"]["passcodeRequired"]
                .as_bool()
                .unwrap(),
        )
  
    
fn launch_app(dev: &AppleSimDevice, app_args: &[&str], _envs: &[&str]) -> Result<()> {
    use std::io::Write;
    let dir = ::tempdir::TempDir::new("mobiledevice-rs-lldb")?;
    let tmppath = dir.path();
    let mut install_path = String::from_utf8(
        process::Command::new("xcrun")
            .args(&["simctl", "get_app_container", &dev.id, "Dinghy"])
            .log_invocation(2)
            .output()?
            .stdout,
    )?;
    install_path.pop();
    let stdout = Path::new(&install_path)
        .join("stdout")
        .to_string_lossy()
        .into_owned();
    let stdout_param = &format!("--stdout={}", stdout);
    let mut xcrun_args: Vec<&str> = vec!["simctl", "launch", "-w", stdout_param, &dev.id, "Dinghy"];
    xcrun_args.extend(app_args);
    debug!("Launching app via xcrun using args: {:?}", xcrun_args);
    let launch_output = process::Command::new("xcrun")
        .args(&xcrun_args)
        .log_invocation(1)
        .output()?;
    let launch_output = String::from_utf8_lossy(&launch_output.stdout);
    debug!("xcrun simctl launch output: {:?}", launch_output);

    // Output from the launch command should be "Dinghy: $PID" which is after the 8th character.
    let dinghy_pid = launch_output.split_at(8).1;

    // Attaching to the processes needs to be done in a script, not a commandline parameter or
    // lldb will say "no simulators found".
    let lldb_script_filename = tmppath.join("lldb-script");
    let mut script = fs::File::create(&lldb_script_filename)?;
    write!(script, "attach {}\n", dinghy_pid)?;
    write!(script, "continue\n")?;
    write!(script, "quit\n")?;
    let output = process::Command::new("lldb")
        .arg("")
        .arg("-s")
        .arg(lldb_script_filename)
        .output()?;
    let test_contents = std::fs::read_to_string(stdout)?;
    println!("{}", test_contents);

    let output: String = String::from_utf8_lossy(&output.stdout).to_string();
    debug!("lldb script: \n{}", output);
    // The stdout from lldb is something like:
    //
    // (lldb) attach 34163
    // Process 34163 stopped
    // * thread #1, stop reason = signal SIGSTOP
    //     frame #0: 0x00000001019cd000 dyld`_dyld_start
    // dyld`_dyld_start:
    // ->  0x1019cd000 <+0>: popq   %rdi
    //     0x1019cd001 <+1>: pushq  $0x0
    //     0x1019cd003 <+3>: movq   %rsp, %rbp
    //     0x1019cd006 <+6>: andq   $-0x10, %rsp
    // Target 0: (Dinghy) stopped.
    // Executable module set to .....
    // Architecture set to: x86_64h-apple-ios-.
    // (lldb) continue
    // Process 34163 resuming
    // Process 34163 exited with status = 101 (0x00000065)
    // (lldb) quit
    //
    // We need the "exit with status" line which is the 3rd from the last
    let exit_status_line = output
        .lines()
        .rev()
        .find(|line| line.contains("exited with status"));
    if let Some(exit_status_line) = exit_status_line {
        let words: Vec<&str> = exit_status_line.split_whitespace().rev().collect();
        if let Some(exit_status) = words.get(1) {
            let exit_status = exit_status.parse::<u32>()?;
            if exit_status == 0 {
                Ok(())
            } else {
                bail!("Test failure, exit code: {}", exit_status)
            }
        } else {
            panic!(
                "Failed to parse lldb exit line for an exit status. {:?}",
                words
            );
        }
    } else {
        panic!("Failed to get the exit status line from lldb: {}", output);
    }
}