1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//! Contains `Error`, `AndroidError`, `AppleError` types used by `crossbundle-tools`.

use apple_bundle::plist;
use displaydoc::Display;
use std::path::PathBuf;
use std::process::Command;
use thiserror::Error;

/// `Result` type that used in `crossbundle-tools`.
pub type Result<T> = std::result::Result<T, Error>;

/// Android specific error type
#[derive(Display, Debug, Error)]
pub enum AndroidError {
    /// Android SDK is not found
    AndroidSdkNotFound,
    /// Android NDK is not found
    AndroidNdkNotFound,
    /// Android SDK has no build tools
    BuildToolsNotFound,
    /// Android SDK has no platforms installed
    NoPlatformsFound,
    /// Failed to create directory
    DirectoryWasNotCreated,
    /// Platform {0} is not installed
    PlatformNotFound(u32),
    /// Target is not supported
    UnsupportedTarget,
    /// Host {0} is not supported
    UnsupportedHost(String),
    /// Invalid semver
    InvalidSemver,
    /// Unsupported or invalid target: {0}
    InvalidBuildTarget(String),
    /// Failed to find AndroidManifest.xml in path: {0}
    FailedToFindAndroidManifest(String),
    /// Unable to find NDK file
    UnableToFindNDKFile,
    /// AndroidTools error
    AndroidTools(#[from] android_tools::error::Error),
    /// AndroidManifest error
    AndroidManifest(#[from] android_manifest::error::Error),
}

/// Apple specific error type.
#[derive(Display, Debug, Error)]
pub enum AppleError {
    /// Code signing profile not found
    CodeSigningProfilesNotFound,
    /// Code signing profile not provided
    CodeSigningProfileNotProvided,
    /// Codesign failed {0}
    CodesignFailed(String),
    /// Failed to archive payload
    ZipCommandFailed,
    /// Codesign allocate not found
    CodesignAllocateNotFound,
    /// Simctl error
    Simctl(simctl::Error),
    /// Target dir does not exists
    TargetNotFound,
    /// Resources dir does not exists
    ResourcesNotFound,
    /// Assets dir does not exists
    AssetsNotFound,
    /// Failed to find Info.plist in path: {0}
    FailedToFindInfoPlist(String),
    /// Plist data error
    Plist(#[from] plist::Error),
}

/// Main error type.
#[derive(Display, Debug, Error)]
#[ignore_extra_doc_attributes]
pub enum Error {
    /// Command '{0:?}' had a non-zero exit code. Stdout: {1} Stderr: {2}
    CmdFailed(Command, String, String),
    /// Command {0} not found
    CmdNotFound(String),
    /// Failed to copy file in specified path `{path}` cause of `{cause}`
    CopyToFileFailed {
        path: PathBuf,
        cause: std::io::Error,
    },
    /// Invalid profile: {0}
    InvalidProfile(String),
    /// Invalid interface orientation: {0:?}
    InvalidInterfaceOrientation(String),
    /// Home dir not found
    HomeDirNotFound,
    /// Failed to create jar file in specified path `{path}` cause of `{cause}`
    JarFileCreationFailed {
        path: PathBuf,
        cause: std::io::Error,
    },
    /// GNU toolchain binary `{gnu_bin}` nor LLVM toolchain binary `{llvm_bin}` found in
    /// `{toolchain_path:?}`
    ToolchainBinaryNotFound {
        toolchain_path: PathBuf,
        gnu_bin: String,
        llvm_bin: String,
    },
    /// Path {0:?} doesn't exist
    PathNotFound(PathBuf),
    /// Failed to find cargo manifest: {0}
    FailedToFindCargoManifest(String),
    /// Failed to choose shell string color.
    /// Argument for --color must be auto, always, or never, but found `{}`
    FailedToChooseShellStringColor(String),
    /// IO error
    Io(#[from] std::io::Error),
    /// FS Extra error
    FsExtra(#[from] fs_extra::error::Error),
    /// Zip error
    Zip(#[from] zip::result::ZipError),
    /// Android error
    Android(#[from] AndroidError),
    /// Apple error
    Apple(#[from] AppleError),
    /// Anyhow error
    AnyhowError(#[from] anyhow::Error),
    /// Other error
    OtherError(#[from] Box<dyn std::error::Error>),
}

/// Extension trait for [`Command`] that helps
/// to wrap output and print logs from command execution.
///
/// [`Command`]: std::process::Command
pub trait CommandExt {
    /// Executes the command as a child process, then captures an output and return it.
    /// If command termination wasn't successful wraps an output into error and return it.
    fn output_err(self, print_logs: bool) -> Result<std::process::Output>;
}

impl CommandExt for Command {
    fn output_err(mut self, print_logs: bool) -> Result<std::process::Output> {
        // Enables log print during command execution
        let output = match print_logs {
            true => self.spawn().and_then(|p| p.wait_with_output())?,
            false => self.output()?,
        };
        if !output.status.success() {
            return Err(Error::CmdFailed(
                self,
                String::from_utf8_lossy(&output.stdout).to_string(),
                String::from_utf8_lossy(&output.stderr).to_string(),
            ));
        }
        Ok(output)
    }
}

impl From<plist::Error> for Error {
    fn from(error: plist::Error) -> Self {
        AppleError::from(error).into()
    }
}

impl From<simctl::Error> for Error {
    fn from(error: simctl::Error) -> Self {
        AppleError::Simctl(error).into()
    }
}

impl From<android_tools::error::Error> for Error {
    fn from(error: android_tools::error::Error) -> Self {
        AndroidError::AndroidTools(error).into()
    }
}