settings.rs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
  1. // Copyright 2019-2021 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. use super::category::AppCategory;
  5. use crate::bundle::{common, platform::target_triple};
  6. use std::{
  7. collections::HashMap,
  8. path::{Path, PathBuf},
  9. };
  10. /// The type of the package we're bundling.
  11. #[derive(Clone, Copy, Debug, Eq, PartialEq)]
  12. pub enum PackageType {
  13. /// The macOS application bundle (.app).
  14. MacOsBundle,
  15. /// The iOS app bundle.
  16. IosBundle,
  17. /// The Windows bundle (.msi).
  18. #[cfg(target_os = "windows")]
  19. WindowsMsi,
  20. /// The Linux Debian package bundle (.deb).
  21. Deb,
  22. /// The Linux RPM bundle (.rpm).
  23. Rpm,
  24. /// The Linux AppImage bundle (.AppImage).
  25. AppImage,
  26. /// The macOS DMG bundle (.dmg).
  27. Dmg,
  28. /// The Updater bundle.
  29. Updater,
  30. }
  31. impl PackageType {
  32. /// Maps a short name to a PackageType.
  33. /// Possible values are "deb", "ios", "msi", "app", "rpm", "appimage", "dmg", "updater".
  34. pub fn from_short_name(name: &str) -> Option<PackageType> {
  35. // Other types we may eventually want to support: apk.
  36. match name {
  37. "deb" => Some(PackageType::Deb),
  38. "ios" => Some(PackageType::IosBundle),
  39. #[cfg(target_os = "windows")]
  40. "msi" => Some(PackageType::WindowsMsi),
  41. "app" => Some(PackageType::MacOsBundle),
  42. "rpm" => Some(PackageType::Rpm),
  43. "appimage" => Some(PackageType::AppImage),
  44. "dmg" => Some(PackageType::Dmg),
  45. "updater" => Some(PackageType::Updater),
  46. _ => None,
  47. }
  48. }
  49. /// Gets the short name of this PackageType.
  50. #[allow(clippy::trivially_copy_pass_by_ref)]
  51. pub fn short_name(&self) -> &'static str {
  52. match *self {
  53. PackageType::Deb => "deb",
  54. PackageType::IosBundle => "ios",
  55. #[cfg(target_os = "windows")]
  56. PackageType::WindowsMsi => "msi",
  57. PackageType::MacOsBundle => "app",
  58. PackageType::Rpm => "rpm",
  59. PackageType::AppImage => "appimage",
  60. PackageType::Dmg => "dmg",
  61. PackageType::Updater => "updater",
  62. }
  63. }
  64. /// Gets the list of the possible package types.
  65. pub fn all() -> &'static [PackageType] {
  66. ALL_PACKAGE_TYPES
  67. }
  68. }
  69. const ALL_PACKAGE_TYPES: &[PackageType] = &[
  70. PackageType::Deb,
  71. PackageType::IosBundle,
  72. #[cfg(target_os = "windows")]
  73. PackageType::WindowsMsi,
  74. PackageType::MacOsBundle,
  75. PackageType::Rpm,
  76. PackageType::Dmg,
  77. PackageType::AppImage,
  78. PackageType::Updater,
  79. ];
  80. /// The package settings.
  81. #[derive(Debug, Clone)]
  82. pub struct PackageSettings {
  83. /// the package's product name.
  84. pub product_name: String,
  85. /// the package's version.
  86. pub version: String,
  87. /// the package's description.
  88. pub description: String,
  89. /// the package's homepage.
  90. pub homepage: Option<String>,
  91. /// the package's authors.
  92. pub authors: Option<Vec<String>>,
  93. /// the default binary to run.
  94. pub default_run: Option<String>,
  95. }
  96. /// The updater settings.
  97. #[derive(Debug, Clone)]
  98. pub struct UpdaterSettings {
  99. /// Whether the updater is active or not.
  100. pub active: bool,
  101. /// The updater endpoints.
  102. pub endpoints: Option<Vec<String>>,
  103. /// Optional pubkey.
  104. pub pubkey: Option<String>,
  105. /// Display built-in dialog or use event system if disabled.
  106. pub dialog: bool,
  107. }
  108. /// The Linux debian bundle settings.
  109. #[derive(Clone, Debug, Default)]
  110. pub struct DebianSettings {
  111. // OS-specific settings:
  112. /// the list of debian dependencies.
  113. pub depends: Option<Vec<String>>,
  114. /// whether we should use the bootstrap script on debian or not.
  115. ///
  116. /// this script goal is to allow your app to access environment variables e.g $PATH.
  117. ///
  118. /// without it, you can't run some applications installed by the user.
  119. pub use_bootstrapper: Option<bool>,
  120. /// List of custom files to add to the deb package.
  121. /// Maps the path on the debian package to the path of the file to include (relative to the current working directory).
  122. pub files: HashMap<PathBuf, PathBuf>,
  123. }
  124. /// The macOS bundle settings.
  125. #[derive(Clone, Debug, Default)]
  126. pub struct MacOsSettings {
  127. /// MacOS frameworks that need to be bundled with the app.
  128. ///
  129. /// Each string can either be the name of a framework (without the `.framework` extension, e.g. `"SDL2"`),
  130. /// in which case we will search for that framework in the standard install locations (`~/Library/Frameworks/`, `/Library/Frameworks/`, and `/Network/Library/Frameworks/`),
  131. /// or a path to a specific framework bundle (e.g. `./data/frameworks/SDL2.framework`). Note that this setting just makes tauri-bundler copy the specified frameworks into the OS X app bundle
  132. /// (under `Foobar.app/Contents/Frameworks/`); you are still responsible for:
  133. ///
  134. /// - arranging for the compiled binary to link against those frameworks (e.g. by emitting lines like `cargo:rustc-link-lib=framework=SDL2` from your `build.rs` script)
  135. ///
  136. /// - embedding the correct rpath in your binary (e.g. by running `install_name_tool -add_rpath "@executable_path/../Frameworks" path/to/binary` after compiling)
  137. pub frameworks: Option<Vec<String>>,
  138. /// A version string indicating the minimum MacOS version that the bundled app supports (e.g. `"10.11"`).
  139. /// If you are using this config field, you may also want have your `build.rs` script emit `cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=10.11`.
  140. pub minimum_system_version: Option<String>,
  141. /// The path to the LICENSE file for macOS apps.
  142. /// Currently only used by the dmg bundle.
  143. pub license: Option<String>,
  144. /// whether we should use the bootstrap script on macOS .app or not.
  145. ///
  146. /// this script goal is to allow your app to access environment variables e.g $PATH.
  147. ///
  148. /// without it, you can't run some applications installed by the user.
  149. pub use_bootstrapper: Option<bool>,
  150. /// The exception domain to use on the macOS .app bundle.
  151. ///
  152. /// This allows communication to the outside world e.g. a web server you're shipping.
  153. pub exception_domain: Option<String>,
  154. pub signing_identity: Option<String>,
  155. pub entitlements: Option<String>,
  156. }
  157. #[cfg(windows)]
  158. #[derive(Clone, Debug, Default)]
  159. pub struct WixSettings {
  160. /// By default, the bundler uses an internal template.
  161. /// This option allows you to define your own wix file.
  162. pub template: Option<PathBuf>,
  163. pub fragment_paths: Vec<PathBuf>,
  164. pub component_group_refs: Vec<String>,
  165. pub component_refs: Vec<String>,
  166. pub feature_group_refs: Vec<String>,
  167. pub feature_refs: Vec<String>,
  168. pub merge_refs: Vec<String>,
  169. pub skip_webview_install: bool,
  170. }
  171. /// The Windows bundle settings.
  172. #[cfg(windows)]
  173. #[derive(Clone, Debug, Default)]
  174. pub struct WindowsSettings {
  175. pub digest_algorithm: Option<String>,
  176. pub certificate_thumbprint: Option<String>,
  177. pub timestamp_url: Option<String>,
  178. pub wix: Option<WixSettings>,
  179. }
  180. /// The bundle settings of the BuildArtifact we're bundling.
  181. #[derive(Clone, Debug, Default)]
  182. pub struct BundleSettings {
  183. /// the app's identifier.
  184. pub identifier: Option<String>,
  185. /// the app's icon list.
  186. pub icon: Option<Vec<String>>,
  187. /// the app's resources to bundle.
  188. ///
  189. /// each item can be a path to a file or a path to a folder.
  190. ///
  191. /// supports glob patterns.
  192. pub resources: Option<Vec<String>>,
  193. /// the app's copyright.
  194. pub copyright: Option<String>,
  195. /// the app's category.
  196. pub category: Option<AppCategory>,
  197. /// the app's short description.
  198. pub short_description: Option<String>,
  199. /// the app's long description.
  200. pub long_description: Option<String>,
  201. // Bundles for other binaries:
  202. /// Configuration map for the possible [bin] apps to bundle.
  203. pub bin: Option<HashMap<String, BundleSettings>>,
  204. /// External binaries to add to the bundle.
  205. ///
  206. /// Note that each binary name will have the target platform's target triple appended,
  207. /// so if you're bundling the `sqlite3` app, the bundler will look for e.g.
  208. /// `sqlite3-x86_64-unknown-linux-gnu` on linux,
  209. /// and `sqlite3-x86_64-pc-windows-gnu.exe` on windows.
  210. ///
  211. /// The possible target triples can be seen by running `$ rustup target list`.
  212. pub external_bin: Option<Vec<String>>,
  213. /// Debian-specific settings.
  214. pub deb: DebianSettings,
  215. /// MacOS-specific settings.
  216. pub macos: MacOsSettings,
  217. // Updater configuration
  218. pub updater: Option<UpdaterSettings>,
  219. /// Windows-specific settings.
  220. #[cfg(windows)]
  221. pub windows: WindowsSettings,
  222. }
  223. #[derive(Clone, Debug)]
  224. pub struct BundleBinary {
  225. name: String,
  226. src_path: Option<String>,
  227. main: bool,
  228. }
  229. impl BundleBinary {
  230. pub fn new(name: String, main: bool) -> Self {
  231. Self {
  232. name: if cfg!(windows) {
  233. format!("{}.exe", name)
  234. } else {
  235. name
  236. },
  237. src_path: None,
  238. main,
  239. }
  240. }
  241. pub fn set_src_path(mut self, src_path: Option<String>) -> Self {
  242. self.src_path = src_path;
  243. self
  244. }
  245. pub fn set_main(&mut self, main: bool) {
  246. self.main = main;
  247. }
  248. pub fn set_name(&mut self, name: String) {
  249. self.name = name;
  250. }
  251. pub fn name(&self) -> &str {
  252. &self.name
  253. }
  254. #[cfg(windows)]
  255. pub fn main(&self) -> bool {
  256. self.main
  257. }
  258. pub fn src_path(&self) -> &Option<String> {
  259. &self.src_path
  260. }
  261. }
  262. /// The Settings exposed by the module.
  263. #[derive(Clone, Debug)]
  264. pub struct Settings {
  265. /// the package settings.
  266. package: PackageSettings,
  267. /// the package types we're bundling.
  268. ///
  269. /// if not present, we'll use the PackageType list for the target OS.
  270. package_types: Option<Vec<PackageType>>,
  271. /// the directory where the bundles will be placed.
  272. project_out_directory: PathBuf,
  273. /// whether or not to enable verbose logging
  274. is_verbose: bool,
  275. /// the bundle settings.
  276. bundle_settings: BundleSettings,
  277. /// the binaries to bundle.
  278. binaries: Vec<BundleBinary>,
  279. }
  280. #[derive(Default)]
  281. pub struct SettingsBuilder {
  282. project_out_directory: Option<PathBuf>,
  283. verbose: bool,
  284. package_types: Option<Vec<PackageType>>,
  285. package_settings: Option<PackageSettings>,
  286. bundle_settings: BundleSettings,
  287. binaries: Vec<BundleBinary>,
  288. }
  289. impl SettingsBuilder {
  290. pub fn new() -> Self {
  291. Default::default()
  292. }
  293. pub fn project_out_directory<P: AsRef<Path>>(mut self, path: P) -> Self {
  294. self
  295. .project_out_directory
  296. .replace(path.as_ref().to_path_buf());
  297. self
  298. }
  299. pub fn verbose(mut self) -> Self {
  300. self.verbose = true;
  301. self
  302. }
  303. pub fn package_types(mut self, package_types: Vec<PackageType>) -> Self {
  304. self.package_types = Some(package_types);
  305. self
  306. }
  307. pub fn package_settings(mut self, settings: PackageSettings) -> Self {
  308. self.package_settings.replace(settings);
  309. self
  310. }
  311. pub fn bundle_settings(mut self, settings: BundleSettings) -> Self {
  312. self.bundle_settings = settings;
  313. self
  314. }
  315. pub fn binaries(mut self, binaries: Vec<BundleBinary>) -> Self {
  316. self.binaries = binaries;
  317. self
  318. }
  319. /// Builds a Settings from the CLI args.
  320. ///
  321. /// Package settings will be read from Cargo.toml.
  322. ///
  323. /// Bundle settings will be read from from $TAURI_DIR/tauri.conf.json if it exists and fallback to Cargo.toml's [package.metadata.bundle].
  324. pub fn build(self) -> crate::Result<Settings> {
  325. let bundle_settings = parse_external_bin(self.bundle_settings)?;
  326. Ok(Settings {
  327. package: self.package_settings.expect("package settings is required"),
  328. package_types: self.package_types,
  329. is_verbose: self.verbose,
  330. project_out_directory: self
  331. .project_out_directory
  332. .expect("out directory is required"),
  333. binaries: self.binaries,
  334. bundle_settings,
  335. })
  336. }
  337. }
  338. impl Settings {
  339. /// Returns the directory where the bundle should be placed.
  340. pub fn project_out_directory(&self) -> &Path {
  341. &self.project_out_directory
  342. }
  343. /// Returns the architecture for the binary being bundled (e.g. "arm", "x86" or "x86_64").
  344. pub fn binary_arch(&self) -> &str {
  345. std::env::consts::ARCH
  346. }
  347. /// Returns the file name of the binary being bundled.
  348. pub fn main_binary_name(&self) -> &str {
  349. self
  350. .binaries
  351. .iter()
  352. .find(|bin| bin.main)
  353. .expect("failed to find main binary")
  354. .name
  355. .as_str()
  356. }
  357. /// Returns the path to the specified binary.
  358. pub fn binary_path(&self, binary: &BundleBinary) -> PathBuf {
  359. let mut path = self.project_out_directory.clone();
  360. path.push(binary.name());
  361. path
  362. }
  363. pub fn binaries(&self) -> &Vec<BundleBinary> {
  364. &self.binaries
  365. }
  366. /// If a list of package types was specified by the command-line, returns
  367. /// that list filtered by the current target OS available targets.
  368. ///
  369. /// If a target triple was specified by the
  370. /// command-line, returns the native package type(s) for that target.
  371. ///
  372. /// Otherwise returns the native package type(s) for the host platform.
  373. ///
  374. /// Fails if the host/target's native package type is not supported.
  375. pub fn package_types(&self) -> crate::Result<Vec<PackageType>> {
  376. let target_os = std::env::consts::OS;
  377. let mut platform_types = match target_os {
  378. "macos" => vec![PackageType::MacOsBundle, PackageType::Dmg],
  379. "ios" => vec![PackageType::IosBundle],
  380. "linux" => vec![PackageType::Deb, PackageType::AppImage],
  381. #[cfg(target_os = "windows")]
  382. "windows" => vec![PackageType::WindowsMsi],
  383. os => {
  384. return Err(crate::Error::GenericError(format!(
  385. "Native {} bundles not yet supported.",
  386. os
  387. )))
  388. }
  389. };
  390. // add updater if needed
  391. if self.is_update_enabled() {
  392. platform_types.push(PackageType::Updater)
  393. }
  394. if let Some(package_types) = &self.package_types {
  395. let mut types = vec![];
  396. for package_type in package_types {
  397. let package_type = *package_type;
  398. if platform_types
  399. .clone()
  400. .into_iter()
  401. .any(|t| t == package_type)
  402. {
  403. types.push(package_type);
  404. }
  405. }
  406. Ok(types)
  407. } else {
  408. Ok(platform_types)
  409. }
  410. }
  411. /// Returns true if verbose logging is enabled
  412. pub fn is_verbose(&self) -> bool {
  413. self.is_verbose
  414. }
  415. /// Returns the product name.
  416. pub fn product_name(&self) -> &str {
  417. &self.package.product_name
  418. }
  419. /// Returns the bundle's identifier
  420. pub fn bundle_identifier(&self) -> &str {
  421. self.bundle_settings.identifier.as_deref().unwrap_or("")
  422. }
  423. /// Returns an iterator over the icon files to be used for this bundle.
  424. pub fn icon_files(&self) -> ResourcePaths<'_> {
  425. match self.bundle_settings.icon {
  426. Some(ref paths) => ResourcePaths::new(paths.as_slice(), false),
  427. None => ResourcePaths::new(&[], false),
  428. }
  429. }
  430. /// Returns an iterator over the resource files to be included in this
  431. /// bundle.
  432. pub fn resource_files(&self) -> ResourcePaths<'_> {
  433. match self.bundle_settings.resources {
  434. Some(ref paths) => ResourcePaths::new(paths.as_slice(), true),
  435. None => ResourcePaths::new(&[], true),
  436. }
  437. }
  438. /// Returns an iterator over the external binaries to be included in this
  439. /// bundle.
  440. pub fn external_binaries(&self) -> ResourcePaths<'_> {
  441. match self.bundle_settings.external_bin {
  442. Some(ref paths) => ResourcePaths::new(paths.as_slice(), true),
  443. None => ResourcePaths::new(&[], true),
  444. }
  445. }
  446. /// Copies external binaries to a path.
  447. pub fn copy_binaries(&self, path: &Path) -> crate::Result<()> {
  448. for src in self.external_binaries() {
  449. let src = src?;
  450. let dest = path.join(
  451. src
  452. .file_name()
  453. .expect("failed to extract external binary filename"),
  454. );
  455. common::copy_file(&src, &dest)?;
  456. }
  457. Ok(())
  458. }
  459. /// Copies resources to a path.
  460. pub fn copy_resources(&self, path: &Path) -> crate::Result<()> {
  461. for src in self.resource_files() {
  462. let src = src?;
  463. let dest = path.join(common::resource_relpath(&src));
  464. common::copy_file(&src, &dest)?;
  465. }
  466. Ok(())
  467. }
  468. /// Returns the version string of the bundle.
  469. pub fn version_string(&self) -> &str {
  470. &self.package.version
  471. }
  472. /// Returns the copyright text.
  473. pub fn copyright_string(&self) -> Option<&str> {
  474. self.bundle_settings.copyright.as_deref()
  475. }
  476. /// Returns the list of authors name.
  477. pub fn author_names(&self) -> &[String] {
  478. match self.package.authors {
  479. Some(ref names) => names.as_slice(),
  480. None => &[],
  481. }
  482. }
  483. /// Returns the authors as a comma-separated string.
  484. pub fn authors_comma_separated(&self) -> Option<String> {
  485. let names = self.author_names();
  486. if names.is_empty() {
  487. None
  488. } else {
  489. Some(names.join(", "))
  490. }
  491. }
  492. /// Returns the package's homepage URL, defaulting to "" if not defined.
  493. pub fn homepage_url(&self) -> &str {
  494. &self.package.homepage.as_deref().unwrap_or("")
  495. }
  496. /// Returns the app's category.
  497. pub fn app_category(&self) -> Option<AppCategory> {
  498. self.bundle_settings.category
  499. }
  500. /// Returns the app's short description.
  501. pub fn short_description(&self) -> &str {
  502. self
  503. .bundle_settings
  504. .short_description
  505. .as_ref()
  506. .unwrap_or(&self.package.description)
  507. }
  508. /// Returns the app's long description.
  509. pub fn long_description(&self) -> Option<&str> {
  510. self.bundle_settings.long_description.as_deref()
  511. }
  512. /// Returns the debian settings.
  513. pub fn deb(&self) -> &DebianSettings {
  514. &self.bundle_settings.deb
  515. }
  516. /// Returns the MacOS settings.
  517. pub fn macos(&self) -> &MacOsSettings {
  518. &self.bundle_settings.macos
  519. }
  520. /// Returns the Windows settings.
  521. #[cfg(windows)]
  522. pub fn windows(&self) -> &WindowsSettings {
  523. &self.bundle_settings.windows
  524. }
  525. /// Is update enabled
  526. pub fn is_update_enabled(&self) -> bool {
  527. match &self.bundle_settings.updater {
  528. Some(val) => val.active,
  529. None => false,
  530. }
  531. }
  532. /// Is pubkey provided?
  533. pub fn is_updater_pubkey(&self) -> bool {
  534. match &self.bundle_settings.updater {
  535. Some(val) => val.pubkey.is_some(),
  536. None => false,
  537. }
  538. }
  539. /// Get pubkey (mainly for testing)
  540. #[cfg(test)]
  541. pub fn updater_pubkey(&self) -> Option<&str> {
  542. self
  543. .bundle_settings
  544. .updater
  545. .as_ref()
  546. .expect("Updater is not defined")
  547. .pubkey
  548. .as_deref()
  549. }
  550. }
  551. /// Parses the external binaries to bundle, adding the target triple suffix to each of them.
  552. fn parse_external_bin(bundle_settings: BundleSettings) -> crate::Result<BundleSettings> {
  553. let target_triple = target_triple()?;
  554. let mut win_paths = Vec::new();
  555. let external_bin = match bundle_settings.external_bin {
  556. Some(paths) => {
  557. for curr_path in paths.iter() {
  558. win_paths.push(format!(
  559. "{}-{}{}",
  560. curr_path,
  561. target_triple,
  562. if cfg!(windows) { ".exe" } else { "" }
  563. ));
  564. }
  565. Some(win_paths)
  566. }
  567. None => Some(vec![]),
  568. };
  569. Ok(BundleSettings {
  570. external_bin,
  571. ..bundle_settings
  572. })
  573. }
  574. /// A helper to iterate through resources.
  575. pub struct ResourcePaths<'a> {
  576. /// the patterns to iterate.
  577. pattern_iter: std::slice::Iter<'a, String>,
  578. /// the glob iterator if the path from the current iteration is a glob pattern.
  579. glob_iter: Option<glob::Paths>,
  580. /// the walkdir iterator if the path from the current iteration is a directory.
  581. walk_iter: Option<walkdir::IntoIter>,
  582. /// whether the resource paths allows directories or not.
  583. allow_walk: bool,
  584. /// the pattern of the current iteration.
  585. current_pattern: Option<String>,
  586. /// whether the current pattern is valid or not.
  587. current_pattern_is_valid: bool,
  588. }
  589. impl<'a> ResourcePaths<'a> {
  590. /// Creates a new ResourcePaths from a slice of patterns to iterate
  591. fn new(patterns: &'a [String], allow_walk: bool) -> ResourcePaths<'a> {
  592. ResourcePaths {
  593. pattern_iter: patterns.iter(),
  594. glob_iter: None,
  595. walk_iter: None,
  596. allow_walk,
  597. current_pattern: None,
  598. current_pattern_is_valid: false,
  599. }
  600. }
  601. }
  602. impl<'a> Iterator for ResourcePaths<'a> {
  603. type Item = crate::Result<PathBuf>;
  604. fn next(&mut self) -> Option<crate::Result<PathBuf>> {
  605. loop {
  606. if let Some(ref mut walk_entries) = self.walk_iter {
  607. if let Some(entry) = walk_entries.next() {
  608. let entry = match entry {
  609. Ok(entry) => entry,
  610. Err(error) => return Some(Err(crate::Error::from(error))),
  611. };
  612. let path = entry.path();
  613. if path.is_dir() {
  614. continue;
  615. }
  616. self.current_pattern_is_valid = true;
  617. return Some(Ok(path.to_path_buf()));
  618. }
  619. }
  620. self.walk_iter = None;
  621. if let Some(ref mut glob_paths) = self.glob_iter {
  622. if let Some(glob_result) = glob_paths.next() {
  623. let path = match glob_result {
  624. Ok(path) => path,
  625. Err(error) => return Some(Err(crate::Error::from(error))),
  626. };
  627. if path.is_dir() {
  628. if self.allow_walk {
  629. let walk = walkdir::WalkDir::new(path);
  630. self.walk_iter = Some(walk.into_iter());
  631. continue;
  632. } else {
  633. let msg = format!("{:?} is a directory", path);
  634. return Some(Err(crate::Error::GenericError(msg)));
  635. }
  636. }
  637. self.current_pattern_is_valid = true;
  638. return Some(Ok(path));
  639. } else if let Some(current_path) = &self.current_pattern {
  640. if !self.current_pattern_is_valid {
  641. return Some(Err(crate::Error::GenericError(format!(
  642. "Path matching '{}' not found",
  643. current_path
  644. ))));
  645. }
  646. }
  647. }
  648. self.glob_iter = None;
  649. if let Some(pattern) = self.pattern_iter.next() {
  650. self.current_pattern = Some(pattern.to_string());
  651. self.current_pattern_is_valid = false;
  652. let glob = match glob::glob(pattern) {
  653. Ok(glob) => glob,
  654. Err(error) => return Some(Err(crate::Error::from(error))),
  655. };
  656. self.glob_iter = Some(glob);
  657. continue;
  658. }
  659. return None;
  660. }
  661. }
  662. }