crossbundle_tools/
error.rs

1//! Contains `Error`, `AndroidError`, `AppleError` types used by `crossbundle-tools`.
2
3#[cfg(feature = "apple")]
4use apple_bundle::plist;
5use displaydoc::Display;
6use std::path::PathBuf;
7use std::process::Command;
8use thiserror::Error;
9
10/// `Result` type that used in `crossbundle-tools`.
11pub type Result<T> = std::result::Result<T, Error>;
12
13/// Android specific error type.
14#[cfg(feature = "android")]
15#[derive(Display, Debug, Error)]
16pub enum AndroidError {
17    /// Android NDK is not found
18    AndroidNdkNotFound,
19    /// Failed to read source.properties
20    FailedToReadSourceProperties,
21    /// Invalid source.properties: {0}
22    InvalidSourceProperties(String),
23    /// Gradle Dependency project dir not found: {0}
24    GradleDependencyProjectNotFound(PathBuf),
25    /// Gradle Dependency project doesn't contain build.gradle: {0}
26    GradleDependencyProjectNoBuildFile(PathBuf),
27    /// Gradle is not found
28    GradleNotFound,
29    /// Android SDK has no build tools
30    BuildToolsNotFound,
31    /// Android SDK has no platforms installed
32    NoPlatformsFound,
33    /// Platform {0} is not installed
34    PlatformNotFound(u32),
35    /// Target is not supported
36    UnsupportedTarget,
37    /// Host {0} is not supported
38    UnsupportedHost(String),
39    /// Invalid semver
40    InvalidSemver,
41    /// Unsupported or invalid target: {0}
42    InvalidBuildTarget(String),
43    /// Unsupported or invalid app wrapper: {0}
44    InvalidAppWrapper(String),
45    /// Unsupported or invalid build strategy: {0}
46    InvalidBuildStrategy(String),
47    /// Failed to find AndroidManifest.xml in path: {0}
48    FailedToFindAndroidManifest(String),
49    /// Unable to find NDK file
50    UnableToFindNDKFile,
51    /// AndroidTools error: {0:?}
52    AndroidTools(#[from] android_tools::error::Error),
53    /// AndroidManifest error: {0:?}
54    AndroidManifest(#[from] android_manifest::error::Error),
55}
56
57/// Apple specific error type.
58#[cfg(feature = "apple")]
59#[derive(Display, Debug, Error)]
60pub enum AppleError {
61    /// Code signing profile not found
62    CodeSigningProfilesNotFound,
63    /// Code signing profile not provided
64    CodeSigningProfileNotProvided,
65    /// Codesign failed {0}
66    CodesignFailed(String),
67    /// Failed to archive payload
68    ZipCommandFailed,
69    /// Codesign allocate not found
70    CodesignAllocateNotFound,
71    /// Simctl error: {0:?}
72    Simctl(simctl::Error),
73    /// Target dir does not exists
74    TargetNotFound,
75    /// Resources dir does not exists
76    ResourcesNotFound,
77    /// Unsupported or invalid build strategy: {0}
78    InvalidBuildStrategy(String),
79    /// Unsupported or invalid target: {0}
80    InvalidBuildTarget(String),
81    /// Assets dir does not exists
82    AssetsNotFound,
83    /// Failed to find Info.plist in path: {0}
84    FailedToFindInfoPlist(String),
85    /// Plist data error: {0:?}
86    Plist(#[from] plist::Error),
87}
88
89/// Main error type.
90#[derive(Display, Debug, Error)]
91#[ignore_extra_doc_attributes]
92pub enum Error {
93    /// Command '{0:?}' had a non-zero exit code. Stdout: {1} Stderr: {2}
94    CmdFailed(Command, String, String),
95    /// Command {0} not found
96    CmdNotFound(String),
97    /// Failed to copy file in specified path `{path}` cause of `{cause}`
98    CopyToFileFailed {
99        path: PathBuf,
100        cause: std::io::Error,
101    },
102    /// Width and height of the icon have different sizes. Choose another image
103    WidthAndHeightDifSizes,
104    /// Icons already exist. Use overwrite flag
105    IconsAlreadyExist,
106    /// Failed to find the manifest in path: {0}
107    FailedToFindManifest(PathBuf),
108    /// Invalid profile: {0}
109    InvalidProfile(String),
110    /// GNU toolchain binary `{gnu_bin}` nor LLVM toolchain binary `{llvm_bin}` found in
111    /// `{toolchain_path:?}`
112    ToolchainBinaryNotFound {
113        toolchain_path: PathBuf,
114        gnu_bin: String,
115        llvm_bin: String,
116    },
117    /// Path {0:?} doesn't exist
118    PathNotFound(PathBuf),
119    /// Failed to find cargo manifest: {0}
120    FailedToFindCargoManifest(String),
121    /// Failed to choose shell string color.
122    /// Argument for --color must be auto, always, or never, but found `{}`
123    FailedToChooseShellStringColor(String),
124    /// IO error: {0:?}
125    Io(#[from] std::io::Error),
126    /// FS Extra error: {0:?}
127    FsExtra(#[from] fs_extra::error::Error),
128    /// Zip error: {0:?}
129    Zip(#[from] zip::result::ZipError),
130    /// Android error: {0:?}
131    #[cfg(feature = "android")]
132    Android(#[from] AndroidError),
133    /// Image crate error: {0:?}
134    ImageError(#[from] image::ImageError),
135    /// Apple error: {0:?}
136    #[cfg(feature = "apple")]
137    Apple(#[from] AppleError),
138    /// Anyhow error: {0:?}
139    AnyhowError(#[from] anyhow::Error),
140    /// Other error: {0:?}
141    OtherError(#[from] Box<dyn std::error::Error>),
142}
143
144/// Extension trait for [`Command`] that helps
145/// to wrap output and print logs from command execution.
146///
147/// [`Command`]: std::process::Command
148pub trait CommandExt {
149    /// Executes the command as a child process, then captures an output and return it.
150    /// If command termination wasn't successful wraps an output into error and return it.
151    fn output_err(self, print_logs: bool) -> Result<std::process::Output>;
152}
153
154impl CommandExt for Command {
155    fn output_err(mut self, print_logs: bool) -> Result<std::process::Output> {
156        // Enables log print during command execution
157        let output = match print_logs {
158            true => self.spawn().and_then(|p| p.wait_with_output())?,
159            false => self.output()?,
160        };
161        if !output.status.success() {
162            return Err(Error::CmdFailed(
163                self,
164                String::from_utf8_lossy(&output.stdout).to_string(),
165                String::from_utf8_lossy(&output.stderr).to_string(),
166            ));
167        }
168        Ok(output)
169    }
170}
171
172#[cfg(feature = "apple")]
173impl From<plist::Error> for Error {
174    fn from(error: plist::Error) -> Self {
175        AppleError::from(error).into()
176    }
177}
178
179#[cfg(feature = "apple")]
180impl From<simctl::Error> for Error {
181    fn from(error: simctl::Error) -> Self {
182        AppleError::Simctl(error).into()
183    }
184}
185
186#[cfg(feature = "android")]
187impl From<android_tools::error::Error> for Error {
188    fn from(error: android_tools::error::Error) -> Self {
189        AndroidError::AndroidTools(error).into()
190    }
191}