[
  {
    "path": ".cargo/config.toml",
    "content": "[alias]\r\nxtask = \"run --package xtask --\"\r\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\n\nversion: 2\nupdates:\n  - package-ecosystem: \"cargo\"\n    directory: \"/\"\n    schedule:\n      interval: \"daily\"\n\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"daily\"\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    branches:\n      - main\n\n  workflow_dispatch:\n\njobs:\n  test:\n    strategy:\n      matrix:\n        rust: [stable, 1.88.0]\n        os: [windows-latest, ubuntu-latest, macos-latest]\n    runs-on: ${{ matrix.os }}\n    steps:\n      - name: Checkout source code\n        uses: actions/checkout@v6\n\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@master\n        with:\n          toolchain: ${{ matrix.rust }}\n\n      - name: Run tests\n        run: cargo test --all-targets\n\n  docs:\n    runs-on: windows-latest\n    env:\n      RUSTDOCFLAGS: -Dwarnings\n    steps:\n      - name: Checkout source code\n        uses: actions/checkout@v6\n\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@stable\n        with:\n          components: rust-docs\n\n      - name: Generate documentation\n        run: cargo doc --workspace --no-deps\n\n  rustfmt:\n    runs-on: windows-latest\n    steps:\n      - name: Checkout source code\n        uses: actions/checkout@v6\n\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@stable\n        with:\n          components: rustfmt\n\n      - name: Format Rust code\n        run: cargo fmt --all -- --check\n\n  clippy:\n    runs-on: windows-latest\n    steps:\n      - name: Checkout source code\n        uses: actions/checkout@v6\n\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@stable\n        with:\n          components: clippy\n\n      - name: Lint Rust code\n        run: cargo clippy --all-targets -- -D warnings\n"
  },
  {
    "path": ".gitignore",
    "content": "/target/\n**/*.rs.bk\n/wix/\n.lock\n.wix/\n.test**\nwix0/\n**.profraw"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"cargo-wix\"\ndescription = \"Build Windows installers using the WiX Toolset\"\nversion = \"0.3.9\"\nauthors = [\"Christopher Field <cfield2@gmail.com>\"]\nlicense = \"MIT/Apache-2.0\"\nrepository = \"https://github.com/volks73/cargo-wix\"\ndocumentation = \"https://volks73.github.io/cargo-wix\"\ncategories = [\"development-tools\"]\nkeywords = [\"cargo-subcommand\", \"installer\", \"wix-toolset\", \"wix\"]\nhomepage = \"https://github.com/volks73/cargo-wix\"\nreadme = \"README.md\"\nedition = \"2024\"\nexclude = [\"/.github\"]\nrust-version = \"1.88.0\"\n\n[[bin]]\nname = \"cargo-wix\"\n\n[lib]\nname = \"wix\"\n\n[dependencies]\ncamino = \"1\"\nchrono = \"0.4\"\nclap = { version = \"4\", features = [\"derive\"] }\nencoding_rs_io = \"0.1\"\nenv_logger = \"0.11\"\nitertools = \"0.14\"\nlazy_static = \"1\"\nlog = \"0.4\"\nmustache = \"0.9\"\nregex = \"1\"\nrustc-cfg = \"0.5\"\nsemver = \"1\"\nsxd-document = \"0.3\"\nsxd-xpath = \"0.4\"\ntermcolor = \"1\"\nuuid = { version = \"1\", features = [\"v4\"] }\ncargo_metadata = \"0.23\"\nserde_json = \"1\"\n\n[dev-dependencies]\nassert_fs = \"1\"\nmaplit = \"1\"\npredicates = \"3\"\nserial_test = \"3\"\ntoml = \"1\"\n\n[workspace]\nmembers = [\n    \"xtask\"\n]\n\n[badges]\nis-it-maintained-issue-resolution = { repository = \"volks73/cargo-wix\" }\nis-it-maintained-open-issues = { repository = \"volks73/cargo-wix\" }\n"
  },
  {
    "path": "LICENSE-APACHE",
    "content": "                              Apache License\r\n                        Version 2.0, January 2004\r\n                     http://www.apache.org/licenses/\r\n\r\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\r\n\r\n1. Definitions.\r\n\r\n   \"License\" shall mean the terms and conditions for use, reproduction,\r\n   and distribution as defined by Sections 1 through 9 of this document.\r\n\r\n   \"Licensor\" shall mean the copyright owner or entity authorized by\r\n   the copyright owner that is granting the License.\r\n\r\n   \"Legal Entity\" shall mean the union of the acting entity and all\r\n   other entities that control, are controlled by, or are under common\r\n   control with that entity. For the purposes of this definition,\r\n   \"control\" means (i) the power, direct or indirect, to cause the\r\n   direction or management of such entity, whether by contract or\r\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\r\n   outstanding shares, or (iii) beneficial ownership of such entity.\r\n\r\n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\r\n   exercising permissions granted by this License.\r\n\r\n   \"Source\" form shall mean the preferred form for making modifications,\r\n   including but not limited to software source code, documentation\r\n   source, and configuration files.\r\n\r\n   \"Object\" form shall mean any form resulting from mechanical\r\n   transformation or translation of a Source form, including but\r\n   not limited to compiled object code, generated documentation,\r\n   and conversions to other media types.\r\n\r\n   \"Work\" shall mean the work of authorship, whether in Source or\r\n   Object form, made available under the License, as indicated by a\r\n   copyright notice that is included in or attached to the work\r\n   (an example is provided in the Appendix below).\r\n\r\n   \"Derivative Works\" shall mean any work, whether in Source or Object\r\n   form, that is based on (or derived from) the Work and for which the\r\n   editorial revisions, annotations, elaborations, or other modifications\r\n   represent, as a whole, an original work of authorship. For the purposes\r\n   of this License, Derivative Works shall not include works that remain\r\n   separable from, or merely link (or bind by name) to the interfaces of,\r\n   the Work and Derivative Works thereof.\r\n\r\n   \"Contribution\" shall mean any work of authorship, including\r\n   the original version of the Work and any modifications or additions\r\n   to that Work or Derivative Works thereof, that is intentionally\r\n   submitted to Licensor for inclusion in the Work by the copyright owner\r\n   or by an individual or Legal Entity authorized to submit on behalf of\r\n   the copyright owner. For the purposes of this definition, \"submitted\"\r\n   means any form of electronic, verbal, or written communication sent\r\n   to the Licensor or its representatives, including but not limited to\r\n   communication on electronic mailing lists, source code control systems,\r\n   and issue tracking systems that are managed by, or on behalf of, the\r\n   Licensor for the purpose of discussing and improving the Work, but\r\n   excluding communication that is conspicuously marked or otherwise\r\n   designated in writing by the copyright owner as \"Not a Contribution.\"\r\n\r\n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\r\n   on behalf of whom a Contribution has been received by Licensor and\r\n   subsequently incorporated within the Work.\r\n\r\n2. Grant of Copyright License. Subject to the terms and conditions of\r\n   this License, each Contributor hereby grants to You a perpetual,\r\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r\n   copyright license to reproduce, prepare Derivative Works of,\r\n   publicly display, publicly perform, sublicense, and distribute the\r\n   Work and such Derivative Works in Source or Object form.\r\n\r\n3. Grant of Patent License. Subject to the terms and conditions of\r\n   this License, each Contributor hereby grants to You a perpetual,\r\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r\n   (except as stated in this section) patent license to make, have made,\r\n   use, offer to sell, sell, import, and otherwise transfer the Work,\r\n   where such license applies only to those patent claims licensable\r\n   by such Contributor that are necessarily infringed by their\r\n   Contribution(s) alone or by combination of their Contribution(s)\r\n   with the Work to which such Contribution(s) was submitted. If You\r\n   institute patent litigation against any entity (including a\r\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\r\n   or a Contribution incorporated within the Work constitutes direct\r\n   or contributory patent infringement, then any patent licenses\r\n   granted to You under this License for that Work shall terminate\r\n   as of the date such litigation is filed.\r\n\r\n4. Redistribution. You may reproduce and distribute copies of the\r\n   Work or Derivative Works thereof in any medium, with or without\r\n   modifications, and in Source or Object form, provided that You\r\n   meet the following conditions:\r\n\r\n   (a) You must give any other recipients of the Work or\r\n       Derivative Works a copy of this License; and\r\n\r\n   (b) You must cause any modified files to carry prominent notices\r\n       stating that You changed the files; and\r\n\r\n   (c) You must retain, in the Source form of any Derivative Works\r\n       that You distribute, all copyright, patent, trademark, and\r\n       attribution notices from the Source form of the Work,\r\n       excluding those notices that do not pertain to any part of\r\n       the Derivative Works; and\r\n\r\n   (d) If the Work includes a \"NOTICE\" text file as part of its\r\n       distribution, then any Derivative Works that You distribute must\r\n       include a readable copy of the attribution notices contained\r\n       within such NOTICE file, excluding those notices that do not\r\n       pertain to any part of the Derivative Works, in at least one\r\n       of the following places: within a NOTICE text file distributed\r\n       as part of the Derivative Works; within the Source form or\r\n       documentation, if provided along with the Derivative Works; or,\r\n       within a display generated by the Derivative Works, if and\r\n       wherever such third-party notices normally appear. The contents\r\n       of the NOTICE file are for informational purposes only and\r\n       do not modify the License. You may add Your own attribution\r\n       notices within Derivative Works that You distribute, alongside\r\n       or as an addendum to the NOTICE text from the Work, provided\r\n       that such additional attribution notices cannot be construed\r\n       as modifying the License.\r\n\r\n   You may add Your own copyright statement to Your modifications and\r\n   may provide additional or different license terms and conditions\r\n   for use, reproduction, or distribution of Your modifications, or\r\n   for any such Derivative Works as a whole, provided Your use,\r\n   reproduction, and distribution of the Work otherwise complies with\r\n   the conditions stated in this License.\r\n\r\n5. Submission of Contributions. Unless You explicitly state otherwise,\r\n   any Contribution intentionally submitted for inclusion in the Work\r\n   by You to the Licensor shall be under the terms and conditions of\r\n   this License, without any additional terms or conditions.\r\n   Notwithstanding the above, nothing herein shall supersede or modify\r\n   the terms of any separate license agreement you may have executed\r\n   with Licensor regarding such Contributions.\r\n\r\n6. Trademarks. This License does not grant permission to use the trade\r\n   names, trademarks, service marks, or product names of the Licensor,\r\n   except as required for reasonable and customary use in describing the\r\n   origin of the Work and reproducing the content of the NOTICE file.\r\n\r\n7. Disclaimer of Warranty. Unless required by applicable law or\r\n   agreed to in writing, Licensor provides the Work (and each\r\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\r\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\r\n   implied, including, without limitation, any warranties or conditions\r\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\r\n   PARTICULAR PURPOSE. You are solely responsible for determining the\r\n   appropriateness of using or redistributing the Work and assume any\r\n   risks associated with Your exercise of permissions under this License.\r\n\r\n8. Limitation of Liability. In no event and under no legal theory,\r\n   whether in tort (including negligence), contract, or otherwise,\r\n   unless required by applicable law (such as deliberate and grossly\r\n   negligent acts) or agreed to in writing, shall any Contributor be\r\n   liable to You for damages, including any direct, indirect, special,\r\n   incidental, or consequential damages of any character arising as a\r\n   result of this License or out of the use or inability to use the\r\n   Work (including but not limited to damages for loss of goodwill,\r\n   work stoppage, computer failure or malfunction, or any and all\r\n   other commercial damages or losses), even if such Contributor\r\n   has been advised of the possibility of such damages.\r\n\r\n9. Accepting Warranty or Additional Liability. While redistributing\r\n   the Work or Derivative Works thereof, You may choose to offer,\r\n   and charge a fee for, acceptance of support, warranty, indemnity,\r\n   or other liability obligations and/or rights consistent with this\r\n   License. However, in accepting such obligations, You may act only\r\n   on Your own behalf and on Your sole responsibility, not on behalf\r\n   of any other Contributor, and only if You agree to indemnify,\r\n   defend, and hold each Contributor harmless for any liability\r\n   incurred by, or claims asserted against, such Contributor by reason\r\n   of your accepting any such warranty or additional liability.\r\n\r\nEND OF TERMS AND CONDITIONS\r\n\r\nAPPENDIX: How to apply the Apache License to your work.\r\n\r\n   To apply the Apache License to your work, attach the following\r\n   boilerplate notice, with the fields enclosed by brackets \"[]\"\r\n   replaced with your own identifying information. (Don't include\r\n   the brackets!)  The text should be enclosed in the appropriate\r\n   comment syntax for the file format. We also recommend that a\r\n   file or class name and description of purpose be included on the\r\n   same \"printed page\" as the copyright notice for easier\r\n   identification within third-party archives.\r\n\r\nCopyright 2017 Christopher R. Field\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n\thttp://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n"
  },
  {
    "path": "LICENSE-MIT",
    "content": "Copyright (c) 2017 Christopher R. Field\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy of\r\nthis software and associated documentation files (the \"Software\"), to deal in\r\nthe Software without restriction, including without limitation the rights to\r\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\r\nof the Software, and to permit persons to whom the Software is furnished to do\r\nso, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice shall be included in all\r\ncopies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\nSOFTWARE.\r\n"
  },
  {
    "path": "README.md",
    "content": "# cargo-wix: A cargo subcommand to create Windows installers\n\nA subcommand for [Cargo] that builds a Windows installer (MSI) using the [WiX\nToolset] from the build of a [Rust] binary project. It also supports signing the\nWindows installer if a code signing certificate is available using the\n[SignTool] application available in the [Windows SDK].\n\n[![Crates.io](https://img.shields.io/crates/v/cargo-wix.svg)](https://crates.io/crates/cargo-wix)\n[![GitHub release](https://img.shields.io/github/release/volks73/cargo-wix.svg)](https://github.com/volks73/cargo-wix/releases)\n[![Crates.io](https://img.shields.io/crates/l/cargo-wix.svg)](https://github.com/volks73/cargo-wix#license)\n[![Build Status](https://github.com/volks73/cargo-wix/actions/workflows/ci.yml/badge.svg)](https://github.com/volks73/cargo-wix/actions/workflows/ci.yml)\n\n## Quick Start\n\nStart a command prompt (cmd.exe) and then execute the following commands:\n\n```dos\nC:\\>cargo install cargo-wix\nC:\\>cd Path\\To\\Project\nC:\\Path\\To\\Project\\>cargo wix init\nC:\\Path\\To\\Project\\>cargo wix\n```\n\nThe Windows installer (MSI) for the project will be in the\n`C:\\Path\\To\\Project\\target\\wix` folder. Please see the [Documentation] for more\nadvanced usage, configuration, and customization.\n\n## Installation\n\nThe cargo-wix project can be installed on any platform supported by the Rust\nprogramming language, but the [WiX Toolset] is Windows only; thus, this project is\nonly useful when installed on a Windows machine. Ensure the following\ndependencies are installed before proceeding. \n\n>[!NOTE]\n>Cargo is installed automatically when installing the Rust programming language.\n>The `stable-x86_64-pc-windows-msvc` toolchain is recommended.\n\n- [Cargo]\n- [Rust v1.88.0 or newer](https://www.rust-lang.org)\n- [WiX Toolset] [v3.14.1](https://github.com/wixtoolset/wix3/releases/tag/wix3141rtm) or [newer](https://github.com/wixtoolset/wix/releases)\n- [Windows SDK] (Optional), needed for signing the installer\n\n>[!IMPORTANT]\n>The [WiX Toolset] is available in two different variants: [Legacy] (v3.14.1) and\n>[Modern] (v4+). The Legacy variant uses a two-stage approach with a compiler,\n>`candle.exe`, and a linker, `light.exe`. The Modern variant uses a single stage\n>approach with a single executable, `wix.exe`, and a new XML schema and\n>namespace. The Legacy variant is no longer supported by the WiX Toolset\n>developers, [FireGiant]. Both variants are supported by the cargo-wix project\n>and the `cargo wix` subcommand, but usage of the Legacy variant is currently the\n>default.\n\n>[!WARNING]\n>As of April 3rd, 2026, the `windows-latest` GitHub Action image still only\n>contains the [Legacy] variant, v3.14.1, of the WiX Toolset. If the [Modern] variant,\n>v4+, of the WiX Toolset is desired, then a newer version will needed to be\n>explicitly installed as a step in the action _before_ any `cargo wix` commands.\n\n>[!WARNING]\n>As of April 3rd, 2026, the GitHub Action Windows 11 ARM64 runner image does not\n>have the [WiX Toolset] installed. The [Legacy] or [Modern] variants will have\n>to be explicitly installed as a step in the action _before_ any `cargo wix`\n>commands. See <https://github.com/actions/partner-runner-images/blob/main/images/arm-windows-11-image.md#omitted-software>.\n\nOnce the prerequisites have been installed, execute the following command to\ninstall the `cargo wix` subcommand.\n\n```dos\nC:\\> cargo install cargo-wix\n```\n\n## Usage\n\nStart a command prompt, such as `cmd.exe`, the [Developer Prompt] installed with\nthe [Build Tools for Visual Studio 2026], [Windows Terminal] with [Powershell],\nor [git bash], and navigate to the project's root folder. Run the subcommand:\n\n```dos\nC:\\Path\\to\\Project> cargo wix init\n```\n\nThis will create the `wix` folder in the project's root (along side the\n`Cargo.toml` file) and then it will create the `wix\\main.wxs` file from the WiX\nSource (wxs) embedded within the subcommand. The generated `wix\\main.wxs` file\ncan be used without modification with the following command to create an\ninstaller for the project:\n\n```dos\nC:\\Path\\to\\Project> cargo wix\n```\n\nThe `cargo wix` subcommand without any arguments searches for a `wix\\main.wxs`\nfile, relative to the project's root. It will compile the `wix\\main.wxs` file\nand then link the object file (`target\\wix\\build\\main.wixobj`) to create the\nWindows installer (MSI). The installer will be located in the `target\\wix`\nfolder. All artifacts of the installer compilation and linking process are\nplaced within the `target\\wix` folder. Paths in the `wix\\main.wxs` file should\nbe relative to the project's root, i.e. the same location as the `Cargo.toml`\nmanifest file. \n\nA different WiX Source (wxs) file from the `wix\\main.wxs` file can be used by\nspecifying a path to it as an argument to the subcommand as follows:\n\n```dos\nC:\\Path\\to\\Project> cargo wix Path\\to\\WiX\\Source\\File.wxs\n```\n\nYou can also automatically run the installer after creating it by specifying the\n`--install` argument:\n\n```dos\nC:\\Path\\to\\Project> cargo wix --install\n```\n\nThe `print <template>` subcommand, which prints one of the embedded templates to\nstdout, can be used to create the `main.wxs` file. A [WXS template] file\nspecifically designed to work with this subcommand is embedded within the\n`cargo-wix` binary during installation. Use the following commands to create a\nWiX Source file and use it to create an installer with this subcommand.\n\n```dos\nC:\\Path\\to\\Project> cargo wix print wxs > example.wxs\nC:\\Path\\to\\Project> cargo wix example.wxs\n```\n\nThe WiX source file can be customized using a text editor, but modification of\nthe XML preprocessor variables should be avoided to ensure the `cargo wix`\ncommand works properly. \n\nTo sign the installer (MSI) as part of the build process, ensure the `signtool`\ncommand is available in the `PATH` system environment variable or use the\n[Developer Prompt] that was installed with the Windows SDK, and use the `sign`\nsub-subcommand as follows: \n\n```dos\nC:\\Path\\to\\Project> cargo wix sign\n```\n\nUse the `-h,--help` flag to display information about additional options and\nfeatures.\n\n```dos\nC:\\Path\\to\\Project> cargo wix -h\n```\n\n## Tests\n\nThere are set environment variables that can be used to help debug a failing\ntest. The `CARGO_WIX_TEST_PERSIST` environment variable can be set to persist\nthe temporary directories that are created during integration tests. This allows\nthe developer to inspect the contents of the temporary directory. The\n`CARGO_WIX_TEST_PERSIST` environment variable accepts any value. Unsetting the\nenvironment variable will delete the temporary directories after each test. \n\nThe `CARGO_WIX_TEST_LOG` environment variable sets the log level while running\nan integration test. It accepts an integer value between 0 and 5, with 0 turning\noff logging, and 5 displaying all log statements (ERROR, WARN, INFO, DEBUG, and\nTRACE). Log statements are __not__ captured during tests, so this environment\nvariable should be used only when running an integration test in isolation to\nprevent \"swampping\" the terminal/console with statements. \n\nFinally, the `CARGO_WIX_TEST_NO_CAPTURE` environment variable accepts any value\nand will display the output from the WiX Toolset compiler (candle.exe) and\nlinker (light.exe) when running an integration test. Similar to the\n`CARGO_WIX_TEST_LOG` environment variable, this variable should only be used in\nisolation to prevent \"swamping\" the terminal/console with the output from the\nWiX Toolset commands. By default, the output is captured by the _test_ not\ncargo's test framework; thus, the `cargo test -- --nocapture` command has no\naffect. Example of setting and unsetting all of the environment variables for\nrunning a specific test:\n\n```dos\nC:\\Path\\to\\Cargo\\Wix> set CARGO_WIX_TEST_PERSIST=1\nC:\\Path\\to\\Cargo\\Wix> set CARGO_WIX_TEST_LOG=5\nC:\\Path\\to\\Cargo\\Wix> set CARGO_WIX_TEST_NO_CAPTURE=1\nC:\\Path\\to\\Cargo\\Wix> cargo test <TEST NAME>\nC:\\Path\\to\\Cargo\\Wix> set \"CARGO_WIX_TEST_NO_CAPTURE=\"\nC:\\Path\\to\\Cargo\\Wix> set \"CARGO_WIX_TEST_LOG=\"\nC:\\Path\\to\\Cargo\\Wix> set \"CARGO_WIX_TEST_PERSIST=\"\n```\n\nwhere `<TEST NAME>` is replaced with the name of an integration test. The last\nthree lines/commands are optional and unsets the three environment variables to\navoid additional tests from also persisting, logging, and dumping output to the\nterminal/console. \n\n>[!NOTE]\n>The `-- --nocapture` option is _not_ needed to display the logging\n>statements or the output from the WiX Toolset compiler (candle.exe) and linker\n>(light.exe). \n\nHere is the same example with [Powershell]:\n\n```powershell\nPS C:\\Path\\to\\Cargo\\Wix> $env:CARGO_WIX_TEST_PERSIST=1; $env:CARGO_WIX_TEST_LOG=5; $env:CARGO_WIX_TEST_NO_CAPTURE=1; \nPS C:\\Path\\to\\Cargo\\Wix> cargo test <TEST NAME>\nPS C:\\Path\\to\\Cargo\\Wix> Remove-Item Env:\\CARGO_WIX_TEST_PERSIST; Remove-Item Env:\\CARGO_WIX_TEST_LOG; Remove-Item Env:\\CARGO_WIX_TEST_NO_CAPTURE\n```\n\n## License\n\nThe `cargo-wix` project is licensed under either the [MIT license] or [Apache\n2.0 license]. See the [LICENSE-MIT] or [LICENSE-APACHE] files for more\ninformation about licensing and copyright.\n\n[apache 2.0 license]: http://www.apache.org/licenses/LICENSE-2.0\n[build tools for visual studio 2026]: https://visualstudio.microsoft.com/downloads/\n[cargo]: http://doc.crates.io/\n[developer prompt]: https://learn.microsoft.com/en-us/visualstudio/ide/reference/command-prompt-powershell\n[documentation]: https://volks73.github.io/cargo-wix/cargo_wix/index.html\n[firegiant]: https://www.firegiant.com\n[git bash]: https://gitforwindows.org/\n[legacy]: https://github.com/wixtoolset/wix3/releases/tag/wix3141rtm\n[license-apache]: https://github.com/volks73/cargo-wix/blob/main/LICENSE-APACHE\n[license-mit]: https://github.com/volks73/cargo-wix/blob/main/LICENSE-MIT\n[mit license]: https://opensource.org/licenses/MIT\n[modern]: https://github.com/wixtoolset/wix/releases\n[powershell]: https://learn.microsoft.com/en-us/powershell/\n[rust]: https://www.rust-lang.org\n[signtool]: https://learn.microsoft.com/en-us/windows/win32/seccrypto/signtool\n[windows sdk]: https://learn.microsoft.com/en-us/windows/apps/windows-sdk/downloads\n[windows terminal]: https://learn.microsoft.com/en-us/windows/terminal/install\n[wix toolset]: http://wixtoolset.org/\n[wxs template]: https://github.com/volks73/cargo-wix/blob/main/src/templates/main.v3.wxs.mustache\n"
  },
  {
    "path": "src/clean.rs",
    "content": "// Copyright (C) 2017 Christopher R. Field.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//! The implementation for the `clean` command. This command is focused on\n//! cleaning up build output, similar to the `cargo clean` subcommand.\n\nuse crate::Result;\nuse crate::WIX;\n\nuse log::{debug, info, trace, warn};\n\nuse std::fs;\nuse std::path::{Path, PathBuf};\n\n/// A builder for creating an execution context to clean a package of WiX\n/// Toolset-related output.\n#[derive(Debug, Clone)]\npub struct Builder<'a> {\n    input: Option<&'a str>,\n}\n\nimpl<'a> Builder<'a> {\n    /// Creates a new `Builder` instance.\n    pub fn new() -> Self {\n        Builder { input: None }\n    }\n\n    /// Sets the path to a package's manifest (Cargo.toml) to be cleaned.\n    ///\n    /// The default is to use the current working directory if a Cargo.toml file\n    /// is found.\n    pub fn input(&mut self, i: Option<&'a str>) -> &mut Self {\n        self.input = i;\n        self\n    }\n\n    /// Builds an execution context to clean the package of WiX Toolset-related\n    /// output.\n    pub fn build(&mut self) -> Execution {\n        Execution {\n            input: self.input.map(PathBuf::from),\n        }\n    }\n}\n\nimpl Default for Builder<'_> {\n    fn default() -> Self {\n        Builder::new()\n    }\n}\n\n/// A context for removing WiX Toolset-related output from a package.\n#[derive(Debug)]\npub struct Execution {\n    input: Option<PathBuf>,\n}\n\nimpl Execution {\n    /// Removes WiX Toolset-related output from the package's `target` folder.\n    ///\n    /// This is similar to the `cargo clean` subcommand.\n    pub fn run(self) -> Result<()> {\n        debug!(\"input = {:?}\", self.input);\n        let manifest = super::manifest(self.input.as_ref())?;\n        debug!(\"target_directory = {:?}\", manifest.target_directory);\n        let target_wix = self.target_wix(manifest.target_directory.as_std_path());\n        debug!(\"target_wix = {:?}\", target_wix);\n        if target_wix.exists() {\n            trace!(\"The 'target\\\\wix' folder exists\");\n            warn!(\"Removing the 'target\\\\wix' folder\");\n            fs::remove_dir_all(target_wix)?;\n        } else {\n            trace!(\"The 'target\\\\wix' folder does not exist\");\n            info!(\"Nothing to clean\");\n        }\n        Ok(())\n    }\n\n    fn target_wix(&self, target_directory: &Path) -> PathBuf {\n        target_directory.join(WIX)\n    }\n}\n\nimpl Default for Execution {\n    fn default() -> Self {\n        Builder::new().build()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    mod builder {\n        use super::*;\n\n        #[test]\n        fn input_works() {\n            const EXPECTED: &str = \"C:\\\\Cargo.toml\";\n            let mut actual = Builder::default();\n            actual.input(Some(EXPECTED));\n            assert_eq!(actual.input, Some(EXPECTED));\n        }\n    }\n\n    mod execution {\n        extern crate assert_fs;\n\n        use super::*;\n        use std::env;\n        use std::fs::File;\n\n        #[test]\n        fn target_wix_works() {\n            let mut cwd = env::current_dir().expect(\"Current Working Directory\");\n            let actual = Execution::default().target_wix(&cwd.join(\"target\"));\n            cwd.push(\"target\");\n            cwd.push(WIX);\n            assert_eq!(actual, cwd);\n        }\n\n        #[test]\n        fn target_wix_with_existing_cargo_toml_works() {\n            let temp_dir = assert_fs::TempDir::new().unwrap();\n            let cargo_toml_path = temp_dir.path().join(\"Cargo.toml\");\n            let target = temp_dir.path().join(\"target\");\n            let expected = target.join(WIX);\n            let _non_cargo_toml_handle = File::create(&cargo_toml_path).expect(\"Create file\");\n            let actual = Builder::new()\n                .input(cargo_toml_path.to_str())\n                .build()\n                .target_wix(&target);\n            assert_eq!(actual, expected);\n        }\n    }\n}\n"
  },
  {
    "path": "src/create.rs",
    "content": "// Copyright (C) 2017 Christopher R. Field.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//! The implementation for the `create`, or default, command. The default\n//! command, `cargo wix`, is focused on creating, or building, the installer\n//! using the WiX Toolset.\n//!\n//! Generally, this involves locating the WiX Source file (wxs) and passing\n//! options and flags to the WiX Toolset's compiler (`candle.exe`) and linker\n//! (`light.exe`). By default, it looks for a `wix\\main.wxs` file relative to\n//! the root of the package's manifest (Cargo.toml). A different WiX Source file\n//! can be set with the `input` method using the `Builder` struct.\n//!\n//! When using the modern WiX Toolset (v4+) via `--toolset modern`, the build\n//! uses `wix build` instead of the separate compile and link steps. The\n//! `--migrate` flag can be used to automatically convert WiX v3 source files\n//! and install required extension packages before building.\n\nuse crate::BINARY_FOLDER_NAME;\nuse crate::CARGO;\nuse crate::Cultures;\nuse crate::EXE_FILE_EXTENSION;\nuse crate::Error;\nuse crate::MSI_FILE_EXTENSION;\nuse crate::MSIEXEC;\nuse crate::Result;\nuse crate::WIX;\nuse crate::WIX_COMPILER;\nuse crate::WIX_LINKER;\nuse crate::WIX_MODERN_TOOLSET;\nuse crate::WIX_OBJECT_FILE_EXTENSION;\nuse crate::WIX_PATH_KEY;\nuse crate::WixArch;\nuse crate::toolset::Includes;\nuse crate::toolset::ProjectProvider;\nuse crate::toolset::Toolset;\nuse crate::toolset::ToolsetSetupMode;\nuse itertools::Itertools;\nuse log::error;\nuse log::{debug, info, trace, warn};\n\nuse semver::Version;\n\nuse std::convert::TryFrom;\nuse std::env;\nuse std::ffi::OsString;\nuse std::fmt;\nuse std::io::{ErrorKind, Read};\nuse std::path::{Path, PathBuf};\nuse std::process::{Command, Stdio};\nuse std::str::FromStr;\n\nuse cargo_metadata::Package;\n\nuse rustc_cfg::Cfg;\n\nuse serde_json::Value;\n\n/// A builder for running the `cargo wix` subcommand.\n#[derive(Debug, Clone)]\npub struct Builder<'a> {\n    bin_path: Option<&'a str>,\n    capture_output: bool,\n    compiler_args: Option<Vec<&'a str>>,\n    culture: Option<&'a str>,\n    debug_build: bool,\n    profile: Option<&'a str>,\n    debug_name: bool,\n    includes: Option<Vec<&'a str>>,\n    input: Option<&'a str>,\n    linker_args: Option<Vec<&'a str>>,\n    locale: Option<&'a str>,\n    name: Option<&'a str>,\n    no_build: bool,\n    target_bin_dir: Option<&'a str>,\n    install: bool,\n    output: Option<&'a str>,\n    package: Option<&'a str>,\n    target: Option<&'a str>,\n    version: Option<&'a str>,\n    toolset: Toolset,\n    toolset_setup_mode: ToolsetSetupMode,\n    // toolset_restore: bool,\n}\n\nimpl<'a> Builder<'a> {\n    /// Creates a new `Builder` instance.\n    pub fn new() -> Self {\n        Builder {\n            bin_path: None,\n            capture_output: true,\n            compiler_args: None,\n            culture: None,\n            debug_build: false,\n            profile: None,\n            debug_name: false,\n            includes: None,\n            input: None,\n            linker_args: None,\n            locale: None,\n            name: None,\n            no_build: false,\n            install: false,\n            target_bin_dir: None,\n            output: None,\n            package: None,\n            target: None,\n            version: None,\n            toolset: Toolset::Legacy,\n            toolset_setup_mode: ToolsetSetupMode::None,\n        }\n    }\n\n    /// Sets the path to the WiX Toolset's `bin` folder.\n    ///\n    /// The WiX Toolset's `bin` folder should contain the needed `candle.exe`\n    /// and `light.exe` applications. The default is to use the WIX system\n    /// environment variable that is created during installation of the WiX\n    /// Toolset. This will override any value obtained from the environment.\n    pub fn bin_path(&mut self, b: Option<&'a str>) -> &mut Self {\n        self.bin_path = b;\n        self\n    }\n\n    /// Enables or disables capturing of the output from the builder (`cargo`),\n    /// compiler (`candle`), linker (`light`), and signer (`signtool`).\n    ///\n    /// The default is to capture all output, i.e. display nothing in the\n    /// console but the log statements.\n    pub fn capture_output(&mut self, c: bool) -> &mut Self {\n        self.capture_output = c;\n        self\n    }\n\n    /// Adds an argument to the compiler command.\n    ///\n    /// This \"passes\" the argument directly to the WiX compiler (candle.exe).\n    /// See the help documentation for the WiX compiler for information about\n    /// valid options and flags.\n    pub fn compiler_args(&mut self, c: Option<Vec<&'a str>>) -> &mut Self {\n        self.compiler_args = c;\n        self\n    }\n\n    /// Sets the culture to use with the linker (light.exe) for building a\n    /// localized installer.\n    ///\n    /// This value will override any defaults and skip looking for a value in\n    /// the `[package.metadata.wix]` section of the package's manifest\n    /// (Cargo.toml).\n    pub fn culture(&mut self, c: Option<&'a str>) -> &mut Self {\n        self.culture = c;\n        self\n    }\n\n    /// Builds the package with the Debug profile instead of the Release profile.\n    ///\n    /// See the [Cargo book] for more information about release profiles. The\n    /// default is to use the Release profile when creating the installer. This\n    /// value is ignored if the `no_build` method is set to `true`.\n    ///\n    /// [Cargo book]: https://doc.rust-lang.org/book/ch14-01-release-profiles.html\n    pub fn debug_build(&mut self, d: bool) -> &mut Self {\n        self.debug_build = d;\n        self\n    }\n\n    /// Appends `-debug` to the file stem for the installer (msi).\n    ///\n    /// If `true`, then `-debug` is added as suffix to the file stem (string\n    /// before the dot and file extension) for the installer's file name. For\n    /// example, if `true`, then file name would be\n    /// `example-0.1.0-x86_64-debug.msi`. The default is to _not_ append the\n    /// `-debug` because the Release profile is the default.\n    ///\n    /// Generally, this should be used in combination with the `debug_build`\n    /// method to indicate the installer is for a debugging variant of the\n    /// installed binary.\n    pub fn debug_name(&mut self, d: bool) -> &mut Self {\n        self.debug_name = d;\n        self\n    }\n\n    /// Adds multiple WiX Source (wxs) files to the creation of an installer.\n    ///\n    /// By default, any `.wxs` file located in the project's `wix` folder will\n    /// be included in the creation of an installer for the project. This method\n    /// adds, or appends, to the list of `.wxs` files. The value is a relative\n    /// or absolute path.\n    ///\n    /// This value will override any default and skip looking for a value in the\n    /// `[package.metadata.wix]` section of the package's manifest (Cargo.toml).\n    pub fn includes(&mut self, i: Option<Vec<&'a str>>) -> &mut Self {\n        self.includes = i;\n        self\n    }\n\n    /// Sets the path to a package's manifest (Cargo.toml) file.\n    ///\n    /// A package's manifest is used to create an installer. If no path is\n    /// specified, then the current working directory (CWD) is used. An error\n    /// will occur if there is no `Cargo.toml` file in the CWD or at the\n    /// specified path. Either an absolute or relative path is valid.\n    ///\n    /// This value will override any default and skip looking for a value in the\n    /// `[package.metadata.wix]` section of the package's manifest (Cargo.toml).\n    pub fn input(&mut self, i: Option<&'a str>) -> &mut Self {\n        self.input = i;\n        self\n    }\n\n    /// Adds an argument to the linker command.\n    ///\n    /// This \"passes\" the argument directly to the WiX linker (light.exe). See\n    /// the help documentation for the WiX compiler for information about valid\n    /// options and flags.\n    pub fn linker_args(&mut self, l: Option<Vec<&'a str>>) -> &mut Self {\n        self.linker_args = l;\n        self\n    }\n\n    /// Sets the path to a WiX localization file, `.wxl`, for the linker\n    /// (light.exe).\n    ///\n    /// The [WiX localization file] is an XML file that contains localization\n    /// strings.\n    ///\n    /// This value will override any default and skip looking for a value in the\n    /// `[package.metadata.wix]` section of the package's manifest (Cargo.toml).\n    ///\n    /// [WiX localization file]: http://wixtoolset.org/documentation/manual/v3/howtos/ui_and_localization/make_installer_localizable.html\n    pub fn locale(&mut self, l: Option<&'a str>) -> &mut Self {\n        self.locale = l;\n        self\n    }\n\n    /// Sets the name.\n    ///\n    /// The default is to use the `name` field under the `[package]` section of\n    /// the package's manifest (Cargo.toml). This overrides that value.\n    ///\n    /// The installer (msi) that is created will be named in the following\n    /// format: \"name-major.minor.patch-platform.msi\", where _name_ is the value\n    /// specified with this method or the value from the `name` field under the\n    /// `[package]` section, the _major.minor.patch_ is the version number from\n    /// the package's manifest `version` field or the value specified at the\n    /// command line, and the _platform_ is either \"i686\" or \"x86_64\" depending\n    /// on the build environment.\n    ///\n    /// This does __not__ change the name of the executable that is installed.\n    /// The name of the executable can be changed by modifying the WiX Source\n    /// (wxs) file with a text editor.\n    ///\n    /// This value will override any default and skip looking for a value in the\n    /// `[package.metadata.wix]` section of the package's manifest (Cargo.toml).\n    pub fn name(&mut self, p: Option<&'a str>) -> &mut Self {\n        self.name = p;\n        self\n    }\n\n    /// Skips the building of the project with the release profile.\n    ///\n    /// If `true`, the project will _not_ be built using the release profile,\n    /// i.e. the `cargo build --release` command will not be executed. The\n    /// default is to build the project before each creation. This is useful if\n    /// building the project is more involved or is handled in a separate\n    /// process.\n    ///\n    /// This value will override any default and skip looking for a value in the\n    /// `[package.metadata.wix]` section of the package's manifest (Cargo.toml).\n    pub fn no_build(&mut self, n: bool) -> &mut Self {\n        self.no_build = n;\n        self\n    }\n\n    /// Specifies that binaries should be sourced from the given directory.\n    ///\n    /// Specifically this sets `CargoTargetBinDir` in wxs templates. It is\n    /// intended to be combined with `no_build(true)` to let another tool\n    /// orchestrate cargo-wix and handle the builds for it.\n    pub fn target_bin_dir(&mut self, p: Option<&'a str>) -> &mut Self {\n        self.target_bin_dir = p;\n        self\n    }\n\n    /// Runs the installer after creating it.\n    ///\n    /// If `true`, the MSI installer will be created and then launched. This will\n    /// automatically open the installation wizard for the project and allow the\n    /// user to install it.\n    ///\n    /// This value will override any default and skip looking for a value in the\n    /// `[package.metadata.wix]` section of the package's manifest (Cargo.toml).\n    pub fn install(&mut self, n: bool) -> &mut Self {\n        self.install = n;\n        self\n    }\n\n    /// Sets the output file and destination.\n    ///\n    /// The default is to create a MSI file with the\n    /// `<product-name>-<version>-<arch>.msi` file name and extension in the\n    /// `target\\wix` folder. Use this method to override the destination and\n    /// file name of the Windows installer.\n    ///\n    /// If the path is to an existing folder or contains a trailing slash\n    /// (forward or backward), then the default MSI file name is used, but the\n    /// installer will be available at the specified path. When specifying a\n    /// file name and path, the `.msi` file is not required. It will be added\n    /// automatically.\n    ///\n    /// This value will override any default and skip looking for a value in the\n    /// `[package.metadata.wix]` section of the package's manifest (Cargo.toml).\n    pub fn output(&mut self, o: Option<&'a str>) -> &mut Self {\n        self.output = o;\n        self\n    }\n\n    /// Sets the package.\n    ///\n    /// If the project is organized using a workspace, this selects the package\n    /// by name to create an installer. If a workspace is not used, then this\n    /// has no effect.\n    pub fn package(&mut self, p: Option<&'a str>) -> &mut Self {\n        self.package = p;\n        self\n    }\n\n    /// Sets the build target.\n    ///\n    /// The default is to use the default target for the environment. Use this\n    /// method to change the target for the build and installer creation. The\n    /// value should be a string from the `rustc --print target-list` command.\n    /// This enables \"cross-compilation\" of installers similar to the\n    /// cross-compilation of Rust code, but only for Windows targets.\n    pub fn target(&mut self, v: Option<&'a str>) -> &mut Self {\n        self.target = v;\n        self\n    }\n\n    /// Sets the version.\n    ///\n    /// This overrides the `version` field of the package's manifest\n    /// (Cargo.toml). The version should be in the \"Major.Minor.Patch\" notation.\n    ///\n    /// This value will override any default and skip looking for a value in the\n    /// `[package.metadata.wix]` section of the package's manifest (Cargo.toml).\n    pub fn version(&mut self, v: Option<&'a str>) -> &mut Self {\n        self.version = v;\n        self\n    }\n\n    /// Sets the wix toolset to use\n    pub fn toolset(&mut self, v: Toolset) -> &mut Self {\n        self.toolset = v;\n        self\n    }\n\n    /// Sets the wix toolset migration setup mode\n    pub fn toolset_migration(&mut self, setup: ToolsetSetupMode) -> &mut Self {\n        self.toolset_setup_mode = setup;\n        self\n    }\n\n    /// Builds a context for creating, or building, the installer.\n    pub fn build(&mut self) -> Execution {\n        Execution {\n            bin_path: self.bin_path.map(PathBuf::from),\n            capture_output: self.capture_output,\n            compiler_args: self\n                .compiler_args\n                .as_ref()\n                .map(|c| c.iter().map(|s| (*s).to_string()).collect()),\n            culture: self.culture.map(String::from),\n            debug_build: self.debug_build,\n            profile: self.profile.map(String::from),\n            debug_name: self.debug_name,\n            includes: self\n                .includes\n                .as_ref()\n                .map(|v| v.iter().map(&PathBuf::from).collect()),\n            input: self.input.map(PathBuf::from),\n            linker_args: self\n                .linker_args\n                .as_ref()\n                .map(|l| l.iter().map(|s| (*s).to_string()).collect()),\n            locale: self.locale.map(PathBuf::from),\n            name: self.name.map(String::from),\n            no_build: self.no_build,\n            toolset: self.toolset.clone(),\n            toolset_setup_mode: self.toolset_setup_mode,\n            target_bin_dir: self.target_bin_dir.map(PathBuf::from),\n            install: self.install,\n            output: self.output.map(String::from),\n            package: self.package.map(String::from),\n            version: self.version.map(String::from),\n            target: self.target.map(String::from),\n        }\n    }\n\n    pub fn profile(&mut self, profile: Option<&'a str>) -> &mut Self {\n        self.profile = profile;\n        self\n    }\n}\n\nimpl Default for Builder<'_> {\n    fn default() -> Self {\n        Builder::new()\n    }\n}\n\n/// A context for creating, or building, an installer.\n#[derive(Debug)]\npub struct Execution {\n    bin_path: Option<PathBuf>,\n    capture_output: bool,\n    compiler_args: Option<Vec<String>>,\n    culture: Option<String>,\n    debug_build: bool,\n    profile: Option<String>,\n    debug_name: bool,\n    includes: Option<Vec<PathBuf>>,\n    input: Option<PathBuf>,\n    target_bin_dir: Option<PathBuf>,\n    linker_args: Option<Vec<String>>,\n    locale: Option<PathBuf>,\n    name: Option<String>,\n    no_build: bool,\n    pub(crate) toolset: Toolset,\n    toolset_setup_mode: ToolsetSetupMode,\n    install: bool,\n    output: Option<String>,\n    package: Option<String>,\n    target: Option<String>,\n    version: Option<String>,\n}\n\nimpl Execution {\n    /// Creates, or builds, an installer within a built context.\n    #[allow(clippy::cognitive_complexity)]\n    pub fn run(self) -> Result<()> {\n        debug!(\"self.bin_path = {:?}\", self.bin_path);\n        debug!(\"self.capture_output = {:?}\", self.capture_output);\n        debug!(\"self.compiler_args = {:?}\", self.compiler_args);\n        debug!(\"self.culture = {:?}\", self.culture);\n        debug!(\"self.debug_build = {:?}\", self.debug_build);\n        debug!(\"self.profile = {:?}\", self.profile);\n        debug!(\"self.debug_name = {:?}\", self.debug_name);\n        debug!(\"self.includes = {:?}\", self.includes);\n        debug!(\"self.input = {:?}\", self.input);\n        debug!(\"self.linker_args = {:?}\", self.linker_args);\n        debug!(\"self.locale = {:?}\", self.locale);\n        debug!(\"self.name = {:?}\", self.name);\n        debug!(\"self.no_build = {:?}\", self.no_build);\n        debug!(\"self.target_bin_dir = {:?}\", self.target_bin_dir);\n        debug!(\"self.install = {:?}\", self.install);\n        debug!(\"self.output = {:?}\", self.output);\n        debug!(\"self.package = {:?}\", self.package);\n        debug!(\"self.target = {:?}\", self.target);\n        debug!(\"self.version = {:?}\", self.version);\n        let manifest_path = super::cargo_toml_file(self.input.as_ref())?;\n        debug!(\"manifest_path = {:?}\", manifest_path);\n        let manifest = super::manifest(self.input.as_ref())?;\n        debug!(\"target_directory = {:?}\", manifest.target_directory);\n        let package = super::package(&manifest, self.package.as_deref())?;\n        debug!(\"package = {:?}\", package);\n        let metadata = package.metadata.clone();\n        debug!(\"metadata = {:?}\", metadata);\n        let name = self.name(&package);\n        debug!(\"name = {:?}\", name);\n        let target = self.target()?;\n        debug!(\"target = {:?}\", target);\n        let version = self.version(&package)?;\n        debug!(\"version = {:?}\", version);\n        let compiler_args = self.compiler_args(&metadata);\n        debug!(\"compiler_args = {:?}\", compiler_args);\n        let culture = self.culture(&metadata)?;\n        debug!(\"culture = {:?}\", culture);\n        let linker_args = self.linker_args(&metadata);\n        debug!(\"linker_args = {:?}\", linker_args);\n        let locale = self.locale(&metadata)?;\n        debug!(\"locale = {:?}\", locale);\n        let profile = self.profile(&metadata);\n        debug!(\"profile = {:?}\", profile);\n        let debug_name = self.debug_name(&metadata);\n        debug!(\"debug_name = {:?}\", debug_name);\n        let wxs_sources = self.wxs_sources(&package)?;\n        debug!(\"wxs_sources = {:?}\", wxs_sources);\n        let wixobj_destination = self.wixobj_destination(manifest.target_directory.as_std_path());\n        debug!(\"wixobj_destination = {:?}\", wixobj_destination);\n        let no_build = self.no_build(&metadata);\n        debug!(\"no_build = {:?}\", no_build);\n        let target_bin_dir =\n            self.target_bin_dir(manifest.target_directory.as_std_path(), &target, &profile);\n        debug!(\"target_bin_dir = {:?}\", target_bin_dir);\n        let cfg = Cfg::of(&target.triple).map_err(|e| Error::Generic(e.to_string()))?;\n        let wix_arch = WixArch::try_from(&cfg)?;\n        debug!(\"wix_arch = {:?}\", wix_arch);\n\n        if no_build {\n            // Only warn if the user isn't clearly trying to be in charge of builds\n            if self.target_bin_dir.is_none() {\n                warn!(\"Skipped building the binary\");\n            }\n        } else {\n            // Build the binary, if a binary been built, then this will essentially do nothing.\n            info!(\"Building the binary\");\n            let mut builder = Command::new(\n                env::var(\"CARGO\")\n                    .map(PathBuf::from)\n                    .ok()\n                    .unwrap_or_else(|| PathBuf::from(CARGO)),\n            );\n            debug!(\"builder = {:?}\", builder);\n            if self.capture_output {\n                trace!(\"Capturing the '{}' output\", CARGO);\n                builder.stdout(Stdio::null());\n                builder.stderr(Stdio::null());\n            }\n            builder.arg(\"build\");\n            builder.arg(format!(\"--profile={}\", profile.name));\n            if let Some(target) = &target.arg {\n                builder.arg(format!(\"--target={target}\"));\n            }\n            if let Some(ref package) = self.package {\n                builder.arg(format!(\"--package={package}\"));\n            }\n            builder.arg(\"--manifest-path\").arg(&manifest_path);\n            debug!(\"command = {:?}\", builder);\n            let status = builder.status()?;\n            if !status.success() {\n                return Err(Error::Command(\n                    CARGO,\n                    status.code().unwrap_or(100),\n                    self.capture_output,\n                ));\n            }\n        }\n\n        // Compile the installer\n        info!(\"Compiling the installer\");\n\n        // Legacy toolset uses `candle` and `light` (compile and link)\n        // Modern toolset only uses `wix build`\n        let mut compiler = if self.toolset.is_legacy() {\n            debug!(\"Using legacy wix build tools\");\n            self.toolset.compiler(self.bin_path.clone())?\n        } else {\n            debug!(\"Using modern wix build tools\");\n            self.toolset.wix(\"build\")?\n        };\n        debug!(\"compiler = {:?}\", compiler);\n\n        if self.capture_output {\n            trace!(\"Capturing the '{}' output\", WIX_COMPILER);\n            compiler.stdout(Stdio::null());\n            compiler.stderr(Stdio::null());\n        }\n        compiler.arg(\"-arch\").arg(wix_arch.to_string());\n\n        if self.toolset.is_legacy() {\n            compiler.arg(\"-ext\").arg(\"WixUtilExtension\");\n\n            if let Some(vendor) = &cfg.target_vendor {\n                compiler.arg(format!(\"-dTargetVendor={vendor}\"));\n            }\n            compiler\n                .arg(format!(\"-dVersion={version}\"))\n                .arg(format!(\"-dPlatform={wix_arch}\"))\n                .arg(format!(\"-dProfile={}\", profile.name))\n                .arg(format!(\"-dTargetEnv={}\", cfg.target_env))\n                .arg(format!(\"-dTargetTriple={}\", target.triple))\n                .arg(format!(\"-dCargoProfile={}\", profile.name))\n                .arg({\n                    let mut s = OsString::from(\"-dCargoTargetDir=\");\n                    s.push(&manifest.target_directory);\n                    s\n                })\n                .arg({\n                    let mut s = OsString::from(\"-dCargoTargetBinDir=\");\n                    s.push(&target_bin_dir);\n                    s\n                })\n                .arg(\"-o\")\n                .arg(&wixobj_destination);\n        } else {\n            let project = self.toolset_setup_mode.setup(&self, &package, None)?;\n\n            // Determine installer type from wxs content before building\n            let pkg_count = project.package_count();\n            debug!(\"pkg_count = {:?}\", pkg_count);\n            if pkg_count == 0 && !project.is_bundle() {\n                return Err(Error::Generic(String::from(\n                    \"No WiX <Package> or <Bundle> definition was found in any of the \\\n                     source files. There needs to be at least one package or bundle \\\n                     definition for the installer to be created.\",\n                )));\n            }\n            // `wix build` produces a single output file per invocation.\n            // Multiple `<Package>` definitions across source files is not supported.\n            if pkg_count > 1 {\n                return Err(Error::Generic(format!(\n                    \"Found {pkg_count} WiX package definitions across the source files. \\\n                     The modern WiX toolset (wix build) can only produce one installer per \\\n                     invocation. Please separate your WiX sources so that only one source \\\n                     file defines a <Package> element, or build each package source separately.\"\n                )));\n            }\n\n            let installer_kind = project.installer_kind();\n            debug!(\"installer_kind = {:?}\", installer_kind);\n            let installer_destination = self.installer_destination(\n                &name,\n                &version,\n                &cfg,\n                debug_name,\n                &installer_kind,\n                &package,\n                manifest.target_directory.as_std_path(),\n            );\n            debug!(\"installer_destination = {:?}\", installer_destination);\n\n            // In WiX v4+, `-d` is a separate flag from the key=value pair\n            // (two args: `-d Key=Value`). Legacy candle.exe uses a single arg: `-dKey=Value`.\n            if let Some(vendor) = &cfg.target_vendor {\n                compiler.arg(\"-d\").arg(format!(\"TargetVendor={vendor}\"));\n            }\n            compiler\n                .arg(\"-d\")\n                .arg(format!(\"Version={version}\"))\n                .arg(\"-d\")\n                .arg(format!(\"Platform={wix_arch}\"))\n                .arg(\"-d\")\n                .arg(format!(\"Profile={}\", profile.name))\n                .arg(\"-d\")\n                .arg(format!(\"TargetEnv={}\", cfg.target_env))\n                .arg(\"-d\")\n                .arg(format!(\"TargetTriple={}\", target.triple))\n                .arg(\"-d\")\n                .arg(format!(\"CargoProfile={}\", profile.name))\n                .arg(\"-d\")\n                .arg({\n                    let mut s = OsString::from(\"CargoTargetDir=\");\n                    s.push(&manifest.target_directory);\n                    s\n                })\n                .arg(\"-d\")\n                .arg({\n                    let mut s = OsString::from(\"CargoTargetBinDir=\");\n                    s.push(&target_bin_dir);\n                    s\n                });\n\n            // \"Linker\" flags\n            // In WiX v4+, `-pdbtype none` replaces the legacy `light.exe -spdb`\n            // flag to suppress PDB generation. The `-culture` flag replaces the legacy `-cultures:{v}`\n            // colon-delimited syntax.\n            compiler\n                .args([\"-pdbtype\", \"none\"])\n                .arg(\"-culture\")\n                .arg(culture.to_string());\n\n            // `wix build -o <path>` infers the output type (msi vs exe) from\n            // the file extension. This is unlike legacy `light.exe -out` which accepts a directory.\n            compiler.arg(\"-o\").arg(&installer_destination);\n\n            // Applies all `-ext` flags\n            project.configure_toolset_extensions(&mut compiler)?;\n\n            if let Some(ref l) = locale {\n                trace!(\"Using the a WiX localization file\");\n                compiler.arg(\"-loc\").arg(l);\n            }\n        }\n\n        if let Some(args) = &compiler_args {\n            trace!(\"Appending compiler arguments\");\n            compiler.args(args);\n        }\n        if self.toolset.is_modern()\n            && let Some(args) = &linker_args\n        {\n            trace!(\"Appending linker arguments to wix build\");\n            compiler.args(args);\n        }\n        compiler.args(&wxs_sources);\n        debug!(\"command = {:?}\", compiler);\n\n        let status = compiler.status().map_err(|err| {\n            if err.kind() == ErrorKind::NotFound {\n                Error::Generic(format!(\n                    \"The compiler application ({WIX_COMPILER}) could not be found in the PATH environment \\\n                    variable. Please check the WiX Toolset (http://wixtoolset.org/) is \\\n                    installed and check the WiX Toolset's '{BINARY_FOLDER_NAME}' folder has been added to the PATH \\\n                    system environment variable, the {WIX_PATH_KEY} system environment variable exists, or use \\\n                    the '-b,--bin-path' command line argument.\"\n                ))\n            } else {\n                err.into()\n            }\n        })?;\n        if !status.success() {\n            error!(\n                \"Could not execute {}\",\n                compiler\n                    .inner\n                    .get_args()\n                    .filter_map(|a| a.to_str())\n                    .join(\" \")\n            );\n            return Err(Error::Command(\n                if self.toolset.is_legacy() {\n                    WIX_COMPILER\n                } else {\n                    WIX_MODERN_TOOLSET\n                },\n                status.code().unwrap_or(100),\n                self.capture_output,\n            ));\n        }\n\n        if self.toolset.is_modern() {\n            // Launch the installer (modern path)\n            if self.install {\n                info!(\"Launching the installer\");\n                let project = self.create_project(&package)?;\n                let installer_kind = project.installer_kind();\n                let dest = self.installer_destination(\n                    &name,\n                    &version,\n                    &cfg,\n                    debug_name,\n                    &installer_kind,\n                    &package,\n                    manifest.target_directory.as_std_path(),\n                );\n                let status = match installer_kind {\n                    InstallerKind::Exe => {\n                        // Bundles produce .exe files that are launched directly\n                        debug!(\"Launching bundle installer: {:?}\", dest);\n                        Command::new(&dest).status()?\n                    }\n                    InstallerKind::Msi => {\n                        // MSI packages are installed via msiexec\n                        debug!(\"Launching MSI installer via msiexec: {:?}\", dest);\n                        let mut installer = Command::new(MSIEXEC);\n                        installer.arg(\"/i\").arg(&dest);\n                        installer.status()?\n                    }\n                };\n                if !status.success() {\n                    return Err(Error::Command(\n                        match installer_kind {\n                            InstallerKind::Exe => \"bundle\",\n                            InstallerKind::Msi => MSIEXEC,\n                        },\n                        status.code().unwrap_or(100),\n                        self.capture_output,\n                    ));\n                }\n            }\n        }\n\n        // Modern wix no longer requires `light`\n        if self.toolset.is_legacy() {\n            let wixobj_sources = self.wixobj_sources(&wixobj_destination)?;\n            debug!(\"wixobj_sources = {:?}\", wixobj_sources);\n            let installer_kind = InstallerKind::try_from(\n                wixobj_sources\n                    .iter()\n                    .map(WixObjKind::try_from)\n                    .collect::<Result<Vec<WixObjKind>>>()?,\n            )?;\n            debug!(\"installer_kind = {:?}\", installer_kind);\n            let installer_destination = self.installer_destination(\n                &name,\n                &version,\n                &cfg,\n                debug_name,\n                &installer_kind,\n                &package,\n                manifest.target_directory.as_std_path(),\n            );\n            debug!(\"installer_destination = {:?}\", installer_destination);\n\n            // Link the installer\n            info!(\"Linking the installer\");\n            let mut linker = self.linker()?;\n            debug!(\"linker = {:?}\", linker);\n            let base_path = manifest_path.parent().ok_or_else(|| {\n                Error::Generic(String::from(\"The base path for the linker is invalid\"))\n            })?;\n            debug!(\"base_path = {:?}\", base_path);\n            if self.capture_output {\n                trace!(\"Capturing the '{}' output\", WIX_LINKER);\n                linker.stdout(Stdio::null());\n                linker.stderr(Stdio::null());\n            }\n            linker\n                .arg(\"-spdb\")\n                .arg(\"-ext\")\n                .arg(\"WixUIExtension\")\n                .arg(\"-ext\")\n                .arg(\"WixUtilExtension\")\n                .arg(format!(\"-cultures:{culture}\"))\n                .arg(\"-out\")\n                .arg(&installer_destination)\n                .arg(\"-b\")\n                .arg(base_path);\n            if let Some(l) = locale {\n                trace!(\"Using the a WiX localization file\");\n                linker.arg(\"-loc\").arg(l);\n            }\n            if let InstallerKind::Exe = installer_kind {\n                trace!(\"Adding the WixBalExtension for the bundle-based installer\");\n                linker.arg(\"-ext\").arg(\"WixBalExtension\");\n            }\n            if let Some(args) = &linker_args {\n                trace!(\"Appending linker arguments\");\n                linker.args(args);\n            }\n            linker.args(&wixobj_sources);\n            debug!(\"command = {:?}\", linker);\n            let status = linker.status().map_err(|err| {\n                if err.kind() == ErrorKind::NotFound {\n                    Error::Generic(format!(\n                        \"The linker application ({WIX_LINKER}) could not be found in the PATH environment \\\n                         variable. Please check the WiX Toolset (http://wixtoolset.org/) is \\\n                         installed and check the WiX Toolset's '{BINARY_FOLDER_NAME}' folder has been added to the PATH \\\n                         environment variable, the {WIX_PATH_KEY} system environment variable exists, or use the \\\n                         '-b,--bin-path' command line argument.\"\n                    ))\n                } else {\n                    err.into()\n                }\n            })?;\n            if !status.success() {\n                return Err(Error::Command(\n                    WIX_LINKER,\n                    status.code().unwrap_or(100),\n                    self.capture_output,\n                ));\n            }\n\n            // Launch the installer\n            if self.install {\n                info!(\"Launching the installer\");\n                let mut installer = Command::new(MSIEXEC);\n                installer.arg(\"/i\").arg(&installer_destination);\n                let status = installer.status()?;\n                if !status.success() {\n                    return Err(Error::Command(\n                        MSIEXEC,\n                        status.code().unwrap_or(100),\n                        self.capture_output,\n                    ));\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    fn compiler_args(&self, metadata: &Value) -> Option<Vec<String>> {\n        self.compiler_args.to_owned().or_else(|| {\n            metadata\n                .get(\"wix\")\n                .and_then(|w| w.as_object())\n                .and_then(|t| t.get(\"compiler-args\"))\n                .and_then(|i| i.as_array())\n                .map(|a| {\n                    a.iter()\n                        .map(|s| s.as_str().map(String::from).unwrap())\n                        .collect::<Vec<String>>()\n                })\n        })\n    }\n\n    fn culture(&self, metadata: &Value) -> Result<Cultures> {\n        if let Some(culture) = &self.culture {\n            Cultures::from_str(culture)\n        } else if let Some(pkg_meta_wix_culture) = metadata\n            .get(\"wix\")\n            .and_then(|w| w.as_object())\n            .and_then(|t| t.get(\"culture\"))\n            .and_then(|c| c.as_str())\n        {\n            Cultures::from_str(pkg_meta_wix_culture)\n        } else {\n            Ok(Cultures::EnUs)\n        }\n    }\n\n    fn debug_build(&self, metadata: &Value) -> bool {\n        if self.debug_build {\n            true\n        } else {\n            metadata\n                .get(\"wix\")\n                .and_then(|w| w.as_object())\n                .and_then(|t| t.get(\"dbg-build\"))\n                .and_then(|c| c.as_bool())\n                .unwrap_or_default()\n        }\n    }\n\n    fn debug_name(&self, metadata: &Value) -> bool {\n        if self.debug_name {\n            true\n        } else {\n            metadata\n                .get(\"wix\")\n                .and_then(|w| w.as_object())\n                .and_then(|t| t.get(\"dbg-name\"))\n                .and_then(|c| c.as_bool())\n                .unwrap_or_default()\n        }\n    }\n\n    /// Get the name of the cargo build profile\n    ///\n    /// If `profile` is set, prefer that.\n    /// Otherwise use `debug_build` to choose if we're debug or release.\n    fn profile_name(&self, metadata: &Value) -> String {\n        if let Some(profile) = self.profile.clone() {\n            profile\n        } else if let Some(pkg_meta_wix_profile) = metadata\n            .get(\"wix\")\n            .and_then(|w| w.as_object())\n            .and_then(|t| t.get(\"profile\"))\n            .and_then(|c| c.as_str())\n        {\n            pkg_meta_wix_profile.to_owned()\n        } else if self.debug_build(metadata) {\n            // The default \"debug\" build profile is called \"dev\" for whatever reason\n            \"dev\".to_owned()\n        } else {\n            \"release\".to_owned()\n        }\n    }\n\n    /// Gets the name and dir of the cargo build profile\n    fn profile(&self, metadata: &Value) -> Profile {\n        let name = self.profile_name(metadata);\n\n        // Figure out what subdir of target will contain our output\n        // Cargo specially maps the builtin profile names to \"debug\" or \"release\"\n        // in the target directory, but custom profiles get forwarded verbatim.\n        let dir = match &*name {\n            \"dev\" | \"test\" => \"debug\",\n            \"release\" | \"bench\" => \"release\",\n            p => p,\n        }\n        .to_owned();\n\n        Profile { name, dir }\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    fn installer_destination(\n        &self,\n        name: &str,\n        version: &str,\n        cfg: &Cfg,\n        debug_name: bool,\n        installer_kind: &InstallerKind,\n        package: &Package,\n        target_directory: &Path,\n    ) -> PathBuf {\n        let filename = if debug_name {\n            format!(\n                \"{}-{}-{}-debug.{}\",\n                name, version, cfg.target_arch, installer_kind\n            )\n        } else {\n            format!(\n                \"{}-{}-{}.{}\",\n                name, version, cfg.target_arch, installer_kind\n            )\n        };\n        if let Some(ref path_str) = self.output {\n            trace!(\"Using the explicitly specified output path for the MSI destination\");\n            let path = Path::new(path_str);\n            if path_str.ends_with('/') || path_str.ends_with('\\\\') || path.is_dir() {\n                path.join(filename)\n            } else {\n                path.to_owned()\n            }\n        } else if let Some(pkg_meta_wix_output) = package\n            .metadata\n            .get(\"wix\")\n            .and_then(|w| w.as_object())\n            .and_then(|t| t.get(\"output\"))\n            .and_then(|o| o.as_str())\n        {\n            trace!(\"Using the output path in the package's metadata for the MSI destination\");\n            let path = Path::new(pkg_meta_wix_output);\n            if pkg_meta_wix_output.ends_with('/')\n                || pkg_meta_wix_output.ends_with('\\\\')\n                || path.is_dir()\n            {\n                path.join(filename)\n            } else {\n                path.to_owned()\n            }\n        } else {\n            trace!(\n                \"Using the package's manifest (Cargo.toml) file path to specify the MSI destination\"\n            );\n            target_directory.join(WIX).join(filename)\n        }\n    }\n\n    fn linker(&self) -> Result<Command> {\n        if let Some(mut path) = self.bin_path.as_ref().map(|s| {\n            let mut p = PathBuf::from(s);\n            trace!(\n                \"Using the '{}' path to the WiX Toolset '{}' folder for the linker\",\n                p.display(),\n                BINARY_FOLDER_NAME\n            );\n            p.push(WIX_LINKER);\n            p.set_extension(EXE_FILE_EXTENSION);\n            p\n        }) {\n            if !path.exists() {\n                path.pop(); // Remove the 'light' application from the path\n                Err(Error::Generic(format!(\n                    \"The linker application ('{}') does not exist at the '{}' path specified via \\\n                     the '-b,--bin-path' command line argument. Please check the path is correct \\\n                     and the linker application exists at the path.\",\n                    WIX_LINKER,\n                    path.display()\n                )))\n            } else {\n                Ok(Command::new(path))\n            }\n        } else if let Some(mut path) = env::var_os(WIX_PATH_KEY).map(|s| {\n            let mut p = PathBuf::from(s);\n            trace!(\n                \"Using the '{}' path to the WiX Toolset's '{}' folder for the linker\",\n                p.display(),\n                BINARY_FOLDER_NAME\n            );\n            p.push(BINARY_FOLDER_NAME);\n            p.push(WIX_LINKER);\n            p.set_extension(EXE_FILE_EXTENSION);\n            p\n        }) {\n            if !path.exists() {\n                path.pop(); // Remove the `candle` application from the path\n                Err(Error::Generic(format!(\n                    \"The linker application ('{}') does not exist at the '{}' path specified \\\n                     via the {} environment variable. Please check the path is correct and the \\\n                     linker application exists at the path.\",\n                    WIX_LINKER,\n                    path.display(),\n                    WIX_PATH_KEY\n                )))\n            } else {\n                Ok(Command::new(path))\n            }\n        } else {\n            Ok(Command::new(WIX_LINKER))\n        }\n    }\n\n    fn linker_args(&self, metadata: &Value) -> Option<Vec<String>> {\n        self.linker_args.to_owned().or_else(|| {\n            metadata\n                .get(\"wix\")\n                .and_then(|w| w.as_object())\n                .and_then(|t| t.get(\"linker-args\"))\n                .and_then(|i| i.as_array())\n                .map(|a| {\n                    a.iter()\n                        .map(|s| s.as_str().map(String::from).unwrap())\n                        .collect::<Vec<String>>()\n                })\n        })\n    }\n\n    fn locale(&self, metadata: &Value) -> Result<Option<PathBuf>> {\n        if let Some(locale) = self.locale.as_ref().map(PathBuf::from) {\n            if locale.exists() {\n                Ok(Some(locale))\n            } else {\n                Err(Error::Generic(format!(\n                    \"The '{}' WiX localization file could not be found, or it does not exist. \\\n                     Please check the path is correct and the file exists.\",\n                    locale.display()\n                )))\n            }\n        } else if let Some(pkg_meta_wix_locale) = metadata\n            .get(\"wix\")\n            .and_then(|w| w.as_object())\n            .and_then(|t| t.get(\"locale\"))\n            .and_then(|l| l.as_str())\n            .map(PathBuf::from)\n        {\n            Ok(Some(pkg_meta_wix_locale))\n        } else {\n            Ok(None)\n        }\n    }\n\n    fn name(&self, package: &Package) -> String {\n        if let Some(ref p) = self.name {\n            p.to_owned()\n        } else if let Some(pkg_meta_wix_name) = package\n            .metadata\n            .get(\"wix\")\n            .and_then(|w| w.as_object())\n            .and_then(|t| t.get(\"name\"))\n            .and_then(|n| n.as_str())\n            .map(String::from)\n        {\n            pkg_meta_wix_name\n        } else {\n            package.name.to_string()\n        }\n    }\n\n    fn no_build(&self, metadata: &Value) -> bool {\n        if self.no_build {\n            true\n        } else {\n            metadata\n                .get(\"wix\")\n                .and_then(|w| w.as_object())\n                .and_then(|t| t.get(\"no-build\"))\n                .and_then(|c| c.as_bool())\n                .unwrap_or_default()\n        }\n    }\n\n    /// Get the value of CargoTargetBinDir\n    ///\n    /// If it's explicitly set, just use that.\n    /// Otherwise compute it from the various relevant settings of a build.\n    fn target_bin_dir(\n        &self,\n        target_directory: &Path,\n        target: &Target,\n        profile: &Profile,\n    ) -> PathBuf {\n        // Setting this via [package.metadata.wix] explicitly unsupported to avoid footguns\n        if let Some(target_bin_dir) = &self.target_bin_dir {\n            target_bin_dir.clone()\n        } else {\n            let mut bin_path = target_directory.to_owned();\n            // Cargo only adds the target to the path if it's explicitly passed via --target\n            if let Some(target) = &target.arg {\n                bin_path.push(target);\n            }\n            bin_path.push(&profile.dir);\n            bin_path\n        }\n    }\n\n    // TODO: Change to use --unit-graph feature of cargo once stable. See #124.\n    //\n    // This does not support default-target. Ideally we would use cargo\n    // --unit-graph to figure this out without having to second-guess the\n    // compiler. Unfortunately, cargo --unit-graph is unstable.\n    fn target(&self) -> Result<Target> {\n        if let Some(t) = &self.target {\n            Ok(Target {\n                triple: t.clone(),\n                arg: Some(t.clone()),\n            })\n        } else {\n            let output = Command::new(\"rustc\")\n                .args([\"--version\", \"--verbose\"])\n                .output()?;\n            for line in output.stdout.split(|b| *b == b'\\n') {\n                let mut line_elt = line.splitn(2, |b| *b == b':');\n                let first = line_elt.next();\n                let second = line_elt.next();\n                if let (Some(b\"host\"), Some(host_triple)) = (first, second) {\n                    let s = String::from_utf8(host_triple.to_vec()).map_err(|_| {\n                        Error::Generic(\n                            \"Failed to parse output of the 'rustc --verbose \\\n                            --version' command: invalid UTF8\"\n                                .to_string(),\n                        )\n                    });\n                    return Ok(Target {\n                        triple: s?.trim().to_string(),\n                        arg: None,\n                    });\n                }\n            }\n            Err(Error::Generic(\n                \"Failed to parse output of the 'rustc --verbose --version' \\\n                command\"\n                    .to_string(),\n            ))\n        }\n    }\n\n    fn wixobj_destination(&self, target_directory: &Path) -> PathBuf {\n        // A trailing slash is needed; otherwise, candle tries to dump the\n        // object files to a `target\\wix` file instead of dumping the object\n        // files in the `target\\wix\\` folder for the `-out` option. The trailing\n        // slash must be done \"manually\" as a string instead of using the\n        // PathBuf API because the PathBuf `push` and/or `join` methods treat a\n        // single slash (forward or backward) without a prefix as the root `C:\\`\n        // or `/` and deletes the full path. This is noted in the documentation\n        // for PathBuf, but it was unexpected and kind of annoying because I am\n        // not sure how to add a trailing slash in a cross-platform way with\n        // PathBuf, not that cargo-wix needs to be cross-platform.\n        target_directory.join(WIX).join(\"\")\n    }\n\n    fn wixobj_sources(&self, wixobj_dst: &Path) -> Result<Vec<PathBuf>> {\n        let wixobj_sources: Vec<PathBuf> = std::fs::read_dir(wixobj_dst)?\n            .filter(|r| r.is_ok())\n            .map(|r| r.unwrap().path())\n            .filter(|p| p.extension().and_then(|s| s.to_str()) == Some(WIX_OBJECT_FILE_EXTENSION))\n            .collect();\n        if wixobj_sources.is_empty() {\n            Err(Error::Generic(String::from(\"No WiX object files found.\")))\n        } else {\n            Ok(wixobj_sources)\n        }\n    }\n\n    /// Attempts to convert a Rust SemVer version to the format WiX desires.\n    ///\n    /// WiX only supports numbers in versions, with a format of \"x.x.x.x\"\n    /// WiX itself requires each component to be an integer from 0 to 65534 (inclusive).\n    /// However the first 3 parts are forwarded to Windows as a [ProductVersion][0],\n    /// which interprets them as \"major.minor.build\" and states:\n    ///\n    /// > The major version and has a maximum value of 255.\n    /// > The minor version and has a maximum value of 255.\n    /// > The build version or the update version and has a maximum value of 65,535.\n    ///\n    /// So we take the intersection of these requirements, and shove the rust \"major.minor.patch\"\n    /// format into it. This leaves the more freeform \"prerelease\" and \"build\" components of a\n    /// SemVer Version to get squeezed into the 4th value.\n    ///\n    /// The 4th value is seemingly just a bonus value that WiX keeps to itself, so it's not\n    /// terribly important that we get it perfect. We therefore attempt to heuritistically\n    /// parse out a numeric \"prerelease version\" based on common formats.\n    ///\n    /// [0]: https://learn.microsoft.com/en-us/windows/win32/msi/productversion\n    fn version(&self, package: &Package) -> Result<String> {\n        // Select the version\n        let version = if let Some(ref v) = self.version {\n            Version::parse(v).map_err(Error::from)?\n        } else if let Some(pkg_meta_wix_version) = package\n            .metadata\n            .get(\"wix\")\n            .and_then(|w| w.as_object())\n            .and_then(|t| t.get(\"version\"))\n            .and_then(|v| v.as_str())\n        {\n            Version::parse(pkg_meta_wix_version).map_err(Error::from)?\n        } else {\n            package.version.clone()\n        };\n\n        // validate basic parts\n        if version.major > 255 {\n            return Err(Error::Generic(format!(\n                \"The app's major version {} can't be greater than 255 for an msi\",\n                version.major\n            )));\n        }\n        if version.minor > 255 {\n            return Err(Error::Generic(format!(\n                \"The app's minor version {} can't be greater than 255 for an msi\",\n                version.minor\n            )));\n        }\n        if version.patch > 65534 {\n            return Err(Error::Generic(format!(\n                \"The app's patch version {} can't be greater than 65534 for an msi\",\n                version.patch\n            )));\n        }\n\n        // Attempt to validate + convert the prerelease parts\n        let needs_prerelease_handling = !version.build.is_empty() || !version.pre.is_empty();\n        if needs_prerelease_handling {\n            // This mess is trying 3 approaches in sequence:\n            //\n            // * parse as if it's `1.2.3-4`\n            // * parse as if it's `1.2.3-prerelease.4`\n            // * parse as if it's `1.2.3-prerelease+4`\n            let bonus = version\n                .pre\n                .parse::<u64>()\n                .or_else(|e| {\n                    if let Some((_, dotted)) = version.pre.split_once('.') {\n                        dotted.parse::<u64>()\n                    } else {\n                        Err(e)\n                    }\n                })\n                .or_else(|_| version.build.parse::<u64>());\n\n            let bonus = if let Ok(bonus) = bonus {\n                bonus\n            } else {\n                return Err(Error::Generic(format!(\n                    \"The app's version {} is a prerelease, but we couldn't convert the prerelease \\\n                     components to an integer. We recommend a format like 1.2.3-prerelease.4, \\\n                     as we can map it to the 1.2.3.4 format that works for an msi.\",\n                    version,\n                )));\n            };\n            if bonus > 65534 {\n                return Err(Error::Generic(format!(\n                    \"The app's prerelease version {} can't be greater than 65534 for an msi\",\n                    bonus\n                )));\n            }\n\n            Ok(format!(\n                \"{}.{}.{}.{}\",\n                version.major, version.minor, version.patch, bonus\n            ))\n        } else {\n            Ok(format!(\n                \"{}.{}.{}\",\n                version.major, version.minor, version.patch\n            ))\n        }\n    }\n}\n\nimpl Includes for Execution {\n    fn includes(&self) -> Option<&Vec<PathBuf>> {\n        self.includes.as_ref()\n    }\n}\n\nimpl Default for Execution {\n    fn default() -> Self {\n        Builder::new().build()\n    }\n}\n\n/// The kind of WiX Object (wixobj) file.\n#[derive(Debug, PartialEq, Eq)]\npub enum WixObjKind {\n    /// A WiX Object (wixobj) file that ultimately links back to a WiX Source\n    /// (wxs) file with a [`bundle`] tag.\n    ///\n    /// [`bundle`]: https://wixtoolset.org/documentation/manual/v3/xsd/wix/bundle.html\n    Bundle,\n    /// A WiX Object (wixobj) file that ultimately links back to a WiX Source\n    /// (wxs) file with a [`fragment`] tag.\n    ///\n    /// [`fragment`]: https://wixtoolset.org/documentation/manual/v3/xsd/wix/fragment.html\n    Fragment,\n    /// A WiX Object (wixobj) file that ultimately links back to a WiX Source\n    /// (wxs) file with a [`product`] tag.\n    ///\n    /// [`product`]: https://wixtoolset.org/documentation/manual/v3/xsd/wix/product.html\n    Product,\n}\n\nimpl WixObjKind {\n    /// Determines if the WiX Object (wixobj) file kind is a [`bundle`].\n    ///\n    /// # Examples\n    ///\n    /// A WiX Object (wixobj) file identified as a WXS Source (wxs) file\n    /// containing a [`bundle`] tag.\n    ///\n    /// ```\n    /// use wix::create::WixObjKind;\n    ///\n    /// assert!(WixObjKind::Bundle.is_bundle())\n    /// ```\n    ///\n    /// A WiX Object (wixobj) file identified as a WXS Source (wxs) file\n    /// containing a [`product`] tag.\n    ///\n    /// ```\n    /// use wix::create::WixObjKind;\n    ///\n    /// assert!(!WixObjKind::Product.is_bundle())\n    /// ```\n    ///\n    /// [`bundle`]: https://wixtoolset.org/documentation/manual/v3/xsd/wix/bundle.html\n    /// [`product`]: https://wixtoolset.org/documentation/manual/v3/xsd/wix/product.html\n    pub fn is_bundle(&self) -> bool {\n        match *self {\n            Self::Bundle => true,\n            Self::Fragment => false,\n            Self::Product => false,\n        }\n    }\n}\n\nimpl FromStr for WixObjKind {\n    type Err = crate::Error;\n\n    fn from_str(value: &str) -> Result<Self> {\n        match &*value.to_lowercase() {\n            \"bundle\" => Ok(Self::Bundle),\n            \"fragment\" => Ok(Self::Fragment),\n            \"product\" => Ok(Self::Product),\n            v => Err(Self::Err::Generic(format!(\n                \"Unknown '{v}' tag name from a WiX Object (wixobj) file.\"\n            ))),\n        }\n    }\n}\n\nimpl TryFrom<&PathBuf> for WixObjKind {\n    type Error = crate::Error;\n\n    fn try_from(path: &PathBuf) -> Result<Self> {\n        let file = std::fs::File::open(path)?;\n        let mut decoder = encoding_rs_io::DecodeReaderBytes::new(file);\n        let mut content = String::new();\n        decoder.read_to_string(&mut content)?;\n        Self::try_from(content.as_str())\n    }\n}\n\nimpl TryFrom<&str> for WixObjKind {\n    type Error = crate::Error;\n\n    fn try_from(content: &str) -> Result<Self> {\n        let package = sxd_document::parser::parse(content)?;\n        let document = package.as_document();\n        let mut context = sxd_xpath::Context::new();\n        context.set_namespace(\"wix\", \"http://schemas.microsoft.com/wix/2006/objects\");\n        // The assumption is that the following cannot fail because the path is known to be valid at\n        // compile-time.\n        let xpath = sxd_xpath::Factory::new()\n            .build(\"/wix:wixObject/wix:section/@type\")\n            .unwrap()\n            .unwrap();\n        let value = xpath.evaluate(&context, document.root())?.string();\n        Self::from_str(&value)\n    }\n}\n\n/// The kinds of installers that can be created using the WiX compiler\n/// (candle.exe) and linker (light.exe).\n#[derive(Debug, Default, PartialEq, Eq)]\npub enum InstallerKind {\n    /// An executable is used when an [Installation Package Bundle] is created.\n    ///\n    /// [Installation Package Bundle]: https://wixtoolset.org/documentation/manual/v3/bundle/\n    Exe,\n    /// A Microsoft installer. This is the more common and typical installer to be created.\n    #[default]\n    Msi,\n}\n\nimpl InstallerKind {\n    /// Gets the file extension _without_ the dot separator.\n    ///\n    /// # Examples\n    ///\n    /// The extension for an installer of an [Installation Package Bundle]. Also\n    /// see the [`EXE_FILE_EXTENSION`] constant.\n    ///\n    /// ```\n    /// use wix::create::InstallerKind;\n    ///\n    /// assert_eq!(InstallerKind::Exe.extension(), \"exe\")\n    /// ```\n    ///\n    /// The extension for a typical Microsoft installer. Also see the\n    /// [`MSI_FILE_EXTENSION`] constant.\n    ///\n    /// ```\n    /// use wix::create::InstallerKind;\n    ///\n    /// assert_eq!(InstallerKind::Msi.extension(), \"msi\")\n    /// ```\n    ///\n    /// [Installation Package Bundle]: https://wixtoolset.org/documentation/manual/v3/bundle/\n    /// [`EXE_FILE_EXTENSION`]: lib.html#exe-file-extension\n    /// [`MSI_FILE_EXTENSION`]: lib.html#msi-file-extension\n    pub fn extension(&self) -> &'static str {\n        match *self {\n            Self::Exe => EXE_FILE_EXTENSION,\n            Self::Msi => MSI_FILE_EXTENSION,\n        }\n    }\n}\n\nimpl FromStr for InstallerKind {\n    type Err = crate::Error;\n\n    fn from_str(value: &str) -> Result<Self> {\n        match &*value.to_lowercase() {\n            \"exe\" => Ok(Self::Exe),\n            \"msi\" => Ok(Self::Msi),\n            _ => Err(Self::Err::Generic(format!(\n                \"Unknown '{value}' file extension for an installer\"\n            ))),\n        }\n    }\n}\n\nimpl fmt::Display for InstallerKind {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"{}\", self.extension())\n    }\n}\n\nimpl TryFrom<Vec<WixObjKind>> for InstallerKind {\n    type Error = crate::Error;\n\n    fn try_from(v: Vec<WixObjKind>) -> Result<Self> {\n        v.iter()\n            .fold(None, |a, v| match v {\n                WixObjKind::Bundle => Some(Self::Exe),\n                WixObjKind::Product => a.or(Some(Self::Msi)),\n                _ => a,\n            })\n            .ok_or_else(|| {\n                Self::Error::Generic(String::from(\n                    \"Could not determine the installer kind based on the WiX \\\n                     object files. There needs to be at least one 'product' or \\\n                     'bundle' tag in the collective WiX source files (wxs).\",\n                ))\n            })\n    }\n}\n\n/// Details of the cargo build profile\n#[derive(Debug, Clone)]\npub struct Profile {\n    /// The name of the profile to pass to `--profile=...`\n    name: String,\n    /// The name of the subdirectory of the cargo target dir that the profile will get\n    dir: String,\n}\n\n/// Details of the cargo build target\n#[derive(Debug, Clone)]\npub struct Target {\n    /// The name of the target triple being built\n    triple: String,\n    /// If an explicit --target flag is being passed to Cargo, this is it\n    arg: Option<String>,\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    mod builder {\n        use super::*;\n\n        #[test]\n        fn defaults_are_correct() {\n            let actual = Builder::new();\n            assert!(actual.bin_path.is_none());\n            assert!(actual.capture_output);\n            assert!(actual.compiler_args.is_none());\n            assert!(actual.culture.is_none());\n            assert!(!actual.debug_build);\n            assert!(!actual.debug_name);\n            assert!(actual.includes.is_none());\n            assert!(actual.input.is_none());\n            assert!(actual.linker_args.is_none());\n            assert!(actual.locale.is_none());\n            assert!(actual.name.is_none());\n            assert!(!actual.no_build);\n            assert!(actual.output.is_none());\n            assert!(actual.version.is_none());\n        }\n\n        #[test]\n        fn bin_path_works() {\n            const EXPECTED: &str = \"C:\\\\Wix Toolset\\\\bin\";\n            let mut actual = Builder::new();\n            actual.bin_path(Some(EXPECTED));\n            assert_eq!(actual.bin_path, Some(EXPECTED));\n        }\n\n        #[test]\n        fn target_bin_dir_works() {\n            const EXPECTED: &str = \"target\\\\special\\\\build\\\\\";\n            let mut actual = Builder::new();\n            actual.target_bin_dir(Some(EXPECTED));\n            assert_eq!(actual.target_bin_dir, Some(EXPECTED));\n        }\n\n        #[test]\n        fn capture_output_works() {\n            let mut actual = Builder::new();\n            actual.capture_output(false);\n            assert!(!actual.capture_output);\n        }\n\n        #[test]\n        fn compiler_args_with_single_value_works() {\n            const EXPECTED: &str = \"-nologo\";\n            let mut actual = Builder::new();\n            actual.compiler_args(Some(vec![EXPECTED]));\n            assert_eq!(actual.compiler_args, Some(vec![EXPECTED]));\n        }\n\n        #[test]\n        fn compiler_args_with_multiple_values_works() {\n            let expected: Vec<&str> = vec![\"-arch\", \"x86\"];\n            let mut actual = Builder::new();\n            actual.compiler_args(Some(expected.clone()));\n            assert_eq!(actual.compiler_args, Some(expected));\n        }\n\n        #[test]\n        fn culture_works() {\n            const EXPECTED: &str = \"FrFr\";\n            let mut actual = Builder::new();\n            actual.culture(Some(EXPECTED));\n            assert_eq!(actual.culture, Some(EXPECTED));\n        }\n\n        #[test]\n        fn russian_culture_works() {\n            const EXPECTED: &str = \"RuRu\";\n            let mut actual = Builder::new();\n            actual.culture(Some(EXPECTED));\n            assert_eq!(actual.culture, Some(EXPECTED));\n        }\n\n        #[test]\n        fn debug_build_works() {\n            let mut actual = Builder::new();\n            actual.debug_build(true);\n            assert!(actual.debug_build);\n        }\n\n        #[test]\n        fn profile_works() {\n            const EXPECTED: &str = \"dist\";\n            let mut actual = Builder::new();\n            actual.profile(Some(EXPECTED));\n            assert_eq!(actual.profile, Some(EXPECTED));\n        }\n\n        #[test]\n        fn debug_name_works() {\n            let mut actual = Builder::new();\n            actual.debug_name(true);\n            assert!(actual.debug_name);\n        }\n\n        #[test]\n        fn includes_works() {\n            const EXPECTED: &str = \"C:\\\\tmp\\\\hello_world\\\\wix\\\\main.wxs\";\n            let mut actual = Builder::new();\n            actual.includes(Some(vec![EXPECTED]));\n            assert_eq!(actual.includes, Some(vec![EXPECTED]));\n        }\n\n        #[test]\n        fn input_works() {\n            const EXPECTED: &str = \"C:\\\\tmp\\\\hello_world\\\\Cargo.toml\";\n            let mut actual = Builder::new();\n            actual.input(Some(EXPECTED));\n            assert_eq!(actual.input, Some(EXPECTED));\n        }\n\n        #[test]\n        fn linker_args_with_single_value_works() {\n            const EXPECTED: &str = \"-nologo\";\n            let mut actual = Builder::new();\n            actual.linker_args(Some(vec![EXPECTED]));\n            assert_eq!(actual.linker_args, Some(vec![EXPECTED]));\n        }\n\n        #[test]\n        fn linker_args_with_multiple_values_works() {\n            let expected: Vec<&str> = vec![\"-ext\", \"HelloExtension\"];\n            let mut actual = Builder::new();\n            actual.linker_args(Some(expected.clone()));\n            assert_eq!(actual.linker_args, Some(expected));\n        }\n\n        #[test]\n        fn locale_works() {\n            const EXPECTED: &str = \"C:\\\\tmp\\\\hello_world\\\\wix\\\\main.wxl\";\n            let mut actual = Builder::new();\n            actual.locale(Some(EXPECTED));\n            assert_eq!(actual.locale, Some(EXPECTED));\n        }\n\n        #[test]\n        fn name_works() {\n            const EXPECTED: &str = \"Name\";\n            let mut actual = Builder::new();\n            actual.name(Some(EXPECTED));\n            assert_eq!(actual.name, Some(EXPECTED));\n        }\n\n        #[test]\n        fn no_build_works() {\n            let mut actual = Builder::new();\n            actual.no_build(true);\n            assert!(actual.no_build);\n        }\n\n        #[test]\n        fn output_works() {\n            const EXPECTED: &str = \"C:\\\\tmp\\\\hello_world\\\\output\";\n            let mut actual = Builder::new();\n            actual.output(Some(EXPECTED));\n            assert_eq!(actual.output, Some(EXPECTED));\n        }\n\n        #[test]\n        fn version_works() {\n            const EXPECTED: &str = \"1.2.3\";\n            let mut actual = Builder::new();\n            actual.version(Some(EXPECTED));\n            assert_eq!(actual.version, Some(EXPECTED));\n        }\n\n        #[test]\n        fn build_with_defaults_works() {\n            let mut b = Builder::new();\n            let default_execution = b.build();\n            assert!(default_execution.bin_path.is_none());\n            assert!(default_execution.capture_output);\n            assert!(default_execution.compiler_args.is_none());\n            assert!(default_execution.culture.is_none());\n            assert!(!default_execution.debug_build);\n            assert!(!default_execution.debug_name);\n            assert!(default_execution.includes.is_none());\n            assert!(default_execution.input.is_none());\n            assert!(default_execution.linker_args.is_none());\n            assert!(default_execution.locale.is_none());\n            assert!(default_execution.name.is_none());\n            assert!(!default_execution.no_build);\n            assert!(default_execution.output.is_none());\n            assert!(default_execution.version.is_none());\n        }\n\n        #[test]\n        fn build_with_all_works() {\n            const EXPECTED_BIN_PATH: &str = \"C:\\\\Wix Toolset\\\\bin\";\n            const EXPECTED_CULTURE: &str = \"FrFr\";\n            const EXPECTED_COMPILER_ARGS: &str = \"-nologo\";\n            const EXPECTED_INCLUDES: &str = \"C:\\\\tmp\\\\hello_world\\\\wix\\\\main.wxs\";\n            const EXPECTED_INPUT: &str = \"C:\\\\tmp\\\\hello_world\\\\Cargo.toml\";\n            const EXPECTED_LINKER_ARGS: &str = \"-nologo\";\n            const EXPECTED_LOCALE: &str = \"C:\\\\tmp\\\\hello_world\\\\wix\\\\main.wxl\";\n            const EXPECTED_NAME: &str = \"Name\";\n            const EXPECTED_OUTPUT: &str = \"C:\\\\tmp\\\\hello_world\\\\output\";\n            const EXPECTED_VERSION: &str = \"1.2.3\";\n            let mut b = Builder::new();\n            b.bin_path(Some(EXPECTED_BIN_PATH));\n            b.capture_output(false);\n            b.culture(Some(EXPECTED_CULTURE));\n            b.compiler_args(Some(vec![EXPECTED_COMPILER_ARGS]));\n            b.debug_build(true);\n            b.debug_name(true);\n            b.includes(Some(vec![EXPECTED_INCLUDES]));\n            b.input(Some(EXPECTED_INPUT));\n            b.linker_args(Some(vec![EXPECTED_LINKER_ARGS]));\n            b.locale(Some(EXPECTED_LOCALE));\n            b.name(Some(EXPECTED_NAME));\n            b.no_build(true);\n            b.output(Some(EXPECTED_OUTPUT));\n            b.version(Some(EXPECTED_VERSION));\n            let execution = b.build();\n            assert_eq!(execution.bin_path, Some(PathBuf::from(EXPECTED_BIN_PATH)));\n            assert!(!execution.capture_output);\n            assert_eq!(\n                execution.compiler_args,\n                Some(vec![String::from(EXPECTED_COMPILER_ARGS)])\n            );\n            assert_eq!(execution.culture, Some(String::from(EXPECTED_CULTURE)));\n            assert!(execution.debug_build);\n            assert!(execution.debug_name);\n            assert_eq!(\n                execution.includes,\n                Some(vec![PathBuf::from(EXPECTED_INCLUDES)])\n            );\n            assert_eq!(execution.input, Some(PathBuf::from(EXPECTED_INPUT)));\n            assert_eq!(\n                execution.linker_args,\n                Some(vec![String::from(EXPECTED_LINKER_ARGS)])\n            );\n            assert_eq!(execution.locale, Some(PathBuf::from(EXPECTED_LOCALE)));\n            assert_eq!(execution.name, Some(String::from(EXPECTED_NAME)));\n            assert!(execution.no_build);\n            assert_eq!(execution.output, Some(String::from(EXPECTED_OUTPUT)));\n            assert_eq!(execution.version, Some(String::from(EXPECTED_VERSION)));\n        }\n    }\n\n    mod execution {\n        use super::*;\n\n        #[test]\n        fn default_profile_works() {\n            const PKG_META_WIX: &str = r#\"{\n                \"wix\": { }\n            }\"#;\n            let execution = Execution::default();\n            let profile = execution.profile(&PKG_META_WIX.parse::<Value>().unwrap());\n            assert_eq!(profile.name, \"release\");\n            assert_eq!(profile.dir, \"release\");\n        }\n\n        #[test]\n        fn debug_build_metadata_works() {\n            const PKG_META_WIX: &str = r#\"{\n                \"wix\": {\n                    \"dbg-build\": true\n                }\n            }\"#;\n            let execution = Execution::default();\n            let profile = execution.profile(&PKG_META_WIX.parse::<Value>().unwrap());\n            assert_eq!(profile.name, \"dev\");\n            assert_eq!(profile.dir, \"debug\");\n        }\n\n        #[test]\n        fn debug_name_metadata_works() {\n            const PKG_META_WIX: &str = r#\"{\n                \"wix\": {\n                    \"dbg-name\": true\n                }\n            }\"#;\n            let execution = Execution::default();\n            let debug_name = execution.debug_name(&PKG_META_WIX.parse::<Value>().unwrap());\n            assert!(debug_name);\n        }\n\n        #[test]\n        fn version_metadata_works() {\n            const PKG_META_WIX: &str = r#\"\n            {\n                \"name\": \"Example\",\n                \"version\": \"0.1.0\",\n                \"authors\": [\"First Last <first.last@example.com>\"],\n                \"license\": \"Apache-2.0\",\n\n                \"id\": \"\",\n                \"dependencies\": [],\n                \"targets\": [],\n                \"features\": {},\n                \"manifest_path\": \"\",\n\n                \"metadata\": {\n                    \"wix\": {\n                        \"version\": \"2.1.0\"\n                    }\n                }\n            }\"#;\n            let execution = Execution::default();\n            let version = execution\n                .version(&serde_json::from_str(PKG_META_WIX).unwrap())\n                .unwrap();\n            assert_eq!(version, \"2.1.0\");\n        }\n\n        #[test]\n        fn profile_metadata_works() {\n            const PKG_META_WIX: &str = r#\" {\n                \"wix\": {\n                    \"profile\": \"dist\"\n                }\n            }\"#;\n            let execution = Execution::default();\n            let profile = execution.profile(&serde_json::from_str(PKG_META_WIX).unwrap());\n            assert_eq!(profile.name, \"dist\");\n            assert_eq!(profile.dir, \"dist\");\n        }\n\n        #[test]\n        fn version_prerelease_parse_works() {\n            const PKG_META_WIX: &str = r#\"\n            {\n                \"name\": \"Example\",\n                \"version\": \"2.1.0-5\",\n                \"authors\": [\"First Last <first.last@example.com>\"],\n                \"license\": \"Apache-2.0\",\n\n                \"id\": \"\",\n                \"dependencies\": [],\n                \"targets\": [],\n                \"features\": {},\n                \"manifest_path\": \"\"\n            }\"#;\n            let execution = Execution::default();\n            let version = execution\n                .version(&serde_json::from_str(PKG_META_WIX).unwrap())\n                .unwrap();\n            assert_eq!(version, \"2.1.0.5\");\n        }\n\n        #[test]\n        fn version_prerelease_dot_parse_works() {\n            const PKG_META_WIX: &str = r#\"\n            {\n                \"name\": \"Example\",\n                \"version\": \"2.1.0-prerelease.5\",\n                \"authors\": [\"First Last <first.last@example.com>\"],\n                \"license\": \"Apache-2.0\",\n\n                \"id\": \"\",\n                \"dependencies\": [],\n                \"targets\": [],\n                \"features\": {},\n                \"manifest_path\": \"\"\n            }\"#;\n            let execution = Execution::default();\n            let version = execution\n                .version(&serde_json::from_str(PKG_META_WIX).unwrap())\n                .unwrap();\n            assert_eq!(version, \"2.1.0.5\");\n        }\n\n        #[test]\n        fn version_build_parse_works() {\n            const PKG_META_WIX: &str = r#\"\n            {\n                \"name\": \"Example\",\n                \"version\": \"2.1.0-prerelease+5\",\n                \"authors\": [\"First Last <first.last@example.com>\"],\n                \"license\": \"Apache-2.0\",\n\n                \"id\": \"\",\n                \"dependencies\": [],\n                \"targets\": [],\n                \"features\": {},\n                \"manifest_path\": \"\"\n            }\"#;\n            let execution = Execution::default();\n            let version = execution\n                .version(&serde_json::from_str(PKG_META_WIX).unwrap())\n                .unwrap();\n            assert_eq!(version, \"2.1.0.5\");\n        }\n\n        #[test]\n        fn name_metadata_works() {\n            const PKG_META_WIX: &str = r#\"{\n                \"name\": \"Example\",\n                \"version\": \"0.1.0\",\n                \"metadata\": {\n                    \"wix\": {\n                        \"name\": \"Metadata\"\n                    }\n                },\n\n                \"id\": \"\",\n                \"dependencies\": [],\n                \"targets\": [],\n                \"features\": {},\n                \"manifest_path\": \"\"\n            }\"#;\n            let execution = Execution::default();\n            let name = execution.name(&serde_json::from_str(PKG_META_WIX).unwrap());\n            assert_eq!(name, \"Metadata\".to_owned());\n        }\n\n        #[test]\n        fn no_build_metadata_works() {\n            const PKG_META_WIX: &str = r#\"{\n                \"wix\": {\n                    \"no-build\": true\n                }\n            }\"#;\n            let execution = Execution::default();\n            let no_build = execution.no_build(&PKG_META_WIX.parse::<Value>().unwrap());\n            assert!(no_build);\n        }\n\n        #[test]\n        fn culture_metadata_works() {\n            const PKG_META_WIX: &str = r#\"{\n                \"wix\": {\n                    \"culture\": \"Fr-Fr\"\n                }\n            }\"#;\n            let execution = Execution::default();\n            let culture = execution\n                .culture(&PKG_META_WIX.parse::<Value>().unwrap())\n                .unwrap();\n            assert_eq!(culture, Cultures::FrFr);\n        }\n\n        #[test]\n        fn locale_metadata_works() {\n            const PKG_META_WIX: &str = r#\"{\n                \"wix\": {\n                    \"locale\": \"wix/French.wxl\"\n                }\n            }\"#;\n            let execution = Execution::default();\n            let locale = execution\n                .locale(&PKG_META_WIX.parse::<Value>().unwrap())\n                .unwrap();\n            assert_eq!(locale, Some(PathBuf::from(\"wix/French.wxl\")));\n        }\n\n        #[test]\n        fn output_metadata_works() {\n            const PKG_META_WIX: &str = r#\"{\n                \"name\": \"Example\",\n                \"version\": \"0.1.0\",\n                \"authors\": [\"First Last <first.last@example.com>\"],\n                \"license\": \"XYZ\",\n\n                \"id\": \"\",\n                \"dependencies\": [],\n                \"targets\": [],\n                \"features\": {},\n                \"manifest_path\": \"\",\n                \"metadata\": {\n                    \"wix\": {\n                        \"output\": \"target/wix/test.msi\"\n                    }\n                }\n            }\"#;\n            let execution = Execution::default();\n            let output = execution.installer_destination(\n                \"Different\",\n                \"2.1.0\",\n                &Cfg::of(\"x86_64-pc-windows-msvc\").unwrap(),\n                false,\n                &InstallerKind::default(),\n                &serde_json::from_str(PKG_META_WIX).unwrap(),\n                Path::new(\"target/\"),\n            );\n            assert_eq!(output, PathBuf::from(\"target/wix/test.msi\"));\n        }\n\n        #[test]\n        fn include_metadata_works() {\n            const PKG_META_WIX: &str = r#\"{\n                \"name\": \"Example\",\n                \"version\": \"0.1.0\",\n                \"authors\": [\"First Last <first.last@example.com>\"],\n                \"license\": \"XYZ\",\n\n                \"id\": \"\",\n                \"dependencies\": [],\n                \"targets\": [],\n                \"features\": {},\n                \"manifest_path\": \"C:\\\\Cargo.toml\",\n                \"metadata\": {\n                    \"wix\": {\n                        \"include\": [\"Cargo.toml\"]\n                    }\n                }\n            }\"#;\n            let execution = Execution::default();\n            let sources = execution\n                .wxs_sources(&serde_json::from_str(PKG_META_WIX).unwrap())\n                .unwrap();\n            assert_eq!(sources, vec![PathBuf::from(\"Cargo.toml\")]);\n        }\n\n        #[test]\n        fn compiler_args_override_works() {\n            const PKG_META_WIX: &str = r#\"{\n                \"wix\": {\n                    \"compiler-args\": [\"-nologo\"]\n                }\n            }\"#;\n            let mut builder = Builder::default();\n            builder.compiler_args(Some(vec![\"-ws\"]));\n            let execution = builder.build();\n            let args = execution.compiler_args(&PKG_META_WIX.parse::<Value>().unwrap());\n            assert_eq!(args, Some(vec![String::from(\"-ws\")]));\n        }\n\n        #[test]\n        fn compiler_args_metadata_works() {\n            const PKG_META_WIX: &str = r#\"{\n                \"wix\": {\n                    \"compiler-args\": [\"-nologo\", \"-ws\"]\n                }\n            }\"#;\n            let execution = Execution::default();\n            let args = execution.compiler_args(&PKG_META_WIX.parse::<Value>().unwrap());\n            assert_eq!(\n                args,\n                Some(vec![String::from(\"-nologo\"), String::from(\"-ws\")])\n            );\n        }\n\n        #[test]\n        fn linker_args_override_works() {\n            const PKG_META_WIX: &str = r#\"{\n                \"wix\": {\n                    \"linker-args\": [\"-nologo\"]\n                }\n            }\"#;\n            let mut builder = Builder::default();\n            builder.linker_args(Some(vec![\"-ws\"]));\n            let execution = builder.build();\n            let args = execution.linker_args(&PKG_META_WIX.parse::<Value>().unwrap());\n            assert_eq!(args, Some(vec![String::from(\"-ws\")]));\n        }\n\n        #[test]\n        fn linker_args_metadata_works() {\n            const PKG_META_WIX: &str = r#\"{\n                \"wix\": {\n                    \"linker-args\": [\"-nologo\", \"-ws\"]\n                }\n            }\"#;\n            let execution = Execution::default();\n            let args = execution.linker_args(&PKG_META_WIX.parse::<Value>().unwrap());\n            assert_eq!(\n                args,\n                Some(vec![String::from(\"-nologo\"), String::from(\"-ws\")])\n            );\n        }\n\n        const EMPTY_PKG_META_WIX: &str = r#\"{\"wix\": {}}\"#;\n\n        #[test]\n        fn culture_works() {\n            let execution = Execution::default();\n            let culture = execution\n                .culture(&EMPTY_PKG_META_WIX.parse::<Value>().unwrap())\n                .unwrap();\n            assert_eq!(culture, Cultures::EnUs);\n        }\n\n        #[test]\n        fn locale_works() {\n            let execution = Execution::default();\n            let locale = execution\n                .locale(&EMPTY_PKG_META_WIX.parse::<Value>().unwrap())\n                .unwrap();\n            assert!(locale.is_none());\n        }\n\n        #[test]\n        fn no_build_works() {\n            let execution = Execution::default();\n            let no_build = execution.no_build(&EMPTY_PKG_META_WIX.parse::<Value>().unwrap());\n            assert!(!no_build);\n        }\n\n        #[test]\n        fn target_bin_dir_overwrite_works() {\n            const EXPECTED: &str = \"C:\\\\my-app\\\\fancy\\\\build\";\n            let mut builder = Builder::new();\n            builder.target_bin_dir(Some(EXPECTED));\n\n            let execution = builder.build();\n            let target_directory = PathBuf::from(\"C:\\\\my-app\\\\target\");\n            let target = execution.target().unwrap();\n            let profile = execution.profile(&EMPTY_PKG_META_WIX.parse::<Value>().unwrap());\n\n            let target_bin_dir = execution.target_bin_dir(&target_directory, &target, &profile);\n            assert_eq!(target_bin_dir, PathBuf::from(EXPECTED));\n        }\n\n        #[test]\n        #[cfg(windows)]\n        fn target_bin_dir_computation_works() {\n            const EXPECTED: &str = \"C:\\\\my-app\\\\target\\\\i686-pc-windows-msvc\\\\release\";\n            let mut builder = Builder::new();\n            builder.target(Some(\"i686-pc-windows-msvc\"));\n            builder.profile(Some(\"bench\"));\n            let target_directory = PathBuf::from(\"C:\\\\my-app\\\\target\");\n\n            let execution = builder.build();\n            let target = execution.target().unwrap();\n            let profile = execution.profile(&EMPTY_PKG_META_WIX.parse::<Value>().unwrap());\n\n            let target_bin_dir = execution.target_bin_dir(&target_directory, &target, &profile);\n            assert_eq!(target_bin_dir, PathBuf::from(EXPECTED));\n        }\n\n        #[test]\n        fn wixobj_destination_works() {\n            let execution = Execution::default();\n            assert_eq!(\n                execution.wixobj_destination(Path::new(\"target\")),\n                PathBuf::from(\"target\").join(\"wix\")\n            )\n        }\n    }\n\n    mod wixobj_kind {\n        use super::*;\n\n        // I do not know if any of these XML strings are actually valid WiX\n        // Object (wixobj) files. These are just snippets to test the XPath\n        // evaluation.\n\n        const PRODUCT_WIXOBJ: &str = r#\"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n            <wixObject version=\"3.0.2002.0\" xmlns=\"http://schemas.microsoft.com/wix/2006/objects\">\n                <section id=\"*\" type=\"product\"></section>\n            </wixObject>\"#;\n\n        const BUNDLE_WIXOBJ: &str = r#\"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n            <wixObject version=\"3.0.2002.0\" xmlns=\"http://schemas.microsoft.com/wix/2006/objects\">\n                <section id=\"*\" type=\"bundle\"></section>\n            </wixObject>\"#;\n\n        const FRAGMENT_WIXOBJ: &str = r#\"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n            <wixObject version=\"3.0.2002.0\" xmlns=\"http://schemas.microsoft.com/wix/2006/objects\">\n                <section id=\"*\" type=\"fragment\"></section>\n            </wixObject>\"#;\n\n        const UNKNOWN_WIXOBJ: &str = r#\"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n            <wixObject version=\"3.0.2002.0\" xmlns=\"http://schemas.microsoft.com/wix/2006/objects\">\n                <section id=\"*\" type=\"unknown\"></section>\n            </wixObject>\"#;\n\n        const BUNDLE_AND_PRODUCT_WIXOBJ: &str = r#\"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n            <wixObject version=\"3.0.2002.0\" xmlns=\"http://schemas.microsoft.com/wix/2006/objects\">\n                <section id=\"*\" type=\"bundle\"></section>\n                <section id=\"*\" type=\"product\"></section>\n            </wixObject>\"#;\n\n        const PRODUCT_AND_FRAGMENT_WIXOBJ: &str = r#\"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n            <wixObject version=\"3.0.2002.0\" xmlns=\"http://schemas.microsoft.com/wix/2006/objects\">\n                <section id=\"*\" type=\"product\"></section>\n                <section id=\"*\" type=\"fragment\"></section>\n            </wixObject>\"#;\n\n        #[test]\n        fn try_from_product_object_works() {\n            assert_eq!(\n                WixObjKind::try_from(PRODUCT_WIXOBJ),\n                Ok(WixObjKind::Product)\n            );\n        }\n\n        #[test]\n        fn try_from_bundle_object_works() {\n            assert_eq!(WixObjKind::try_from(BUNDLE_WIXOBJ), Ok(WixObjKind::Bundle));\n        }\n\n        #[test]\n        fn try_from_fragment_object_works() {\n            assert_eq!(\n                WixObjKind::try_from(FRAGMENT_WIXOBJ),\n                Ok(WixObjKind::Fragment)\n            );\n        }\n\n        #[test]\n        fn try_from_bundle_and_product_object_works() {\n            assert_eq!(\n                WixObjKind::try_from(BUNDLE_AND_PRODUCT_WIXOBJ),\n                Ok(WixObjKind::Bundle)\n            );\n        }\n\n        #[test]\n        fn try_from_product_and_fragment_object_works() {\n            assert_eq!(\n                WixObjKind::try_from(PRODUCT_AND_FRAGMENT_WIXOBJ),\n                Ok(WixObjKind::Product)\n            );\n        }\n\n        #[test]\n        #[should_panic]\n        fn try_from_unknown_object_fails() {\n            WixObjKind::try_from(UNKNOWN_WIXOBJ).unwrap();\n        }\n    }\n\n    mod installer_kind {\n        use super::*;\n\n        #[test]\n        fn try_from_wixobj_single_product_works() {\n            assert_eq!(\n                InstallerKind::try_from(vec![WixObjKind::Product]),\n                Ok(InstallerKind::Msi)\n            )\n        }\n\n        #[test]\n        fn try_from_wixobj_single_bundle_works() {\n            assert_eq!(\n                InstallerKind::try_from(vec![WixObjKind::Bundle]),\n                Ok(InstallerKind::Exe)\n            )\n        }\n\n        #[test]\n        #[should_panic]\n        fn try_from_wixobj_single_fragment_fails() {\n            InstallerKind::try_from(vec![WixObjKind::Fragment]).unwrap();\n        }\n\n        #[test]\n        #[should_panic]\n        fn try_from_wixobj_multiple_fragments_fails() {\n            InstallerKind::try_from(vec![\n                WixObjKind::Fragment,\n                WixObjKind::Fragment,\n                WixObjKind::Fragment,\n            ])\n            .unwrap();\n        }\n\n        #[test]\n        fn try_from_wixobj_product_and_bundle_works() {\n            assert_eq!(\n                InstallerKind::try_from(vec![WixObjKind::Product, WixObjKind::Bundle]),\n                Ok(InstallerKind::Exe)\n            )\n        }\n\n        #[test]\n        fn try_from_wixobj_multiple_products_and_single_bundle_works() {\n            assert_eq!(\n                InstallerKind::try_from(vec![\n                    WixObjKind::Product,\n                    WixObjKind::Product,\n                    WixObjKind::Bundle,\n                    WixObjKind::Product\n                ]),\n                Ok(InstallerKind::Exe)\n            )\n        }\n\n        #[test]\n        fn try_from_wixobj_multiple_fragments_and_single_product_works() {\n            assert_eq!(\n                InstallerKind::try_from(vec![\n                    WixObjKind::Fragment,\n                    WixObjKind::Fragment,\n                    WixObjKind::Product,\n                    WixObjKind::Fragment\n                ]),\n                Ok(InstallerKind::Msi)\n            )\n        }\n\n        #[test]\n        fn try_from_wixobj_multiple_fragments_and_single_bundle_works() {\n            assert_eq!(\n                InstallerKind::try_from(vec![\n                    WixObjKind::Fragment,\n                    WixObjKind::Fragment,\n                    WixObjKind::Bundle,\n                    WixObjKind::Fragment\n                ]),\n                Ok(InstallerKind::Exe)\n            )\n        }\n\n        #[test]\n        fn try_from_wixobj_multiple_fragments_and_products_works() {\n            assert_eq!(\n                InstallerKind::try_from(vec![\n                    WixObjKind::Fragment,\n                    WixObjKind::Fragment,\n                    WixObjKind::Product,\n                    WixObjKind::Fragment,\n                    WixObjKind::Product\n                ]),\n                Ok(InstallerKind::Msi)\n            )\n        }\n\n        #[test]\n        fn try_from_wixobj_multiple_products_and_bundles_works() {\n            assert_eq!(\n                InstallerKind::try_from(vec![\n                    WixObjKind::Product,\n                    WixObjKind::Product,\n                    WixObjKind::Bundle,\n                    WixObjKind::Product,\n                    WixObjKind::Bundle,\n                    WixObjKind::Product\n                ]),\n                Ok(InstallerKind::Exe)\n            )\n        }\n\n        #[test]\n        fn try_from_wixobj_multiple_products_fragments_and_single_bundle_works() {\n            assert_eq!(\n                InstallerKind::try_from(vec![\n                    WixObjKind::Product,\n                    WixObjKind::Product,\n                    WixObjKind::Bundle,\n                    WixObjKind::Fragment,\n                    WixObjKind::Fragment,\n                    WixObjKind::Product\n                ]),\n                Ok(InstallerKind::Exe)\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "src/initialize.rs",
    "content": "// Copyright (C) 2018 Christopher R. Field.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//! The implementation for the `init` command. The `init` command for the `cargo\n//! wix` subcommand is focused on creating a WiX Source file (wxs) based on the\n//! contents of the Cargo manifest file (Cargo.toml) for the project and any\n//! run-time based settings.\n//!\n//! The `init` command should generally be called before any other commands and\n//! it should only be called once per project. Once a WiX Source file (wxs)\n//! exists for the project, the `init` command does not need to be executed\n//! again.\n\nuse camino::Utf8PathBuf;\nuse cargo_metadata::Package;\n\nuse crate::Error;\nuse crate::Result;\nuse crate::WIX;\nuse crate::WIX_SOURCE_FILE_EXTENSION;\nuse crate::WIX_SOURCE_FILE_NAME;\nuse crate::print;\nuse crate::stored_path::StoredPathBuf;\nuse crate::toolset::project::WxsSchema;\n\nuse log::{debug, info, trace};\n\nuse std::fs;\nuse std::path::{Path, PathBuf};\n\n/// A builder for running the `cargo wix init` subcommand.\n#[derive(Debug, Clone)]\npub struct Builder<'a> {\n    banner: Option<&'a str>,\n    binaries: Option<Vec<&'a str>>,\n    copyright_year: Option<&'a str>,\n    copyright_holder: Option<&'a str>,\n    description: Option<&'a str>,\n    dialog: Option<&'a str>,\n    eula: Option<&'a str>,\n    force: bool,\n    help_url: Option<&'a str>,\n    input: Option<&'a str>,\n    license: Option<&'a str>,\n    manufacturer: Option<&'a str>,\n    output: Option<&'a str>,\n    package: Option<&'a str>,\n    path_guid: Option<&'a str>,\n    product_icon: Option<&'a str>,\n    product_name: Option<&'a str>,\n    upgrade_guid: Option<&'a str>,\n    schema: Option<WxsSchema>,\n}\n\nimpl<'a> Builder<'a> {\n    /// Creates a new `Builder` instance.\n    pub fn new() -> Self {\n        Builder {\n            banner: None,\n            binaries: None,\n            copyright_year: None,\n            copyright_holder: None,\n            description: None,\n            dialog: None,\n            eula: None,\n            force: false,\n            help_url: None,\n            input: None,\n            license: None,\n            manufacturer: None,\n            output: None,\n            package: None,\n            path_guid: None,\n            product_icon: None,\n            product_name: None,\n            upgrade_guid: None,\n            schema: None,\n        }\n    }\n\n    /// Sets the path to a bitmap (BMP) file to be used as a banner image across\n    /// the top of each dialog in the installer.\n    ///\n    /// The banner image must be 493 x 58 pixels. See the [Wix Toolset\n    /// documentation] for details about [customization].\n    ///\n    /// [Wix Toolset documentation]: http://wixtoolset.org/documentation/\n    /// [customization]: http://wixtoolset.org/documentation/manual/v3/wixui/wixui_customizations.html\n    pub fn banner(&mut self, b: Option<&'a str>) -> &mut Self {\n        self.banner = b;\n        self\n    }\n\n    /// Sets the path to the binaries.\n    ///\n    /// The default is to, first, collect all of the `bin` sections and use the\n    /// `name` field within each `bin` section of the package's manifest for\n    /// each binary's name and create the following source with the `.exe` file\n    /// extension: `target\\release\\<binary-name>.exe`, where `<binary-name>` is\n    /// replaced with the name obtained from each `bin` section. All binaries\n    /// are included in the installer. If no `bin` sections exist, then the\n    /// package's `name` field is used and only one binary is included in the\n    /// installer.\n    ///\n    /// This method skips creating the binary names and sources from the\n    /// package's manifest (Cargo.toml) and uses the supplied paths, regardless\n    /// of the number of `bin` sections in the package's manifest. The binary\n    /// name is extracted from each supplied path as the file stem (file name\n    /// without extension).\n    ///\n    /// This method is useful for including binaries, a.k.a. executables, in the\n    /// installer that are necessary for the application to run but are not\n    /// necessarily Rust/Cargo built binaries. However, this method overrides\n    /// _all_ binaries in the Cargo-based project, so if the installer is to\n    /// include a mixture of external and internal binaries, the internal\n    /// binaries must be explicitly included in this method.\n    pub fn binaries(&mut self, b: Option<Vec<&'a str>>) -> &mut Self {\n        self.binaries = b;\n        self\n    }\n\n    /// Sets the copyright holder for the generated license file and EULA.\n    ///\n    /// The default is to use the `authors` field of the\n    /// package's manifest (Cargo.toml). This method can be used to override the\n    /// default and set a different copyright holder if and when a Rich Text\n    /// Format (RTF) license and EULA are generated based on the value of the\n    /// `license` field in the package's manifest (Cargo.toml).\n    ///\n    /// This value is ignored and not used if an EULA is set with the [`eula`]\n    /// method, if a custom EULA is set using the `license-file` field in the\n    /// package's manifest (Cargo.toml), or an EULA is _not_ generated from the\n    /// `license` field in the package's manifest (Cargo.toml).\n    ///\n    /// [`eula`]: https://volks73.github.io/cargo-wix/cargo_wix/initialize.html#eula\n    pub fn copyright_holder(&mut self, h: Option<&'a str>) -> &mut Self {\n        self.copyright_holder = h;\n        self\n    }\n\n    /// Sets the copyright year for the generated license file and EULA.\n    ///\n    /// The default is to use the current year. This method can be used to\n    /// override the default and set a specific year if and when a Rich Text\n    /// Format (RTF) license and EULA are generated based on the value of the\n    /// `license` field in the package's manifest (Cargo.toml).\n    ///\n    /// This value is ignored and not used if an EULA is set with the [`eula`]\n    /// method, if a custom EULA is set using the `license-file` field in the\n    /// package's manifest (Cargo.toml), or an EULA is _not_ generated from the\n    /// `license` field in the package's manifest (Cargo.toml).\n    ///\n    /// [`eula`]: https://volks73.github.io/cargo-wix/cargo_wix/initialize.html#eula\n    pub fn copyright_year(&mut self, y: Option<&'a str>) -> &mut Self {\n        self.copyright_year = y;\n        self\n    }\n\n    /// Sets the description.\n    ///\n    /// This overrides the description determined from the `description` field\n    /// in the package's manifest (Cargo.toml).\n    pub fn description(&mut self, d: Option<&'a str>) -> &mut Self {\n        self.description = d;\n        self\n    }\n\n    /// Sets the path to a bitmap (`.bmp`) file that will be displayed on the\n    /// first dialog to the left.\n    ///\n    /// The image must be 493 x 312 pixels. See the [Wix Toolset\n    /// documentation] for details about [customization].\n    ///\n    /// [Wix Toolset documentation]: http://wixtoolset.org/documentation/\n    /// [customization]: http://wixtoolset.org/documentation/manual/v3/wixui/wixui_customizations.html\n    pub fn dialog(&mut self, d: Option<&'a str>) -> &mut Self {\n        self.dialog = d;\n        self\n    }\n\n    /// Sets the path to a custom End User License Agreement (EULA).\n    ///\n    /// The EULA is the text that appears in the license agreement dialog of the\n    /// installer, where a checkbox is present for the user to agree to the\n    /// terms of the license. Typically, this is the same as the license file\n    /// that is included as a [sidecar] file in the installation destination of\n    /// the executable.\n    ///\n    /// The default is to generate an EULA from an embedded template as a RTF\n    /// file based on the name of the license specified in the `license` field\n    /// of the package's manifest (Cargo.toml). This method can be used to\n    /// override the default and specify a custom EULA. A custom EULA must be in\n    /// the RTF format and have the `.rtf` file extension.\n    ///\n    /// If the `license` field is not specified or a template for the license\n    /// does not exist but the `license-file` field does specify a path to a\n    /// file with the RTF extension, then that RTF file is used as the EULA for\n    /// the license agreement dialog in the installer. Finally, if the\n    /// `license-file` does not exist or it specifies a file that does not have\n    /// the `.rtf` extension, then the license agreement dialog is skipped and\n    /// there is no EULA for the installer. This would override the default\n    /// behavior and ensure the license agreement dialog is used.\n    ///\n    /// [sidecar]: https://en.wikipedia.org/wiki/Sidecar_file\n    pub fn eula(&mut self, e: Option<&'a str>) -> &mut Self {\n        self.eula = e;\n        self\n    }\n\n    /// Forces the generation of new output even if the various outputs already\n    /// exists at the destination.\n    pub fn force(&mut self, f: bool) -> &mut Self {\n        self.force = f;\n        self\n    }\n\n    /// Sets the help URL.\n    ///\n    /// The default is to obtain a URL from one of the following fields in the\n    /// package's manifest (Cargo.toml): `documentation`, `homepage`, or\n    /// `repository`. If none of these are specified, then the default is to\n    /// exclude a help URL from the installer. This will override the default\n    /// behavior and provide a help URL for the installer if none of the fields\n    /// exist.\n    ///\n    /// The help URL is the URL that appears in the Add/Remove Program control\n    /// panel, a.k.a. `ARPHELPLINK`.\n    pub fn help_url(&mut self, h: Option<&'a str>) -> &mut Self {\n        self.help_url = h;\n        self\n    }\n\n    /// Sets the path to a package's manifest (Cargo.toml) to be used to\n    /// generate a WiX Source (wxs) file from the embedded template.\n    ///\n    /// A `wix` and `wix\\main.wxs` file will be created in the same directory as\n    /// the package's manifest. The default is to use the package's manifest in\n    /// the current working directory.\n    pub fn input(&mut self, i: Option<&'a str>) -> &mut Self {\n        self.input = i;\n        self\n    }\n\n    /// Sets the path to a file to be used as the [sidecar] license file.\n    ///\n    /// This will override the `license-file` field in the package's manifest\n    /// (Cargo.toml). If the file has the `.rtf` extension, then it will also be\n    /// used for the EULA in the license agreement dialog for the installer.\n    /// Otherwise, the [`eula`] method can be used to set an RTF file as the\n    /// EULA for the license agreement dialog that is independent of the sidecar\n    /// license file.\n    ///\n    /// The default is to use the value specified in the `license-file` field of\n    /// the package's manifest or generate a license file and EULA from an\n    /// embedded template based on the license ID used in the `license` field\n    /// of the package's manifest. If none of these fields are specified or\n    /// overridden, then a license file is _not_ included in the installation\n    /// directory and the license agreement dialog is skipped in the installer.\n    ///\n    /// [sidecar]: https://en.wikipedia.org/wiki/Sidecar_file\n    /// [`eula`]: #eula\n    pub fn license(&mut self, l: Option<&'a str>) -> &mut Self {\n        self.license = l;\n        self\n    }\n\n    /// Sets the manufacturer.\n    ///\n    /// Default is to use the `authors` field of the\n    /// package's manifest (Cargo.toml). This would override the default value.\n    pub fn manufacturer(&mut self, m: Option<&'a str>) -> &mut Self {\n        self.manufacturer = m;\n        self\n    }\n\n    /// Sets the destination for creating all of the output from initialization.\n    ///\n    /// The default is to create all initialization output in the same folder as\n    /// the package's manifest (Cargo.toml). Thus, a `wix` folder will be\n    /// created within the same folder as the `Cargo.toml` file and all\n    /// initialization created files will be placed in the `wix` folder.\n    ///\n    /// This method can be used to override the default output destination and\n    /// have the files related to creating an installer placed in a different\n    /// location inside or outside of the package's project folder.\n    pub fn output(&mut self, o: Option<&'a str>) -> &mut Self {\n        self.output = o;\n        self\n    }\n\n    /// Sets the path to an image file to be used for product icon.\n    ///\n    /// The product icon is the icon that appears for an installed application\n    /// in the Add/Remove Programs (ARP) control panel. If a product icon is\n    /// _not_ defined for an application within the installer, then the Windows\n    /// OS assigns a generic one.\n    pub fn product_icon(&mut self, p: Option<&'a str>) -> &mut Self {\n        self.product_icon = p;\n        self\n    }\n\n    /// Sets the package within a workspace to initialize an installer.\n    ///\n    /// Each package within a workspace has its own package manifest, i.e.\n    /// `Cargo.toml`. This indicates within package manifest within a workspace\n    /// should be used when initializing an installer.\n    pub fn package(&mut self, p: Option<&'a str>) -> &mut Self {\n        self.package = p;\n        self\n    }\n\n    /// Sets the GUID for the path component.\n    ///\n    /// The default automatically generates the GUID needed for the path\n    /// component. A GUID is needed so that the path component can be\n    /// successfully removed on uninstall.\n    ///\n    /// Generally, the path component GUID should be generated only once per\n    /// project/product and then the same GUID used every time the installer is\n    /// created. The GUID is stored in the WiX Source (WXS) file. However,\n    /// this allows using an existing GUID, possibly obtained with another tool.\n    pub fn path_guid(&mut self, p: Option<&'a str>) -> &mut Self {\n        self.path_guid = p;\n        self\n    }\n\n    /// Sets the product name.\n    ///\n    /// The default is to use the `name` field under the `package` section of\n    /// the package's manifest (Cargo.toml). This overrides that value. An error\n    /// occurs if the `name` field is not found in the manifest.\n    ///\n    /// The product name is also used for the disk prompt during installation\n    /// and the name of the default installation destination. For example, a\n    /// product anme of `Example` will have an installation destination of\n    /// `C:\\Program Files\\Example` as the default during installation.\n    pub fn product_name(&mut self, p: Option<&'a str>) -> &mut Self {\n        self.product_name = p;\n        self\n    }\n\n    /// Sets the Upgrade Code GUID.\n    ///\n    /// The default automatically generates the need GUID for the `UpgradeCode`\n    /// attribute to the `Product` tag. The Upgrade Code uniquely identifies the\n    /// installer. It is used to determine if the new installer is the same\n    /// product and the current installation should be removed and upgraded to\n    /// this version. If the GUIDs of the current product and new product do\n    /// _not_ match, then Windows will treat the two installers as separate\n    /// products.\n    ///\n    /// Generally, the upgrade code should be generated only one per\n    /// project/product and then the same code used every time the installer is\n    /// created and the GUID is stored in the WiX Source (WXS) file. However,\n    /// this allows the user to provide an existing GUID for the upgrade code.\n    pub fn upgrade_guid(&mut self, u: Option<&'a str>) -> &mut Self {\n        self.upgrade_guid = u;\n        self\n    }\n\n    /// Sets the Wxs Schema\n    ///\n    /// Wix3 follows the \"v3\" schema, where as Wix4 and beyond use the \"Modern\" schema uri.\n    pub fn schema(&mut self, schema: Option<WxsSchema>) -> &mut Self {\n        self.schema = schema;\n        self\n    }\n\n    /// Builds a read-only initialization execution.\n    pub fn build(&mut self) -> Execution {\n        Execution {\n            banner: self.banner.map(StoredPathBuf::from),\n            binaries: self\n                .binaries\n                .as_ref()\n                .map(|b| b.iter().copied().map(StoredPathBuf::from).collect()),\n            copyright_year: self.copyright_year.map(String::from),\n            copyright_holder: self.copyright_holder.map(String::from),\n            description: self.description.map(String::from),\n            dialog: self.dialog.map(StoredPathBuf::from),\n            eula: self.eula.map(StoredPathBuf::from),\n            force: self.force,\n            help_url: self.help_url.map(String::from),\n            input: self.input.map(PathBuf::from),\n            license: self.license.map(StoredPathBuf::from),\n            manufacturer: self.manufacturer.map(String::from),\n            output: self.output.map(PathBuf::from),\n            package: self.package.map(String::from),\n            path_guid: self.path_guid.map(String::from),\n            product_icon: self.product_icon.map(StoredPathBuf::from),\n            product_name: self.product_name.map(String::from),\n            upgrade_guid: self.upgrade_guid.map(String::from),\n            schema: self.schema.unwrap_or(WxsSchema::Legacy),\n        }\n    }\n}\n\nimpl Default for Builder<'_> {\n    fn default() -> Self {\n        Builder::new()\n    }\n}\n\n/// A context for creating the necessary files to eventually build an installer.\n#[derive(Debug)]\npub struct Execution {\n    banner: Option<StoredPathBuf>,\n    binaries: Option<Vec<StoredPathBuf>>,\n    copyright_holder: Option<String>,\n    copyright_year: Option<String>,\n    description: Option<String>,\n    dialog: Option<StoredPathBuf>,\n    eula: Option<StoredPathBuf>,\n    force: bool,\n    help_url: Option<String>,\n    input: Option<PathBuf>,\n    license: Option<StoredPathBuf>,\n    manufacturer: Option<String>,\n    output: Option<PathBuf>,\n    package: Option<String>,\n    path_guid: Option<String>,\n    product_icon: Option<StoredPathBuf>,\n    product_name: Option<String>,\n    upgrade_guid: Option<String>,\n    schema: WxsSchema,\n}\n\nimpl Execution {\n    /// Generates the necessary files to eventually create, or build, an\n    /// installer based on a built context.\n    pub fn run(self) -> Result<()> {\n        debug!(\"banner = {:?}\", self.banner);\n        debug!(\"binaries = {:?}\", self.binaries);\n        debug!(\"copyright_holder = {:?}\", self.copyright_holder);\n        debug!(\"copyright_year = {:?}\", self.copyright_year);\n        debug!(\"description = {:?}\", self.description);\n        debug!(\"dialog = {:?}\", self.dialog);\n        debug!(\"eula = {:?}\", self.eula);\n        debug!(\"force = {:?}\", self.force);\n        debug!(\"help_url = {:?}\", self.help_url);\n        debug!(\"input = {:?}\", self.input);\n        debug!(\"license = {:?}\", self.license);\n        debug!(\"manufacturer = {:?}\", self.manufacturer);\n        debug!(\"output = {:?}\", self.output);\n        debug!(\"package = {:?}\", self.package);\n        debug!(\"path_guid = {:?}\", self.path_guid);\n        debug!(\"product_icon = {:?}\", self.product_icon);\n        debug!(\"product_name = {:?}\", self.product_name);\n        debug!(\"upgrade_guid = {:?}\", self.upgrade_guid);\n        let manifest = super::manifest(self.input.as_ref())?;\n        let package = super::package(&manifest, self.package.as_deref())?;\n        let mut destination = self.destination(&package);\n        debug!(\"destination = {:?}\", destination);\n        if !destination.exists() {\n            info!(\"Creating the '{}' directory\", destination);\n            fs::create_dir(&destination)?;\n        }\n\n        destination.push(WIX_SOURCE_FILE_NAME);\n        destination.set_extension(WIX_SOURCE_FILE_EXTENSION);\n        if destination.exists() && !self.force {\n            return Err(Error::already_exists(&destination));\n        } else {\n            info!(\"Creating the '{}' file\", destination);\n            let mut wxs_printer = print::wxs::Builder::new();\n            wxs_printer.banner(self.banner.as_ref().map(|s| s.as_str()));\n            wxs_printer.binaries(\n                self.binaries\n                    .as_ref()\n                    .map(|b| b.iter().map(|s| s.as_str()).collect()),\n            );\n            wxs_printer.description(self.description.as_ref().map(String::as_ref));\n            wxs_printer.dialog(self.dialog.as_deref().map(|s| s.as_str()));\n            wxs_printer.eula(self.eula.as_deref().map(|p| p.as_str()));\n            wxs_printer.help_url(self.help_url.as_ref().map(String::as_ref));\n            wxs_printer.input(self.input.as_deref().and_then(Path::to_str));\n            wxs_printer.license(self.license.as_deref().map(|p| p.as_str()));\n            wxs_printer.manufacturer(self.manufacturer.as_ref().map(String::as_ref));\n            wxs_printer.output(Some(destination.as_str()));\n            wxs_printer.package(self.package.as_deref());\n            wxs_printer.path_guid(self.path_guid.as_ref().map(String::as_ref));\n            wxs_printer.product_icon(self.product_icon.as_ref().map(|s| s.as_str()));\n            wxs_printer.product_name(self.product_name.as_ref().map(String::as_ref));\n            wxs_printer.upgrade_guid(self.upgrade_guid.as_ref().map(String::as_ref));\n            wxs_printer.schema(Some(self.schema));\n\n            wxs_printer.build().run()?;\n        }\n        Ok(())\n    }\n\n    fn destination(&self, package: &Package) -> Utf8PathBuf {\n        if let Some(output) = &self.output {\n            trace!(\"An output path has been explicitly specified\");\n            Utf8PathBuf::from_path_buf(output.to_owned()).unwrap()\n        } else {\n            trace!(\n                \"An output path has NOT been explicitly specified. Implicitly determine output from manifest location.\"\n            );\n            package\n                .manifest_path\n                .parent()\n                .map(|p| p.to_path_buf())\n                .map(|mut p| {\n                    p.push(WIX);\n                    p\n                })\n                .unwrap()\n        }\n    }\n}\n\nimpl Default for Execution {\n    fn default() -> Self {\n        Builder::new().build()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    mod builder {\n        use super::*;\n\n        const PATH_GUID: &str = \"0B5DFC00-1480-4044-AC1A-BEF00E0A91BB\";\n        const UPGRADE_GUID: &str = \"0631BBDF-4079-4C20-823F-7EA8DE40BF08\";\n\n        #[test]\n        fn defaults_are_correct() {\n            let actual = Builder::new();\n            assert!(actual.banner.is_none());\n            assert!(actual.binaries.is_none());\n            assert!(actual.copyright_year.is_none());\n            assert!(actual.copyright_holder.is_none());\n            assert!(actual.description.is_none());\n            assert!(actual.dialog.is_none());\n            assert!(actual.eula.is_none());\n            assert!(!actual.force);\n            assert!(actual.help_url.is_none());\n            assert!(actual.input.is_none());\n            assert!(actual.license.is_none());\n            assert!(actual.manufacturer.is_none());\n            assert!(actual.output.is_none());\n            assert!(actual.path_guid.is_none());\n            assert!(actual.product_icon.is_none());\n            assert!(actual.product_name.is_none());\n            assert!(actual.upgrade_guid.is_none());\n            assert!(actual.schema.is_none());\n        }\n\n        #[test]\n        fn banner_works() {\n            const EXPECTED: &str = \"img\\\\Banner.bmp\";\n            let mut actual = Builder::new();\n            actual.banner(Some(EXPECTED));\n            assert_eq!(actual.banner, Some(EXPECTED));\n        }\n\n        #[test]\n        fn binaries_works() {\n            const EXPECTED: &str = \"bin\\\\Example.exe\";\n            let mut actual = Builder::new();\n            actual.binaries(Some(vec![EXPECTED]));\n            assert_eq!(actual.binaries, Some(vec![EXPECTED]));\n        }\n\n        #[test]\n        fn copyright_holder_works() {\n            const EXPECTED: &str = \"holder\";\n            let mut actual = Builder::new();\n            actual.copyright_holder(Some(EXPECTED));\n            assert_eq!(actual.copyright_holder, Some(EXPECTED));\n        }\n\n        #[test]\n        fn copyright_year_works() {\n            const EXPECTED: &str = \"2018\";\n            let mut actual = Builder::new();\n            actual.copyright_year(Some(EXPECTED));\n            assert_eq!(actual.copyright_year, Some(EXPECTED));\n        }\n\n        #[test]\n        fn description_works() {\n            const EXPECTED: &str = \"description\";\n            let mut actual = Builder::new();\n            actual.description(Some(EXPECTED));\n            assert_eq!(actual.description, Some(EXPECTED));\n        }\n\n        #[test]\n        fn dialog_works() {\n            const EXPECTED: &str = \"img\\\\Dialog.bmp\";\n            let mut actual = Builder::new();\n            actual.dialog(Some(EXPECTED));\n            assert_eq!(actual.dialog, Some(EXPECTED));\n        }\n\n        #[test]\n        fn eula_works() {\n            const EXPECTED: &str = \"eula.rtf\";\n            let mut actual = Builder::new();\n            actual.eula(Some(EXPECTED));\n            assert_eq!(actual.eula, Some(EXPECTED));\n        }\n\n        #[test]\n        fn force_works() {\n            let mut actual = Builder::new();\n            actual.force(true);\n            assert!(actual.force);\n        }\n\n        #[test]\n        fn help_url_works() {\n            const EXPECTED: &str = \"http://github.com/volks73/cargo-wix\";\n            let mut actual = Builder::new();\n            actual.help_url(Some(EXPECTED));\n            assert_eq!(actual.help_url, Some(EXPECTED));\n        }\n\n        #[test]\n        fn input_works() {\n            const EXPECTED: &str = \"input.wxs\";\n            let mut actual = Builder::new();\n            actual.input(Some(EXPECTED));\n            assert_eq!(actual.input, Some(EXPECTED));\n        }\n\n        #[test]\n        fn license_works() {\n            const EXPECTED: &str = \"License.txt\";\n            let mut actual = Builder::new();\n            actual.license(Some(EXPECTED));\n            assert_eq!(actual.license, Some(EXPECTED));\n        }\n\n        #[test]\n        fn manufacturer_works() {\n            const EXPECTED: &str = \"manufacturer\";\n            let mut actual = Builder::new();\n            actual.manufacturer(Some(EXPECTED));\n            assert_eq!(actual.manufacturer, Some(EXPECTED));\n        }\n\n        #[test]\n        fn output_works() {\n            const EXPECTED: &str = \"output\";\n            let mut actual = Builder::new();\n            actual.output(Some(EXPECTED));\n            assert_eq!(actual.output, Some(EXPECTED));\n        }\n\n        #[test]\n        fn path_guid_works() {\n            let mut actual = Builder::new();\n            actual.path_guid(Some(PATH_GUID));\n            assert_eq!(actual.path_guid, Some(PATH_GUID));\n        }\n\n        #[test]\n        fn product_icon_works() {\n            const EXPECTED: &str = \"img\\\\Product.ico\";\n            let mut actual = Builder::new();\n            actual.product_icon(Some(EXPECTED));\n            assert_eq!(actual.product_icon, Some(EXPECTED));\n        }\n\n        #[test]\n        fn product_name_works() {\n            const EXPECTED: &str = \"product name\";\n            let mut actual = Builder::new();\n            actual.product_name(Some(EXPECTED));\n            assert_eq!(actual.product_name, Some(EXPECTED));\n        }\n\n        #[test]\n        fn upgrade_guid_works() {\n            let mut actual = Builder::new();\n            actual.upgrade_guid(Some(UPGRADE_GUID));\n            assert_eq!(actual.upgrade_guid, Some(UPGRADE_GUID));\n        }\n\n        #[test]\n        fn schema_works() {\n            use crate::toolset::project::WxsSchema;\n            let mut actual = Builder::new();\n            actual.schema(Some(WxsSchema::V4));\n            assert_eq!(actual.schema, Some(WxsSchema::V4));\n        }\n\n        #[test]\n        fn schema_defaults_to_legacy_in_execution() {\n            use crate::toolset::project::WxsSchema;\n            let mut b = Builder::new();\n            let execution = b.build();\n            assert!(matches!(execution.schema, WxsSchema::Legacy));\n        }\n\n        #[test]\n        fn build_with_defaults_works() {\n            let mut b = Builder::new();\n            let default_execution = b.build();\n            assert!(default_execution.binaries.is_none());\n            assert!(default_execution.copyright_year.is_none());\n            assert!(default_execution.copyright_holder.is_none());\n            assert!(default_execution.description.is_none());\n            assert!(default_execution.eula.is_none());\n            assert!(!default_execution.force);\n            assert!(default_execution.help_url.is_none());\n            assert!(default_execution.input.is_none());\n            assert!(default_execution.license.is_none());\n            assert!(default_execution.manufacturer.is_none());\n            assert!(default_execution.output.is_none());\n            assert!(default_execution.product_icon.is_none());\n            assert!(default_execution.product_name.is_none());\n            assert!(default_execution.upgrade_guid.is_none());\n            assert!(matches!(\n                default_execution.schema,\n                crate::toolset::project::WxsSchema::Legacy\n            ));\n        }\n\n        #[test]\n        fn build_with_all_works() {\n            const EXPECTED_BINARY: &str = \"bin\\\\Example.exe\";\n            const EXPECTED_COPYRIGHT_HOLDER: &str = \"Copyright Holder\";\n            const EXPECTED_COPYRIGHT_YEAR: &str = \"Copyright Year\";\n            const EXPECTED_DESCRIPTION: &str = \"Description\";\n            const EXPECTED_EULA: &str = \"C:\\\\tmp\\\\eula.rtf\";\n            const EXPECTED_URL: &str = \"http://github.com/volks73/cargo-wix\";\n            const EXPECTED_INPUT: &str = \"C:\\\\tmp\\\\hello_world\";\n            const EXPECTED_LICENSE: &str = \"C:\\\\tmp\\\\hello_world\\\\License.rtf\";\n            const EXPECTED_MANUFACTURER: &str = \"Manufacturer\";\n            const EXPECTED_OUTPUT: &str = \"C:\\\\tmp\\\\output\";\n            const EXPECTED_PRODUCT_ICON: &str = \"img\\\\Product.ico\";\n            const EXPECTED_PRODUCT_NAME: &str = \"Product Name\";\n            let mut b = Builder::new();\n            b.binaries(Some(vec![EXPECTED_BINARY]));\n            b.copyright_holder(Some(EXPECTED_COPYRIGHT_HOLDER));\n            b.copyright_year(Some(EXPECTED_COPYRIGHT_YEAR));\n            b.description(Some(EXPECTED_DESCRIPTION));\n            b.eula(Some(EXPECTED_EULA));\n            b.force(true);\n            b.help_url(Some(EXPECTED_URL));\n            b.input(Some(EXPECTED_INPUT));\n            b.license(Some(EXPECTED_LICENSE));\n            b.manufacturer(Some(EXPECTED_MANUFACTURER));\n            b.output(Some(EXPECTED_OUTPUT));\n            b.path_guid(Some(PATH_GUID));\n            b.product_icon(Some(EXPECTED_PRODUCT_ICON));\n            b.product_name(Some(EXPECTED_PRODUCT_NAME));\n            b.upgrade_guid(Some(UPGRADE_GUID));\n            let execution = b.build();\n            assert_eq!(\n                execution.binaries,\n                Some(vec![EXPECTED_BINARY])\n                    .map(|s| s.into_iter().map(StoredPathBuf::from).collect())\n            );\n            assert_eq!(\n                execution.copyright_year,\n                Some(String::from(EXPECTED_COPYRIGHT_YEAR))\n            );\n            assert_eq!(\n                execution.copyright_holder,\n                Some(String::from(EXPECTED_COPYRIGHT_HOLDER))\n            );\n            assert_eq!(\n                execution.description,\n                Some(String::from(EXPECTED_DESCRIPTION))\n            );\n            assert_eq!(execution.eula, Some(StoredPathBuf::from(EXPECTED_EULA)));\n            assert!(execution.force);\n            assert_eq!(execution.help_url, Some(String::from(EXPECTED_URL)));\n            assert_eq!(execution.input, Some(PathBuf::from(EXPECTED_INPUT)));\n            assert_eq!(\n                execution.license,\n                Some(StoredPathBuf::from(EXPECTED_LICENSE))\n            );\n            assert_eq!(\n                execution.manufacturer,\n                Some(String::from(EXPECTED_MANUFACTURER))\n            );\n            assert_eq!(execution.output, Some(PathBuf::from(EXPECTED_OUTPUT)));\n            assert_eq!(execution.path_guid, Some(String::from(PATH_GUID)));\n            assert_eq!(\n                execution.product_icon,\n                Some(StoredPathBuf::from(EXPECTED_PRODUCT_ICON))\n            );\n            assert_eq!(\n                execution.product_name,\n                Some(String::from(EXPECTED_PRODUCT_NAME))\n            );\n            assert_eq!(execution.upgrade_guid, Some(String::from(UPGRADE_GUID)));\n        }\n    }\n\n    #[cfg(windows)]\n    mod execution {\n        extern crate assert_fs;\n\n        use super::*;\n        use serial_test::serial;\n        use std::env;\n\n        const MIN_PACKAGE: &str = r#\"[package]\n        name = \"cargowixtest\"\n        version = \"1.0.0\"\n        \"#;\n\n        #[test]\n        #[serial]\n        fn destination_is_correct_with_defaults() {\n            let original = env::current_dir().unwrap();\n            let temp_dir = crate::tests::setup_project(MIN_PACKAGE);\n            env::set_current_dir(temp_dir.path()).unwrap();\n            let mut expected = env::current_dir().unwrap();\n            expected.push(WIX);\n            let e = Execution::default();\n\n            let result = crate::manifest(None)\n                .and_then(|manifest| crate::package(&manifest, None))\n                .map(|package| e.destination(&package));\n\n            env::set_current_dir(original).unwrap();\n            let actual = result.unwrap();\n            assert_eq!(actual, expected);\n        }\n\n        #[test]\n        #[serial]\n        fn destination_is_correct_with_output() {\n            let expected = PathBuf::from(\"output\");\n            let temp_dir = crate::tests::setup_project(MIN_PACKAGE);\n            let e = Execution {\n                output: Some(expected.clone()),\n                ..Default::default()\n            };\n            let actual = crate::manifest(Some(&temp_dir.path().join(\"Cargo.toml\")))\n                .and_then(|manifest| crate::package(&manifest, None))\n                .map(|package| e.destination(&package))\n                .unwrap();\n\n            assert_eq!(actual, expected);\n        }\n    }\n}\n"
  },
  {
    "path": "src/lib.rs",
    "content": "// Copyright (C) 2017 Christopher R. Field.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//! # `wix` Library\n//!\n//! The goal of the cargo-wix project and the `wix` library is to make it easy\n//! to create a Windows installer (msi) for any Rust project. The cargo-wix\n//! project is primarily implemented as a [cargo subcommand], but its core\n//! functionality has been organized into a separate library. Documentation for\n//! the binary and Command Line Interface (CLI) are provided in the module-level\n//! documentation for the [binary] and the `cargo wix --help` command.\n//!\n//! ## Table of Contents\n//!\n//! - [Usage](#usage)\n//! - [Organization](#organization)\n//!\n//! ## Usage\n//!\n//! First, add this to your `Cargo.toml`:\n//!\n//! ```toml\n//! [dependencies]\n//! wix = \"0.1\"\n//! ```\n//!\n//! Next, if not using Rust 2018 edition, then add this to the `lib.rs` or\n//! `main.rs` file for your project:\n//!\n//! ```ignore\n//! extern crate wix;\n//! ```\n//!\n//! ## Organization\n//!\n//! Each subcommand is organized into a separate module. So, there is a\n//! `create`, `initialize`, `print`, etc. module within the crate. Some of the\n//! modules are in a single Rust source file, while others are organized into\n//! sub-folders. Each module follows the [Builder] design pattern, so there is a\n//! `Builder` and `Execution` struct for each module/subcommand. The `Builder`\n//! struct is used to customize the execution of the subcommand and builds an\n//! `Execution` struct. The `Execution` struct is responsible for actually\n//! executing the subcommand, which generally involves executing a process with\n//! the [`std::process::Command`] struct, but not always. Each method for the\n//! `Builder` struct generally corresponds to a CLI option or argument found in\n//! the [`cargo wix`] subcommand and binary.\n//!\n//! [binary]: ../cargo_wix/index.html\n//! [Builder]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html\n//! [cargo subcommand]: https://github.com/rust-lang/cargo/wiki/Third-party-cargo-subcommands\n//! [`cargo wix`]: ../cargo_wix/index.html\n//! [`std::process::Command`]: https://doc.rust-lang.org/std/process/struct.Command.html\n\npub use crate::templates::Template;\n\npub mod clean;\npub mod create;\npub mod initialize;\nmod licenses;\npub mod migrate;\npub mod print;\npub mod purge;\npub mod sign;\npub mod stored_path;\nmod templates;\npub mod toolset;\n\nuse camino::Utf8Path;\nuse log::debug;\n\nuse std::convert::TryFrom;\nuse std::default::Default;\nuse std::env;\nuse std::error::Error as StdError;\nuse std::ffi::OsStr;\nuse std::fmt;\nuse std::fmt::Display;\nuse std::io::{self, ErrorKind};\nuse std::path::{Path, PathBuf};\nuse std::str::FromStr;\n\nuse cargo_metadata::{Metadata, MetadataCommand, Package};\n\nuse rustc_cfg::Cfg;\n\n/// The name of the folder where binaries are typically stored.\npub const BINARY_FOLDER_NAME: &str = \"bin\";\n\n/// The file name with extension for a package's manifest.\npub const CARGO_MANIFEST_FILE: &str = \"Cargo.toml\";\n\n/// The name of the builder application for a Rust project.\npub const CARGO: &str = \"cargo\";\n\n/// The file extension for an executable.\npub const EXE_FILE_EXTENSION: &str = \"exe\";\n\n/// The file name without an extension when generating a license.\npub const LICENSE_FILE_NAME: &str = \"License\";\n\n/// The file extension for a Windows installer.\npub const MSI_FILE_EXTENSION: &str = \"msi\";\n\n/// The file extension for a Rich Text Format (RTF) file.\npub const RTF_FILE_EXTENSION: &str = \"rtf\";\n\n/// The name of the signer application from the Windows SDK.\npub const SIGNTOOL: &str = \"signtool\";\n\n/// The name of the environment variable to specify the path to the signer\n/// application.\npub const SIGNTOOL_PATH_KEY: &str = \"SIGNTOOL_PATH\";\n\n/// The default name of the folder for output from this subcommand.\npub const WIX: &str = \"wix\";\n\n/// The application name without the file extension of the compiler for the\n/// Windows installer.\npub const WIX_COMPILER: &str = \"candle\";\n\n/// The application name without the file extension of the linker for the\n/// Windows installer.\npub const WIX_LINKER: &str = \"light\";\n\n/// The application name of the modern toolset binary\npub const WIX_MODERN_TOOLSET: &str = \"wix\";\n\n/// The application name without the file extension of the `msiexec` utility.\npub const MSIEXEC: &str = \"msiexec\";\n\n/// The file extension for a WiX Toolset object file, which is the output from\n/// the WiX compiler.\npub const WIX_OBJECT_FILE_EXTENSION: &str = \"wixobj\";\n\n/// The name of the environment variable created by the WiX Toolset installer\n/// that points to the `bin` folder for the WiX Toolet's compiler (candle.exe)\n/// and linker (light.exe).\npub const WIX_PATH_KEY: &str = \"WIX\";\n\n/// The file extension of the WiX Source file, which is the input to the WiX\n/// Toolset compiler.\npub const WIX_SOURCE_FILE_EXTENSION: &str = \"wxs\";\n\n/// The default file name for the WiX Source file, which is the input to the WiX\n/// Toolset compiler.\npub const WIX_SOURCE_FILE_NAME: &str = \"main\";\n\n/// A specialized [`Result`] type for wix operations.\n///\n/// [`Result`]: https://doc.rust-lang.org/std/result/\npub type Result<T> = std::result::Result<T, Error>;\n\nfn cargo_toml_file(input: Option<&PathBuf>) -> Result<PathBuf> {\n    let i = match input {\n        Some(i) => i.to_owned(),\n        None => {\n            let mut cwd = env::current_dir()?;\n            cwd.push(CARGO_MANIFEST_FILE);\n            cwd\n        }\n    };\n    if i.exists() {\n        if i.is_file() {\n            if i.file_name() == Some(OsStr::new(CARGO_MANIFEST_FILE)) {\n                Ok(i)\n            } else {\n                Err(Error::not_a_manifest(&i))\n            }\n        } else {\n            Err(Error::not_a_file(&i))\n        }\n    } else {\n        Err(Error::not_found(&i))\n    }\n}\n\nfn description(description: Option<String>, manifest: &Package) -> Option<String> {\n    description.or_else(|| manifest.description.clone())\n}\n\nfn manifest(input: Option<&PathBuf>) -> Result<Metadata> {\n    let cargo_file_path = cargo_toml_file(input)?;\n    debug!(\"cargo_file_path = {:?}\", cargo_file_path);\n    Ok(MetadataCommand::new()\n        .no_deps()\n        .manifest_path(cargo_file_path)\n        .exec()?)\n}\n\nfn package(manifest: &Metadata, package: Option<&str>) -> Result<Package> {\n    let package_id = if let Some(p) = package {\n        manifest\n            .workspace_members\n            .iter()\n            .find(|n| manifest[n].name == p)\n            .ok_or_else(|| Error::Generic(format!(\"No `{p}` package found in the project\")))?\n    } else if manifest.workspace_members.len() == 1 {\n        &manifest.workspace_members[0]\n    } else {\n        // TODO: Replace error with creating installers for all packages in a\n        // workspace. I think this currently means that to create installers for\n        // all packages in workspace, a `cargo wix --package <name>` must be\n        // executed for each workspace member.\n        return Err(Error::Generic(String::from(\n            \"Workspace detected. Please pass a package name.\",\n        )));\n    };\n    Ok(manifest[package_id].clone())\n}\n\nfn product_name(product_name: Option<&String>, manifest: &Package) -> String {\n    if let Some(p) = product_name {\n        p.to_owned()\n    } else {\n        manifest.name.to_string()\n    }\n}\n\n/// The error type for wix-related operations and associated traits.\n///\n/// Errors mostly originate from the dependencies, but custom instances of `Error` can be created\n/// with the `Generic` variant and a message.\n#[derive(Debug)]\npub enum Error {\n    /// Parsing of Cargo metadata failed.\n    CargoMetadata(cargo_metadata::Error),\n    /// A command operation failed.\n    Command(&'static str, i32, bool),\n    /// A generic or custom error occurred. The message should contain the detailed information.\n    Generic(String),\n    /// An I/O operation failed.\n    Io(io::Error),\n    /// A needed field within the `Cargo.toml` manifest could not be found.\n    Manifest(&'static str),\n    /// An error occurred with rendering the template using the mustache renderer.\n    Mustache(mustache::Error),\n    /// UUID generation or parsing failed.\n    Uuid(uuid::Error),\n    /// Parsing error for a version string or field.\n    Version(semver::Error),\n    /// Parsing the intermediate WiX Object (wixobj) file, which is XML, failed.\n    Xml(sxd_document::parser::Error),\n    /// Evaluation of an XPath expression failed.\n    XPath(sxd_xpath::Error),\n    /// Could not convert bytes to utf8\n    Utf8Error(std::string::FromUtf8Error),\n}\n\nimpl Error {\n    /// Gets an error code related to the error.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// use wix::Error;\n    ///\n    /// let err = Error::from(\"A generic error\");\n    /// assert_ne!(err.code(), 0)\n    /// ```\n    ///\n    /// This is useful as a return, or exit, code for a command line application, where a non-zero\n    /// integer indicates a failure in the application. it can also be used for quickly and easily\n    /// testing equality between two errors.\n    pub fn code(&self) -> i32 {\n        match *self {\n            Error::Command(..) => 1,\n            Error::Generic(..) => 2,\n            Error::Io(..) => 3,\n            Error::Manifest(..) => 4,\n            Error::Mustache(..) => 5,\n            Error::Uuid(..) => 6,\n            Error::Version(..) => 7,\n            Error::Xml(..) => 8,\n            Error::XPath(..) => 9,\n            Error::CargoMetadata(..) => 10,\n            Error::Utf8Error(..) => 11,\n        }\n    }\n\n    /// Creates a new `Error` from a [std::io::Error] with the\n    /// [std::io::ErrorKind::AlreadyExists] variant.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// use std::io;\n    /// use camino::Utf8Path;\n    /// use wix::Error;\n    ///\n    /// let path = Utf8Path::new(\"C:\\\\\");\n    /// let expected = Error::Io(io::Error::new(\n    ///     io::ErrorKind::AlreadyExists,\n    ///     path.to_string()\n    /// ));\n    /// assert_eq!(expected, Error::already_exists(path));\n    /// ```\n    ///\n    /// [std::io::Error]: https://doc.rust-lang.org/std/io/struct.Error.html\n    /// [std::io::ErrorKind::AlreadyExists]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html\n    pub fn already_exists(p: &Utf8Path) -> Self {\n        io::Error::new(ErrorKind::AlreadyExists, p.to_string()).into()\n    }\n\n    /// Creates a new `Error` from a [std::io::Error] with the\n    /// [std::io::ErrorKind::NotFound] variant.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// use std::io;\n    /// use std::path::Path;\n    /// use wix::Error;\n    ///\n    /// let path = Path::new(\"C:\\\\Cargo\\\\Wix\\\\file.txt\");\n    /// let expected = Error::Io(io::Error::new(\n    ///     io::ErrorKind::NotFound,\n    ///     path.display().to_string()\n    /// ));\n    /// assert_eq!(expected, Error::not_found(path));\n    /// ```\n    ///\n    /// [std::io::Error]: https://doc.rust-lang.org/std/io/struct.Error.html\n    /// [std::io::ErrorKind::NotFound]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html\n    pub fn not_found(p: &Path) -> Self {\n        io::Error::new(ErrorKind::NotFound, p.display().to_string()).into()\n    }\n\n    /// Creates a new `Error` from a [std::io::Error] with the\n    /// [std::io::ErrorKind::InvalidInput] variant if a path is not a file.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// use std::io;\n    /// use std::path::Path;\n    /// use wix::Error;\n    ///\n    /// let path = Path::new(\"C:\\\\Cargo\\\\Wix\\\\file.txt\");\n    /// let expected = Error::Io(io::Error::new(\n    ///     io::ErrorKind::InvalidInput,\n    ///     format!(\"The '{}' path is not a file.\", path.display())\n    /// ));\n    /// assert_eq!(expected, Error::not_a_file(path));\n    /// ```\n    ///\n    /// [std::io::Error]: https://doc.rust-lang.org/std/io/struct.Error.html\n    /// [std::io::ErrorKind::InvalidInput]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html\n    pub fn not_a_file(p: &Path) -> Self {\n        io::Error::new(\n            ErrorKind::InvalidInput,\n            format!(\"The '{}' path is not a file.\", p.display()),\n        )\n        .into()\n    }\n\n    /// Creates a new `Error` from a [std::io::Error] with the\n    /// [std::io::ErrorKind::InvalidInput] variant if a path is not to a\n    /// `Cargo.toml` file.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// use std::io;\n    /// use std::path::Path;\n    /// use wix::Error;\n    ///\n    /// let path = Path::new(\"C:\\\\Cargo\\\\Wix\\\\file.txt\");\n    /// let expected = Error::Io(io::Error::new(\n    ///     io::ErrorKind::InvalidInput,\n    ///     format!(\n    ///         \"The '{}' path does not appear to be to a 'Cargo.toml' file.\",\n    ///         path.display(),\n    ///     ),\n    /// ));\n    /// assert_eq!(expected, Error::not_a_manifest(path));\n    /// ```\n    ///\n    /// [std::io::Error]: https://doc.rust-lang.org/std/io/struct.Error.html\n    /// [std::io::ErrorKind::InvalidInput]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html\n    pub fn not_a_manifest(p: &Path) -> Self {\n        io::Error::new(\n            ErrorKind::InvalidInput,\n            format!(\n                \"The '{}' path does not appear to be to a '{}' file.\",\n                p.display(),\n                CARGO_MANIFEST_FILE\n            ),\n        )\n        .into()\n    }\n\n    /// Extracts a short, single word representation of the error.\n    ///\n    /// The `std::error::Error::description` method is \"soft-deprecated\"\n    /// according to the Rust stdlib documentation. It is recommended to use the\n    /// `std::fmt::Display` implementation for a \"description\" string. However,\n    /// there is already a `std::fmt::Display` implementation for this error\n    /// type, and it is nice to have a short, single word representation for\n    /// nicely formatting errors to humans. This method maintains the error\n    /// message formatting.\n    pub fn as_str(&self) -> &str {\n        match *self {\n            Error::CargoMetadata(..) => \"CargoMetadata\",\n            Error::Command(..) => \"Command\",\n            Error::Generic(..) => \"Generic\",\n            Error::Io(..) => \"Io\",\n            Error::Manifest(..) => \"Manifest\",\n            Error::Mustache(..) => \"Mustache\",\n            Error::Uuid(..) => \"UUID\",\n            Error::Version(..) => \"Version\",\n            Error::Xml(..) => \"XML\",\n            Error::XPath(..) => \"XPath\",\n            Error::Utf8Error(..) => \"Utf8\",\n        }\n    }\n}\n\nimpl StdError for Error {\n    fn description(&self) -> &str {\n        self.as_str()\n    }\n\n    fn source(&self) -> Option<&(dyn StdError + 'static)> {\n        match *self {\n            Error::CargoMetadata(ref err) => Some(err),\n            Error::Io(ref err) => Some(err),\n            Error::Mustache(ref err) => Some(err),\n            Error::Uuid(ref err) => Some(err),\n            Error::Version(ref err) => Some(err),\n            Error::Xml(ref err) => Some(err),\n            Error::XPath(ref err) => Some(err),\n            _ => None,\n        }\n    }\n}\n\nimpl fmt::Display for Error {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        match *self {\n            Error::CargoMetadata(ref err) => err.fmt(f),\n            Error::Command(ref command, ref code, captured_output) => {\n                if captured_output {\n                    write!(\n                        f,\n                        \"The '{command}' application failed with exit code = {code}. Consider using the \\\n                         '--nocapture' flag to obtain more information.\"\n                    )\n                } else {\n                    write!(\n                        f,\n                        \"The '{command}' application failed with exit code = {code}\"\n                    )\n                }\n            }\n            Error::Generic(ref msg) => msg.fmt(f),\n            Error::Io(ref err) => match err.kind() {\n                ErrorKind::AlreadyExists => {\n                    if let Some(path) = err.get_ref() {\n                        write!(\n                            f,\n                            \"The '{path}' file already exists. Use the '--force' flag to overwrite the contents.\"\n                        )\n                    } else {\n                        err.fmt(f)\n                    }\n                }\n                ErrorKind::NotFound => {\n                    if let Some(path) = err.get_ref() {\n                        write!(f, \"The '{path}' path does not exist\")\n                    } else {\n                        err.fmt(f)\n                    }\n                }\n                _ => err.fmt(f),\n            },\n            Error::Manifest(ref var) => write!(\n                f,\n                \"No '{var}' field found in the package's manifest (Cargo.toml)\"\n            ),\n            Error::Mustache(ref err) => err.fmt(f),\n            Error::Uuid(ref err) => err.fmt(f),\n            Error::Version(ref err) => err.fmt(f),\n            Error::Xml(ref err) => err.fmt(f),\n            Error::XPath(ref err) => err.fmt(f),\n            Error::Utf8Error(ref err) => err.fmt(f),\n        }\n    }\n}\n\nimpl PartialEq for Error {\n    fn eq(&self, other: &Error) -> bool {\n        self.code() == other.code()\n    }\n}\n\nimpl From<&str> for Error {\n    fn from(s: &str) -> Self {\n        Error::Generic(s.to_string())\n    }\n}\n\nimpl From<cargo_metadata::Error> for Error {\n    fn from(err: cargo_metadata::Error) -> Self {\n        Error::CargoMetadata(err)\n    }\n}\n\nimpl From<io::Error> for Error {\n    fn from(err: io::Error) -> Self {\n        Error::Io(err)\n    }\n}\n\nimpl From<mustache::Error> for Error {\n    fn from(err: mustache::Error) -> Self {\n        Error::Mustache(err)\n    }\n}\n\nimpl From<semver::Error> for Error {\n    fn from(err: semver::Error) -> Self {\n        Error::Version(err)\n    }\n}\n\nimpl From<std::path::StripPrefixError> for Error {\n    fn from(err: std::path::StripPrefixError) -> Self {\n        Error::Generic(err.to_string())\n    }\n}\n\nimpl From<sxd_document::parser::Error> for Error {\n    fn from(err: sxd_document::parser::Error) -> Self {\n        Error::Xml(err)\n    }\n}\n\nimpl From<uuid::Error> for Error {\n    fn from(err: uuid::Error) -> Self {\n        Error::Uuid(err)\n    }\n}\n\nimpl From<sxd_xpath::Error> for Error {\n    fn from(value: sxd_xpath::Error) -> Self {\n        Error::XPath(value)\n    }\n}\n\nimpl From<sxd_xpath::ExecutionError> for Error {\n    fn from(value: sxd_xpath::ExecutionError) -> Self {\n        Error::XPath(sxd_xpath::Error::Executing(value))\n    }\n}\n\nimpl From<std::string::FromUtf8Error> for Error {\n    fn from(value: std::string::FromUtf8Error) -> Self {\n        Error::Utf8Error(value)\n    }\n}\n\n/// The different architectures supported by the WiX Toolset.\n///\n/// These are also the valid values for the `-arch` option to the WiX compiler\n/// (candle.exe).\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum WixArch {\n    /// The x86 32-bit architecture.\n    X86,\n    /// The x86_64 or AMD64 64-bit architecture.\n    X64,\n    /// The ARM 32-bit architecture.\n    Arm,\n    /// The ARM 64-bit architecture.\n    Arm64,\n}\n\nimpl Display for WixArch {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match &self {\n            Self::X86 => write!(f, \"x86\"),\n            Self::X64 => write!(f, \"x64\"),\n            Self::Arm => write!(f, \"arm\"),\n            Self::Arm64 => write!(f, \"arm64\"),\n        }\n    }\n}\n\nimpl TryFrom<&Cfg> for WixArch {\n    type Error = crate::Error;\n\n    fn try_from(c: &Cfg) -> std::result::Result<Self, Self::Error> {\n        match &*c.target_arch {\n            \"x86\" => Ok(Self::X86),\n            \"x86_64\" => Ok(Self::X64),\n            \"aarch64\" => Ok(Self::Arm64),\n            \"thumbv7a\" => Ok(Self::Arm),\n            a => {\n                if a.starts_with(\"arm\") {\n                    Ok(Self::Arm)\n                } else {\n                    Err(Error::Generic(format!(\n                        \"Unsupported target architecture: {a}\"\n                    )))\n                }\n            }\n        }\n    }\n}\n\nimpl FromStr for WixArch {\n    type Err = crate::Error;\n\n    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {\n        Self::try_from(&Cfg::of(s).map_err(|e| Error::Generic(e.to_string()))?)\n    }\n}\n\n/// The aliases for the URLs to different Microsoft Authenticode timestamp servers.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum TimestampServer {\n    /// A URL to a timestamp server.\n    Custom(String),\n    /// The alias for the Comodo timestamp server.\n    Comodo,\n    /// The alias for the Verisign timestamp server.\n    Verisign,\n}\n\nimpl TimestampServer {\n    /// Gets the URL of the timestamp server for an alias.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// use std::str::FromStr;\n    /// use wix::TimestampServer;\n    ///\n    /// assert_eq!(\n    ///     TimestampServer::from_str(\"http://www.example.com\").unwrap().url(),\n    ///     \"http://www.example.com\"\n    /// );\n    /// assert_eq!(\n    ///     TimestampServer::Comodo.url(),\n    ///     \"http://timestamp.comodoca.com/\"\n    /// );\n    /// assert_eq!(\n    ///     TimestampServer::Verisign.url(),\n    ///     \"http://timestamp.verisign.com/scripts/timstamp.dll\"\n    /// );\n    /// ```\n    pub fn url(&self) -> &str {\n        match *self {\n            TimestampServer::Custom(ref url) => url,\n            TimestampServer::Comodo => \"http://timestamp.comodoca.com/\",\n            TimestampServer::Verisign => \"http://timestamp.verisign.com/scripts/timstamp.dll\",\n        }\n    }\n}\n\nimpl fmt::Display for TimestampServer {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        write!(f, \"{}\", self.url())\n    }\n}\n\nimpl FromStr for TimestampServer {\n    type Err = Error;\n\n    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {\n        match s.to_lowercase().trim() {\n            \"comodo\" => Ok(TimestampServer::Comodo),\n            \"verisign\" => Ok(TimestampServer::Verisign),\n            u => Ok(TimestampServer::Custom(String::from(u))),\n        }\n    }\n}\n\n/// The various culture codes for localization.\n///\n/// These are taken from the table in the [WixUI localization] documentation.\n///\n/// [WixUI localization]: http://wixtoolset.org/documentation/manual/v3/wixui/wixui_localization.html\n#[derive(Clone, Debug, Default, PartialEq, Eq)]\npub enum Cultures {\n    /// Arabic, Saudi Arabia\n    ArSa,\n    /// Bulgarian, Bulgaria\n    BgBg,\n    /// Catalan, Spain\n    CaEs,\n    /// Croatian, Croatia\n    HrHr,\n    /// Czech, Czech Republic\n    CsCz,\n    /// Danish, Denmark\n    DaDk,\n    /// Dutch, Netherlands\n    NlNl,\n    /// English, United States\n    #[default]\n    EnUs,\n    /// Estonian, Estonia\n    EtEe,\n    /// Finnish, Finland\n    FiFi,\n    /// French, France\n    FrFr,\n    /// German, Germany\n    DeDe,\n    /// Greek, Greece\n    ElGr,\n    /// Hebrew, Israel\n    HeIl,\n    /// Hindi, India\n    HiIn,\n    /// Hungarian, Hungary\n    HuHu,\n    /// Italian, Italy\n    ItIt,\n    /// Japanese, Japan\n    JaJp,\n    /// Kazakh, Kazakhstan\n    KkKz,\n    /// Korean, Korea\n    KoKr,\n    /// Latvian, Latvia\n    LvLv,\n    /// Lithuanian, Lithuania\n    LtLt,\n    /// Norwegian, Norway\n    NbNo,\n    /// Polish, Poland\n    PlPl,\n    /// Portuguese, Brazil\n    PtBr,\n    /// Portuguese, Portugal\n    PtPt,\n    /// Romanian, Romania\n    RoRo,\n    /// Russian, Russian\n    RuRu,\n    /// Serbian, Serbia and Montenegro\n    SrLatnCs,\n    /// Simplified Chinese, China\n    ZhCn,\n    /// Slovak, Slovak Republic\n    SkSk,\n    /// Solvenian, Solvenia\n    SlSi,\n    /// Spanish, Spain\n    EsEs,\n    /// Swedish, Sweden\n    SvSe,\n    /// Thai, Thailand\n    ThTh,\n    /// Traditional Chinese, Hong Kong SAR\n    ZhHk,\n    /// Traditional Chinese, Taiwan\n    ZhTw,\n    /// Turkish, Turkey\n    TrTr,\n    /// Ukrainian, Ukraine\n    UkUa,\n}\n\nimpl Cultures {\n    /// The language of the culture code.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// use wix::Cultures;\n    ///\n    /// assert_eq!(Cultures::ArSa.language(), \"Arabic\");\n    /// assert_eq!(Cultures::BgBg.language(), \"Bulgarian\");\n    /// assert_eq!(Cultures::CaEs.language(), \"Catalan\");\n    /// assert_eq!(Cultures::HrHr.language(), \"Croatian\");\n    /// assert_eq!(Cultures::CsCz.language(), \"Czech\");\n    /// assert_eq!(Cultures::DaDk.language(), \"Danish\");\n    /// assert_eq!(Cultures::NlNl.language(), \"Dutch\");\n    /// assert_eq!(Cultures::EnUs.language(), \"English\");\n    /// assert_eq!(Cultures::EtEe.language(), \"Estonian\");\n    /// assert_eq!(Cultures::FiFi.language(), \"Finnish\");\n    /// assert_eq!(Cultures::FrFr.language(), \"French\");\n    /// assert_eq!(Cultures::DeDe.language(), \"German\");\n    /// assert_eq!(Cultures::ElGr.language(), \"Greek\");\n    /// assert_eq!(Cultures::HeIl.language(), \"Hebrew\");\n    /// assert_eq!(Cultures::HiIn.language(), \"Hindi\");\n    /// assert_eq!(Cultures::HuHu.language(), \"Hungarian\");\n    /// assert_eq!(Cultures::ItIt.language(), \"Italian\");\n    /// assert_eq!(Cultures::JaJp.language(), \"Japanese\");\n    /// assert_eq!(Cultures::KkKz.language(), \"Kazakh\");\n    /// assert_eq!(Cultures::KoKr.language(), \"Korean\");\n    /// assert_eq!(Cultures::LvLv.language(), \"Latvian\");\n    /// assert_eq!(Cultures::LtLt.language(), \"Lithuanian\");\n    /// assert_eq!(Cultures::NbNo.language(), \"Norwegian\");\n    /// assert_eq!(Cultures::PlPl.language(), \"Polish\");\n    /// assert_eq!(Cultures::PtBr.language(), \"Portuguese\");\n    /// assert_eq!(Cultures::PtPt.language(), \"Portuguese\");\n    /// assert_eq!(Cultures::RoRo.language(), \"Romanian\");\n    /// assert_eq!(Cultures::RuRu.language(), \"Russian\");\n    /// assert_eq!(Cultures::SrLatnCs.language(), \"Serbian (Latin)\");\n    /// assert_eq!(Cultures::ZhCn.language(), \"Simplified Chinese\");\n    /// assert_eq!(Cultures::SkSk.language(), \"Slovak\");\n    /// assert_eq!(Cultures::SlSi.language(), \"Slovenian\");\n    /// assert_eq!(Cultures::EsEs.language(), \"Spanish\");\n    /// assert_eq!(Cultures::SvSe.language(), \"Swedish\");\n    /// assert_eq!(Cultures::ThTh.language(), \"Thai\");\n    /// assert_eq!(Cultures::ZhHk.language(), \"Traditional Chinese\");\n    /// assert_eq!(Cultures::ZhTw.language(), \"Traditional Chinese\");\n    /// assert_eq!(Cultures::TrTr.language(), \"Turkish\");\n    /// assert_eq!(Cultures::UkUa.language(), \"Ukrainian\");\n    /// ```\n    pub fn language(&self) -> &'static str {\n        match *self {\n            Cultures::ArSa => \"Arabic\",\n            Cultures::BgBg => \"Bulgarian\",\n            Cultures::CaEs => \"Catalan\",\n            Cultures::HrHr => \"Croatian\",\n            Cultures::CsCz => \"Czech\",\n            Cultures::DaDk => \"Danish\",\n            Cultures::NlNl => \"Dutch\",\n            Cultures::EnUs => \"English\",\n            Cultures::EtEe => \"Estonian\",\n            Cultures::FiFi => \"Finnish\",\n            Cultures::FrFr => \"French\",\n            Cultures::DeDe => \"German\",\n            Cultures::ElGr => \"Greek\",\n            Cultures::HeIl => \"Hebrew\",\n            Cultures::HiIn => \"Hindi\",\n            Cultures::HuHu => \"Hungarian\",\n            Cultures::ItIt => \"Italian\",\n            Cultures::JaJp => \"Japanese\",\n            Cultures::KkKz => \"Kazakh\",\n            Cultures::KoKr => \"Korean\",\n            Cultures::LvLv => \"Latvian\",\n            Cultures::LtLt => \"Lithuanian\",\n            Cultures::NbNo => \"Norwegian\",\n            Cultures::PlPl => \"Polish\",\n            Cultures::PtBr => \"Portuguese\",\n            Cultures::PtPt => \"Portuguese\",\n            Cultures::RoRo => \"Romanian\",\n            Cultures::RuRu => \"Russian\",\n            Cultures::SrLatnCs => \"Serbian (Latin)\",\n            Cultures::ZhCn => \"Simplified Chinese\",\n            Cultures::SkSk => \"Slovak\",\n            Cultures::SlSi => \"Slovenian\",\n            Cultures::EsEs => \"Spanish\",\n            Cultures::SvSe => \"Swedish\",\n            Cultures::ThTh => \"Thai\",\n            Cultures::ZhHk => \"Traditional Chinese\",\n            Cultures::ZhTw => \"Traditional Chinese\",\n            Cultures::TrTr => \"Turkish\",\n            Cultures::UkUa => \"Ukrainian\",\n        }\n    }\n\n    /// The location of the culture component, typically the country that speaks the language.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// use wix::Cultures;\n    ///\n    /// assert_eq!(Cultures::ArSa.location(), \"Saudi Arabia\");\n    /// assert_eq!(Cultures::BgBg.location(), \"Bulgaria\");\n    /// assert_eq!(Cultures::CaEs.location(), \"Spain\");\n    /// assert_eq!(Cultures::HrHr.location(), \"Croatia\");\n    /// assert_eq!(Cultures::CsCz.location(), \"Czech Republic\");\n    /// assert_eq!(Cultures::DaDk.location(), \"Denmark\");\n    /// assert_eq!(Cultures::NlNl.location(), \"Netherlands\");\n    /// assert_eq!(Cultures::EnUs.location(), \"United States\");\n    /// assert_eq!(Cultures::EtEe.location(), \"Estonia\");\n    /// assert_eq!(Cultures::FiFi.location(), \"Finland\");\n    /// assert_eq!(Cultures::FrFr.location(), \"France\");\n    /// assert_eq!(Cultures::DeDe.location(), \"Germany\");\n    /// assert_eq!(Cultures::ElGr.location(), \"Greece\");\n    /// assert_eq!(Cultures::HeIl.location(), \"Israel\");\n    /// assert_eq!(Cultures::HiIn.location(), \"India\");\n    /// assert_eq!(Cultures::HuHu.location(), \"Hungary\");\n    /// assert_eq!(Cultures::ItIt.location(), \"Italy\");\n    /// assert_eq!(Cultures::JaJp.location(), \"Japan\");\n    /// assert_eq!(Cultures::KkKz.location(), \"Kazakhstan\");\n    /// assert_eq!(Cultures::KoKr.location(), \"Korea\");\n    /// assert_eq!(Cultures::LvLv.location(), \"Latvia\");\n    /// assert_eq!(Cultures::LtLt.location(), \"Lithuania\");\n    /// assert_eq!(Cultures::NbNo.location(), \"Norway\");\n    /// assert_eq!(Cultures::PlPl.location(), \"Poland\");\n    /// assert_eq!(Cultures::PtBr.location(), \"Brazil\");\n    /// assert_eq!(Cultures::PtPt.location(), \"Portugal\");\n    /// assert_eq!(Cultures::RoRo.location(), \"Romania\");\n    /// assert_eq!(Cultures::RuRu.location(), \"Russia\");\n    /// assert_eq!(Cultures::SrLatnCs.location(), \"Serbia and Montenegro\");\n    /// assert_eq!(Cultures::ZhCn.location(), \"China\");\n    /// assert_eq!(Cultures::SkSk.location(), \"Slovak Republic\");\n    /// assert_eq!(Cultures::SlSi.location(), \"Solvenia\");\n    /// assert_eq!(Cultures::EsEs.location(), \"Spain\");\n    /// assert_eq!(Cultures::SvSe.location(), \"Sweden\");\n    /// assert_eq!(Cultures::ThTh.location(), \"Thailand\");\n    /// assert_eq!(Cultures::ZhHk.location(), \"Hong Kong SAR\");\n    /// assert_eq!(Cultures::ZhTw.location(), \"Taiwan\");\n    /// assert_eq!(Cultures::TrTr.location(), \"Turkey\");\n    /// assert_eq!(Cultures::UkUa.location(), \"Ukraine\");\n    /// ```\n    pub fn location(&self) -> &'static str {\n        match *self {\n            Cultures::ArSa => \"Saudi Arabia\",\n            Cultures::BgBg => \"Bulgaria\",\n            Cultures::CaEs => \"Spain\",\n            Cultures::HrHr => \"Croatia\",\n            Cultures::CsCz => \"Czech Republic\",\n            Cultures::DaDk => \"Denmark\",\n            Cultures::NlNl => \"Netherlands\",\n            Cultures::EnUs => \"United States\",\n            Cultures::EtEe => \"Estonia\",\n            Cultures::FiFi => \"Finland\",\n            Cultures::FrFr => \"France\",\n            Cultures::DeDe => \"Germany\",\n            Cultures::ElGr => \"Greece\",\n            Cultures::HeIl => \"Israel\",\n            Cultures::HiIn => \"India\",\n            Cultures::HuHu => \"Hungary\",\n            Cultures::ItIt => \"Italy\",\n            Cultures::JaJp => \"Japan\",\n            Cultures::KkKz => \"Kazakhstan\",\n            Cultures::KoKr => \"Korea\",\n            Cultures::LvLv => \"Latvia\",\n            Cultures::LtLt => \"Lithuania\",\n            Cultures::NbNo => \"Norway\",\n            Cultures::PlPl => \"Poland\",\n            Cultures::PtBr => \"Brazil\",\n            Cultures::PtPt => \"Portugal\",\n            Cultures::RoRo => \"Romania\",\n            Cultures::RuRu => \"Russia\",\n            Cultures::SrLatnCs => \"Serbia and Montenegro\",\n            Cultures::ZhCn => \"China\",\n            Cultures::SkSk => \"Slovak Republic\",\n            Cultures::SlSi => \"Solvenia\",\n            Cultures::EsEs => \"Spain\",\n            Cultures::SvSe => \"Sweden\",\n            Cultures::ThTh => \"Thailand\",\n            Cultures::ZhHk => \"Hong Kong SAR\",\n            Cultures::ZhTw => \"Taiwan\",\n            Cultures::TrTr => \"Turkey\",\n            Cultures::UkUa => \"Ukraine\",\n        }\n    }\n}\n\nimpl fmt::Display for Cultures {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        match *self {\n            Cultures::ArSa => write!(f, \"ar-SA\"),\n            Cultures::BgBg => write!(f, \"bg-BG\"),\n            Cultures::CaEs => write!(f, \"ca-ES\"),\n            Cultures::HrHr => write!(f, \"hr-HR\"),\n            Cultures::CsCz => write!(f, \"cs-CZ\"),\n            Cultures::DaDk => write!(f, \"da-DK\"),\n            Cultures::NlNl => write!(f, \"nl-NL\"),\n            Cultures::EnUs => write!(f, \"en-US\"),\n            Cultures::EtEe => write!(f, \"et-EE\"),\n            Cultures::FiFi => write!(f, \"fi-FI\"),\n            Cultures::FrFr => write!(f, \"fr-FR\"),\n            Cultures::DeDe => write!(f, \"de-DE\"),\n            Cultures::ElGr => write!(f, \"el-GR\"),\n            Cultures::HeIl => write!(f, \"he-IL\"),\n            Cultures::HiIn => write!(f, \"hi-IN\"),\n            Cultures::HuHu => write!(f, \"hu-HU\"),\n            Cultures::ItIt => write!(f, \"it-IT\"),\n            Cultures::JaJp => write!(f, \"ja-JP\"),\n            Cultures::KkKz => write!(f, \"kk-KZ\"),\n            Cultures::KoKr => write!(f, \"ko-KR\"),\n            Cultures::LvLv => write!(f, \"lv-LV\"),\n            Cultures::LtLt => write!(f, \"lt-LT\"),\n            Cultures::NbNo => write!(f, \"nb-NO\"),\n            Cultures::PlPl => write!(f, \"pl-PL\"),\n            Cultures::PtBr => write!(f, \"pt-BR\"),\n            Cultures::PtPt => write!(f, \"pt-PT\"),\n            Cultures::RoRo => write!(f, \"ro-RO\"),\n            Cultures::RuRu => write!(f, \"ru-RU\"),\n            Cultures::SrLatnCs => write!(f, \"sr-Latn-CS\"),\n            Cultures::ZhCn => write!(f, \"zh-CN\"),\n            Cultures::SkSk => write!(f, \"sk-SK\"),\n            Cultures::SlSi => write!(f, \"sl-SI\"),\n            Cultures::EsEs => write!(f, \"es-ES\"),\n            Cultures::SvSe => write!(f, \"sv-SE\"),\n            Cultures::ThTh => write!(f, \"th-TH\"),\n            Cultures::ZhHk => write!(f, \"zh-HK\"),\n            Cultures::ZhTw => write!(f, \"zh-TW\"),\n            Cultures::TrTr => write!(f, \"tr-TR\"),\n            Cultures::UkUa => write!(f, \"uk-UA\"),\n        }\n    }\n}\n\nimpl FromStr for Cultures {\n    type Err = Error;\n\n    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {\n        match s.to_lowercase().trim() {\n            \"ar-sa\" => Ok(Cultures::ArSa),\n            \"bg-bg\" => Ok(Cultures::BgBg),\n            \"ca-es\" => Ok(Cultures::CaEs),\n            \"hr-hr\" => Ok(Cultures::HrHr),\n            \"cs-cz\" => Ok(Cultures::CsCz),\n            \"da-dk\" => Ok(Cultures::DaDk),\n            \"nl-nl\" => Ok(Cultures::NlNl),\n            \"en-us\" => Ok(Cultures::EnUs),\n            \"et-ee\" => Ok(Cultures::EtEe),\n            \"fi-fi\" => Ok(Cultures::FiFi),\n            \"fr-fr\" => Ok(Cultures::FrFr),\n            \"de-de\" => Ok(Cultures::DeDe),\n            \"el-gr\" => Ok(Cultures::ElGr),\n            \"he-il\" => Ok(Cultures::HeIl),\n            \"hi-in\" => Ok(Cultures::HiIn),\n            \"hu-hu\" => Ok(Cultures::HuHu),\n            \"it-it\" => Ok(Cultures::ItIt),\n            \"ja-jp\" => Ok(Cultures::JaJp),\n            \"kk-kz\" => Ok(Cultures::KkKz),\n            \"ko-kr\" => Ok(Cultures::KoKr),\n            \"lv-lv\" => Ok(Cultures::LvLv),\n            \"lt-lt\" => Ok(Cultures::LtLt),\n            \"nb-no\" => Ok(Cultures::NbNo),\n            \"pl-pl\" => Ok(Cultures::PlPl),\n            \"pt-br\" => Ok(Cultures::PtBr),\n            \"pt-pt\" => Ok(Cultures::PtPt),\n            \"ro-ro\" => Ok(Cultures::RoRo),\n            \"ru-ru\" => Ok(Cultures::RuRu),\n            \"sr-latn-cs\" => Ok(Cultures::SrLatnCs),\n            \"zh-cn\" => Ok(Cultures::ZhCn),\n            \"sk-sk\" => Ok(Cultures::SkSk),\n            \"sl-si\" => Ok(Cultures::SlSi),\n            \"es-es\" => Ok(Cultures::EsEs),\n            \"sv-se\" => Ok(Cultures::SvSe),\n            \"th-th\" => Ok(Cultures::ThTh),\n            \"zh-hk\" => Ok(Cultures::ZhHk),\n            \"zh-tw\" => Ok(Cultures::ZhTw),\n            \"tr-tr\" => Ok(Cultures::TrTr),\n            \"uk-ua\" => Ok(Cultures::UkUa),\n            e => Err(Error::Generic(format!(\"Unknown '{e}' culture\"))),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use assert_fs::TempDir;\n    use std::env;\n    use std::fs;\n\n    /// Create a simple project with the provided TOML.\n    pub fn setup_project(toml: &str) -> TempDir {\n        pub const PERSIST_VAR_NAME: &str = \"CARGO_WIX_TEST_PERSIST\";\n\n        let temp_dir = TempDir::new().unwrap();\n        fs::write(temp_dir.path().join(\"Cargo.toml\"), toml).unwrap();\n        fs::create_dir(temp_dir.path().join(\"src\")).unwrap();\n        fs::write(temp_dir.path().join(\"src\").join(\"main.rs\"), \"fn main() {}\").unwrap();\n\n        temp_dir.into_persistent_if(env::var(PERSIST_VAR_NAME).is_ok())\n    }\n\n    mod culture {\n        use super::*;\n\n        #[test]\n        fn from_str_is_correct_for_dash_russian() {\n            assert_eq!(Cultures::from_str(\"ru-ru\"), Ok(Cultures::RuRu));\n        }\n\n        #[test]\n        #[should_panic]\n        fn from_str_fails_for_underscore_russian() {\n            Cultures::from_str(\"ru_ru\").unwrap();\n        }\n\n        #[test]\n        fn display_is_correct_for_russian() {\n            assert_eq!(format!(\"{}\", Cultures::RuRu), String::from(\"ru-RU\"));\n        }\n\n        #[test]\n        fn from_str_is_correct_for_lowercase_slovak() {\n            assert_eq!(Cultures::from_str(\"sk-sk\"), Ok(Cultures::SkSk));\n        }\n\n        #[test]\n        fn from_str_is_correct_for_uppercase_slovak() {\n            assert_eq!(Cultures::from_str(\"sk-SK\"), Ok(Cultures::SkSk));\n        }\n\n        #[test]\n        #[should_panic]\n        fn from_str_fails_for_underscore_slovak() {\n            Cultures::from_str(\"sk_sk\").unwrap();\n        }\n\n        #[test]\n        fn display_is_correct_for_slovak() {\n            assert_eq!(format!(\"{}\", Cultures::SkSk), String::from(\"sk-SK\"));\n        }\n    }\n\n    mod wix_arch {\n        use super::*;\n\n        #[test]\n        fn try_from_x86_64_pc_windows_msvc_is_correct() {\n            let arch = WixArch::try_from(&Cfg::of(\"x86_64-pc-windows-msvc\").expect(\"Cfg parsing\"))\n                .unwrap();\n            assert_eq!(arch, WixArch::X64);\n        }\n\n        #[test]\n        fn try_from_x86_64_pc_windows_gnu_is_correct() {\n            let arch =\n                WixArch::try_from(&Cfg::of(\"x86_64-pc-windows-gnu\").expect(\"Cfg parsing\")).unwrap();\n            assert_eq!(arch, WixArch::X64);\n        }\n\n        #[test]\n        fn try_from_x86_64_uwp_windows_msvc_is_correct() {\n            let arch = WixArch::try_from(&Cfg::of(\"x86_64-uwp-windows-msvc\").expect(\"Cfg parsing\"))\n                .unwrap();\n            assert_eq!(arch, WixArch::X64);\n        }\n\n        #[test]\n        fn try_from_x86_64_uwp_windows_gnu_is_correct() {\n            let arch = WixArch::try_from(&Cfg::of(\"x86_64-uwp-windows-gnu\").expect(\"Cfg parsing\"))\n                .unwrap();\n            assert_eq!(arch, WixArch::X64);\n        }\n\n        #[test]\n        fn try_from_i686_pc_windows_msvc_is_correct() {\n            let arch =\n                WixArch::try_from(&Cfg::of(\"i686-pc-windows-msvc\").expect(\"Cfg parsing\")).unwrap();\n            assert_eq!(arch, WixArch::X86);\n        }\n\n        #[test]\n        fn try_from_i686_pc_windows_gnu_is_correct() {\n            let arch =\n                WixArch::try_from(&Cfg::of(\"i686-pc-windows-gnu\").expect(\"Cfg parsing\")).unwrap();\n            assert_eq!(arch, WixArch::X86);\n        }\n\n        #[test]\n        fn try_from_i686_uwp_windows_msvc_is_correct() {\n            let arch =\n                WixArch::try_from(&Cfg::of(\"i686-uwp-windows-msvc\").expect(\"Cfg parsing\")).unwrap();\n            assert_eq!(arch, WixArch::X86);\n        }\n\n        #[test]\n        fn try_from_i686_uwp_windows_gnu_is_correct() {\n            let arch =\n                WixArch::try_from(&Cfg::of(\"i686-uwp-windows-gnu\").expect(\"Cfg parsing\")).unwrap();\n            assert_eq!(arch, WixArch::X86);\n        }\n\n        #[test]\n        fn try_from_aarch64_pc_windows_msvc_is_correct() {\n            let arch = WixArch::try_from(&Cfg::of(\"aarch64-pc-windows-msvc\").expect(\"Cfg parsing\"))\n                .unwrap();\n            assert_eq!(arch, WixArch::Arm64);\n        }\n\n        #[test]\n        fn try_from_aarch64_uwp_windows_msvc_is_correct() {\n            let arch =\n                WixArch::try_from(&Cfg::of(\"aarch64-uwp-windows-msvc\").expect(\"Cfg parsing\"))\n                    .unwrap();\n            assert_eq!(arch, WixArch::Arm64);\n        }\n\n        #[test]\n        fn try_from_thumbv7a_pc_windows_msvc_is_correct() {\n            let arch =\n                WixArch::try_from(&Cfg::of(\"thumbv7a-pc-windows-msvc\").expect(\"Cfg parsing\"))\n                    .unwrap();\n            assert_eq!(arch, WixArch::Arm);\n        }\n\n        #[test]\n        fn try_from_thumbv7a_uwp_windows_msvc_is_correct() {\n            let arch =\n                WixArch::try_from(&Cfg::of(\"thumbv7a-uwp-windows-msvc\").expect(\"Cfg parsing\"))\n                    .unwrap();\n            assert_eq!(arch, WixArch::Arm);\n        }\n\n        #[test]\n        fn from_str_is_correct() {\n            let arch = WixArch::from_str(\"thumbv7a-uwp-windows-msvc\").unwrap();\n            assert_eq!(arch, WixArch::Arm);\n        }\n    }\n}\n"
  },
  {
    "path": "src/licenses.rs",
    "content": "// Copyright (C) 2017 Christopher R. Field.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nuse crate::Error;\nuse crate::LICENSE_FILE_NAME;\nuse crate::RTF_FILE_EXTENSION;\nuse crate::Result;\nuse crate::Template;\nuse crate::stored_path::{StoredPath, StoredPathBuf};\n\nuse camino::Utf8Path;\nuse camino::Utf8PathBuf;\nuse log::trace;\nuse log::warn;\n\nuse std::str::FromStr;\n\nuse cargo_metadata::Package;\n\n/// License info\n#[derive(Clone, Debug)]\npub struct Licenses {\n    /// The license for the actual source code\n    ///\n    /// This likely will become/contain a Vec at some point,\n    /// since dual MIT/Apache wants to have two license files!\n    pub source: Option<License>,\n    /// The end-user license (EULA) that must be agreed to when installing\n    pub end_user: Option<License>,\n}\n\n/// A license file/item\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct License {\n    /// Path to the file, relative to package's root dir.\n    ///\n    /// So generated files would be \"wix\\License.rtf\"\n    /// and the typical LICENSE file would just be \"LICENSE-MIT\".\n    ///\n    /// Conveniently this means we don't need to do any special handling\n    /// to rewrite/relativize a path we get out of a Cargo.toml.\n    ///\n    /// It can also be an absolute path if the user passed this via CLI\n    /// and doesn't care about persistence/portability.\n    pub stored_path: StoredPathBuf,\n    /// File name to use for the license when installed to the user's system\n    ///\n    /// If None, the source file name is used.\n    pub name: Option<String>,\n\n    /// This file needs to be generated, write it to the given path\n    /// using the given Template.\n    pub generate: Option<(Utf8PathBuf, Template)>,\n}\n\nimpl License {\n    /// Create a License entry with just the StoredPath\n    fn from_stored_path(path: &StoredPath) -> Self {\n        Self {\n            name: None,\n            stored_path: path.to_owned(),\n            generate: None,\n        }\n    }\n}\n\nimpl Licenses {\n    /// Get license/eula info for a package\n    pub fn new(\n        dest_dir: Option<&Utf8Path>,\n        license_path: Option<&StoredPath>,\n        eula_path: Option<&StoredPath>,\n        package: &Package,\n    ) -> Result<Self> {\n        let source_license = Self::find_source_license(dest_dir, license_path, package)?;\n        let end_user_license =\n            Self::find_end_user_license(eula_path, package, source_license.as_ref())?;\n\n        Ok(Self {\n            source: source_license,\n            end_user: end_user_license,\n        })\n    }\n\n    /// Find the source-license for a package\n    fn find_source_license(\n        dest_dir: Option<&Utf8Path>,\n        path: Option<&StoredPath>,\n        package: &Package,\n    ) -> Result<Option<License>> {\n        trace!(\"finding source license for {}\", package.name);\n        // If explicitly passed, use that\n        if let Some(path) = path {\n            trace!(\"explicit source-license path passed as argument, using that\");\n            return Ok(Some(License::from_stored_path(path)));\n        }\n\n        let package_dir = package.manifest_path.parent().expect(\"non-root Cargo.toml\");\n        let dest_dir = dest_dir\n            .map(|p| p.to_owned())\n            .unwrap_or_else(|| package_dir.join(crate::WIX));\n\n        // If there's [package.manifest.wix].license, respect that\n        if let Some(license) = package\n            .metadata\n            .get(\"wix\")\n            .and_then(|w| w.as_object())\n            .and_then(|t| t.get(\"license\"))\n        {\n            trace!(\"[package.manifest.wix].license is specified\");\n            if let Some(license_enabled) = license.as_bool() {\n                // If the user sets `eula = false`, disable the eula\n                // (= true just falls through to the auto-detection stuff below)\n                if !license_enabled {\n                    trace!(\"[package.manifest.wix].license is false, disabling license support\");\n                    return Ok(None);\n                } else {\n                    trace!(\"[package.manifest.wix].license is true, continuing to auto-detect\");\n                }\n            } else if let Some(path) = license.as_str() {\n                // If the user sets `license = \"path/to/license\"`, use that\n                trace!(\"[package.manifest.wix].license is a path, using that\");\n                if package_dir.join(path).exists() {\n                    return Ok(Some(License::from_stored_path(StoredPath::new(path))));\n                } else {\n                    return Err(Error::Generic(format!(\n                        r#\"{} specifies package.metadata.wix.license=\"{}\" in its Cargo.toml, but no such file exists.\"#,\n                        package.name, path,\n                    )));\n                }\n            } else {\n                // Don't accept anything else\n                trace!(\"[package.manifest.wix].license is an invalid type\");\n                return Err(Error::Generic(format!(\n                    \"{}'s [package.metadata.wix].license must be a bool or a path\",\n                    package.name\n                )));\n            }\n        }\n\n        // First try Cargo's license_file field\n        if let Some(path) = &package.license_file {\n            trace!(\"Cargo.toml license_file is specified, using that\");\n            // NOTE: this join will mishandle an absolute path stored in the Cargo.toml\n            // but there's very little justification for such a thing, so it's fine.\n            if package_dir.join(path).exists() {\n                // This is already a path relative to the Cargo.toml, so it can be used verbatim\n                return Ok(Some(License::from_stored_path(\n                    &StoredPathBuf::from_utf8_path(path),\n                )));\n            } else {\n                return Err(Error::Generic(format!(\n                    r#\"{} specifies license-file=\"{}\" in its Cargo.toml, but no such file exists.\"#,\n                    package.name, path,\n                )));\n            }\n        }\n\n        // Next try Cargo's license field\n        if let Some(name) = package.license.clone() {\n            trace!(\"Cargo.toml license is specified\");\n            // If there's a template for this license, generate it\n            if let Ok(generate) = Template::from_str(&name) {\n                trace!(\"Found a matching template, generating that\");\n                let file_name = format!(\"{LICENSE_FILE_NAME}.{RTF_FILE_EXTENSION}\");\n                let dest_file = dest_dir.join(file_name);\n                let rel_file = dest_file.strip_prefix(package_dir).unwrap_or(&dest_file);\n                let stored_path = StoredPathBuf::from_utf8_path(rel_file);\n                return Ok(Some(License {\n                    name: None,\n                    stored_path,\n                    generate: Some((dest_file, generate)),\n                }));\n            } else {\n                trace!(\"No matching template, ignoring license\");\n            }\n        }\n\n        trace!(\"No source-license found\");\n        Ok(None)\n    }\n\n    /// Find the eula for a package\n    fn find_end_user_license(\n        path: Option<&StoredPath>,\n        package: &Package,\n        source_license: Option<&License>,\n    ) -> Result<Option<License>> {\n        trace!(\"finding end-user-license for {}\", package.name);\n\n        // If explicitly passed, use that\n        if let Some(path) = path {\n            trace!(\"explicit end-user-license path passed as argument, using that\");\n            return Ok(Some(License::from_stored_path(path)));\n        }\n\n        let package_dir = package.manifest_path.parent().expect(\"non-root Cargo.toml\");\n\n        // If there's [package.manifest.wix].eula, respect that\n        if let Some(eula) = package\n            .metadata\n            .get(\"wix\")\n            .and_then(|w| w.as_object())\n            .and_then(|t| t.get(\"eula\"))\n        {\n            trace!(\"[package.manifest.wix].eula is specified\");\n            if let Some(eula_enabled) = eula.as_bool() {\n                // If the user sets `eula = false`, disable the eula\n                // (= true just falls through to the auto-detection stuff below)\n                if !eula_enabled {\n                    trace!(\"[package.manifest.wix].eula is false, disabling license support\");\n                    return Ok(None);\n                } else {\n                    trace!(\"[package.manifest.wix].eula is true, continuing to auto-detect\");\n                }\n            } else if let Some(path) = eula.as_str() {\n                // If the user sets `eula = \"path/to/license\"`, use that\n                trace!(\"[package.manifest.wix].eula is a path, using that\");\n                if package_dir.join(path).exists() {\n                    return Ok(Some(License::from_stored_path(StoredPath::new(path))));\n                } else {\n                    return Err(Error::Generic(format!(\n                        r#\"{} specifies package.metadata.wix.eula=\"{}\" in its Cargo.toml, but no such file exists.\"#,\n                        package.name, path,\n                    )));\n                }\n            } else {\n                // Don't accept anything else\n                trace!(\"[package.manifest.wix].eula is an invalid type\");\n                return Err(Error::Generic(format!(\n                    \"{}'s [package.metadata.wix].eula must be a bool or a path\",\n                    package.name\n                )));\n            }\n        }\n\n        // Try to use the source-license, if it has a path and is RTF,\n        if let Some(license) = source_license {\n            trace!(\"source-license is defined\");\n            let path = &license.stored_path;\n            if path.extension() == Some(RTF_FILE_EXTENSION) {\n                trace!(\"using path from license\");\n                return Ok(Some(License::from_stored_path(path)));\n            }\n        }\n\n        trace!(\"No end-user-license found\");\n\n        warn!(\n            \"Could not find your project's EULA. The license agreement dialog will be excluded \\\nfrom the installer. You can add one by either:\n\n* Setting 'package.license' to a recognized value (MIT, Apache-2.0, or GPL-3.0)\n* Setting 'package.license-file', 'package.metadata.wix.license', or 'package.metadata.wix.eula' \\\nto point to an RTF file\n* Passing an RTF file with --license or --eula to the cargo-wix CLI\n* Editing the generated WiX Source (wxs) with a text editor\n\nTo supress this warning, set 'package.metadata.wix.eula = false'\"\n        );\n        Ok(None)\n    }\n}\n"
  },
  {
    "path": "src/main.rs",
    "content": "// Copyright (C) 2017 Christopher R. Field.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//! # `cargo-wix` Binary and Subcommand\n//!\n//! The goal of the cargo-wix project and the `cargo wix` subcommand is to make\n//! it easy to create a Windows installer (msi) for any Rust project. The\n//! project is primarily implemented as a [cargo subcommand], but the core\n//! functionality is provided in a library (crate). See the module-level\n//! comments for the [library] for more information about usage and\n//! organization of the `wix` crate. The remainder of this documentation\n//! focuses on the usage and features of the `cargo wix` subcommand.\n//!\n//! ## Table of Contents\n//!\n//! - [Quick Start](#quick-start)\n//! - [C Runtime](#c-runtime)\n//! - [Examples](#examples)\n//! - [Features](#features)\n//!   - [Signing](#signing)\n//!   - [Templates](#templates)\n//!   - [Variables](#variables)\n//!   - [Extensions](#extensions)\n//!   - [Multiple WiX Sources](#multiple-wix-sources)\n//!   - [Bundles](#bundles)\n//! - [Configuration](#configuration)\n//! - [Flags and Options](#flags-and-options)\n//!\n//! ## Quick Start\n//!\n//! Ensure the [WiX Toolset] is installed and a `WIX` system environment\n//! variable has been created. The installer for the WiX Toolset should create\n//! the `WIX` system environment variable automatically. Then, start, or\n//! restart, a command prompt (cmd.exe) and execute the following commands:\n//!\n//! ```dos\n//! C:\\>cargo install cargo-wix\n//! C:\\>cd Path\\To\\Project\n//! C:\\Path\\To\\Project\\>cargo wix init\n//! C:\\Path\\To\\Project\\>cargo wix\n//! ```\n//!\n//! The Windows installer (msi) will be in the `C:\\Path\\To\\Project\\target\\wix`\n//! folder. The `cargo wix init` command will create a `wix` folder alongside\n//! the `src` folder and the project's manifest (Cargo.toml). The `wix` folder\n//! will contain the WiX Source file (`main.wxs`) that was automatically\n//! generated for the project based the contents of its manifest. The WiX Source\n//! file can be customized using a text editor, and once the file exists, the\n//! `cargo wix init` command does not need to be used again.\n//!\n//! The `cargo wix` command uses the `wix\\main.wxs` file generated from the\n//! previous `cargo wix init` command as input for the WiX Toolset's \"compiler\"\n//! and \"linker\", i.e. `candle.exe` and `light.exe`, respectively, to create the\n//! Windows installer (msi). A variety of artifact files will be created in the\n//! `target\\wix` folder. These can be ignored and/or deleted.\n//!\n//! ### Using WiX Toolset v4 and Newer (Modern Toolset)\n//!\n//! WiX Toolset v4 and newer use a single `wix.exe` binary instead of the\n//! separate `candle.exe` and `light.exe` tools. To use the modern toolset,\n//! install WiX v4+ via `dotnet tool install --global wix` and use the\n//! `--toolset modern` flag:\n//!\n//! ```dos\n//! C:\\Path\\To\\Project\\>cargo wix --toolset modern\n//! ```\n//!\n//! If your project has existing WiX v3 source files (`.wxs`), they must be\n//! converted to the v4 format before building. The `--migrate` flag automates\n//! this conversion and installs any required WiX extension packages:\n//!\n//! ```dos\n//! C:\\Path\\To\\Project\\>cargo wix --toolset modern --migrate global\n//! ```\n//!\n//! The `--migrate` flag accepts two modes:\n//!\n//! - `global` — Converts `.wxs` files in place and installs extensions to the\n//!   global WiX extension cache. Best for simple projects and CI pipelines with\n//!   network access.\n//! - `vendor` — Converts `.wxs` files in place and installs extensions to a\n//!   local `.wix/extensions` folder. Best for offline builds or when tracking\n//!   extension packages in version control.\n//!\n//! Alternatively, migration can be performed as a standalone step using the\n//! `migrate` subcommand:\n//!\n//! ```dos\n//! C:\\Path\\To\\Project\\>cargo wix migrate\n//! ```\n//!\n//! After migration, the `--migrate` flag is no longer needed for subsequent\n//! builds.\n//!\n//! For new projects starting with WiX v4, use `--schema v4` when initializing\n//! to generate a v4-format `.wxs` file directly:\n//!\n//! ```dos\n//! C:\\Path\\To\\Project\\>cargo wix init --schema v4\n//! ```\n//!\n//! The created installer will install the executable file in a `bin` folder\n//! within the destination selected by the user during installation. It will add\n//! a license file to the same folder as the `bin` folder, and it will add the\n//! `bin` folder to the `PATH` system environment variable so that the\n//! executable can be called from anywhere with a command prompt. Most of these\n//! behaviors can be adjusted during the installation process. The default\n//! installation destination is `C:\\Program Files\\<project name>`, where\n//! `<project name>` is replaced with the name of the project.\n//!\n//! ## C Runtime\n//!\n//! If using the `x86_64-pc-windows-msvc` or the `i686-pc-windows-msvc`\n//! toolchain, then an appropriate C RunTime (CRT) must be available on the host\n//! system _or_ the CRT must be statically compiled with the Rust binary\n//! (executable). By default, the CRT is dynamically linked to a Rust binary if\n//! using Microsoft Visual C compiler (MSVC). This may cause issues and errors\n//! when running the Rust binary immediately after installation on a clean\n//! Windows installation, i.e. the CRT has not already been installed. If the\n//! Rust compiler is installed and an `-msvc` toolchain is available, then the\n//! Rust binary will execute after installation without an issue.\n//!\n//! **Note**, the Rust programming language does _not_ need to be installed\n//! to run an executable built using Cargo and Rust. Only a Microsoft-provided CRT\n//! needs to be installed. These are often referred to as \"redistributables\". Rust\n//! with either of the `-msvc` toolchains will dynamically link against the\n//! `vcruntime140.dll`, which is part of the Visual C++ 2015 redistributable and\n//! freely provided by Microsoft.\n//!\n//! For developers using the `cargo wix` subcommand to create an installer, this\n//! dependency on the presence of an appropriate C runtime can lead to a sub-optimal\n//! user experience. There are three options: (i) statically link the C runtime\n//! within the Rust binary (recommended), (ii) add the Visual C++ redistributable to\n//! the installer via the [Merge module] method for the WiX Toolset, or (iii)\n//! provide the user with instructions for downloading and installing the CRT\n//! _before_ running the executable.\n//!\n//! The current recommended option is to [statically link the CRT] when building\n//! the Rust binary. Rust v1.19 or newer is required, and the solution\n//! ultimately becomes adding the `-C target-feature=+crt-static` option to the\n//! invocation of the Rust compiler (rustc). There are a variety of methods for\n//! adding the option to the invocation, including but not limited to: (i)\n//! creating a Cargo configuration file for the user or project, i.e.\n//! `.cargo/config.toml`, and adding the following:\n//!\n//! ```\n//! [target.x86_64-pc-windows-msvc]\n//! rustflags = [\"-C\", \"target-feature=+crt-static\"]\n//!\n//! [target.i686-pc-windows-msvc]\n//! rustflags = [\"-C\", \"target-feature=+crt-static\"]\n//! ```\n//!\n//! to the `config.toml` file or (ii) creating the `RUSTFLAGS` environment\n//! variable and setting its value to `\"-C target-feature=+crt-static\"`. Please\n//! see the Rust compiler documentation on static linking for more information\n//! and details.\n//!\n//! ## Examples\n//!\n//! All of the following examples use the native Command Prompt (cmd.exe) for\n//! the Windows OS; however, the [Developer Prompt] installed with the [VC Build\n//! Tools] is recommended. A [git bash], [PowerShell], or [Alacritty] terminal can\n//! also be used.\n//!\n//! Begin each example by starting the appropriate prompt and navigating to the\n//! root folder of the Rust project. The `cargo wix` subcommand and binary\n//! assumes the current working directory (cwd) is the project, a.k.a. package,\n//! root folder, i.e. the same folder as the package's manifest (Cargo.toml).\n//!\n//! Let's create a basic project with Cargo and then an installer.\n//!\n//! ```dos\n//! C:\\Path\\to\\Project> cargo init --bin --vcs none --name example\n//! ```\n//!\n//! This will create a simple binary package named \"example\" without any version\n//! control. While the `cargo wix` subcommand and binary does work with\n//! libraries (crates) in addition to binaries (applications), generally an\n//! installer is needed for a binary (application) not a library. Rust libraries\n//! are typically distributed via [crates.io] and do not need an installer. The\n//! package's manifest should look like the following, but with your name and\n//! email address for the `authors` field:\n//!\n//! ```toml\n//! [package]\n//! name = \"example\"\n//! version = \"0.1.0\"\n//! authors = [\"First Last <first.last@example.com>\"]\n//!\n//! [dependencies]\n//! ```\n//!\n//! The next step is to create the WiX Source (wxs) file, which is needed to\n//! create the installer for the project.\n//!\n//! ```dos\n//! C:\\Path\\to\\Project> cargo wix init\n//!  WARN: A description was not specified at the command line or in the package's manifest (Cargo.toml). The description can be added manually to the generated WiX Source (wxs) file using a text editor.\n//!  WARN: An EULA was not specified at the command line, a RTF license file was not specified in the package manifest's (Cargo.toml) 'license-file' field, or the license ID from the package manifest's 'license' field is not recognized. The license agreement dialog will be excluded from the installer. An EULA can be added manually to the generated WiX Source (wxs) file using a text editor.\n//!  WARN: A help URL could not be found and it will be excluded from the installer. A help URL can be added manually to the generated WiX Source (wxs) file using a text editor.\n//!  WARN: A license file could not be found and it will be excluded from the installer. A license file can be added manually to the generated WiX Source (wxs) file using a text editor.\n//! ```\n//!\n//! The warnings can be ignored for the time being, and they will be addressed in\n//! subsequent examples. The above command will create a `wix` folder with the\n//! `main.wxs` file:\n//!\n//! ```dos\n//! C:\\Path\\to\\Project> dir /B\n//! Cargo.toml\n//! src\n//! wix\n//! C:\\Path\\to\\Project> dir wix /B\n//! main.wxs\n//! ```\n//!\n//! The `wix` folder and the `main.wxs` file now exist, and an installer can be\n//! created:\n//!\n//! ```dos\n//! C:\\Path\\to\\Project> cargo wix\n//! ```\n//!\n//! This may take a moment to complete as the `cargo wix` subcommand will build\n//! the application with the _Release_ target profile and then build the\n//! installer. The installer will be located in the `target\\wix` folder.\n//!\n//! ```dos\n//! C:\\Path\\to\\Project> dir target\\wix /B\n//! example-0.1.0-x86_64.msi\n//! main.wixobj\n//! ```\n//!\n//! Great! An installer (msi) exists for the application. The `main.wixobj` file\n//! is an artifact of the installer build process and can be ignored and/or\n//! deleted.\n//!\n//! You can also automatically run the installer after creating it by\n//! specifying the `--install` argument:\n//!\n//! ```dos\n//! C:\\Path\\to\\Project> cargo wix --install\n//!  ```\n//!\n//! The installer that is created with the above steps and commands will install\n//! the `example.exe` file to: `C:\\Program Files\\example\\bin\\example.exe`. It\n//! will also add the `C:\\Program Files\\example\\bin` path to the `PATH` system\n//! environment variable, unless this feature is disabled during the\n//! installation process from the Custom Setup dialog. The default welcome\n//! screen, banner, and icons from the WiX Toolset's default UI component will\n//! be used. A license dialog will _not_ appear for this installer.\n//!\n//! The installer that is created from the previous example is relatively simple,\n//! but so is the application and package. It would be nice to have a license\n//! dialog appear in the installer that allows the end-user to review and\n//! acknowledge the End User License Agreement (EULA). It would also be nice to\n//! have this same license appear in the installation folder for the application\n//! so end-users can review it after installation.\n//!\n//! The license agreement dialog for a WiX Toolset-created installer must be in\n//! the [Rich Text Format] (.rtf). The majority of Rust binaries and libraries\n//! are developed and distributed with an open source license. We will do the\n//! same for the example binary here and use the GPL-3.0 license. Creating a RTF\n//! version of the GPL-3.0 requires a third-party tool, such as [WordPad], which\n//! is freely available with any Windows distribution, [Microsoft Office],\n//! [LibreOffice], or any number of freely available text editors, word\n//! processors, or document conversion tools. Luckily, the `cargo wix`\n//! subcommand and binary has a RTF version of the GPL-3.0 license available and\n//! eliminates the need to install, pay, and/or learn another tool to create the\n//! EULA.\n//!\n//! First, open the package's manifest (Cargo.toml) in a text editor, like\n//! [Microsoft Notepad], and add the [`license`] field to the `package`\n//! section with the `GPL-3.0` value:\n//!\n//! ```toml\n//! [package]\n//! name = \"example\"\n//! version = \"0.1.0\"\n//! authors = [\"First Last <first.last@example.com>\"]\n//! license = \"GPL-3.0\"\n//!\n//! [dependencies]\n//! ```\n//!\n//! Save the package's manifest and exit the text editor. Now, we can create a\n//! `wix\\main.wxs` file that will include the license dialog with a GPL-3.0\n//! EULA.\n//!\n//! ```dos\n//! C:\\Path\\to\\Project> cargo wix init --force\n//!  WARN: A description was not specified at the command line or in the package's manifest (Cargo.toml). The description can be added manually to the generated WiX Source (wxs) file using a text editor.\n//!  WARN: A help URL could not be found and it will be excluded from the installer. A help URL can be added manually to the generated WiX Source (wxs) file using a text editor.\n//!\n//! C:\\Path\\to\\Project> dir wix /B\n//! License.rtf\n//! main.wxs\n//! C:\\Path\\to\\Project> cargo wix\n//! ```\n//!\n//! The `--force` flag is needed so that the existing `wix\\main.wxs` is\n//! overwritten with the new EULA-enabled `wix\\main.wxs` file. The majority of\n//! the warnings have also been addressed. Notice there is now a `License.rtf`\n//! file in the `wix` folder. This will be used as the EULA in the license\n//! agreement dialog for the installer and be included in the installation\n//! folder with the binary.\n//!\n//! A side note, while version control has been disabled for these examples, it is\n//! best practice to include installer-related files in version control; thus,\n//! the `wix\\main.wxs` and `wix\\License.rtf` should be added to version control.\n//!\n//! Let's fix the remaining warnings. Both of the remaining warnings can be\n//! resolved in multiple ways. The first is to use the options available for the\n//! `cargo wix init` subcommmand following the previous example project:\n//!\n//! ```dos\n//! C:\\Path\\to\\Project> cargo wix init --force -d \"This is a description\" -u http://www.example.com\n//!\n//! C:\\Path\\to\\Project> dir wix /B\n//! License.rtf\n//! main.wxs\n//! C:\\Path\\to\\Project> cargo wix\n//! ```\n//!\n//! The warnings for the description and help URL have disappeared. The\n//! `-d,--description` option for the `cargo wix init` command adds a\n//! description for the installer. Similarly, the `-u,--url` option adds a help\n//! URL. The `--force` flag is still needed to overwrite the previous\n//! `wix\\main.wxs` file.\n//!\n//! Another possibility is to add the `description` and `homepage` fields to the\n//! package's manifest (Cargo.toml), and then initialize and create the\n//! installer. The cargo-wix binary and subcommand will use these fields, among\n//! others, to automatically include the values into the installer. The\n//! `-d,--description` and `-u,--url` options can still be used to override the\n//! values from the package's manifest. This can be useful if the contents of\n//! the installer might need to be different or more detailed than the package.\n//!\n//! Following from the previous example, open the package's manifest\n//! (Cargo.toml) in a text editor, like [Microsoft Notepad], and add the\n//! [`description`] and [`homepage`] fields to the `package` section:\n//!\n//! ```toml\n//! [package]\n//! name = \"example\"\n//! version = \"0.1.0\"\n//! authors = [\"First Last <first.last@example.com>\"]\n//! license = \"GPL-3.0\"\n//! description = \"This is a description\"\n//! homepage = \"http://www.example.com\"\n//!\n//! [dependencies]\n//! ```\n//!\n//! Save the package's manifest and exit the text editor. Now, we can create a\n//! `wix\\main.wxs` file without any warnings and uses the description and\n//! homepage from the package's manifest:\n//!\n//! ```dos\n//! C:\\Path\\to\\Project> cargo wix init --force\n//! C:\\Path\\to\\Project> dir wix /B\n//! License.rtf\n//! main.wxs\n//! C:\\Path\\to\\Project> cargo wix\n//! ```\n//!\n//! The [`documentation`] and [`repository`] fields can be used instead of the\n//! [`homepage`] field for the help URL, too.\n//!\n//! ## Features\n//!\n//! The cargo-wix binary, and related `cargo wix` subcommand, use the WiX\n//! Toolset and the [SignTool] application available in the [Windows 10 SDK].\n//! These are obviously Windows-only applications, so while the crate and binary\n//! can be built on any platform supported by the [Rust] programming language\n//! and [Cargo], the `cargo wix` subcommand is only really useful in a Windows\n//! environment.\n//!\n//! The WiX Toolset provides a \"compiler\" (`candle.exe`) and \"linker\"\n//! (`light.exe`). These can be found in the `bin` directory of the installation\n//! location for the WiX Toolset. The value of the `WIX` system environment\n//! variable that is created during installation of the WiX Toolset is a path to\n//! the installation folder that contains the `bin` folder. The `WIX` system\n//! environment variable is used by the `cargo wix` subcommand with\n//! the [`std::process::Command`] module to create installers.\n//!\n//! ### Signing\n//!\n//! The Windows SDK provides a signer (`signtool`) application for signing\n//! installers. The application is installed in the `bin` folder of the Windows\n//! SDK installation. The location of the `bin` folder varies depending on the\n//! version. It is recommended to use the Developer Prompt to ensure the\n//! `signtool` application is available. Signing an installer is optional.\n//!\n//! ### Templates\n//!\n//! The WiX Toolset requires a WiX Source (WXS) file, which is an [XML] file. A\n//! template is provided with this binary that attempts to meet the majority\n//! of use cases for developers and avoid requiring extensive knowledge of the\n//! WiX Toolset and Windows installer technologies. Modification of the template\n//! is encouraged, but please consult the WiX Toolset's extensive\n//! [documentation] and [tutorials] for information about writing (authoring),\n//! customizing, and using WXS files. This documentation here is only for this\n//! binary and subcommand.\n//!\n//! The [WXS] template is embedded in the binary installation of the subcommand\n//! and it can be printed to STDOUT using the `cargo wix print wxs` command from\n//! the command prompt (cmd.exe). Note, each time the `cargo wix print wxs`\n//! command is invoked, new Globally Unique Identifiers ([GUID]) are generated\n//! for fields that require them. Thus, a developer does not need to worry about\n//! generating GUIDs and can begin using the template immediately with this\n//! subcommand or the WiX Toolset's compiler (`candle.exe`) and linker\n//! (`light.exe`) applications.\n//!\n//! In addition to the WXS template, there are several license templates which\n//! are used to generate an End User License Agreement (EULA) during the `cargo\n//! wix init` command. Depending on the license ID(s) in the `license` field for\n//! a package's manifest (Cargo.toml), a license file in the Rich Text Format\n//! (RTF) is generated from a template and placed in the `wix` folder. This RTF\n//! file is then displayed in the license agreement dialog of the installer. See\n//! the help information on the `carge wix print` subcommand:\n//!\n//! ```dos\n//! C:\\Path\\to\\Project> cargo wix print --help\n//! ```\n//!\n//! for information about supported licenses. If the `license` field is\n//! not used, or the license ID is not supported, then the EULA is _not_\n//! automatically created during initialization and it will have to be created\n//! manually with a text editor or some other authoring tool.\n//!\n//! The `cargo wix init` subcommand uses a combination of the [`license`] and\n//! [`license-file`] fields of the project's manifest (Cargo.toml) to determine\n//! if a [sidecar] license file should be included in the installation folder\n//! alongside the `bin` folder. The `license` field appears to be the more\n//! commonly used field to describe the licensing for a Rust project and\n//! package, while the `license-file` field is used to specify a custom, or\n//! proprietary, license.\n//!\n//! The top three most common licenses for Rust projects are supported from the\n//! `license` field, i.e. MIT, Apache-2.0, and GPLv3. If any of these three\n//! supported open source licenses are used for the `license` field, then a\n//! `License.rtf` file is generated from an embedded template and placed in the\n//! `wix` folder as part of the `cargo wix init` subcommand. This generated RTF\n//! file will be used as a sidecar file and for the End User License Agreement\n//! (EULA) that is displayed in the license agreement dialog of the installer.\n//! If the `license-file` field is used and it contains a path to a file with\n//! the `.rtf` extension, then this file will be used as a sidecar file and for\n//! the EULA. If neither of these fields exist or contain valid values, then no\n//! sidecar file is included in the installation and no license agreement dialog\n//! appears during installation. This default behavior can be overridden with\n//! the `-l,--license` and `-e,--eula` options for the `cargo wix init`\n//! subcommand.\n//!\n//! ### Variables\n//!\n//! The cargo-wix subcommand automatically passes some Cargo and build\n//! configuration-related values to the WiX Toolset compiler (candle.exe). These\n//! variables can be used within a WiX Source (WXS) file using the\n//! `$(var.<VARIABLE>)` notation, where `<VARIABLE>` is replaced with the name\n//! of variable passed from the cargo-wix subcommand to the WiX Toolset\n//! compiler using the `-DKEY=VALUE` option. Notable usage of these variables\n//! are in [WiX preprocessor] directives to dynamically change the installer\n//! creation at build time. Below is a current list of variables passed from the\n//! cargo-wix subcommand to a WXS file during installer creation.\n//!\n//! - `TargetTriple` = The rustc target triple name as seen with the `rustc\n//!   --print target-list` command\n//! - `TargetEnv` = The rustc target environment, as seen with the output from\n//!   the `rustc --print cfg` command as `target_env`. On Windows, this typically\n//!   either `msvc` or `gnu` depending on the toolchain downloaded and installed.\n//! - `TargetVendor` = The rustc target vendor, as seen with the output from the\n//!   `rustc --print cfg` command as `target_vendor`. This is typically `pc`, but Rust\n//!   does support other vendors, like `uwp`.\n//! - `CargoTargetBinDir` = The complete path to the binary (exe). The default\n//!   would be `target\\release\\<BINARY_NAME>.exe` where `<BINARY_NAME>` is replaced\n//!   with the name of each binary target defined in the package's manifest\n//!   (Cargo.toml). If a different rustc target triple is used than the host, i.e.\n//!   cross-compiling, then the default path would be\n//!   `target\\<CARGO_TARGET>\\<CARGO_PROFILE>\\<BINARY_NAME>.exe`, where\n//!   `<CARGO_TARGET>` is replaced with the `CargoTarget` variable value and\n//!   `<CARGO_PROFILE>` is replaced with the value from the `CargoProfile` variable.\n//! - `CargoTargetDir` = The path to the directory for the build artifacts, i.e.\n//!   `target`.\n//! - `CargoProfile` = Either `debug` or `release` depending on the build\n//!   profile. The default is `release`.\n//! - `Platform` = (Deprecated) Either `x86`, `x64`, `arm`, or `arm64`. See the\n//!   documentation for the WiX Toolset compiler (candle.exe) `-arch` option.\n//!   Note, this variable is deprecated and will eventually be removed because it is\n//!   ultimately redundant to the `$(sys.BUILDARCH)` variable that is already provided\n//!   by the WiX Toolset compiler. Existing projects should replace usage of\n//!   `$(var.Platform)` with `$(sys.BUILDARCH)`. No action is needed for new projects.\n//! - `Profile` = (Deprecated) See `CargoProfile`.\n//! - `Version` = The version for the installer. The default is the\n//!   `Major.Minor.Fix` semantic versioning number of the Rust package.\n//!\n//! Additional, user-defined variables for custom WXS files can be passed to the\n//! WiX Toolset compiler (candle.exe) using the cargo-wix subcommand\n//! `-C,--compiler-arg` option. For example,\n//!\n//! ```dos\n//! C:\\Path\\To\\Project\\>cargo wix -C \"-DUSER_KEY=USER_VALUE\"\n//! ```\n//!\n//! ### Extensions\n//!\n//! The [WixUIExtension] and [WixUtilExtension] are included in every execution\n//! of the default _create_ cargo-wix subcommand, i.e. `cargo wix`. This is the\n//! same as calling either the compiler (candle.exe) or the linker (light.exe)\n//! with the `-ext WixUIExtension -ext WixUtilExtension` options. These two\n//! extensions are commonly used to create installers when using the WiX\n//! Toolset, so these are included by default. Additionally, the WixUIExtension\n//! is used for the template WXS file.\n//!\n//! Additionally, the [WixBalExtension] is automatically included if the\n//! cargo-wix subcommand detects a bundle (exe) is to be created instead of a\n//! MSI package. See the [Bundles](#bundles) section for more information about\n//! creating and managing bundles with the cargo-wix subcommand.\n//!\n//! ### Multiple WiX Sources\n//!\n//! The cargo-wix subcommand supports including multiple WXS files when creating\n//! an installer. A lot of customization is possible through the WXS file and\n//! sometimes the installer's source code becomes its own project where\n//! organization and formatting are important. Breaking up a single, large WXS\n//! file into multiple WXS files can be useful for code readability and project\n//! navigation. Thus, cargo-wix will include any file with the `.wxs` file\n//! extension found in the default source folder, `wix`, when creating an\n//! installer. For example, say you have the following project with three WXS\n//! files in the `wix` sub-folder:\n//!\n//! ```dos\n//! C:\\Path\\to\\Project> dir /B\n//! Cargo.toml\n//! src\n//! wix\n//! C:\\Path\\to\\Project> dir wix /B\n//! first.wxs\n//! second.wxs\n//! third.wxs\n//! ```\n//!\n//! When the `cargo wix` default _create_ command is executed, all three WXS files\n//! will be included and used to create the installer. Generally, this\n//! translates to the following set of commands:\n//!\n//! ```dos\n//! C:\\Path\\to\\Project> candle -out target\\wix\\ wix\\first.wxs wix\\second.wxs wix\\third.wxs\n//! C:\\Path\\to\\Project> light -out target\\wix\\example-0.1.0-x86_64.msi target\\wix\\first.wixobj target\\wix\\second.wixobj target\\wix\\third.wixobj\n//! ```\n//!\n//! Alternatively, multiple WXS files can also be included when creating an\n//! installer by including the relative or absolute paths to the WXS files as\n//! arguments to the subcommand, but any WXS files in the default, `wix`,\n//! sub-folder are ignored and would have to be explicitly included. For\n//! example,\n//!\n//! ```dos\n//! C:\\Path\\To\\Project> cargo wix path\\to\\first\\wxs\\file\\one.wxs path\\to\\second\\wxs\\file\\two.wxs\n//! ```\n//!\n//! ### Bundles\n//!\n//! It is possible to create [bundle-based installers] with the WiX Toolset. The\n//! cargo-wix subcommand and binary currently offer limited support for creating\n//! bundles from Rust projects. This includes automatically detecting if a\n//! bundle is to be created by inspecting all WiX Object (wixobj) files before\n//! linking and changing the file extension from `msi` to `exe`, as bundles\n//! require the executable (exe) file extension. In addition to automatically\n//! changing the file extension of the installer, the [WixBalExtension] is\n//! included automatically during linking if a bundle is detected because this\n//! extension includes a standard bootstrapper application that is commonly used\n//! to build and customize bundles.\n//!\n//! While the cargo-wix subcommand does provide some support for bundles with\n//! automatic file extension determination and inclusion of useful\n//! bundle-centric extensions, the process for creating a bundle for a Rust\n//! project is currently a manual process. Let's assume the following\n//! [workspace]-based Rust project layout and structure:\n//!\n//! ```text\n//! |-- C:\\Path\\to\\Rust\\Project\n//! |-- |-- Cargo.toml\n//! |-- |-- client\n//! |-- |-- |-- Cargo.toml\n//! |-- |-- |-- src\n//! |-- |-- |-- |-- main.rs\n//! |-- |-- server\n//! |-- |-- |-- Cargo.toml\n//! |-- |-- |-- src\n//! |-- |-- |-- |-- main.rs\n//! ```\n//!\n//! The virtual manifest, Cargo.toml, in the root of the project is a [virtual\n//! manifest] that only contains the following:\n//!\n//! ```toml\n//! [workspace]\n//! members = [\"client\", \"server\"]\n//! ```\n//!\n//! The package manifests for the workspace members, `client\\Cargo.toml` and\n//! `server\\Cargo.toml`, are the typical package manifests content for a binary\n//! package.\n//!\n//! The goal is to create a bundle-based executable that contains and\n//! installs both the client and server packages through their respective\n//! installers (MSI packages). A combination of manual file manipulation and the\n//! cargo-wix subcommand can be used to accomplish this goal, all from the root\n//! of the workspace. Begin by creating the WiX Source (wxs) files for the\n//! client and server MSI packages:\n//!\n//! ```dos\n//! C:\\Path\\to\\Rust\\Workspace> cargo wix init client\\Cargo.toml\n//! C:\\Path\\to\\Rust\\Workspace> cargo wix init server\\Cargo.toml\n//! ```\n//!\n//! The following project layout should have been created:\n//!\n//! ```text\n//! |-- C:\\Path\\to\\Rust\\Workspace\n//! |-- |-- Cargo.toml\n//! |-- |-- client\n//! |-- |-- |-- Cargo.toml\n//! |-- |-- |-- src\n//! |-- |-- |-- |-- main.rs\n//! |-- |-- |-- wix\n//! |-- |-- |-- |-- main.wxs\n//! |-- |-- server\n//! |-- |-- |-- Cargo.toml\n//! |-- |-- |-- src\n//! |-- |-- |-- |-- main.rs\n//! |-- |-- |-- wix\n//! |-- |-- |-- |-- main.wxs\n//! ```\n//!\n//! The WiX Source (wxs) files created for the client and server crates of the\n//! Rust workspace using the cargo-wix subcommand will be generated following\n//! the normal rules, features, and templates when using the `cargo wix init`\n//! within a non-workspace-based project.\n//!\n//! With the WiX Source (wxs) files created for the two MSI-based installers, a\n//! bundle-based WiX Source (wxs) file must be created for the workspace and\n//! bundle. Create a new `main.wxs` file and place it in a `wix` sub-folder\n//! relative to the workspace root. The following project layout should exist:\n//!\n//! ```text\n//! |-- C:\\Path\\to\\Rust\\Workspace\n//! |-- |-- Cargo.toml\n//! |-- |-- client\n//! |-- |-- |-- Cargo.toml\n//! |-- |-- |-- src\n//! |-- |-- |-- |-- main.rs\n//! |-- |-- |-- wix\n//! |-- |-- |-- |-- main.wxs\n//! |-- |-- server\n//! |-- |-- |-- Cargo.toml\n//! |-- |-- |-- src\n//! |-- |-- |-- |-- main.rs\n//! |-- |-- |-- wix\n//! |-- |-- |-- |-- main.wxs\n//! |-- |-- wix\n//! |-- |-- |-- main.wxs\n//! ```\n//!\n//! Open the `wix\\main.wxs` file in a suitable text editor and add the following\n//! XML to it to define the bundle:\n//!\n//! ```xml\n//! <?xml version=\"1.0\"?>\n//! <Wix xmlns=\"http://schemas.microsoft.com/wix/2006/wi\">\n//!   <Bundle Version=\"1.0.0\" UpgradeCode=\"[Your GUID Here]\">\n//!     <BootstrapperApplicationRef Id=\"WixStandardBootstrapperApplication.RtfLicense\"/>\n//!     <Chain>\n//!       <MsiPackage SourceFile=\"client\\target\\wix\\client-0.1.0-x86_64.msi\" />\n//!       <MsiPackage SourceFile=\"server\\target\\wix\\server-0.1.0-x86_64.msi\" />\n//!     </Chain>\n//!   </Bundle>\n//! </Wix>\n//! ```\n//!\n//! A GUID will need to be manually generated for the `UpgradeCode` attribute of\n//! the `Bundle` tag and used to replace the `[Your GUID Here]` value. Now, the\n//! bundle can be created but first both the client and server MSI packages must\n//! be created. Thus, creating the bundle is a multi-step, or multi-command,\n//! process:\n//!\n//! ```dos\n//! C:\\Path\\to\\Workspace> cargo wix client\\Cargo.toml\n//! C:\\Path\\to\\Workspace> cargo wix server\\Cargo.toml\n//! C:\\Path\\to\\Workspace> cargo wix --name Bundle --install-version 1.0.0\n//! ```\n//!\n//! The following project layout should exist:\n//!\n//! ```text\n//! |-- C:\\Path\\to\\Rust\\Workspace\n//! |-- |-- Cargo.toml\n//! |-- |-- client\n//! |-- |-- |-- Cargo.toml\n//! |-- |-- |-- src\n//! |-- |-- |-- |-- main.rs\n//! |-- |-- |-- target\n//! |-- |-- |-- |-- wix\n//! |-- |-- |-- |-- |-- server-0.1.0-x86_64.msi\n//! |-- |-- |-- wix\n//! |-- |-- |-- |-- main.wxs\n//! |-- |-- server\n//! |-- |-- |-- Cargo.toml\n//! |-- |-- |-- src\n//! |-- |-- |-- |-- main.rs\n//! |-- |-- |-- target\n//! |-- |-- |-- |-- wix\n//! |-- |-- |-- |-- |-- server-0.1.0-x86_64.msi\n//! |-- |-- |-- wix\n//! |-- |-- |-- |-- main.wxs\n//! |-- |-- target\n//! |-- |-- |-- Release\n//! |-- |-- |-- |-- client.exe\n//! |-- |-- |-- |-- server.exe\n//! |-- |-- |-- wix\n//! |-- |-- |-- |-- Bundle-1.0.0-x86_64.exe\n//! |-- |-- wix\n//! |-- |-- |-- main.wxs\n//! ```\n//!\n//! Note the built binaries are located in the `target\\Release` folder relative\n//! to the workspace root as opposed to the `client\\target\\Release` and\n//! `server\\target\\Release` folders, even though the MSI packages are available\n//! in the member's `target\\wix` folders. This will fail if the various `cargo\n//! wix` commands are _not_ executed from the workspace root.\n//!\n//! The `name` and `install-version` options can be moved into a\n//! [configuration](#configuration) section for the subcommand within the\n//! virtual manifest of the workspace, i.e. the Cargo.toml file with the\n//! `[workspace]` section to avoid having to retype them each time a bundle is\n//! created, but all three `cargo wix` commands must be issued each time.\n//!\n//! While the above steps will create a bundle installer for the workspace-based\n//! Rust project with a default, placeholder EULA, it is very manual and\n//! cumbersome. For example, the bundle-based WiX Source (wxs) file will have to\n//! be manually updated each time the version numbers of the member MSI packages\n//! are changed because the paths to the source files are hard-coded in the XML.\n//! Efforts are underway to improve support within the cargo-wix subcommand for\n//! both workspaces and bundles ([Issue #74] and [Issue #98]).\n//!\n//! [Issue #74]: https://github.com/volks73/cargo-wix/issues/74\n//! [Issue #98]: https://github.com/volks73/cargo-wix/issues/98\n//!\n//! ## Configuration\n//!\n//! The default subcommand, `cargo wix`, which creates a MSI based on the\n//! contents of the package's manifest (Cargo.toml) can be configured by adding\n//! a `[package.metadata.wix]` section to the manifest. For each CLI option for\n//! the default _create_ subcommand, a field can be added to the\n//! `[package.metadata.wix]` section. If the corresponding CLI option is used\n//! with the default _create_ subcommand, then the CLI option value will\n//! override the value in the metadata section.\n//!\n//! Below is an example `[package.metadata.wix]` section from a package's\n//! manifest that configures the default _create_ subcommand:\n//!\n//! ```toml\n//! [package.metadata.wix]\n//! banner = \"path\\to\\banner.png\"\n//! compiler-args = [\"-nologo\", \"-wn\"]\n//! culture = \"Fr-Fr\"\n//! dbg-build = false\n//! dbg-name = false\n//! dialog = \"path\\to\\dialog.png\"\n//! eula = \"path\\to\\eula.rtf\"\n//! include = [\"Path\\to\\WIX\\Source\\File\\One.wxs\", \"Path\\to\\WIX\\Source\\File\\Two.wxs\"]\n//! license = \"path\\to\\license.txt\"\n//! linker-args = [\"-nologo\"]\n//! locale = \"Path\\to\\WIX\\Localization\\File.wxl\"\n//! name = \"example\"\n//! no-build = false\n//! output = \"Path\\and\\file\\name\\for\\installer.msi\"\n//! path-guid = \"BFD25009-65A4-4D1E-97F1-0030465D90D6\"\n//! product-icon = \"path\\to\\product_icon.ico\"\n//! upgrade-guid = \"B36177BE-EA4D-44FB-B05C-EDDABDAA95CA\"\n//! version = \"2.1.0\"\n//! ```\n//!\n//! See the documentation for each CLI option for more information about each\n//! field and its purpose. Note, the `name` and `version` fields will be the\n//! name and version number of the application as it appears in the Add/Remove\n//! Programs control panel, and the version number does _not_ need to match the\n//! package's version number. In other words, the installed application can have\n//! a different version number from the package, which is useful with multiple\n//! binaries, workspaces, or distributing a \"suite\" of applications, where the\n//! version number would be for the suite and not necessarily the individual\n//! applications within the suite.\n//!\n//! Please note that unlike most of the fields, the `include` field is an [TOML\n//! array] instead of a string value. This is the same as passing multiple paths\n//! to the default _create_ subcommand using multiple `-I,--include` options or\n//! including multiple WXS files in the default, `wix`, project source location.\n//!\n//! The only CLI option, or argument, that is not supported in the\n//! `[package.metadata.wix]` section is the `<INPUT>` argument for the default\n//! _create_ command, which specifies a relative or absolute path to a package's\n//! manifest file. The assumption is that the package manifest (Cargo.toml) to\n//! be used for the default _create_ subcommand is the same manifest that\n//! contains the `[package.metadata.wix]` section.\n//!\n//! ## Flags and Options\n//!\n//! Generally, any value that is obtained from the package's manifest\n//! (Cargo.toml) can be overridden at the command line with an appropriate\n//! option. For example, the manufacturer, which is displayed as the \"Publisher\"\n//! in the Add/Remove Programs (ARP) control panel is obtained from the first\n//! author listed in the `authors` field of a project's manifest, but it can be\n//! overridden using the `-m,--manufacturer` option with the `cargo wix init`\n//! subcommand.\n//!\n//! Use the `-h,--help` flag with each subcommand to get a full list of options\n//! and flags that are available. The short flag, `-h`, will print a condensed\n//! version of each flag and option for the subcommand, while the long flag,\n//! `--help`, will print a detailed help for each flag and option. The rest of\n//! this section is a list of all flags and options implemented for all\n//! subcommands.\n//!\n//! ### `-b,--banner`\n//!\n//! Available for the _init_ (`cargo wix init`) and _print_ (`cargo wix print`)\n//! subcommands.\n//!\n//! Sets the path to an image file (.png, .bmp, ...) that will be displayed across\n//! the top of each dialog in the installer. The banner image dimensions should\n//! be 493 x 58 pixels, but the left-most 50% of that image is covered with text,\n//! so if you want to leave a blank background for text readability, you only want\n//! to color in the right-most ~200 pixels of that image.\n//!\n//!\n//! ### `-b,--bin-path`\n//!\n//! Available for the default _create_ (`cargo wix`) and _sign_ (`cargo wix\n//! sign`) subcommands.\n//!\n//! The `-b,--bin-path` option can be used to specify a path (relative or\n//! absolute) to the WiX Toolset `bin` folder. The `-b,--bin-path` option is\n//! useful if a different version of the WiX Toolset needs to be used to create\n//! the installer. The descending order of precedence is: (1) `-b,--bin-path`\n//! option then (2) `WIX` system environment variable. An error will be\n//! displayed if the compiler and/or linker cannot be found.\n//!\n//! This option is also available for the `cargo wix sign` subcommand and can be\n//! used to specify a path to the Windows SDK `bin` folder. This can be used to\n//! override default `signtool` application found using the\n//! [`std::process::Command::status`] method.\n//!\n//! ### `-B,--binary`\n//!\n//! Available for the _init_ (`cargo wix init`) and _print_ (`cargo wix print`)\n//! subcommands.\n//!\n//! A path to a binary, a.k.a. executable, to include in the installer _instead_\n//! of any and all binaries defined in the package's manifest (Cargo.toml). By\n//! default, all binaries defined with the `[[bin]]` section in the package's\n//! manifest are included in the installer. If no `[[bin]]` section is defined,\n//! then the package's `name` field is used. This option can be used to\n//! override, i.e. _not_ append, the binaries that should be included in the\n//! installer.\n//!\n//! This option can be used multiple times to define multiple binaries to\n//! include in the installer. The value is a path to a binary file. The file\n//! stem (file name without extension) is used as the binary name within the WXS\n//! file. A relative or absolute path is acceptable.\n//!\n//! ### `-C,--compiler-arg`\n//!\n//! Available for the default _create (`cargo wix`) subcommand.\n//!\n//! Appends an argument to the WiX compiler (candle.exe) invocation. This\n//! provides a mechanism for \"passing\" arguments to the compiler. This can be called\n//! multiple times to pass multiple arguments (flags or options), but only one\n//! value per occurrence is allowed to avoid ambiguity during argument parsing.\n//! Note, if it is an option, i.e. argument with an accompanying value, then the\n//! value must be passed as a separate usage of this option. For example, adding\n//! an user-defined compiler extension would require the following command\n//! `cargo wix -C -ext -C UserDefinedExtension` to yield a `candle -ext\n//! UserDefinedExtension` invocation.\n//!\n//! ### `-c,--culture`\n//!\n//! Available for the default _create_ (`cargo wix`) subcommand.\n//!\n//! Sets the culture for localization. Use with the `-l,--locale` option. See\n//! the [WixUI localization documentation] for more information about acceptable\n//! culture codes. The codes are case insensitive.\n//!\n//! ### `-d,--dbg-build`\n//!\n//! Available only for the default _create_ (`cargo wix`) subcommmand.\n//!\n//! Builds the package using the Debug profile instead of the Release profile\n//! with Cargo. This flag is ignored if the `--no-build` flag is used. The\n//! default is to build the package with the Release profile.\n//!\n//! ### `-D,--dbg-name`\n//!\n//! Available only for the default _create_ (`cargo wix`) subcommand.\n//!\n//! Appends `-debug` to the file stem (portion before the dot and file\n//! extension) of the installer's file name. The default is to _not_ include the\n//! suffix. Generally, this should be used to indicate an installer contains a\n//! binary that was built with debugging information and minimal optimizations\n//! as a tradeoff for being able to troubleshoot execution of the application on\n//! an end-user's system. A release build generally does not contain any\n//! debugging information but includes optimizations. It is typical to use this\n//! flag with the `-d,--dbg-build` flag but it is not required. This allows a\n//! developer to provide other mechanisms for creating a debugging variant of\n//! his or her application and still use the Release profile.\n//!\n//! ### `-d,--description`\n//!\n//! Available for the _init_ (`cargo wix init`), _print_ (`cargo wix print`),\n//! and _sign_ (`cargo wix sign`) subcommands.\n//!\n//! The package description is used in multiple places for the installer,\n//! including the text that appears in the blue UAC dialog when using a signed\n//! installer. This can be overridden using the `-d,--description` option with\n//! the `cargo wix init` or `cargo wix sign` subcommands, respectively.\n//!\n//! ### `-D,--dialog`\n//!\n//! Available for the _init_ (`cargo wix init`) and _print_ (`cargo wix print`)\n//! subcommands.\n//!\n//! Sets the path to an image file (.png, bmp, ...) that will be displayed as\n//! the background of the first dialog of the installer. The dialog image dimensions\n//! should be 493 x 312 pixels, but the right-most 60% of that area is covered\n//! by the actual text of the dialog, so if you want to leave a blank background for text\n//! readability, you only want to color in the left-most ~200 pixels of that image.\n//!\n//! The first dialog is known as the \"Welcome\" dialog.\n//!\n//! ### `-e,--eula`\n//!\n//! Available for the _init_ (`cargo wix init`) and _print_ (`cargo wix print`)\n//! subcommands.\n//!\n//! Specifies a Rich Text Format (RTF) file to use as the End User License\n//! Agreement (EULA) for the license agreement dialog of the installer. The\n//! default is to disable the license agreement dialog unless one of the\n//! supported licenses (GPL-3.0, Apache-2.0, or MIT) is generated based on the\n//! value of the `license` field in the package's manifest (Cargo.toml). An EULA\n//! can be enabled later by directly modifying the WiX Source (WXS) file with a\n//! text editor.\n//!\n//! When specified via `package.metadata.wix.eula` the path is assumed to be relative\n//! to the Cargo.toml (directory). This field can also be set to `false` to disable\n//! the eula even if we could auto-generate one for you, as described above.\n//!\n//! ### `--force`\n//!\n//! Available for the _init_ (`cargo wix init`) subcommand.\n//!\n//! Forces overwriting of generated files from the _init_ subcommand. Use with\n//! caution! This cannot be undone.\n//!\n//! ### `-h,--help`\n//!\n//! Available for all subcommands.\n//!\n//! The short flag, `-h`, will print a condensed version of the help text, while\n//! the long flag, `--help`, will print a more detailed version of the help\n//! text.\n//!\n//! ### `-u,--homepage`\n//!\n//! Available for the _sign_ (`cargo wix sign`) subcommand.\n//!\n//! This will be displayed in the ACL dialog.\n//!\n//! ### `--install`\n//!\n//! Available for the default _create_ (`cargo wix`) subcommand.\n//!\n//! Automatically runs the installer after creating it.\n//!\n//! ### `-i,--install-version`\n//!\n//! Available for the default _create_ (`cargo wix`) subcommand.\n//!\n//! Overrides the version from the package's manifest (Cargo.toml), which is\n//! used for the installer name and appears in the Add/Remove Programs (ARP)\n//! control panel.\n//!\n//! ### `-I,--include`\n//!\n//! Available only for the default _create_ (`cargo wix`) subcommand.\n//!\n//! This option can be used multiple times to include multiple WiX Source (WXS)\n//! files in the creation of an installer. The option takes a path to a single\n//! WXS file as its value. Any WXS files located in the default `wix` folder\n//! located within the package's root folder, i.e. same location as the\n//! package's manifest (Cargo.toml) are automatically included and used in the\n//! creation of the installer. This option allows the inclusion of other WXS\n//! files outside of the default `wix` location.\n//!\n//! ### `-l,--license`\n//!\n//! Available for the _init_ (`cargo wix init`) and _print_ (`cargo wix print`)\n//! subcommands.\n//!\n//! Overrides the `license-file` field or any license generated from the\n//! `license` field of the package's manifest (Cargo.toml). If an appropriate\n//! license file does not exist, cannot be found, or is not specified, then no\n//! license file is included in the installer or in the installation folder\n//! along side the `bin` folder. A file containing the license, such as a TXT,\n//! PDF, or RTF file, can be added later by directly editing the generated WiX\n//! Source file (WXS) in a text editor.\n//!\n//! When specified via `package.metadata.wix.license` the path is assumed to be relative\n//! to the Cargo.toml (directory). This field can also be set to `false` to disable\n//! the license auto-generation features described above.\n//!\n//! ### `-L,--linker-arg`\n//!\n//! Available for the default _create (`cargo wix`) subcommand.\n//!\n//! Appends an argument to the WiX linker (light.exe) invocation. This provides\n//! a mechanism for \"passing\" arguments to the linker. This can be called\n//! multiple times to pass multiple arguments (flags or options). Only one value\n//! per occurrence is allowed to avoid ambiguity during argument parsing. Note,\n//! if it is an option, i.e. argument with an accompanying value, then the value\n//! must be passed as a separate usage of this option. For example, adding an\n//! user-defined linker extension would require the following command `cargo wix\n//! -L -ext -L UserDefinedExtension` to yield a `light -ext\n//! UserDefinedExtension` invocation.\n//!\n//! ### `-l,--locale`\n//!\n//! Available for the default _create_ (`cargo wix`) subcommand.\n//!\n//! Sets the path to a WiX localization file (wxl) which contains localized\n//! strings. Use in conjunction with the `-c,--culture` option.\n//!\n//! ### `-m,--manufacturer`\n//!\n//! Available for the _init_ (`cargo wix init`) and _print_ (`cargo wix print`)\n//! subcommands.\n//!\n//! Overrides the `authors` field of the package's manifest\n//! (Cargo.toml) as the manufacturer within the installer. The manufacturer can\n//! be changed after initialization by directly modifying the WiX Source file\n//! (WXS) with a text editor.\n//!\n//! ### `-n,--name`\n//!\n//! Available for the default _create_ (`cargo wix`) subcommand.\n//!\n//! Overrides the `name` field in the package's manifest (Cargo.toml), which is\n//! used in the file name of the installer (msi). This does not change the name\n//! of the executable _within_ the installer.\n//!\n//! ### `--no-build`\n//!\n//! Available for the default _create_ (`cargo wix`) subcommand.\n//!\n//! This skips building the Rust package using Cargo for the Release target.\n//!\n//! ### `--nocapture`\n//!\n//! Available for the default _create_ (`cargo wix`) and _sign_ (`cargo wix sign`)\n//! subcommands.\n//!\n//! Displays all output from the builder (Cargo), compiler (candle.exe), linker\n//! (light.exe), and signer (signtool.exe) applications.\n//!\n//! ### `-o,--output`\n//!\n//! Available for the default _create_ (`cargo wix`), _init_ (`cargo wix init`) and\n//! _print_ (`cargo wix print`) subcommands.\n//!\n//! Sets the destination for _init_ subcommand files, such as the WiX Source\n//! file (WXS), an alternative to stdout for _print_ subcommand, and the created\n//! installer for the default _create_ subcommand.\n//!\n//! When used with the default _create_ subcommand to create an installer (MSI),\n//! if the path is to an existing directory or the path has a trailing `/` or\n//! `\\`, then the MSI will be available after creation at the specified path,\n//! but the MSI file name will be the default file name based on the package\n//! name, version, and platform.\n//!\n//! ### `-O,--owner`\n//!\n//! Available for the _init_ (`cargo wix init`) and _print_ (`cargo wix print`)\n//! subcommands.\n//!\n//! Sets the copyright owner of the license during initialization or printing.\n//! The default is to use the `authors` field of the package's manifest\n//! (Cargo.toml). This is only used when generating a license based on the value\n//! of the `license` field in the package's manifest.\n//!\n//! ### `-p,--package`\n//!\n//! Available for the _create_ (`cargo wix`), _init_ (`cargo wix init`), and\n//! _print_ (`cargo wix print`) subcommands.\n//!\n//! Selects the package within a workspace. This is required if a project\n//! organized with a workspace. A workspace can have one or more members, where\n//! each member may have a separate installer. This option has no effect if the\n//! project does not use a workspace.\n//!\n//! ### `--path-guid`\n//!\n//! Available for the _init_ (`cargo wix init`) and _print_ (`cargo wix print`)\n//! subcommands.\n//!\n//! Overrides the automatically generated GUID for the path component with an\n//! existing GUID in the hyphenated, uppercase format. The path GUID should only\n//! be generated once for a product/project. The same GUID should be used for\n//! all installer creations to ensure no artifacts are left after uninstalling\n//! and proper modification of the `PATH` environment variable.\n//!\n//! ### `--product-icon`\n//!\n//! Available for the _init_ (`cargo wix init`) and _print_ (`cargo wix print`)\n//! subcommands.\n//!\n//! Sets the path to a 16x16 image file (.ico) that will be display as an icon in the\n//! Add/Remove Programs (ARP) control panel for the installed application.\n//!\n//! ### `--product-name`\n//!\n//! Available for the _init_ (`cargo wix init`), _print_ (`cargo wix print`),\n//! and _sign_ (`cargo wix sign`) subcommands.\n//!\n//! Overrides the `name` field of the package's manifest (Cargo.toml) as the\n//! product name within the installer. The product name can be changed after\n//! initialization or printing by directly modifying the WiX Source file (wxs)\n//! with a text editor.\n//!\n//! ### `-t,--timestamp`\n//!\n//! Available for the _sign_ (`cargo wix sign`) subcommand.\n//!\n//! An alias or URL to a timestamp server when signing an installer with a\n//! certificate. Valid aliases are: `Comodo` and `Versign`, which are case\n//! insensitive.\n//!\n//! ### `--upgrade-guid`\n//!\n//! Available for the _init_ (`cargo wix init`) and _print_ (`cargo wix print`)\n//! subcommands.\n//!\n//! Overrides the automatically generated GUID for the product's upgrade code\n//! with an existing GUID in the hyphenated, uppercase format. The upgrade code\n//! should only be generated once for a product/project. The same upgrade code\n//! should then be used for all installer creations of the same product/project. If\n//! a new GUID is used every time an installer is created, then each installer will\n//! be installing the same product but as separate installations.\n//!\n//! ### `-u,--url`\n//!\n//! Available for the _init_ (`cargo wix init`) and _print_ (`cargo wix print`)\n//! subcommands.\n//!\n//! Adds a URL to the installer that will be displayed in the Add/Remove\n//! Programs (ARP) control panel for the application. The default is to disable\n//! it unless a URL is specified for either the `homepage`, `documentation`, or\n//! `repository` fields in the package's manifest (Cargo.toml). The help URL can\n//! be enabled after initialization by directly modifying the WiX Source (wxs)\n//! file with a text editor.\n//!\n//! ### `-V,--version`\n//!\n//! Available for all subcommands.\n//!\n//! Prints the cargo-wix binary and subcommand version.\n//!\n//! ### `-v,--verbose`\n//!\n//! Available for all subcommands.\n//!\n//! Increases the level of logging statements based on occurrence count of the\n//! flag. The more `-v,--verbose` flags used, the more logging statements that\n//! will be printed during execution of a subcommand. When combined with the\n//! `--nocapture` flag, this is useful for debugging and testing.\n//!\n//! ### `-i,--installer`\n//!\n//! Available for the _sign_ (`cargo wix sign`) subcommand.\n//!\n//! Speicifies path to the installer(msi) to be signed.\n//!\n//! ### `-y,--year`\n//!\n//! Available for the _init_ (`cargo wix init`) and _print_ (`cargo wix print`)\n//! subcommands.\n//!\n//! Sets the copyright year for the license during initialization. The default\n//! is to use the current year. This is only used if a license is generated from\n//! one of the supported licenses based on the value of the `license` field in\n//! the package's manifest (Cargo.toml).\n//!\n//! [Alacritty]: https://github.com/alacritty/alacritty\n//! [bundle-based installers]: https://wixtoolset.org/documentation/manual/v3/bundle/\n//! [Cargo]: https://crates.io\n//! [cargo subcommand]: https://github.com/rust-lang/cargo/wiki/Third-party-cargo-subcommands\n//! [crates.io]: https://crates.io\n//! [Developer Prompt]: https://msdn.microsoft.com/en-us/library/f35ctcxw.aspx\n//! [`description`]: https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata\n//! [`documentation`]: https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata\n//! [documentation]: http://wixtoolset.org/documentation/\n//! [git bash]: https://gitforwindows.org/\n//! [GUID]: https://en.wikipedia.org/wiki/Universally_unique_identifier\n//! [`homepage`]: https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata\n//! [library]: ../wix/index.html\n//! [LibreOffice]: https://www.libreoffice.org/\n//! [`license`]: https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata\n//! [`license-file`]: https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata\n//! [Merge module]: https://wixtoolset.org/documentation/manual/v3/howtos/redistributables_and_install_checks/install_vcredist.html\n//! [Microsoft Office]: https://products.office.com/en-us/home\n//! [Microsoft Notepad]: https://en.wikipedia.org/wiki/Microsoft_Notepad\n//! [PowerShell]: https://github.com/PowerShell/PowerShell\n//! [`repository`]: https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata\n//! [Rich Text Format]: https://en.wikipedia.org/wiki/Rich_Text_Format\n//! [Rust]: https://www.rust-lang.org\n//! [sidecar]: https://en.wikipedia.org/wiki/Sidecar_file\n//! [SignTool]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa387764(v=vs.85).aspx\n//! [statically link the CRT]: https://doc.rust-lang.org/reference/linkage.html#static-and-dynamic-c-runtimes\n//! [`std::process::Command`]: https://doc.rust-lang.org/std/process/struct.Command.html\n//! [`std::process::Command::status`]: https://doc.rust-lang.org/std/process/struct.Command.html#method.status\n//! [TOML array]: https://github.com/toml-lang/toml#user-content-array\n//! [tutorials]: https://www.firegiant.com/wix/tutorial/\n//! [VC Build Tools]: https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2017\n//! [virtual manifest]: https://doc.rust-lang.org/cargo/reference/workspaces.html\n//! [Windows 10 SDK]: https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk\n//! [WixBalExtension]: https://wixtoolset.org/documentation/manual/v3/bundle/wixstdba/\n//! [WixUIExtension]: https://wixtoolset.org//documentation/manual/v3/wixui/wixui_dialog_library.html\n//! [WixUtilExtension]: https://wixtoolset.org/documentation/manual/v3/xsd/util/\n//! [WixUI localization documentation]: http://wixtoolset.org/documentation/manual/v3/wixui/wixui_localization.html\n//! [WiX preprocessor]: https://wixtoolset.org/documentation/manual/v3/overview/preprocessor.html\n//! [WiX Toolset]: http://wixtoolset.org\n//! [WordPad]: https://en.wikipedia.org/wiki/WordPad\n//! [workspace]: https://doc.rust-lang.org/cargo/reference/workspaces.html\n//! [WXS]: ../wix/enum.Template.html\n//! [XML]: https://en.wikipedia.org/wiki/XML\n\nuse clap::builder::EnumValueParser;\nuse clap::{Arg, ArgAction, Command};\n\nuse env_logger::Builder;\nuse env_logger::fmt::style::{AnsiColor as LogColor, Style};\n\nuse log::{Level, LevelFilter};\nuse std::io::Write;\nuse termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};\nuse wix::toolset::project::WxsSchema;\nuse wix::toolset::{Toolset, ToolsetSetupMode};\n\nuse wix::clean;\nuse wix::create;\nuse wix::initialize;\nuse wix::migrate;\nuse wix::print;\nuse wix::purge;\nuse wix::sign;\nuse wix::{BINARY_FOLDER_NAME, Template, WIX_PATH_KEY};\n\npub const PKG_DESCRIPTION: &str = env!(\"CARGO_PKG_DESCRIPTION\");\npub const PKG_NAME: &str = env!(\"CARGO_PKG_NAME\");\npub const PKG_VERSION: &str = env!(\"CARGO_PKG_VERSION\");\n\nconst SUBCOMMAND_NAME: &str = \"wix\";\n\nfn main() {\n    // The banner option for the `init` and `print` subcommands.\n    let banner = Arg::new(\"banner\")\n        .help(\"A path to an image file (.bmp) for the installer's banner\")\n        .long_help(\n            \"Sets the path to a bitmap (.bmp) image file that will be \\\n             displayed across the top of each dialog in the installer. The banner \\\n             image dimensions should be 493 x 58 pixels.\",\n        )\n        .long(\"banner\")\n        .short('b')\n        .num_args(1);\n    // The binaries option for the `init` and `print` subcommands.\n    let binaries = Arg::new(\"binaries\")\n        .help(\"A path to an executable file (.exe) for the application\")\n        .long_help(\n            \"Sets the path to an executable file that will override the default \\\n             binaries included in the installer. The default binaries are the \\\n             'target\\\\release\\\\<package name>.exe' file, where <package-name> is \\\n             the value from the 'name' field in the '[package]' section of the \\\n             package's manifest (Cargo.toml) if no '[[bin]]' sections are \\\n             defined; otherwise, all binaries defined in the package's manifest \\\n             in each '[[bin]]' section are included. This option overrides any \\\n             and all binaries defined in the package's manifest. Use this option \\\n             repeatedly to include multiple binaries.\",\n        )\n        .long(\"binary\")\n        .action(ArgAction::Append)\n        .short('B')\n        .num_args(1);\n    // The description option for the `init` and `print` subcommands.\n    let description = Arg::new(\"description\")\n        .help(\"A string describing the application in the installer\")\n        .long_help(\n            \"Overrides the 'description' field of the package's manifest \\\n             (Cargo.toml) as the description within the installer. Text with spaces \\\n             should be surrounded by double quotes.\",\n        )\n        .long(\"description\")\n        .short('d')\n        .num_args(1);\n    // The dialog option for the `init` and `print` subcommands.\n    let dialog = Arg::new(\"dialog\")\n        .help(\"A path to an image file (.bmp) for the installer's welcome dialog\")\n        .long_help(\n            \"Sets the path to a bitmap (.bmp) image file that will be \\\n             displayed to the left on the first dialog of the installer. The dialog \\\n             image dimensions should be 493 x 312 pxiels.\",\n        )\n        .long(\"dialog\")\n        .short('D')\n        .num_args(1);\n    // The eula option for the `init` and `print` subcommands.\n    let eula = Arg::new(\"eula\")\n        .help(\"A path to a RTF file (.rtf) for the installer's license agreement dialog\")\n        .long_help(\n            \"Specifies a Rich Text Format (RTF) file to use as the End \\\n             User License Agreement (EULA) for the license agreement dialog of the \\\n             installer. The default is to disable the license agreement dialog unless \\\n             one of the supported licenses (GPL-3.0, Apache-2.0, or MIT) is generated \\\n             based on the value of the 'license' field in the package's manifest \\\n             (Cargo.toml). An EULA can be enabled later by directly modifying the WiX \\\n             Source (wxs) file with a text editor.\",\n        )\n        .long(\"eula\")\n        .short('e')\n        .num_args(1);\n    // The license option for the `init` and `print` subcommands.\n    let license = Arg::new(\"license\")\n        .help(\"A path to any file (.txt, .pdf, .rtf, etc.) to be used as a license\")\n        .long_help(\n            \"Overrides the 'license-file' field or any license generated \\\n             from the 'license' field of the package's manifest (Cargo.toml). If an \\\n             appropriate license file does not exist, cannot be found, or is not \\\n             specified, then no license file is included in the installer or the \\\n             installation folder along side the binary. A file containing the \\\n             license, such as a TXT, PDF, or RTF file, can be added later by directly \\\n             editing the generated WiX Source file (wxs) in a text editor.\",\n        )\n        .long(\"license\")\n        .short('l')\n        .num_args(1);\n    // The url option for the `init` and `print` subcommands\n    let url = Arg::new(\"url\")\n        .help(\"A URL for the Add/Remove Programs control panel's Help Link\")\n        .long_help(\n            \"Adds a URL to the installer that will be displayed in the \\\n             Add/Remove Programs control panel for the application. The default is to \\\n             disable it unless a URL is specified for either the 'homepage', \\\n             'documentation', or 'repository' fields in the package's manifest \\\n             (Cargo.toml). The help URL can be enabled after initialization by \\\n             directly modifying the WiX Source (wxs) file with a text editor.\",\n        )\n        .long(\"url\")\n        .short('u')\n        .num_args(1);\n    // The manufacturer option for the `init` and `print` subcommands\n    let manufacturer = Arg::new(\"manufacturer\")\n        .help(\"A string for the Add/Remove Programs control panel's Manufacturer\")\n        .long_help(\n            \"Overrides the 'authors' field of the \\\n             package's manifest (Cargo.toml) as the manufacturer within the \\\n             installer. The manufacturer can be changed after initialization by \\\n             directly modifying the WiX Source file (wxs) with a text editor.\",\n        )\n        .long(\"manufacturer\")\n        .short('m')\n        .num_args(1);\n    // The owner option for the `init` and `print` subcommands\n    let owner = Arg::new(\"owner\")\n        .help(\"A string for a generated license's copyright holder\")\n        .long_help(\n            \"Sets the copyright owner for the license during \\\n             initialization. The default is to use the `authors` field from the \\\n             package's manifest (Cargo.toml). This is only used when generating a \\\n             license based on the value of the 'license' field in the package's \\\n             manifest.\",\n        )\n        .long(\"owner\")\n        .short('O')\n        .num_args(1);\n    // The package option for `create` or `migrate`\n    let include = Arg::new(\"include\")\n        .help(\"Include an additional WiX Source (wxs) file\")\n        .long_help(\n            \"Includes a WiX source (wxs) file for a project, \\\n        where the wxs file is not located in the default location, \\\n        i.e. 'wix'. Use this option multiple times to include \\\n        multiple wxs files.\",\n        )\n        .long(\"include\")\n        .short('I')\n        .num_args(1)\n        .action(ArgAction::Append);\n    // The package option for the `create`, `init`, and `print` subcommands\n    let package = Arg::new(\"package\")\n        .help(\"The name of the package in the current workspace\")\n        .long_help(\n            \"Selects the package within a project organized as a workspace. \\\n             Workspaces have one or more members, where each member is a package. \\\n             This option selects the package by name.\",\n        )\n        .long(\"package\")\n        .short('p')\n        .num_args(1);\n    // The path guid option for the `init` and `print` subcommands\n    let path_guid = Arg::new(\"path-guid\")\n        .help(\"A string formatted as a v4 hyphenated, uppercase UUID for the path component\")\n        .long_help(\n            \"Overrides the automatically generated GUID for the path component. \\\n             The path component needs a constant GUID so that the PATH \\\n             environment variable can be updated properly during uninstall and \\\n             upgrading. Generally, the GUID is generated once at the start \\\n             of a product/project and stored in the WiX Source (WXS) file. Using \\\n             a different GUID for each installer creation will leave artifacts \\\n             after uninstallation.\",\n        )\n        .long(\"path-guid\")\n        .num_args(1);\n    // The product icon option for the `init` and `print` subcommands\n    let product_icon = Arg::new(\"product-icon\")\n        .help(\"A path to an image file (.ico) for the Add/Remove Programs control panel\")\n        .long_help(\n            \"Sets the path to an image file that will be displayed as an \\\n             icon in the Add/Remove Programs (ARP) control panel for the installed \\\n             application.\",\n        )\n        .long(\"product-icon\")\n        .num_args(1);\n\n    // The product name option for the `init`, `print`, and `sign` subcommands.\n    let product_name = Arg::new(\"product-name\")\n        .help(\"A string for the Add/Remove Programs control panel's Name\")\n        .long_help(\n            \"Overrides the 'name' field of the package's manifest \\\n             (Cargo.toml) as the product name within the installer. The product name \\\n             can be changed after initialization by directly modifying the WiX Source \\\n             file (wxs) with a text editor.\",\n        )\n        .long(\"product-name\")\n        .num_args(1);\n    // The upgrade guid option for the `init` and `print` subcommands.\n    let upgrade_guid = Arg::new(\"upgrade-guid\")\n        .help(\"A string formatted as a v4 hyphenated, uppercase UUID for the globally unique upgrade code\")\n        .long_help(\n            \"Overrides the automatically generated GUID for the product's \\\n             upgrade code. The upgrade code is used to determine if the installer \\\n             is for a different product or the same product but should be \\\n             upgraded instead of a new install. Generally, the upgrade code is \\\n             generated once at the start of a product/project and stored in the \\\n             WiX Source (WXS) file. Using a different GUID for each installer \\\n             creation will install separate versions of a product.\"\n        )\n        .long(\"upgrade-guid\")\n        .num_args(1);\n    // The \"global\" verbose flag for all subcommands.\n    let verbose = Arg::new(\"verbose\")\n        .help(\"The verbosity level for logging statements\")\n        .long_help(\n            \"Sets the level of verbosity. The higher the level of \\\n             verbosity, the more information that is printed and logged when the \\\n             application is executed. This flag can be specified multiple times, \\\n             where each occurrence increases the level and/or details written for \\\n             each statement.\",\n        )\n        .long(\"verbose\")\n        .short('v')\n        .action(ArgAction::Count);\n    let year = Arg::new(\"year\")\n        .help(\"A string for a generated license's copyright year\")\n        .long_help(\n            \"Sets the copyright year for the license during \\\n             initialization. The default is to use the current year. This is only \\\n             used if a license is generated from one of the supported licenses based \\\n             on the value of the 'license' field in the package's manifest \\\n             (Cargo.toml).\",\n        )\n        .long(\"year\")\n        .short('y')\n        .num_args(1);\n    let matches = Command::new(PKG_NAME)\n        .bin_name(\"cargo\")\n        .subcommand(\n            Command::new(SUBCOMMAND_NAME)\n                .version(PKG_VERSION)\n                .about(PKG_DESCRIPTION)\n                .arg(Arg::new(\"bin-path\")\n                     .help(format!(\n                         \"A path to the WiX Toolset's '{BINARY_FOLDER_NAME}' folder\"))\n                     .long_help(format!(\n                         \"Specifies the path to the WiX Toolset's '{BINARY_FOLDER_NAME}' folder, which should contain \\\n                         the needed 'candle.exe' and 'light.exe' applications. The default is to use \\\n                         the path specified with the {WIX_PATH_KEY} system environment variable that is created \\\n                         during the installation of the WiX Toolset. Failing the existence of the \\\n                         {WIX_PATH_KEY} system environment variable, the path specified in the PATH system \\\n                         environment variable is used. This is useful when working with multiple \\\n                         versions of the WiX Toolset.\"))\n                     .long(\"bin-path\")\n                     .short('b')\n                     .num_args(1))\n                .subcommand(Command::new(\"clean\")\n                    .version(PKG_VERSION)\n                    .about(\"Deletes the 'target\\\\wix' folder\")\n                    .long_about(\"Deletes the 'target\\\\wix' folder if it exists.\")\n                    .arg(verbose.clone())\n                    .arg(Arg::new(\"INPUT\")\n                         .help(\"A path to a package's manifest (Cargo.toml)\")\n                         .long_help(\"The 'target\\\\wix' folder that exists \\\n                            alongside the package's manifest will be removed. This \\\n                            is optional and the default is to use the current \\\n                            working directory (cwd).\")\n                         .index(1)))\n                .arg(Arg::new(\"culture\")\n                    .help(\"The culture code for localization\")\n                    .long_help(\"Sets the culture for localization. Use with the \\\n                        '-l,--locale' option. See the WixUI localization \\\n                        documentation for more information about acceptable culture \\\n                        codes. The codes are case insensitive.\")\n                    .long(\"culture\")\n                    .short('c')\n                    .num_args(1))\n                .arg(Arg::new(\"compiler-arg\")\n                    .help(\"Send an argument to the WiX compiler (candle.exe)\")\n                    .long_help(\"Appends the argument to the command that is \\\n                        invoked when compiling an installer. This is the same as \\\n                        manually typing the option or flag for the compiler at the \\\n                        command line. If the argument is for an option with a value, \\\n                        the option's value must be passed as a separate call of this \\\n                        option. Multiple occurrences are possible, but only one \\\n                        value per occurrence is allowed to avoid ambiguity in \\\n                        argument parsing. For example, '-C -ext -C \\\n                        WixUtilExtension'.\")\n                    .long(\"compiler-arg\")\n                    .short('C')\n                    .num_args(1)\n                    .action(ArgAction::Append)\n                    .allow_hyphen_values(true))\n                .arg(Arg::new(\"target\")\n                    .help(\"The cargo target to build the WiX installer for.\")\n                    .long_help(\"Tells cargo to build the given target, and instructs \\\n                        WiX to build an installer targeting the right architecture.\")\n                    .long(\"target\")\n                    .short('t')\n                    .num_args(1))\n                .arg(Arg::new(\"debug-build\")\n                    .help(\"Builds the package using the Debug profile\")\n                    .long_help(\"Uses the Debug profile when building the package \\\n                        using Cargo. The default is to build the package using the \\\n                        Release profile.\")\n                    .long(\"dbg-build\")\n                    .short('d')\n                    .action(ArgAction::SetTrue))\n                .arg(Arg::new(\"profile\")\n                    .help(\"Builds the package using the given profile\")\n                    .long_help(\"Uses the given profile when building the package \\\n                        using Cargo. The default is to build the package using the \\\n                        Release profile.\")\n                    .long(\"profile\")\n                    .num_args(1))\n                .arg(Arg::new(\"debug-name\")\n                    .help(\"Appends '-debug' to the file stem of installer's file name\")\n                    .long_help(\"Adds the '-debug' suffix to the file stem \\\n                        (content before the file extension) for the installer's file \\\n                        name. This should be used to indicate the binary distributed \\\n                        within the installer was built using debugging information \\\n                        and optimizations. Generally, this should be used in \\\n                        combination with the '-d,--dbg-build' flag to build the \\\n                        binary with the Debug profile.\")\n                    .long(\"dbg-name\")\n                    .short('D')\n                    .action(ArgAction::SetTrue))\n                .arg(include.clone())\n                .subcommand(Command::new(\"init\")\n                    .version(PKG_VERSION)\n                    .about(\"Generates files from a package's manifest (Cargo.toml) to create an installer\")\n                    .long_about(\"Uses a package's manifest (Cargo.toml) to generate a WiX Source (wxs) \\\n                        file that can be used immediately without modification to create an \\\n                        installer for the package. This will also generate an EULA in the Rich \\\n                        Text Format (RTF) if the 'license' field is specified with a supported \\\n                        license (GPL-3.0, Apache-2.0, or MIT). All generated files are placed in \\\n                        the 'wix' sub-folder by default.\")\n                        .arg(Arg::new(\"INPUT\")\n                        .help(\"A path to a package's manifest (Cargo.toml)\")\n                        .long_help(\"If the '-o,--output' option is not used, \\\n                            then all output from initialization will be placed in a \\\n                            'wix' folder created alongside this path.\")\n                        .index(1))\n                    .arg(banner.clone())\n                    .arg(binaries.clone())\n                    .arg(description.clone())\n                    .arg(dialog.clone())\n                    .arg(eula.clone())\n                    .arg(Arg::new(\"force\")\n                        .help(\"Overwrite existing WiX-related files\")\n                        .long_help(\"Overwrites any existing files that are \\\n                            generated during initialization. Use with caution.\")\n                        .long(\"force\")\n                        .action(ArgAction::SetTrue))\n                    .arg(license.clone())\n                    .arg(manufacturer.clone())\n                    .arg(Arg::new(\"output\")\n                        .help(\"A path to a folder for generated files\")\n                        .long_help(\"Sets the destination for all files \\\n                            generated during initialization. The default is to \\\n                            create a 'wix' folder within the project then generate \\\n                            all files in the 'wix' sub-folder.\")\n                        .long(\"output\")\n                        .short('o')\n                        .num_args(1))\n                    .arg(owner.clone())\n                    .arg(package.clone())\n                    .arg(path_guid.clone())\n                    .arg(product_icon.clone())\n                    .arg(product_name.clone())\n                    .arg(upgrade_guid.clone())\n                    .arg(url.clone())\n                    .arg(verbose.clone())\n                    .arg(year.clone())\n                    .arg(Arg::new(\"schema\")\n                        .long(\"schema\")\n                        .help(\"The schema version of wxs source to render\")\n                        .long_help(\n                            \"Between Wix3 and Wix4, wxs files have differing schemas.\\n\\\n                            This setting allows initialization to render the corresponding schema \\n\\\n                            The default setting is the legacy format (Supported by Wix3)\")\n                        .num_args(1)\n                        .value_parser(EnumValueParser::<WxsSchema>::new())))\n                .arg(Arg::new(\"INPUT\")\n                     .help(\"Path to a package's manifest (Cargo.toml) file.\")\n                     .long_help(\"If no value is provided, then the current \\\n                        working directory (CWD) will be used to locate a package's \\\n                        manifest. An error will occur if a manifest cannot be \\\n                        found. A relative or absolute path to a package's manifest \\\n                        (Cargo.toml) file can be used. Only one manifest is \\\n                        allowed. The creation of an installer will be relative to \\\n                        the specified manifest.\")\n                     .required(false)\n                     .index(1))\n                .arg(Arg::new(\"install-version\")\n                    .help(\"A string for the Add/Remove Programs control panel's version number\")\n                    .long_help(\"Overrides the version from the package's manifest \\\n                        (Cargo.toml), which is used for the installer name and \\\n                        appears in the Add/Remove Programs control panel.\")\n                    .long(\"install-version\")\n                    .short('i')\n                    .num_args(1))\n                .arg(Arg::new(\"linker-arg\")\n                    .help(\"Send an argument to the WiX linker (light.exe)\")\n                    .long_help(\"Appends the argument to the command that is \\\n                        invoked when linking an installer. This is the same as \\\n                        manually typing the option or flag for the linker at the \\\n                        command line. If the argument is for an option with a value, \\\n                        the option's value must be passed as a separate call of this \\\n                        option. Multiple occurrences are possible, but only one \\\n                        value per occurrence is allowed to avoid ambiguity in \\\n                        argument parsing. For example, '-L -ext -L \\\n                        WixUIExtension'.\")\n                    .long(\"linker-arg\")\n                    .short('L')\n                    .num_args(1)\n                    .action(ArgAction::Append)\n                    .allow_hyphen_values(true))\n                .arg(Arg::new(\"locale\")\n                    .help(\"A path to a WiX localization file (.wxl)\")\n                    .long_help(\"Sets the path to a WiX localization file (wxl) \\\n                        which contains localized strings. Use in conjunction with \\\n                        the '-c,--culture' option.\")\n                    .long(\"locale\")\n                    .short('l')\n                    .num_args(1))\n                .arg(Arg::new(\"name\")\n                    .help(\"A string for the installer's product name\")\n                    .long_help(\"Overrides the 'name' field in the package's \\\n                        manifest (Cargo.toml), which is used in the file name of the \\\n                        installer (msi). This does not change the name of the \\\n                        executable within the installer.\")\n                    .long(\"name\")\n                    .short('n')\n                    .num_args(1))\n                .arg(Arg::new(\"no-build\")\n                    .help(\"Skips building the release binary\")\n                    .long_help(\"The installer is created, but the 'cargo build \\\n                        --release' is not executed.\")\n                    .long(\"no-build\")\n                    .action(ArgAction::SetTrue))\n                .arg(Arg::new(\"target-bin-dir\")\n                    .help(\"A path to the directory of binaries to include in the installer\")\n                    .long_help(\"Sets the CargoTargetBinDir variable that will be substituted \\\n                        into main.wxs. Use in conjunction with --no-build to fully handle builds.\")\n                    .long(\"target-bin-dir\")\n                    .num_args(1))\n                .arg(Arg::new(\"no-capture\")\n                    .help(\"Displays all output from the builder, compiler, linker, and signer\")\n                    .long_help(\"By default, this subcommand captures, or hides, \\\n                        all output from the builder, compiler, linker, and signer \\\n                        for the binary and Windows installer, respectively. Use this \\\n                        flag to show the output.\")\n                    .long(\"nocapture\")\n                    .action(ArgAction::SetTrue))\n                .arg(Arg::new(\"install\")\n                    .help(\"Runs the installer after creating it\")\n                    .long_help(\"Creates the installer and runs it after that.\")\n                    .long(\"install\")\n                    .action(ArgAction::SetTrue))\n                .arg(Arg::new(\"output\")\n                    .help(\"A path to a destination file or an existing folder\")\n                    .long_help(\"Sets the destination file name and path for the \\\n                        created installer, or the destination folder for the \\\n                        installer with the default file name. If the path is to \\\n                        an existing folder or has a trailing slash (forward and \\\n                        backward), then the default installer file name will be \\\n                        used and the installer will be available in the folder \\\n                        after creation. Otherwise, this value overwrites the \\\n                        default file name and path for the installer. The \\\n                        default is to create an installer with the \\\n                        '<product-name>-<version>-<arch>.msi' file name in the \\\n                        'target\\\\wix' folder.\")\n                    .long(\"output\")\n                    .short('o')\n                    .num_args(1))\n                .arg(Arg::new(\"toolset\")\n                    .long(\"toolset\")\n                    .help(\"Toolset to use for wix related operations\")\n                    .long_help(\n                        \"Before WiX4 the binaries that were used to build the installer were light.exe and candle.exe.\\n\\\n                         The WiX toolset now uses a single binary, wix.exe, that performs all the previous operations.\\n\\\n                         In addition WiX3 did not have a unified package installation method, where-as WiX4 and beyond\\n\\\n                         can be installed via `dotnet install`.\\n\\\n                         \\n\\\n                         This argument can be used to opt-in to the modern toolset, however it is important to note that\\n\\\n                         while WiX4+ are backwards compatible, WiX3 .wxs files are not compatible and must be converted\\n\\\n                         to WiX4 semantics. Luckily the modern wix toolsets contain a convert tool, however there are\\n\\\n                         several key differences after WiX3 that must be addressed before the files may be built.\\n\\\n                         If the `toolset-upgrade` argument is provided in addition to this command, cargo-wix can ensure\\n\\\n                         that the included wix source files are updated to the modern format and that the dependencies used\\n\\\n                         in those source files are installed in the current context. This also provides the benefit between\\n\\\n                         WiX4+ versions, as the wix toolset extensions now depend on the WiX toolset version, `cargo-wix`\\n\\\n                         can ensure that before the build starts all wix extensions are up to date.\")\n                    .value_parser(EnumValueParser::<Toolset>::new())\n                    .required(false)\n                    .num_args(1)\n                )\n                .arg(Arg::new(\"migrate\")\n                    .long(\"migrate\")\n                    .help(\"Toolset migration setup mode to use before building the installer\")\n                    .long_help(\n                        \"Note: Only applied if `--toolset modern` is present, if no migration setup is required this will have no additional effect\\n\\\n                        When enabled, this will perform checks to ensure that the current wix environment\\n\\\n                        is up to date before attempting to build the installer which includes:\\n\\\n                        \\t1) Ensuring that *.wxs files are in the correct format according to the wix toolset setting\\n\\\n                        \\t2) Installing extensions that are required to build the installer\\n\\\n                        There are several modes that can be used with inplace being the simplest and sxs being the least destructive\")\n                        .value_parser(EnumValueParser::<ToolsetSetupMode>::new())\n                        .num_args(1))\n                .arg(package.clone())\n                .subcommand(Command::new(\"print\")\n                    .version(PKG_VERSION)\n                    .about(\"Prints a template\")\n                    .long_about(\"Prints a template to stdout or a file. In the case \\\n                        of a license template, the output is in the Rich Text Format \\\n                        (RTF) and for a WiX Source file (wxs), the output is in XML. \\\n                        New GUIDs are generated for the 'UpgradeCode' and Path \\\n                        Component each time the 'WXS' template is printed. [values: \\\n                        Apache-2.0, GPL-3.0, MIT, WXS]\")\n                        .arg(Arg::new(\"TEMPLATE\")\n                        .help(\"A name of a template\")\n                        .long_help(\"This is required and values are case \\\n                            insensitive. [values: Apache-2.0, GPL-3.0, MIT, WXS]\")\n                        .hide_possible_values(true)\n                        .value_parser(Template::possible_values().iter().map(String::as_str).collect::<Vec<&str>>())\n                        .required(true)\n                        .index(1))\n                        .arg(Arg::new(\"INPUT\")\n                        .help(\"A path to a package's manifest (Cargo.toml)\")\n                        .long_help(\"The selected template will be printed to \\\n                            stdout or a file based on the field values in this \\\n                            manifest. The default is to use the manifest in the \\\n                            current working directory (cwd). An error occurs if a \\\n                            manifest is not found.\")\n                        .index(2))\n                    .arg(banner)\n                    .arg(binaries)\n                    .arg(description)\n                    .arg(dialog)\n                    .arg(eula)\n                    .arg(license)\n                    .arg(manufacturer)\n                    .arg(Arg::new(\"output\")\n                        .help(\"A path to a folder for generated files\")\n                        .long_help(\"Sets the destination for printing the \\\n                            template. The default is to print/write the rendered \\\n                            template to stdout. If the destination, a.k.a. file, \\\n                            does not exist, it will be created.\")\n                        .long(\"output\")\n                        .short('o')\n                        .num_args(1))\n                    .arg(owner)\n                    .arg(package.clone())\n                    .arg(path_guid)\n                    .arg(product_icon)\n                    .arg(product_name.clone())\n                    .arg(upgrade_guid)\n                    .arg(url)\n                    .arg(year)\n                    .arg(verbose.clone()))\n                .subcommand(Command::new(\"purge\")\n                    .version(PKG_VERSION)\n                    .about(\"Deletes the 'target\\\\wix' and 'wix' folders\")\n                    .long_about(\"Deletes the 'target\\\\wix' and 'wix' folders if they \\\n                        exist. Use with caution!\")\n                    .arg(Arg::new(\"INPUT\")\n                        .help(\"A path to a package's manifest (Cargo.toml)\")\n                        .long_help(\"The 'target\\\\wix' and 'wix' folders that \\\n                            exists alongside the package's manifest will be removed. \\\n                            This is optional and the default is to use the current \\\n                            working directory (cwd).\")\n                        .index(1)))\n                .subcommand(Command::new(\"sign\")\n                    .version(PKG_VERSION)\n                    .about(\"Signs an installer\")\n                    .long_about(\"The Windows installer (msi) will be signed using the \\\n                        SignTool application available in the Windows 10 SDK. The \\\n                        signtool is invoked with the '/a' flag to automatically \\\n                        obtain an appropriate certificate from the Windows \\\n                        certificate manager. The default is to also use the Comodo \\\n                        timestamp server with the '/t' flag.\")\n                    .arg(Arg::new(\"bin-path\")\n                        .help(\"A path to the folder containing the 'signtool' application\")\n                        .long_help(\"The default is to use the PATH system environment \\\n                             variable to locate the application.\")\n                        .long(\"bin-path\")\n                        .short('b')\n                        .num_args(1))\n                    .arg(Arg::new(\"description\")\n                        .help(\"A string for the extended ACL dialog\")\n                        .long_help(\"The information for the extended text of \\\n                            the ACL dialog that appears. This will be appended to \\\n                            the product name and delimited by a dash, '-'. The \\\n                            default is to use the description from the package's \\\n                            manifest (Cargo.toml). This option will override the \\\n                            default.\")\n                        .long(\"description\")\n                        .short('d')\n                        .num_args(1))\n                    .arg(Arg::new(\"homepage\")\n                        .help(\"A URL for the product's homepage\")\n                        .long_help(\"This will be displayed in the ACL dialog.\")\n                        .long(\"homepage\")\n                        .short('u')\n                        .num_args(1))\n                    .arg(Arg::new(\"INPUT\")\n                        .help(\"A path to a package's manifest (Cargo.toml)\")\n                        .long_help(\"The installer located in the 'target\\\\wix' \\\n                            folder alongside this manifest will be signed based on \\\n                            the metadata within the manifest.\")\n                        .index(1))\n                    .arg(Arg::new(\"installer\")\n                        .help(\"specify the installer to be signed\")\n                        .long_help(\"Specify the installer to be signed.\")\n                        .long(\"installer\")\n                        .short('i')\n                        .num_args(1))\n                    .arg(Arg::new(\"no-capture\")\n                        .help(\"Display output from the signer\")\n                        .long_help(\"By default, this subcommand captures, or \\\n                            hides, all output from the signer. Use this flag to \\\n                            show the output.\")\n                        .long(\"nocapture\")\n                        .action(ArgAction::SetTrue))\n                    .arg(product_name)\n                    .arg(package.clone())\n                    .arg(Arg::new(\"timestamp\")\n                        .help(\"An alias or URL to a timestamp server\")\n                        .long_help(\"Either an alias or URL can be used. Aliases \\\n                            are case-insensitive. [values: Comodo, Verisign]\")\n                        .short('t')\n                        .long(\"timestamp\")\n                        .num_args(1))\n                    .arg(verbose.clone()))\n                .subcommand(Command::new(\"migrate\")\n                        .version(PKG_VERSION)\n                        .about(\"WiX project migration setup utilities\")\n                        .long_about(\n                            \"Perform various wix toolset migration setup operations such as:\\n\\\n                            - Converting wxs files to the modern WiX toolset format\\n\\\n                            - Restoring wix extension packages by analyzing extension dependencies found in wxs files.\\n\\\n                            - Enable vendoring of wix extension dependencies for offline builds\\n\\\n                            \\n\\\n                            If no additional flags are passed such as `--vendor`\\n\\\n                            and no mode is set via `-m` or `--mode`\\n\\\n                            Then this will apply the 'global' setup mode, meaning:\\n\\\n                            - all wxs files will be converted in place\\n\\\n                            - all wix extensions will be installed globally\")\n                        .arg(Arg::new(\"INPUT\")\n                            .help(\"Path to a package's manifest (Cargo.toml) file.\")\n                            .long_help(\n                                \"If no value is provided, then the current\\n\\\n                                working directory (CWD) will be used to locate a package's\\n\\\n                                manifest. An error will occur if a manifest cannot be\\n\\\n                                found. A relative or absolute path to a package's manifest\\n\\\n                                (Cargo.toml) file can be used. Only one manifest is\\n\\\n                                allowed. The creation of an installer will be relative to\\n\\\n                                the specified manifest.\")\n                            .required(false)\n                            .index(1))\n                        .arg(package)\n                        .arg(include)\n                        .arg(Arg::new(\"vendor\")\n                            .long(\"vendor\")\n                            .help(\"Will enable vendoring when installing WiX extensions\")\n                            .long_help(\n                                \"By using vendoring mode, all extension dependencies will be installed in the current directory\\n\\\n                                This is suited for proejcts that must be built in environments without network access and/or\\n\\\n                                If tracking extension packages in SVC is desired\\n\\\n                                \\n\\\n                                Equivalent to `-m vendor` or `--mode vendor`\")\n                            .action(ArgAction::SetTrue))\n                        .arg(Arg::new(\"mode\")\n                            .long(\"mode\")\n                            .short('m')\n                            .help(\"Will explicitly configure the migration setup mode. Note: Using `none` will not have any effect with the `migrate` command.\")\n                            .value_parser(EnumValueParser::<ToolsetSetupMode>::new()))\n                        .arg(verbose.clone()))\n                .arg(verbose)\n        ).get_matches();\n    let matches = matches.subcommand_matches(SUBCOMMAND_NAME).unwrap();\n    let verbosity = match matches.subcommand() {\n        Some((\"clean\", m)) => m,\n        Some((\"init\", m)) => m,\n        Some((\"print\", m)) => m,\n        Some((\"purge\", m)) => m,\n        Some((\"migrate\", m)) => m,\n        _ => matches,\n    }\n    .get_count(\"verbose\");\n    // Using the `Builder::new` instead of the `Builder::from_env` or `Builder::from_default_env`\n    // skips reading the configuration from any environment variable, i.e. `RUST_LOG`. The log\n    // level is later configured with the verbosity using the `filter` method. There are many\n    // questions related to implementing support for environment variables:\n    //\n    // 1. What should the environment variable be called, WIX_LOG, CARGO_WIX_LOG, CARGO_LOG, etc.?\n    //    WIX_LOG might conflict with a system variable that is used for the WiX Toolset. CARGO_LOG\n    //    is too generic. The only viable one is CARGO_WIX_LOG.\n    // 2. How is the environment variable supposed to work with the verbosity without crazy side\n    //    effects? What if the level is set to TRACE with the environment variable, but the\n    //    verbosity is only one?\n    // 3. Should the RUST_LOG environment variable be \"obeyed\" for a cargo subcommand?\n    //\n    // For now, implementing environment variable support is held off and only the verbosity is\n    // used to set the log level.\n    let mut builder = Builder::new();\n    builder\n        .format(|buf, record| {\n            // This implementation for a format is copied from the default format implemented for the\n            // `env_logger` crate but modified to use a colon, `:`, to separate the level from the\n            // message and change the colors to match the previous colors used by the `loggerv` crate.\n            let level = record.level();\n            let style = match level {\n                // Light Gray, or just Gray, is not a supported color for non-ANSI enabled Windows\n                // consoles, so TRACE and DEBUG statements are differentiated by boldness but use the\n                // same white color.\n                Level::Trace => Style::new().fg_color(Some(LogColor::White.into())),\n                Level::Debug => Style::new().fg_color(Some(LogColor::White.into())).bold(),\n                Level::Info => Style::new().fg_color(Some(LogColor::Green.into())).bold(),\n                Level::Warn => Style::new().fg_color(Some(LogColor::Yellow.into())).bold(),\n                Level::Error => Style::new().fg_color(Some(LogColor::Red.into())).bold(),\n            };\n            let write_level = write!(buf, \"{style}{level:>5}{style:#}: \");\n            let write_args = writeln!(buf, \"{}\", record.args());\n            write_level.and(write_args)\n        })\n        .filter(\n            Some(\"wix\"),\n            match verbosity {\n                0 => LevelFilter::Warn,\n                1 => LevelFilter::Info,\n                2 => LevelFilter::Debug,\n                _ => LevelFilter::Trace,\n            },\n        )\n        .init();\n    let result = match matches.subcommand() {\n        Some((\"clean\", m)) => {\n            let mut clean = clean::Builder::new();\n            clean.input(m.get_one(\"INPUT\").map(String::as_str));\n            clean.build().run()\n        }\n        Some((\"init\", m)) => {\n            let mut init = initialize::Builder::new();\n            init.banner(m.get_one(\"banner\").map(String::as_str));\n            init.binaries(\n                m.get_many::<String>(\"binaries\")\n                    .map(|v| v.map(String::as_str).collect()),\n            );\n            init.copyright_holder(m.get_one(\"owner\").map(String::as_str));\n            init.copyright_year(m.get_one(\"year\").map(String::as_str));\n            init.description(m.get_one(\"description\").map(String::as_str));\n            init.dialog(m.get_one(\"dialog\").map(String::as_str));\n            init.eula(m.get_one(\"eula\").map(String::as_str));\n            init.force(m.get_flag(\"force\"));\n            init.help_url(m.get_one(\"url\").map(String::as_str));\n            init.input(m.get_one(\"INPUT\").map(String::as_str));\n            init.license(m.get_one(\"license\").map(String::as_str));\n            init.manufacturer(m.get_one(\"manufacturer\").map(String::as_str));\n            init.output(m.get_one(\"output\").map(String::as_str));\n            init.package(m.get_one(\"package\").map(String::as_str));\n            init.path_guid(m.get_one(\"path-guid\").map(String::as_str));\n            init.product_icon(m.get_one(\"product-icon\").map(String::as_str));\n            init.product_name(m.get_one(\"product-name\").map(String::as_str));\n            init.upgrade_guid(m.get_one(\"upgrade-guid\").map(String::as_str));\n            init.schema(m.get_one::<WxsSchema>(\"schema\").copied());\n            init.build().run()\n        }\n        Some((\"print\", m)) => {\n            let template = m\n                .get_one::<String>(\"TEMPLATE\")\n                .unwrap()\n                .parse::<Template>()\n                .unwrap();\n            match template {\n                Template::WxsV3 | Template::WxsV4 => {\n                    let mut print = print::wxs::Builder::new();\n                    print.banner(m.get_one(\"banner\").map(String::as_str));\n                    print.binaries(\n                        m.get_many(\"binaries\")\n                            .map(|v| v.map(String::as_str).collect()),\n                    );\n                    print.description(m.get_one(\"description\").map(String::as_str));\n                    print.dialog(m.get_one(\"dialog\").map(String::as_str));\n                    print.eula(m.get_one(\"eula\").map(String::as_str));\n                    print.help_url(m.get_one(\"url\").map(String::as_str));\n                    print.input(m.get_one(\"INPUT\").map(String::as_str));\n                    print.license(m.get_one(\"license\").map(String::as_str));\n                    print.manufacturer(m.get_one(\"manufacturer\").map(String::as_str));\n                    print.output(m.get_one(\"output\").map(String::as_str));\n                    print.package(m.get_one(\"package\").map(String::as_str));\n                    print.path_guid(m.get_one(\"path-guid\").map(String::as_str));\n                    print.product_icon(m.get_one(\"product-icon\").map(String::as_str));\n                    print.product_name(m.get_one(\"product-name\").map(String::as_str));\n                    print.upgrade_guid(m.get_one(\"upgrade-guid\").map(String::as_str));\n                    if matches!(template, Template::WxsV4) {\n                        print.schema(Some(WxsSchema::V4));\n                    }\n                    print.build().run()\n                }\n                t => {\n                    let mut print = print::license::Builder::new();\n                    print.copyright_holder(m.get_one(\"owner\").map(String::as_str));\n                    print.copyright_year(m.get_one(\"year\").map(String::as_str));\n                    print.input(m.get_one(\"INPUT\").map(String::as_str));\n                    print.output(m.get_one(\"output\").map(String::as_str));\n                    print.package(m.get_one(\"package\").map(String::as_str));\n                    print.build().run(&t)\n                }\n            }\n        }\n        Some((\"purge\", m)) => {\n            let mut purge = purge::Builder::new();\n            purge.input(m.get_one(\"INPUT\").map(String::as_str));\n            purge.build().run()\n        }\n        Some((\"sign\", m)) => {\n            let mut sign = sign::Builder::new();\n            sign.bin_path(m.get_one(\"bin-path\").map(String::as_str));\n            sign.capture_output(!m.get_flag(\"no-capture\"));\n            sign.description(m.get_one(\"description\").map(String::as_str));\n            sign.homepage(m.get_one(\"homepage\").map(String::as_str));\n            sign.input(m.get_one(\"INPUT\").map(String::as_str));\n            sign.installer(m.get_one(\"installer\").map(String::as_str));\n            sign.package(m.get_one(\"package\").map(String::as_str));\n            sign.product_name(m.get_one(\"product-name\").map(String::as_str));\n            sign.timestamp(m.get_one(\"timestamp\").map(String::as_str));\n            sign.build().run()\n        }\n        Some((\"migrate\", m)) => {\n            let mut setup = migrate::Builder::new();\n            setup.input(\n                m.get_one(\"INPUT\")\n                    .map(String::as_str)\n                    .or(matches.get_one(\"INPUT\").map(String::as_str)),\n            );\n            setup.package(\n                m.get_one(\"package\")\n                    .map(String::as_str)\n                    .or(matches.get_one(\"package\").map(String::as_str)),\n            );\n            setup.includes(\n                m.get_many(\"include\")\n                    .map(|v| v.map(String::as_str).collect())\n                    .or(matches\n                        .get_many(\"include\")\n                        .map(|v| v.map(String::as_str).collect())),\n            );\n            if let Some(mode) = m.get_one::<ToolsetSetupMode>(\"mode\") {\n                match mode {\n                    ToolsetSetupMode::None | ToolsetSetupMode::Global => {}\n                    ToolsetSetupMode::Vendor => {\n                        setup.vendor(true);\n                    }\n                }\n            } else {\n                setup.vendor(m.get_flag(\"vendor\"));\n            }\n            setup.build().run()\n        }\n        _ => {\n            let mut create = create::Builder::new();\n            create.bin_path(matches.get_one(\"bin-path\").map(String::as_str));\n            create.capture_output(!matches.get_flag(\"no-capture\"));\n            create.compiler_args(\n                matches\n                    .get_many(\"compiler-arg\")\n                    .map(|v| v.map(String::as_str).collect()),\n            );\n            create.culture(matches.get_one(\"culture\").map(String::as_str));\n            create.debug_build(matches.get_flag(\"debug-build\"));\n            create.profile(matches.get_one(\"profile\").map(String::as_str));\n            create.debug_name(matches.get_flag(\"debug-name\"));\n            create.includes(\n                matches\n                    .get_many(\"include\")\n                    .map(|v| v.map(String::as_str).collect()),\n            );\n            create.input(matches.get_one(\"INPUT\").map(String::as_str));\n            create.linker_args(\n                matches\n                    .get_many(\"linker-arg\")\n                    .map(|v| v.map(String::as_str).collect()),\n            );\n            create.locale(matches.get_one(\"locale\").map(String::as_str));\n            create.name(matches.get_one(\"name\").map(String::as_str));\n            create.no_build(matches.get_flag(\"no-build\"));\n            create.target_bin_dir(matches.get_one(\"target-bin-dir\").map(String::as_str));\n            create.install(matches.get_flag(\"install\"));\n            create.output(matches.get_one(\"output\").map(String::as_str));\n            create.version(matches.get_one(\"install-version\").map(String::as_str));\n            create.package(matches.get_one(\"package\").map(String::as_str));\n            create.target(matches.get_one(\"target\").map(String::as_str));\n            create.toolset(\n                matches\n                    .get_one(\"toolset\")\n                    .cloned()\n                    .unwrap_or(Toolset::Legacy),\n            );\n            create.toolset_migration(\n                matches\n                    .get_one(\"migrate\")\n                    .cloned()\n                    .unwrap_or(ToolsetSetupMode::None),\n            );\n            create.build().run()\n        }\n    };\n    match result {\n        Ok(_) => std::process::exit(0),\n        Err(e) => {\n            {\n                let mut stderr = StandardStream::stderr(ColorChoice::Auto);\n                stderr\n                    .set_color(ColorSpec::new().set_fg(Some(Color::Red)).set_bold(true))\n                    .expect(\"Coloring stderr\");\n                write!(&mut stderr, \"Error[{}] ({}): \", e.code(), e.as_str())\n                    .expect(\"Write tag to stderr\");\n                // This prevents \"leaking\" the color settings to the console after the\n                // sub-command/application has completed and ensures the message is not printed in\n                // Red.\n                //\n                // See:\n                //\n                // - [Issue #47](https://github.com/volks73/cargo-wix/issues/47)\n                // - [Issue #48](https://github.com/volks73/cargo-wix/issues/48).\n                stderr\n                    .reset()\n                    .expect(\"Revert color settings after printing the tag\");\n                stderr\n                    .set_color(ColorSpec::new().set_fg(Some(Color::White)).set_bold(false))\n                    .expect(\"Coloring stderr\");\n                writeln!(&mut stderr, \"{e}\").expect(\"Write message to stderr\");\n                // This prevents \"leaking\" the color settings to the console after the\n                // sub-command/application has completed.\n                //\n                // See:\n                //\n                // - [Issue #47](https://github.com/volks73/cargo-wix/issues/47)\n                // - [Issue #48](https://github.com/volks73/cargo-wix/issues/48).\n                stderr\n                    .reset()\n                    .expect(\"Revert color settings after printing the message\");\n            }\n            std::process::exit(e.code());\n        }\n    }\n}\n"
  },
  {
    "path": "src/migrate.rs",
    "content": "// Copyright (C) 2017 Christopher R. Field.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//! The implementation for the `migrate` command. The default behavior will use the\n//! \"Project\" setup mode, which will update any *.wxs source files to the modern format as well\n//! as install the extension packages to the global cache.\n//!\n//! Specific information about the different toolset modes can be found in the documentation for\n//! `ToolsetSetupMode`.\n\nuse crate::toolset::*;\nuse log::debug;\nuse std::path::PathBuf;\n\n/// A builder for running the `cargo wix migrate` subcommand.\n#[derive(Debug, Clone)]\npub struct Builder<'a> {\n    input: Option<&'a str>,\n    package: Option<&'a str>,\n    includes: Option<Vec<&'a str>>,\n    vendor: bool,\n}\n\nimpl<'a> Builder<'a> {\n    /// Creates a new Builder\n    pub fn new() -> Self {\n        Self {\n            input: None,\n            package: None,\n            includes: None,\n            vendor: false,\n        }\n    }\n\n    /// Sets the path to a package's manifest (Cargo.toml) file.\n    ///\n    /// A package's manifest is used to create an installer. If no path is\n    /// specified, then the current working directory (CWD) is used. An error\n    /// will occur if there is no `Cargo.toml` file in the CWD or at the\n    /// specified path. Either an absolute or relative path is valid.\n    ///\n    /// This value will override any default and skip looking for a value in the\n    /// `[package.metadata.wix]` section of the package's manifest (Cargo.toml).\n    pub fn input(&mut self, i: Option<&'a str>) -> &mut Self {\n        self.input = i;\n        self\n    }\n\n    /// Adds multiple WiX Source (wxs) files to the creation of an installer.\n    ///\n    /// By default, any `.wxs` file located in the project's `wix` folder will\n    /// be included in the creation of an installer for the project. This method\n    /// adds, or appends, to the list of `.wxs` files. The value is a relative\n    /// or absolute path.\n    ///\n    /// This value will override any default and skip looking for a value in the\n    /// `[package.metadata.wix]` section of the package's manifest (Cargo.toml).\n    pub fn includes(&mut self, i: Option<Vec<&'a str>>) -> &mut Self {\n        self.includes = i;\n        self\n    }\n\n    /// Sets the package.\n    ///\n    /// If the project is organized using a workspace, this selects the package\n    /// by name to create an installer. If a workspace is not used, then this\n    /// has no effect.\n    pub fn package(&mut self, p: Option<&'a str>) -> &mut Self {\n        self.package = p;\n        self\n    }\n\n    /// Enables vendored mode when restoring Wix extensions\n    pub fn vendor(&mut self, vendored: bool) -> &mut Self {\n        self.vendor = vendored;\n        self\n    }\n\n    /// Consumes the builder and returns the upgrade execution context\n    ///\n    /// Will resolve toolset migration setup mode from the provided flags\n    pub fn build(self) -> Execution {\n        Execution {\n            input: self.input.map(PathBuf::from),\n            package: self.package.map(String::from),\n            includes: self\n                .includes\n                .as_ref()\n                .map(|v| v.iter().map(&PathBuf::from).collect()),\n            toolset_setup_mode: if self.vendor {\n                ToolsetSetupMode::Vendor\n            } else {\n                ToolsetSetupMode::Global\n            },\n        }\n    }\n}\n\n/// A context for setting up a WiX project migration to a new toolset\n#[derive(Debug)]\npub struct Execution {\n    input: Option<PathBuf>,\n    package: Option<String>,\n    includes: Option<Vec<PathBuf>>,\n    toolset_setup_mode: ToolsetSetupMode,\n}\n\nimpl Execution {\n    /// Consumes the execution context and performs the migration setup\n    pub fn run(self) -> crate::Result<()> {\n        debug!(\"self.input = {:?}\", self.input);\n        debug!(\"self.package = {:?}\", self.package);\n        debug!(\"self.includes = {:?}\", self.includes);\n        debug!(\"self.toolset_upgrade = {:?}\", self.toolset_setup_mode);\n\n        debug!(\"Resolving manifest\");\n        let manifest = crate::manifest(self.input.as_ref())?;\n        debug!(\"{manifest:?}\");\n        debug!(\"Resolving package\");\n        let package = crate::package(&manifest, self.package.as_deref())?;\n\n        debug!(\"Evaluating project and beginning setup\");\n        self.toolset_setup_mode.setup(&self, &package, None)?;\n        Ok(())\n    }\n}\n\nimpl Includes for Execution {\n    fn includes(&self) -> Option<&Vec<PathBuf>> {\n        self.includes.as_ref()\n    }\n}\n\nimpl<'a> Default for Builder<'a> {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn builder_defaults() {\n        let builder = Builder::new();\n        assert!(builder.input.is_none());\n        assert!(builder.package.is_none());\n        assert!(builder.includes.is_none());\n        assert!(!builder.vendor);\n    }\n\n    #[test]\n    fn builder_input_works() {\n        let mut builder = Builder::new();\n        builder.input(Some(\"path/to/Cargo.toml\"));\n        assert_eq!(builder.input, Some(\"path/to/Cargo.toml\"));\n    }\n\n    #[test]\n    fn builder_package_works() {\n        let mut builder = Builder::new();\n        builder.package(Some(\"my-package\"));\n        assert_eq!(builder.package, Some(\"my-package\"));\n    }\n\n    #[test]\n    fn builder_includes_works() {\n        let mut builder = Builder::new();\n        builder.includes(Some(vec![\"a.wxs\", \"b.wxs\"]));\n        assert_eq!(builder.includes, Some(vec![\"a.wxs\", \"b.wxs\"]));\n    }\n\n    #[test]\n    fn builder_vendor_works() {\n        let mut builder = Builder::new();\n        builder.vendor(true);\n        assert!(builder.vendor);\n    }\n\n    #[test]\n    fn build_sets_global_mode_by_default() {\n        let execution = Builder::new().build();\n        assert!(matches!(\n            execution.toolset_setup_mode,\n            ToolsetSetupMode::Global\n        ));\n    }\n\n    #[test]\n    fn build_sets_vendor_mode_when_vendor_enabled() {\n        let mut builder = Builder::new();\n        builder.vendor(true);\n        let execution = builder.build();\n        assert!(matches!(\n            execution.toolset_setup_mode,\n            ToolsetSetupMode::Vendor\n        ));\n    }\n\n    #[test]\n    fn build_converts_input_to_pathbuf() {\n        let mut builder = Builder::new();\n        builder.input(Some(\"some/path\"));\n        let execution = builder.build();\n        assert_eq!(execution.input, Some(PathBuf::from(\"some/path\")));\n    }\n\n    #[test]\n    fn build_converts_includes_to_pathbufs() {\n        let mut builder = Builder::new();\n        builder.includes(Some(vec![\"a.wxs\", \"b.wxs\"]));\n        let execution = builder.build();\n        assert_eq!(\n            execution.includes,\n            Some(vec![PathBuf::from(\"a.wxs\"), PathBuf::from(\"b.wxs\")])\n        );\n    }\n}\n"
  },
  {
    "path": "src/print/license.rs",
    "content": "// Copyright (C) 2017 Christopher R. Field.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//! The implementation for printing a license.\n\nuse chrono::{Datelike, Utc};\n\nuse crate::Error;\nuse crate::Result;\nuse crate::Template;\nuse crate::manifest;\nuse crate::package;\n\nuse log::debug;\n\nuse mustache::{self, MapBuilder};\n\nuse std::path::PathBuf;\n\nuse cargo_metadata::Package;\n\nuse super::RenderOutput;\n\n/// A builder for creating an execution context to print a license.\n#[derive(Debug, Clone)]\npub struct Builder<'a> {\n    copyright_year: Option<&'a str>,\n    copyright_holder: Option<&'a str>,\n    input: Option<&'a str>,\n    output: Option<&'a str>,\n    package: Option<&'a str>,\n}\n\nimpl<'a> Builder<'a> {\n    /// Creates a new `Builder` instance.\n    pub fn new() -> Self {\n        Builder {\n            copyright_year: None,\n            copyright_holder: None,\n            input: None,\n            output: None,\n            package: None,\n        }\n    }\n\n    /// Sets the copyright holder for the generated license.\n    ///\n    /// If the license template does not use a copyright holder, then this value\n    /// is ignored.\n    ///\n    /// The default is to use `authors` field of the package's manifest (Cargo.toml).\n    pub fn copyright_holder(&mut self, h: Option<&'a str>) -> &mut Self {\n        self.copyright_holder = h;\n        self\n    }\n\n    /// Sets the copyright year for the generated license.\n    ///\n    /// If the license template does not use a copyright year, then this value\n    /// is ignored.\n    ///\n    /// The default is to use this year when generating the license.\n    pub fn copyright_year(&mut self, y: Option<&'a str>) -> &mut Self {\n        self.copyright_year = y;\n        self\n    }\n\n    /// Sets the path to a package's manifest (Cargo.toml) to be used to\n    /// generate license in the Rich Text Format (RTF).\n    ///\n    /// By default, the license will be printed to `STDOUT` unless the\n    /// [`output`] method is used.\n    ///\n    /// [`output`]: #output\n    pub fn input(&mut self, i: Option<&'a str>) -> &mut Self {\n        self.input = i;\n        self\n    }\n\n    /// Sets the destination.\n    ///\n    /// The default is to print all output to `STDOUT`. This method can be used\n    /// to specify that the generated license be written, or \"printed\", to a\n    /// file instead of `STDOUT`.\n    pub fn output(&mut self, o: Option<&'a str>) -> &mut Self {\n        self.output = o;\n        self\n    }\n\n    /// Sets the package.\n    ///\n    pub fn package(&mut self, o: Option<&'a str>) -> &mut Self {\n        self.package = o;\n        self\n    }\n\n    /// Builds an execution context based on the configuration.\n    pub fn build(&self) -> Execution {\n        Execution {\n            copyright_holder: self.copyright_holder.map(String::from),\n            copyright_year: self.copyright_year.map(String::from),\n            input: self.input.map(PathBuf::from),\n            output: self.output.map(PathBuf::from),\n            package: self.package.map(PathBuf::from),\n        }\n    }\n}\n\nimpl Default for Builder<'_> {\n    fn default() -> Self {\n        Builder::new()\n    }\n}\n\n/// A context for printing a license.\n#[derive(Debug)]\npub struct Execution {\n    copyright_holder: Option<String>,\n    copyright_year: Option<String>,\n    input: Option<PathBuf>,\n    output: Option<PathBuf>,\n    package: Option<PathBuf>,\n}\n\nimpl Execution {\n    /// Prints a license based on the built context.\n    pub fn run(self, template: &Template) -> Result<()> {\n        let render = self.render(template)?;\n        render.write()?;\n        Ok(())\n    }\n\n    pub fn render(self, template: &Template) -> Result<RenderOutput> {\n        debug!(\"copyright_holder = {:?}\", self.copyright_holder);\n        debug!(\"copyright_year = {:?}\", self.copyright_year);\n        debug!(\"input = {:?}\", self.input);\n        debug!(\"output = {:?}\", self.output);\n        let manifest = manifest(self.input.as_ref())?;\n        let package = package(&manifest, self.package.as_ref().and_then(|p| p.to_str()))?;\n        let template = mustache::compile_str(template.to_str())?;\n        let data = MapBuilder::new()\n            .insert_str(\"copyright-year\", self.copyright_year())\n            .insert_str(\"copyright-holder\", self.copyright_holder(&package)?)\n            .build();\n        let rendered = template.render_data_to_string(&data).map_err(Error::from)?;\n\n        Ok(RenderOutput {\n            path: self.output,\n            rendered,\n        })\n    }\n\n    fn copyright_holder(&self, manifest: &Package) -> Result<String> {\n        if let Some(ref h) = self.copyright_holder {\n            Ok(h.to_owned())\n        } else {\n            super::authors(manifest)\n        }\n    }\n\n    fn copyright_year(&self) -> String {\n        self.copyright_year\n            .clone()\n            .unwrap_or_else(|| Utc::now().year().to_string())\n    }\n}\n\nimpl Default for Execution {\n    fn default() -> Self {\n        Builder::new().build()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    mod builder {\n        use super::*;\n\n        #[test]\n        fn copyright_holder_works() {\n            const EXPECTED: &str = \"Example\";\n            let mut actual = Builder::new();\n            actual.copyright_holder(Some(EXPECTED));\n            assert_eq!(actual.copyright_holder, Some(EXPECTED));\n        }\n\n        #[test]\n        fn copyright_year_works() {\n            const EXPECTED: &str = \"1982\";\n            let mut actual = Builder::new();\n            actual.copyright_year(Some(EXPECTED));\n            assert_eq!(actual.copyright_year, Some(EXPECTED));\n        }\n\n        #[test]\n        fn input_works() {\n            const EXPECTED: &str = \"Example.wxs\";\n            let mut actual = Builder::new();\n            actual.input(Some(EXPECTED));\n            assert_eq!(actual.input, Some(EXPECTED));\n        }\n\n        #[test]\n        fn output_works() {\n            const EXPECTED: &str = \"C:\\\\Example\\\\output\";\n            let mut actual = Builder::new();\n            actual.output(Some(EXPECTED));\n            assert_eq!(actual.output, Some(EXPECTED));\n        }\n    }\n\n    mod execution {\n        use super::*;\n\n        const MIN_MANIFEST: &str = r#\"{\n            \"name\": \"Example\",\n            \"version\": \"0.1.0\",\n            \"authors\": [\"First Last <first.last@example.com>\"],\n\n            \"id\": \"\",\n            \"dependencies\": [],\n            \"targets\": [],\n            \"features\": {},\n            \"manifest_path\": \"\"\n        }\"#;\n\n        #[test]\n        fn copyright_holder_works() {\n            let manifest = serde_json::from_str(MIN_MANIFEST).expect(\"Parsing TOML\");\n            let actual = Execution::default().copyright_holder(&manifest).unwrap();\n            assert_eq!(actual, String::from(\"First Last\"));\n        }\n\n        #[test]\n        fn copyright_holder_with_override_works() {\n            const EXPECTED: &str = \"Dr. Example\";\n            let manifest = serde_json::from_str(MIN_MANIFEST).expect(\"Parsing TOML\");\n            let actual = Builder::new()\n                .copyright_holder(Some(EXPECTED))\n                .build()\n                .copyright_holder(&manifest)\n                .unwrap();\n            assert_eq!(actual, String::from(EXPECTED));\n        }\n\n        #[test]\n        fn copyright_year_works() {\n            let actual = Execution::default().copyright_year();\n            assert_eq!(actual, Utc::now().year().to_string());\n        }\n\n        #[test]\n        fn copyright_year_with_override_works() {\n            const EXPECTED: &str = \"1982\";\n            let actual = Builder::new()\n                .copyright_year(Some(EXPECTED))\n                .build()\n                .copyright_year();\n            assert_eq!(actual, String::from(EXPECTED));\n        }\n    }\n}\n"
  },
  {
    "path": "src/print/mod.rs",
    "content": "// Copyright (C) 2017 Christopher R. Field.\r\n//\r\n// Licensed under the Apache License, Version 2.0 (the \"License\");\r\n// you may not use this file except in compliance with the License.\r\n// You may obtain a copy of the License at\r\n//\r\n// http://www.apache.org/licenses/LICENSE-2.0\r\n//\r\n// Unless required by applicable law or agreed to in writing, software\r\n// distributed under the License is distributed on an \"AS IS\" BASIS,\r\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n// See the License for the specific language governing permissions and\r\n// limitations under the License.\r\n\r\n//! The implementation for the `print` command. This command is focused on\r\n//! printing various templates based on a package's manifest (Cargo.toml) or\r\n//! end-user input.\r\n\r\nuse itertools::Itertools;\r\nuse log::warn;\r\npub mod license;\r\npub mod wxs;\r\n\r\nuse crate::Error;\r\nuse crate::Result;\r\n\r\nuse log::trace;\r\n\r\nuse regex::Regex;\r\n\r\nuse std::fs::File;\r\nuse std::io::{self, Write};\r\nuse std::path::PathBuf;\r\n\r\nuse cargo_metadata::Package;\r\n\r\n/// The result of rendering a template (main.wxs, License.rtf, ...)\r\npub struct RenderOutput {\r\n    /// The path the template should be written to\r\n    /// (we needed to know this at generation time to properly embed relative paths)\r\n    ///\r\n    /// If this is None then the template should be written to stdout\r\n    /// FIMXE: this is kinda busted! You still need to know relative paths!\r\n    pub path: Option<PathBuf>,\r\n    /// The contents of the file\r\n    pub rendered: String,\r\n}\r\n\r\nimpl RenderOutput {\r\n    /// Write the output to its expected destination.\r\n    ///\r\n    /// See [`RenderOutput::path`][] for details.\r\n    pub fn write(&self) -> Result<()> {\r\n        let mut out = destination(self.path.as_ref())?;\r\n        out.write_all(self.rendered.as_bytes())?;\r\n        out.flush()?;\r\n        Ok(())\r\n    }\r\n\r\n    /// Write the output to its expected destination, if that destination is a file.\r\n    ///\r\n    /// This is for \"auxiliary files\" which also need to be produced somehow, but\r\n    /// which we can't emit when printing the \"main file\" to stdout. With nowhere\r\n    /// to put them, all we can do is warn.\r\n    ///\r\n    /// See [`RenderOutput::path`][] for details.\r\n    pub fn write_disk_only(&self) -> Result<()> {\r\n        if self.path.is_none() {\r\n            warn!(\"License.rtf also needs to be generated!\");\r\n            return Ok(());\r\n        }\r\n        self.write()\r\n    }\r\n}\r\n\r\nfn destination(output: Option<&PathBuf>) -> Result<Box<dyn Write>> {\r\n    if let Some(ref output) = output {\r\n        trace!(\"An output path has been explicitly specified\");\r\n        let f = File::create(output)?;\r\n        Ok(Box::new(f))\r\n    } else {\r\n        trace!(\r\n            \"An output path has NOT been explicitly specified. Implicitly \\\r\n             determine output.\"\r\n        );\r\n        Ok(Box::new(io::stdout()))\r\n    }\r\n}\r\n\r\nfn authors(package: &Package) -> Result<String> {\r\n    let result = package\r\n        .authors\r\n        .iter()\r\n        .map(|s| {\r\n            // Strip email if it exists.\r\n            let re = Regex::new(r\"<(.*?)>\").unwrap();\r\n            re.replace_all(s, \"\")\r\n        })\r\n        .map(|s| String::from(s.trim()))\r\n        // Replace this with intersperse from stdlib when it is stabilized: https://github.com/rust-lang/rust/issues/79524\r\n        .join(\"; \");\r\n\r\n    if result.is_empty() {\r\n        Err(Error::Manifest(\"authors\"))\r\n    } else {\r\n        Ok(result)\r\n    }\r\n}\r\n\r\n#[cfg(test)]\r\nmod tests {\r\n    use super::*;\r\n\r\n    const SINGLE_AUTHOR_MANIFEST: &str = r#\"{\r\n            \"name\": \"Example\",\r\n            \"version\": \"0.1.0\",\r\n            \"authors\": [\"First Last <first.last@example.com>\"],\r\n\r\n            \"id\": \"\",\r\n            \"dependencies\": [],\r\n            \"targets\": [],\r\n            \"features\": {},\r\n            \"manifest_path\": \"\"\r\n        }\"#;\r\n\r\n    const MULTIPLE_AUTHORS_MANIFEST: &str = r#\"{\r\n            \"name\": \"Example\",\r\n            \"version\": \"0.1.0\",\r\n            \"authors\": [\"1 Author <first.last@example.com>\", \"2 Author <2.author@example.com>\", \"3 author <3.author@example.com>\"],\r\n\r\n            \"id\": \"\",\r\n            \"dependencies\": [],\r\n            \"targets\": [],\r\n            \"features\": {},\r\n            \"manifest_path\": \"\"\r\n        }\"#;\r\n\r\n    #[test]\r\n    fn authors_with_single_author_works() {\r\n        let manifest = serde_json::from_str(SINGLE_AUTHOR_MANIFEST).expect(\"Parsing TOML\");\r\n        let actual = authors(&manifest).unwrap();\r\n        assert_eq!(actual, String::from(\"First Last\"));\r\n    }\r\n\r\n    #[test]\r\n    fn authors_with_multiple_authors_works() {\r\n        let manifest = serde_json::from_str(MULTIPLE_AUTHORS_MANIFEST).expect(\"Parsing TOML\");\r\n        let actual = authors(&manifest).unwrap();\r\n        assert_eq!(actual, String::from(\"1 Author; 2 Author; 3 author\"));\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/print/wxs.rs",
    "content": "// Copyright (C) 2017 Christopher R. Field.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//! The implementation for printing a WiX Source (wxs) file.\n\nuse crate::EXE_FILE_EXTENSION;\nuse crate::Error;\nuse crate::Result;\nuse crate::Template;\nuse crate::description;\nuse crate::licenses::{License, Licenses};\nuse crate::manifest;\nuse crate::package;\nuse crate::product_name;\nuse crate::stored_path::StoredPathBuf;\nuse crate::toolset::project::WxsSchema;\n\nuse camino::Utf8Path;\nuse log::{debug, trace, warn};\n\nuse mustache::{self, MapBuilder};\n\nuse std::path::Path;\nuse std::{collections::HashMap, str::FromStr};\n\nuse cargo_metadata::{Package, TargetKind};\n\nuse uuid::Uuid;\n\nuse super::RenderOutput;\n\n/// A builder for creating an execution context to print a WiX Toolset source file (wxs).\n#[derive(Debug, Clone)]\npub struct Builder<'a> {\n    banner: Option<&'a str>,\n    binaries: Option<Vec<&'a str>>,\n    copyright_year: Option<&'a str>,\n    copyright_holder: Option<&'a str>,\n    description: Option<&'a str>,\n    dialog: Option<&'a str>,\n    eula: Option<&'a str>,\n    help_url: Option<&'a str>,\n    input: Option<&'a str>,\n    license: Option<&'a str>,\n    manufacturer: Option<&'a str>,\n    output: Option<&'a str>,\n    package: Option<&'a str>,\n    path_guid: Option<&'a str>,\n    product_icon: Option<&'a str>,\n    product_name: Option<&'a str>,\n    upgrade_guid: Option<&'a str>,\n    schema: Option<WxsSchema>,\n}\n\nimpl<'a> Builder<'a> {\n    /// Creates a new `Builder` instance.\n    pub fn new() -> Self {\n        Builder {\n            banner: None,\n            binaries: None,\n            copyright_year: None,\n            copyright_holder: None,\n            description: None,\n            dialog: None,\n            eula: None,\n            help_url: None,\n            input: None,\n            license: None,\n            manufacturer: None,\n            output: None,\n            package: None,\n            path_guid: None,\n            product_icon: None,\n            product_name: None,\n            upgrade_guid: None,\n            schema: None,\n        }\n    }\n\n    /// Sets the path to a bitmap (BMP) file to be used as a banner image across\n    /// the top of each dialog in the installer.\n    ///\n    /// The banner image must be 493 x 58 pixels. See the [Wix Toolset\n    /// documentation] for details about [customization].\n    ///\n    /// [Wix Toolset documentation]: http://wixtoolset.org/documentation/\n    /// [customization]: http://wixtoolset.org/documentation/manual/v3/wixui/wixui_customizations.html\n    pub fn banner(&mut self, b: Option<&'a str>) -> &mut Self {\n        self.banner = b;\n        self\n    }\n\n    /// Sets the path to one or more binaries to include in the installer.\n    ///\n    /// The default is to first find _all_ binaries defined in the `bin`\n    /// sections of the package's manifest (Cargo.toml) and include all of them\n    /// in the installer. If there are no `bin` sections, then the `name` field\n    /// uder the `package` section is used. This overrides either of these\n    /// cases, regardless of the existence and number of `bin` sections.\n    ///\n    /// A path to each binary should be used for the value. The file stem (file\n    /// name without the extension) will be used for each binary's name, while\n    /// the path will be used for the source. The binary names will _not_ appear\n    /// in the Add/Remove Programs control panel. Use the `product_name` method\n    /// to change the name that appears in the Add/Remove Programs control\n    /// panel.\n    pub fn binaries(&mut self, b: Option<Vec<&'a str>>) -> &mut Self {\n        self.binaries = b;\n        self\n    }\n\n    /// Sets the copyright holder for the generated license file and EULA.\n    ///\n    /// The default is to use the `authors` field of the\n    /// package's manifest (Cargo.toml). This method can be used to override the\n    /// default and set a different copyright holder if and when a Rich Text\n    /// Format (RTF) license and EULA are generated based on the value of the\n    /// `license` field in the package's manifest (Cargo.toml).\n    ///\n    /// This value is ignored and not used if an EULA is set with the [`eula`]\n    /// method, if a custom EULA is set using the `license-file` field in the\n    /// package's manifest (Cargo.toml), or an EULA is _not_ generated from the\n    /// `license` field in the package's manifest (Cargo.toml).\n    ///\n    /// [`eula`]: https://volks73.github.io/cargo-wix/cargo_wix/initialize.html#eula\n    pub fn copyright_holder(&mut self, h: Option<&'a str>) -> &mut Self {\n        self.copyright_holder = h;\n        self\n    }\n\n    /// Sets the copyright year for the generated license file and EULA.\n    ///\n    /// The default is to use the current year. This method can be used to\n    /// override the default and set a specific year if and when a Rich Text\n    /// Format (RTF) license and EULA are generated based on the value of the\n    /// `license` field in the package's manifest (Cargo.toml).\n    ///\n    /// This value is ignored and not used if an EULA is set with the [`eula`]\n    /// method, if a custom EULA is set using the `license-file` field in the\n    /// package's manifest (Cargo.toml), or an EULA is _not_ generated from the\n    /// `license` field in the package's manifest (Cargo.toml).\n    ///\n    /// [`eula`]: https://volks73.github.io/cargo-wix/cargo_wix/initialize.html#eula\n    pub fn copyright_year(&mut self, y: Option<&'a str>) -> &mut Self {\n        self.copyright_year = y;\n        self\n    }\n\n    /// Sets the description.\n    ///\n    /// This overrides the description determined from the `description` field\n    /// in the package'\n    pub fn description(&mut self, d: Option<&'a str>) -> &mut Self {\n        self.description = d;\n        self\n    }\n\n    /// Sets the path to a bitmap (`.bmp`) file that will be displayed on the\n    /// first dialog to the left.\n    ///\n    /// The image must be 493 x 312 pixels. See the [Wix Toolset\n    /// documentation] for details about [customization].\n    ///\n    /// [Wix Toolset documentation]: http://wixtoolset.org/documentation/\n    /// [customization]: http://wixtoolset.org/documentation/manual/v3/wixui/wixui_customizations.html\n    pub fn dialog(&mut self, d: Option<&'a str>) -> &mut Self {\n        self.dialog = d;\n        self\n    }\n\n    /// Sets the path to a custom EULA.\n    ///\n    /// The default is to generate an EULA from an embedded template as a RTF\n    /// file based on the name of the license specified in the `license` field\n    /// of the package's manifest (Cargo.toml). If the `license` field is not\n    /// specified or a template for the license does not exist but the\n    /// `license-file` field does specify a path to a file with the RTF\n    /// extension, then that RTF file is used as the EULA for the license\n    /// agreement dialog in the installer. Finally, if the `license-file` does\n    /// not exist or it specifies a file that does not have the `.rtf`\n    /// extension, then the license agreement dialog is skipped and there is no\n    /// EULA for the installer. This would override the default behavior and\n    /// ensure the license agreement dialog is used.\n    pub fn eula(&mut self, e: Option<&'a str>) -> &mut Self {\n        self.eula = e;\n        self\n    }\n\n    /// Sets the help URL.\n    ///\n    /// The default is to obtain a URL from one of the following fields in the\n    /// package's manifest (Cargo.toml): `documentation`, `homepage`, or\n    /// `repository`. If none of these are specified, then the default is to\n    /// exclude a help URL from the installer. This will override the default\n    /// behavior and provide a help URL for the installer if none of the fields\n    /// exist.\n    pub fn help_url(&mut self, h: Option<&'a str>) -> &mut Self {\n        self.help_url = h;\n        self\n    }\n\n    /// Sets the path to a package's manifest (Cargo.toml) to be used to\n    /// generate a WiX Source (wxs) file from the embedded template.\n    ///\n    /// A `wix` and `wix\\main.wxs` file will be created in the same directory as\n    /// the package's manifest. The default is to use the package's manifest in\n    /// the current working directory.\n    pub fn input(&mut self, i: Option<&'a str>) -> &mut Self {\n        self.input = i;\n        self\n    }\n\n    /// Sets the path to a file to be used as the license [sidecar] file.\n    ///\n    /// The default is to use the value specified in the `license-file` field of\n    /// the package's manifest (Cargo.toml) or generate a Rich Text Format (RTF)\n    /// license file from an embedded template based on the license ID used in\n    /// the `license` field of the package's manifest. If none of these fields\n    /// are specified or overridden, then a license sidecar file is _not_\n    /// included in the installation directory.\n    ///\n    /// This will override the default behavior and skip using either the\n    /// `license` or `license-file` fields in the package's manifest.\n    ///\n    /// [sidecar]: https://en.wikipedia.org/wiki/Sidecar_file\n    pub fn license(&mut self, l: Option<&'a str>) -> &mut Self {\n        self.license = l;\n        self\n    }\n\n    /// Sets the manufacturer.\n    ///\n    /// Default is to use the `authors` field of the\n    /// package's manifest (Cargo.toml). This would override the default value.\n    pub fn manufacturer(&mut self, m: Option<&'a str>) -> &mut Self {\n        self.manufacturer = m;\n        self\n    }\n\n    /// Sets the destination for creating all of the output from initialization.\n    ///\n    /// The default is to create all initialization output in the current\n    /// working directory.\n    pub fn output(&mut self, o: Option<&'a str>) -> &mut Self {\n        self.output = o;\n        self\n    }\n\n    /// Sets the package within a workspace to print a template.\n    ///\n    /// Each package within a workspace has its own package manifest, i.e.\n    /// `Cargo.toml`. This indicates which package manifest within a workspace\n    /// should be used to populate a template.\n    pub fn package(&mut self, p: Option<&'a str>) -> &mut Self {\n        self.package = p;\n        self\n    }\n\n    /// Sets the GUID for the path component.\n    ///\n    /// The default automatically generates the GUID needed for the path\n    /// component. A GUID is needed so that the path component can be\n    /// successfully removed on uninstall.\n    ///\n    /// Generally, the path component GUID should be generated only once per\n    /// project/product and then the same GUID used every time the installer is\n    /// created. The GUID is stored in the WiX Source (WXS) file. However,\n    /// this allows using an existing GUID, possibly obtained with another tool.\n    pub fn path_guid(&mut self, p: Option<&'a str>) -> &mut Self {\n        self.path_guid = p;\n        self\n    }\n\n    /// Sets the path to an image file to be used for product icon.\n    ///\n    /// The product icon is the icon that appears for an installed application\n    /// in the Add/Remove Programs (ARP) control panel. If a product icon is\n    /// _not_ defined for an application within the installer, then the Windows\n    /// OS assigns a generic one.\n    pub fn product_icon(&mut self, p: Option<&'a str>) -> &mut Self {\n        self.product_icon = p;\n        self\n    }\n\n    /// Sets the product name.\n    ///\n    /// The default is to use the `name` field under the `package` section of\n    /// the package's manifest (Cargo.toml). This overrides that value. An error\n    /// occurs if the `name` field is not found in the manifest.\n    ///\n    /// This is different from the binary name in that it is the name that\n    /// appears in the Add/Remove Programs (ARP) control panel, _not_ the name\n    /// of the executable. The [`binary_name`] method can be used to change the\n    /// executable name. This value can have spaces and special characters,\n    /// where the binary name should avoid spaces and special characters.\n    ///\n    /// [`binary_name`]: #binary_name\n    pub fn product_name(&mut self, p: Option<&'a str>) -> &mut Self {\n        self.product_name = p;\n        self\n    }\n\n    /// Sets the Upgrade Code GUID.\n    ///\n    /// The default automatically generates the GUID needed for the `UpgradeCode`\n    /// attribute to the `Product` tag. The Upgrade Code uniquely identifies the\n    /// installer. It is used to determine if the new installer is the same\n    /// product and the current installation should be removed and upgraded to\n    /// this version. If the GUIDs of the current product and new product do\n    /// _not_ match, then Windows will treat the two installers as separate\n    /// products.\n    ///\n    /// Generally, the upgrade code should be generated only once per\n    /// project/product and then the same code used every time the installer is\n    /// created. The GUID is stored in the WiX Source (WXS) file. However,\n    /// this allows usage of an existing GUID for the upgrade code.\n    pub fn upgrade_guid(&mut self, u: Option<&'a str>) -> &mut Self {\n        self.upgrade_guid = u;\n        self\n    }\n\n    /// Sets the Wxs Schema\n    ///\n    /// Wix3 follows the \"v3\" schema, where as Wix4 and beyond use the \"Modern\" schema uri.\n    pub fn schema(&mut self, schema: Option<WxsSchema>) -> &mut Self {\n        self.schema = schema;\n        self\n    }\n\n    /// Builds an execution context for printing a template.\n    pub fn build(&self) -> Execution {\n        Execution {\n            banner: self.banner.map(StoredPathBuf::from),\n            binaries: self\n                .binaries\n                .as_ref()\n                .map(|b| b.iter().copied().map(StoredPathBuf::from).collect()),\n            copyright_holder: self.copyright_holder.map(String::from),\n            copyright_year: self.copyright_year.map(String::from),\n            description: self.description.map(String::from),\n            dialog: self.dialog.map(StoredPathBuf::from),\n            eula: self.eula.map(StoredPathBuf::from),\n            help_url: self.help_url.map(String::from),\n            input: self.input.map(std::path::PathBuf::from),\n            license: self.license.map(StoredPathBuf::from),\n            manufacturer: self.manufacturer.map(String::from),\n            output: self.output.map(std::path::PathBuf::from),\n            package: self.package.map(String::from),\n            path_guid: self.path_guid.map(String::from),\n            product_icon: self.product_icon.map(StoredPathBuf::from),\n            product_name: self.product_name.map(String::from),\n            upgrade_guid: self.upgrade_guid.map(String::from),\n            schema: self.schema.unwrap_or(WxsSchema::Legacy),\n        }\n    }\n}\n\nimpl Default for Builder<'_> {\n    fn default() -> Self {\n        Builder::new()\n    }\n}\n\n/// A context for printing a WiX Toolset source file (wxs).\n#[derive(Debug)]\npub struct Execution {\n    banner: Option<StoredPathBuf>,\n    binaries: Option<Vec<StoredPathBuf>>,\n    copyright_holder: Option<String>,\n    copyright_year: Option<String>,\n    description: Option<String>,\n    dialog: Option<StoredPathBuf>,\n    eula: Option<StoredPathBuf>,\n    help_url: Option<String>,\n    input: Option<std::path::PathBuf>,\n    license: Option<StoredPathBuf>,\n    manufacturer: Option<String>,\n    output: Option<std::path::PathBuf>,\n    package: Option<String>,\n    path_guid: Option<String>,\n    product_icon: Option<StoredPathBuf>,\n    product_name: Option<String>,\n    upgrade_guid: Option<String>,\n    schema: WxsSchema,\n}\n\n/// All the possible output files of [`Execution::render`][].\n///\n/// Although we're only trying to output the \"main\" output of main.wxs,\n/// that file may want to reference other files that also need to be generated,\n/// so we need to report them all together.\npub struct WxsRenders {\n    /// The main output, typically wix\\main.wxs\n    pub wxs: RenderOutput,\n    /// A potential source-license for the project (MIT, Apache, ...), typically wix\\License.rtf\n    pub license: Option<RenderOutput>,\n    /// A potential eula for the project\n    ///\n    /// This is currently always None, as the generation of eula texts\n    /// is currently always accomplished by generating a source-license,\n    /// but you should assume this can be Some so that you don't need to\n    /// do anything if we ever start using this.\n    pub eula: Option<RenderOutput>,\n}\n\nimpl Execution {\n    /// Prints a WiX Source (wxs) file based on the built context.\n    pub fn run(self) -> Result<()> {\n        let renders = self.render()?;\n        renders.wxs.write()?;\n        if let Some(license) = renders.license {\n            license.write_disk_only()?;\n        }\n        if let Some(eula) = renders.eula {\n            eula.write_disk_only()?;\n        }\n        Ok(())\n    }\n\n    /// Instead of printing the output like [`Execution::run`][], render the output to Strings.\n    ///\n    /// See [`WxsRenders`][] for details of the output.\n    pub fn render(self) -> Result<WxsRenders> {\n        debug!(\"banner = {:?}\", self.banner);\n        debug!(\"binaries = {:?}\", self.binaries);\n        debug!(\"copyright_holder = {:?}\", self.copyright_holder);\n        debug!(\"copyright_year = {:?}\", self.copyright_year);\n        debug!(\"description = {:?}\", self.description);\n        debug!(\"dialog = {:?}\", self.description);\n        debug!(\"eula = {:?}\", self.eula);\n        debug!(\"help_url = {:?}\", self.help_url);\n        debug!(\"input = {:?}\", self.input);\n        debug!(\"license = {:?}\", self.license);\n        debug!(\"manufacturer = {:?}\", self.manufacturer);\n        debug!(\"output = {:?}\", self.output);\n        debug!(\"package = {:?}\", self.package);\n        debug!(\"path_guid = {:?}\", self.path_guid);\n        debug!(\"product_icon = {:?}\", self.product_icon);\n        debug!(\"product_name = {:?}\", self.product_name);\n        debug!(\"upgrade_guid = {:?}\", self.upgrade_guid);\n        let manifest = manifest(self.input.as_ref())?;\n        let package = package(&manifest, self.package.as_deref())?;\n        let binaries = self.binaries(&package)?;\n        let licenses = self.licenses(&package)?;\n        let mut map = MapBuilder::new()\n            .insert_vec(\"binaries\", |mut builder| {\n                for binary in &binaries {\n                    builder = builder.push_map(|builder| {\n                        builder\n                            .insert_str(\"binary-index\", binary.get(\"binary-index\").unwrap())\n                            .insert_str(\"binary-name\", binary.get(\"binary-name\").unwrap())\n                            .insert_str(\"binary-source\", binary.get(\"binary-source\").unwrap())\n                    });\n                }\n                builder\n            })\n            .insert_str(\n                \"product-name\",\n                product_name(self.product_name.as_ref(), &package),\n            )\n            .insert_str(\"manufacturer\", self.manufacturer(&package)?)\n            .insert_str(\"upgrade-code-guid\", self.upgrade_guid(&package)?)\n            .insert_str(\"path-component-guid\", self.path_guid(&package)?);\n        if let Some(banner) = self.banner_image(&package) {\n            map = map.insert_str(\"banner\", banner);\n        }\n        if let Some(description) = description(self.description.clone(), &package) {\n            map = map.insert_str(\"description\", description);\n        } else {\n            warn!(\n                \"A description was not specified at the command line or in the package's manifest \\\n                 (Cargo.toml). The description can be added manually to the generated WiX \\\n                 Source (wxs) file using a text editor.\"\n            );\n        }\n        if let Some(dialog) = self.dialog_image(&package) {\n            map = map.insert_str(\"dialog\", dialog);\n        }\n        if let Some(eula) = &licenses.end_user {\n            map = map.insert_str(\"eula\", &eula.stored_path);\n        }\n        if let Some(url) = self.help_url(&package) {\n            map = map.insert_str(\"help-url\", url);\n        } else {\n            warn!(\n                \"A help URL could not be found and it will be excluded from the installer. \\\n                 A help URL can be added manually to the generated WiX Source (wxs) file \\\n                 using a text editor.\"\n            );\n        }\n        if let Some(license) = &licenses.source {\n            map = map.insert_str(\"license-source\", &license.stored_path);\n            if let Some(name) = &license.name {\n                map = map.insert_str(\"license-name\", name);\n            }\n        }\n        if let Some(icon) = self.product_icon(&package) {\n            map = map.insert_str(\"product-icon\", icon);\n        }\n\n        let wxs = {\n            let data = map.build();\n            let main_destination = self.output.clone();\n            let wxs_template = match self.schema {\n                WxsSchema::Legacy => Template::WxsV3.to_str(),\n                WxsSchema::V4 => Template::WxsV4.to_str(),\n                _ => {\n                    unreachable!(\"should always have one of the above valid schemas set\")\n                }\n            };\n            let template = mustache::compile_str(wxs_template)?;\n            let rendered = template.render_data_to_string(&data).map_err(Error::from)?;\n            RenderOutput {\n                path: main_destination,\n                rendered,\n            }\n        };\n        let license = self.render_license_string(licenses.source.as_ref())?;\n        let eula = self.render_license_string(licenses.end_user.as_ref())?;\n        Ok(WxsRenders { wxs, license, eula })\n    }\n\n    fn render_license_string(&self, license: Option<&License>) -> Result<Option<RenderOutput>> {\n        let Some(license) = license else {\n            return Ok(None);\n        };\n        let Some((output, template)) = &license.generate else {\n            return Ok(None);\n        };\n\n        let mut printer = crate::print::license::Builder::new();\n        printer.copyright_holder(self.copyright_holder.as_ref().map(String::as_ref));\n        printer.copyright_year(self.copyright_year.as_ref().map(String::as_ref));\n        printer.input(self.input.as_deref().and_then(Path::to_str));\n        // Slightly hacky: only respect the template's desire to be written to disk\n        // if we're writing the overall wxs to disk! This makes \"stdout\" mode\n        // behave in the desired manner.\n        if self.output.is_some() {\n            printer.output(Some(output.as_str()));\n        }\n        printer.package(self.package.as_deref());\n\n        let render = printer.build().render(template)?;\n        Ok(Some(render))\n    }\n\n    fn binaries(&self, package: &Package) -> Result<Vec<HashMap<&'static str, String>>> {\n        let mut binaries = Vec::new();\n        if let Some(binary_paths) = &self.binaries {\n            let mut map = HashMap::with_capacity(3);\n            for (index, binary) in binary_paths.iter().enumerate() {\n                let binary_file_stem = binary.file_stem().ok_or_else(|| {\n                    Error::Generic(format!(\n                        \"The '{}' binary path does not have a file name\",\n                        binary\n                    ))\n                })?;\n                map.insert(\"binary-index\", index.to_string());\n                map.insert(\"binary-name\", binary_file_stem.to_owned());\n                map.insert(\"binary-source\", binary.to_string());\n            }\n            binaries.push(map);\n        } else {\n            // cargo-metadata attempts to sort binaries by name to keep things stable,\n            // but for whatever reason it internally uses the platform-specific binary name\n            // with \".exe\" appended, even though the output doesn't refer to that extension.\n            // As such, the ordering ends up being platform-specific, because any time one\n            // binary has a name that's a prefix of the other (e.g. \"app\" and \"app-helper\")\n            // the `.exe` extension changes the ordering of the sort. We sort the list again\n            // with the agnostic name to avoid this issue.\n            let mut binaries_list = package\n                .targets\n                .iter()\n                .filter(|v| v.kind.iter().any(|v| v == &TargetKind::Bin))\n                .collect::<Vec<_>>();\n            binaries_list.sort_by_key(|k| &k.name);\n\n            for (index, binary) in binaries_list.into_iter().enumerate() {\n                let mut map = HashMap::with_capacity(3);\n                map.insert(\"binary-index\", index.to_string());\n                map.insert(\"binary-name\", binary.name.clone());\n                map.insert(\n                    \"binary-source\",\n                    Self::default_binary_path(&binary.name).to_string(),\n                );\n                binaries.push(map);\n            }\n        }\n        Ok(binaries)\n    }\n\n    fn default_binary_path(name: &str) -> StoredPathBuf {\n        // Use hardcoded path separator here to avoid platform-specific output\n        StoredPathBuf::from(format!(\n            \"$(var.CargoTargetBinDir)\\\\{name}.{EXE_FILE_EXTENSION}\"\n        ))\n    }\n\n    fn help_url(&self, manifest: &Package) -> Option<String> {\n        self.help_url\n            .as_ref()\n            .map(String::from)\n            .or_else(|| manifest.documentation.clone())\n            .or_else(|| manifest.homepage.clone())\n            .or_else(|| manifest.repository.clone())\n    }\n\n    fn licenses(&self, manifest: &Package) -> Result<Licenses> {\n        let output_dir = self\n            .output\n            .as_deref()\n            .and_then(|p| p.parent())\n            .and_then(Utf8Path::from_path);\n        let licenses = Licenses::new(\n            output_dir,\n            self.license.as_deref(),\n            self.eula.as_deref(),\n            manifest,\n        )?;\n        Ok(licenses)\n    }\n\n    fn manufacturer(&self, manifest: &Package) -> Result<String> {\n        if let Some(ref m) = self.manufacturer {\n            Ok(m.to_owned())\n        } else {\n            super::authors(manifest)\n        }\n    }\n\n    fn path_guid(&self, manifest: &Package) -> Result<String> {\n        if let Some(ref u) = self.path_guid {\n            trace!(\"An path GUID has been explicitly specified\");\n            Ok(u.to_owned())\n        } else if let Some(pkg_meta_wix_path_guid) = manifest\n            .metadata\n            .get(\"wix\")\n            .and_then(|w| w.as_object())\n            .and_then(|t| t.get(\"path-guid\"))\n            .and_then(|u| u.as_str())\n        {\n            Uuid::from_str(pkg_meta_wix_path_guid)\n                .map(|u| u.as_hyphenated().to_string().to_uppercase())\n                .map_err(Error::from)\n        } else {\n            Ok(Uuid::new_v4().as_hyphenated().to_string().to_uppercase())\n        }\n    }\n\n    fn upgrade_guid(&self, manifest: &Package) -> Result<String> {\n        if let Some(ref u) = self.upgrade_guid {\n            trace!(\"An upgrade GUID has been explicitly specified\");\n            Ok(u.to_owned())\n        } else if let Some(pkg_meta_wix_upgrade_guid) = manifest\n            .metadata\n            .get(\"wix\")\n            .and_then(|w| w.as_object())\n            .and_then(|t| t.get(\"upgrade-guid\"))\n            .and_then(|u| u.as_str())\n        {\n            Uuid::from_str(pkg_meta_wix_upgrade_guid)\n                .map(|u| u.as_hyphenated().to_string().to_uppercase())\n                .map_err(Error::from)\n        } else {\n            Ok(Uuid::new_v4().as_hyphenated().to_string().to_uppercase())\n        }\n    }\n\n    fn banner_image(&self, manifest: &Package) -> Option<StoredPathBuf> {\n        if let Some(path) = &self.banner {\n            trace!(\"A banner image has been explicitly specified\");\n            Some(path.clone())\n        } else {\n            manifest\n                .metadata\n                .get(\"wix\")\n                .and_then(|w| w.as_object())\n                .and_then(|t| t.get(\"banner\"))\n                .and_then(|p| p.as_str())\n                .map(|p| StoredPathBuf::new(p.to_owned()))\n        }\n    }\n\n    fn dialog_image(&self, manifest: &Package) -> Option<StoredPathBuf> {\n        if let Some(path) = &self.dialog {\n            trace!(\"A dialog image has been explicitly specified\");\n            Some(path.clone())\n        } else {\n            manifest\n                .metadata\n                .get(\"wix\")\n                .and_then(|w| w.as_object())\n                .and_then(|t| t.get(\"dialog\"))\n                .and_then(|p| p.as_str())\n                .map(|p| StoredPathBuf::new(p.to_owned()))\n        }\n    }\n\n    fn product_icon(&self, manifest: &Package) -> Option<StoredPathBuf> {\n        if let Some(path) = &self.product_icon {\n            trace!(\"A product icon has been explicitly specified\");\n            Some(path.clone())\n        } else {\n            manifest\n                .metadata\n                .get(\"wix\")\n                .and_then(|w| w.as_object())\n                .and_then(|t| t.get(\"product-icon\"))\n                .and_then(|p| p.as_str())\n                .map(|p| StoredPathBuf::new(p.to_owned()))\n        }\n    }\n\n    #[cfg(test)]\n    pub fn for_test(input: &Path) -> Self {\n        let input = Utf8Path::from_path(input).expect(\"utf8 path\");\n        let output = input\n            .parent()\n            .expect(\"Cargo.toml to not be a root\")\n            .join(crate::WIX)\n            .join(format!(\n                \"{}.{}\",\n                crate::WIX_SOURCE_FILE_NAME,\n                crate::WIX_SOURCE_FILE_EXTENSION\n            ));\n        Builder::new()\n            .input(Some(input.as_str()))\n            .output(Some(output.as_str()))\n            .build()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    use maplit::hashmap;\n\n    mod builder {\n        use super::*;\n\n        #[test]\n        fn banner_works() {\n            const EXPECTED: &str = \"img\\\\Banner.bmp\";\n            let mut actual = Builder::new();\n            actual.banner(Some(EXPECTED));\n            assert_eq!(actual.banner, Some(EXPECTED));\n        }\n\n        #[test]\n        fn binaries_name_works() {\n            const EXPECTED: &str = \"bin\\\\Example.exe\";\n            let mut actual = Builder::new();\n            actual.binaries(Some(vec![EXPECTED]));\n            assert_eq!(actual.binaries, Some(vec![EXPECTED]));\n        }\n\n        #[test]\n        fn description_works() {\n            const EXPECTED: &str = \"This is a description.\";\n            let mut actual = Builder::new();\n            actual.description(Some(EXPECTED));\n            assert_eq!(actual.description, Some(EXPECTED));\n        }\n\n        #[test]\n        fn dialog_work() {\n            const EXPECTED: &str = \"img\\\\Dialog.bmp\";\n            let mut actual = Builder::new();\n            actual.dialog(Some(EXPECTED));\n            assert_eq!(actual.dialog, Some(EXPECTED));\n        }\n\n        #[test]\n        fn eula_works() {\n            const EXPECTED: &str = \"Example_Eula.rtf\";\n            let mut actual = Builder::new();\n            actual.eula(Some(EXPECTED));\n            assert_eq!(actual.eula, Some(EXPECTED));\n        }\n\n        #[test]\n        fn help_url_works() {\n            const EXPECTED: &str = \"http://www.example.com\";\n            let mut actual = Builder::new();\n            actual.help_url(Some(EXPECTED));\n            assert_eq!(actual.help_url, Some(EXPECTED));\n        }\n\n        #[test]\n        fn input_works() {\n            const EXPECTED: &str = \"C:\\\\example\\\\Cargo.toml\";\n            let mut actual = Builder::new();\n            actual.input(Some(EXPECTED));\n            assert_eq!(actual.input, Some(EXPECTED));\n        }\n\n        #[test]\n        fn license_works() {\n            const EXPECTED: &str = \"C:\\\\example\\\\Example License.rtf\";\n            let mut actual = Builder::new();\n            actual.license(Some(EXPECTED));\n            assert_eq!(actual.license, Some(EXPECTED));\n        }\n\n        #[test]\n        fn manufacturer_works() {\n            const EXPECTED: &str = \"Example\";\n            let mut actual = Builder::new();\n            actual.manufacturer(Some(EXPECTED));\n            assert_eq!(actual.manufacturer, Some(EXPECTED));\n        }\n\n        #[test]\n        fn output_works() {\n            const EXPECTED: &str = \"C:\\\\example\\\\output\";\n            let mut actual = Builder::new();\n            actual.output(Some(EXPECTED));\n            assert_eq!(actual.output, Some(EXPECTED));\n        }\n\n        #[test]\n        fn path_guid_works() {\n            let expected = Uuid::new_v4().as_hyphenated().to_string().to_uppercase();\n            let mut actual = Builder::new();\n            actual.path_guid(Some(&expected));\n            assert_eq!(actual.path_guid, Some(expected.as_ref()));\n        }\n\n        #[test]\n        fn product_icon_works() {\n            const EXPECTED: &str = \"img\\\\Product.ico\";\n            let mut actual = Builder::new();\n            actual.product_icon(Some(EXPECTED));\n            assert_eq!(actual.product_icon, Some(EXPECTED));\n        }\n\n        #[test]\n        fn product_name_works() {\n            const EXPECTED: &str = \"Example Product Name\";\n            let mut actual = Builder::new();\n            actual.product_name(Some(EXPECTED));\n            assert_eq!(actual.product_name, Some(EXPECTED));\n        }\n\n        #[test]\n        fn upgrade_guid_works() {\n            let expected = Uuid::new_v4().as_hyphenated().to_string().to_uppercase();\n            let mut actual = Builder::new();\n            actual.upgrade_guid(Some(&expected));\n            assert_eq!(actual.upgrade_guid, Some(expected.as_ref()));\n        }\n\n        #[test]\n        fn schema_default_is_none() {\n            let actual = Builder::new();\n            assert_eq!(actual.schema, None);\n        }\n\n        #[test]\n        fn schema_v4_works() {\n            use crate::toolset::project::WxsSchema;\n            let mut actual = Builder::new();\n            actual.schema(Some(WxsSchema::V4));\n            assert_eq!(actual.schema, Some(WxsSchema::V4));\n        }\n\n        #[test]\n        fn schema_defaults_to_legacy_in_execution() {\n            use crate::toolset::project::WxsSchema;\n            let execution = Builder::new().build();\n            assert!(matches!(execution.schema, WxsSchema::Legacy));\n        }\n    }\n\n    mod execution {\n        extern crate assert_fs;\n\n        use super::*;\n        use crate::tests::setup_project;\n        use crate::{LICENSE_FILE_NAME, RTF_FILE_EXTENSION, WIX};\n        use std::fs::File;\n\n        const MIN_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n        \"#;\n\n        const MIT_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license = \"MIT\"\n        \"#;\n\n        const GPL3_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license = \"GPL-3.0\"\n        \"#;\n\n        const APACHE2_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license = \"Apache-2.0\"\n        \"#;\n\n        const UNKNOWN_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license = \"XYZ\"\n        \"#;\n\n        const MIT_MANIFEST_BIN: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license = \"MIT\"\n\n            [[bin]]\n            name = \"Different\"\n        \"#;\n\n        const MULTIPLE_BIN_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license = \"MIT\"\n\n            [[bin]]\n            name = \"binary0\"\n            path = \"src/binary0/main.rs\"\n\n            [[bin]]\n            name = \"binary1\"\n            path = \"src/binary1/main.rs\"\n\n            [[bin]]\n            name = \"binary2\"\n            path = \"src/binary2/main.rs\"\n        \"#;\n\n        const DOCUMENTATION_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license = \"MIT\"\n            documentation = \"http://www.example.com\"\n        \"#;\n\n        const HOMEPAGE_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license = \"MIT\"\n            homepage = \"http://www.example.com\"\n        \"#;\n\n        const REPOSITORY_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license = \"MIT\"\n            repository = \"http://www.example.com\"\n        \"#;\n\n        const LICENSE_FILE_RTF_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license-file = \"Example.rtf\"\n        \"#;\n\n        const LICENSE_FILE_TXT_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license-file = \"Example.txt\"\n        \"#;\n\n        const LICENSE_FALSE_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license = \"MIT\"\n\n            [package.metadata.wix]\n            license = false\n            eula = true\n        \"#;\n\n        const EULA_FALSE_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license = \"MIT\"\n\n            [package.metadata.wix]\n            license = true\n            eula = false\n        \"#;\n\n        const EULA_AND_LICENSE_FALSE_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license = \"MIT\"\n\n            [package.metadata.wix]\n            license = false\n            eula = false\n        \"#;\n\n        const LICENSE_PATH_RTF_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license = \"MIT\"\n\n            [package.metadata.wix]\n            license = \"MyLicense.rtf\"\n        \"#;\n\n        const LICENSE_PATH_TXT_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license = \"MIT\"\n\n            [package.metadata.wix]\n            license = \"MyLicense.txt\"\n        \"#;\n\n        const EULA_PATH_RTF_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license = \"MIT\"\n\n            [package.metadata.wix]\n            eula = \"MyEula.rtf\"\n        \"#;\n\n        const EULA_PATH_TXT_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license = \"MIT\"\n\n            [package.metadata.wix]\n            eula = \"MyEula.txt\"\n        \"#;\n\n        const EULA_AND_LICENSE_PATH_RTF_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license = \"MIT\"\n\n            [package.metadata.wix]\n            license = \"MyLicense.rtf\"\n            eula = \"MyEula.rtf\"\n        \"#;\n\n        const EULA_BAD_PATH_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license = \"MIT\"\n\n            [package.metadata.wix]\n            eula = \"MyFakeEula.rtf\"\n        \"#;\n\n        const LICENSE_BAD_PATH_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license = \"MIT\"\n\n            [package.metadata.wix]\n            eula = \"MyFakeLicense.rtf\"\n        \"#;\n\n        const LICENSE_FILE_BAD_PATH_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            license-file = \"MyFakeLicense.rtf\"\n        \"#;\n\n        const PATH_GUID: &str = \"C38A18DB-12CC-4BDC-8A05-DFCB981A0F33\";\n        const UPGRADE_GUID: &str = \"71C1A58D-3FD2-493D-BB62-4B27C66FCCF9\";\n\n        const PATH_GUID_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n\n            [package.metadata.wix]\n            path-guid = \"C38A18DB-12CC-4BDC-8A05-DFCB981A0F33\"\n        \"#;\n\n        const UPGRADE_GUID_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n\n            [package.metadata.wix]\n            upgrade-guid = \"71C1A58D-3FD2-493D-BB62-4B27C66FCCF9\"\n        \"#;\n\n        const IMAGES_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n\n            [package.metadata.wix]\n            product-icon = \"wix/product.ico\"\n            dialog = \"wix/dialog.png\"\n            banner = \"wix/banner.png\"\n        \"#;\n\n        #[test]\n        fn license_name_with_mit_license_field_works() {\n            let project = setup_project(MIT_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Execution::for_test(&input)\n                .licenses(&package)\n                .expect(\"licenses\")\n                .source\n                .expect(\"source license\")\n                .stored_path;\n            assert_eq!(\n                actual,\n                StoredPathBuf::from(format!(\"{WIX}\\\\{LICENSE_FILE_NAME}.{RTF_FILE_EXTENSION}\"))\n            );\n        }\n\n        #[test]\n        fn license_name_with_gpl3_license_field_works() {\n            let project = setup_project(GPL3_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Execution::for_test(&input)\n                .licenses(&package)\n                .expect(\"licenses\")\n                .source\n                .expect(\"source license\")\n                .stored_path;\n            assert_eq!(\n                actual,\n                StoredPathBuf::from(format!(\"{WIX}\\\\{LICENSE_FILE_NAME}.{RTF_FILE_EXTENSION}\"))\n            );\n        }\n\n        #[test]\n        fn license_name_with_apache2_license_field_works() {\n            let project = setup_project(APACHE2_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Execution::for_test(&input)\n                .licenses(&package)\n                .expect(\"licenses\")\n                .source\n                .expect(\"source license\")\n                .stored_path;\n            assert_eq!(\n                actual,\n                StoredPathBuf::from(format!(\"{WIX}\\\\{LICENSE_FILE_NAME}.{RTF_FILE_EXTENSION}\"))\n            );\n        }\n\n        #[test]\n        fn license_name_with_unknown_license_field_works() {\n            let project = setup_project(UNKNOWN_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Execution::for_test(&input)\n                .licenses(&package)\n                .expect(\"licenses\")\n                .source;\n            assert!(actual.is_none());\n        }\n\n        #[test]\n        fn license_source_with_mit_license_field_works() {\n            let project = setup_project(MIT_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Execution::for_test(&input)\n                .licenses(&package)\n                .expect(\"licenses\")\n                .source\n                .expect(\"source license\")\n                .stored_path;\n            assert_eq!(\n                actual,\n                StoredPathBuf::from(format!(\"{WIX}\\\\{LICENSE_FILE_NAME}.{RTF_FILE_EXTENSION}\"))\n            );\n        }\n\n        #[test]\n        fn license_source_with_gpl3_license_field_works() {\n            let project = setup_project(GPL3_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Execution::for_test(&input)\n                .licenses(&package)\n                .expect(\"licenses\")\n                .source\n                .expect(\"source license\")\n                .stored_path;\n            assert_eq!(\n                actual,\n                StoredPathBuf::from(format!(\"{WIX}\\\\{LICENSE_FILE_NAME}.{RTF_FILE_EXTENSION}\"))\n            );\n        }\n\n        #[test]\n        fn license_source_with_apache2_license_field_works() {\n            let project = setup_project(APACHE2_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Execution::for_test(&input)\n                .licenses(&package)\n                .expect(\"licenses\")\n                .source\n                .expect(\"source license\")\n                .stored_path;\n            assert_eq!(\n                actual,\n                StoredPathBuf::from(format!(\"{WIX}\\\\{LICENSE_FILE_NAME}.{RTF_FILE_EXTENSION}\"))\n            );\n        }\n\n        #[test]\n        fn license_source_with_unknown_license_field_works() {\n            let project = setup_project(UNKNOWN_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Execution::for_test(&input)\n                .licenses(&package)\n                .expect(\"licenses\")\n                .source;\n            assert!(actual.is_none());\n        }\n\n        #[test]\n        fn license_false_works() {\n            let project = setup_project(LICENSE_FALSE_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let licenses = Execution::for_test(&input)\n                .licenses(&package)\n                .expect(\"licenses\");\n            assert_eq!(licenses.source, None);\n            assert_eq!(licenses.end_user, None);\n        }\n\n        #[test]\n        fn eula_false_works() {\n            let project = setup_project(EULA_FALSE_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let licenses = Execution::for_test(&input)\n                .licenses(&package)\n                .expect(\"licenses\");\n            assert_eq!(\n                licenses.source.unwrap().stored_path,\n                StoredPathBuf::from(format!(\"{WIX}\\\\{LICENSE_FILE_NAME}.{RTF_FILE_EXTENSION}\"))\n            );\n            assert_eq!(licenses.end_user, None);\n        }\n\n        #[test]\n        fn eula_and_license_false_works() {\n            let project = setup_project(EULA_AND_LICENSE_FALSE_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let licenses = Execution::for_test(&input)\n                .licenses(&package)\n                .expect(\"licenses\");\n            assert_eq!(licenses.source, None);\n            assert_eq!(licenses.end_user, None);\n        }\n\n        #[test]\n        fn license_path_rtf_works() {\n            let project = setup_project(LICENSE_PATH_RTF_MANIFEST);\n            let license_file_path = project.path().join(\"MyLicense.rtf\");\n            let _license_file_handle = File::create(license_file_path).expect(\"Create file\");\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let licenses = Execution::for_test(&input)\n                .licenses(&package)\n                .expect(\"licenses\");\n            assert_eq!(\n                licenses.source.unwrap().stored_path.as_str(),\n                \"MyLicense.rtf\"\n            );\n            assert_eq!(\n                licenses.end_user.unwrap().stored_path.as_str(),\n                \"MyLicense.rtf\"\n            );\n        }\n\n        #[test]\n        fn license_path_txt_works() {\n            let project = setup_project(LICENSE_PATH_TXT_MANIFEST);\n            let license_file_path = project.path().join(\"MyLicense.txt\");\n            let _license_file_handle = File::create(license_file_path).expect(\"Create file\");\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let licenses = Execution::for_test(&input)\n                .licenses(&package)\n                .expect(\"licenses\");\n            assert_eq!(\n                licenses.source.unwrap().stored_path.as_str(),\n                \"MyLicense.txt\"\n            );\n            assert_eq!(licenses.end_user, None);\n        }\n\n        #[test]\n        fn eula_path_rtf_works() {\n            let project = setup_project(EULA_PATH_RTF_MANIFEST);\n            let license_file_path = project.path().join(\"MyEula.rtf\");\n            let _license_file_handle = File::create(license_file_path).expect(\"Create file\");\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let licenses = Execution::for_test(&input)\n                .licenses(&package)\n                .expect(\"licenses\");\n            assert_eq!(\n                licenses.source.unwrap().stored_path.as_str(),\n                format!(\"{WIX}\\\\{LICENSE_FILE_NAME}.{RTF_FILE_EXTENSION}\")\n            );\n            assert_eq!(\n                licenses.end_user.unwrap().stored_path.as_str(),\n                \"MyEula.rtf\"\n            );\n        }\n\n        #[test]\n        fn eula_path_txt_works() {\n            let project = setup_project(EULA_PATH_TXT_MANIFEST);\n            let license_file_path = project.path().join(\"MyEula.txt\");\n            let _license_file_handle = File::create(license_file_path).expect(\"Create file\");\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let licenses = Execution::for_test(&input)\n                .licenses(&package)\n                .expect(\"licenses\");\n            assert_eq!(\n                licenses.source.unwrap().stored_path.as_str(),\n                format!(\"{WIX}\\\\{LICENSE_FILE_NAME}.{RTF_FILE_EXTENSION}\")\n            );\n            assert_eq!(\n                licenses.end_user.unwrap().stored_path.as_str(),\n                \"MyEula.txt\"\n            );\n        }\n\n        #[test]\n        fn eula_and_license_path_rtf_works() {\n            let project = setup_project(EULA_AND_LICENSE_PATH_RTF_MANIFEST);\n            let license_file_path = project.path().join(\"MyLicense.rtf\");\n            let _license_file_handle = File::create(license_file_path).expect(\"Create file\");\n            let eula_file_path = project.path().join(\"MyEula.rtf\");\n            let _eula_file_handle = File::create(eula_file_path).expect(\"Create file\");\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let licenses = Execution::for_test(&input)\n                .licenses(&package)\n                .expect(\"licenses\");\n            assert_eq!(\n                licenses.source.unwrap().stored_path.as_str(),\n                \"MyLicense.rtf\"\n            );\n            assert_eq!(\n                licenses.end_user.unwrap().stored_path.as_str(),\n                \"MyEula.rtf\"\n            );\n        }\n\n        #[test]\n        fn eula_bad_path_errors() {\n            let project = setup_project(EULA_BAD_PATH_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let licenses = Execution::for_test(&input).licenses(&package);\n            assert!(licenses.is_err());\n        }\n\n        #[test]\n        fn license_bad_path_errors() {\n            let project = setup_project(LICENSE_BAD_PATH_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let licenses = Execution::for_test(&input).licenses(&package);\n            assert!(licenses.is_err());\n        }\n\n        #[test]\n        fn license_file_bad_path_errors() {\n            let project = setup_project(LICENSE_FILE_BAD_PATH_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let licenses = Execution::for_test(&input).licenses(&package);\n            assert!(licenses.is_err());\n        }\n\n        #[test]\n        fn binaries_with_no_bin_section_works() {\n            let project = setup_project(MIT_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Execution::for_test(&input).binaries(&package).unwrap();\n            assert_eq!(\n                actual,\n                vec![hashmap! {\n                    \"binary-index\" => 0.to_string(),\n                    \"binary-name\" => String::from(\"Example\"),\n                    \"binary-source\" => String::from(\"$(var.CargoTargetBinDir)\\\\Example.exe\")\n                }]\n            )\n        }\n\n        #[test]\n        fn binaries_with_single_bin_section_works() {\n            let project = setup_project(MIT_MANIFEST_BIN);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Execution::for_test(&input).binaries(&package).unwrap();\n            assert_eq!(\n                actual,\n                vec![hashmap! {\n                    \"binary-index\" => 0.to_string(),\n                    \"binary-name\" => String::from(\"Different\"),\n                    \"binary-source\" => String::from(\"$(var.CargoTargetBinDir)\\\\Different.exe\")\n                }]\n            )\n        }\n\n        #[test]\n        fn binaries_with_multiple_bin_sections_works() {\n            let project = setup_project(MULTIPLE_BIN_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Execution::for_test(&input).binaries(&package).unwrap();\n            assert_eq!(\n                actual,\n                vec![\n                    hashmap! {\n                        \"binary-index\" => 0.to_string(),\n                        \"binary-name\" => String::from(\"binary0\"),\n                        \"binary-source\" => String::from(\"$(var.CargoTargetBinDir)\\\\binary0.exe\")\n                    },\n                    hashmap! {\n                        \"binary-index\" => 1.to_string(),\n                        \"binary-name\" => String::from(\"binary1\"),\n                        \"binary-source\" => String::from(\"$(var.CargoTargetBinDir)\\\\binary1.exe\")\n                    },\n                    hashmap! {\n                        \"binary-index\" => 2.to_string(),\n                        \"binary-name\" => String::from(\"binary2\"),\n                        \"binary-source\" => String::from(\"$(var.CargoTargetBinDir)\\\\binary2.exe\")\n                    }\n                ]\n            )\n        }\n\n        #[test]\n        fn manufacturer_with_defaults_works() {\n            const EXPECTED: &str = \"First Last\";\n\n            let project = setup_project(MIN_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Execution::for_test(&input).manufacturer(&package).unwrap();\n            assert_eq!(actual, String::from(EXPECTED));\n        }\n\n        #[test]\n        fn manufacturer_with_override_works() {\n            const EXPECTED: &str = \"Example\";\n\n            let project = setup_project(MIN_MANIFEST);\n            let manifest = crate::manifest(Some(&project.path().join(\"Cargo.toml\"))).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Builder::default()\n                .manufacturer(Some(EXPECTED))\n                .build()\n                .manufacturer(&package)\n                .unwrap();\n            assert_eq!(actual, String::from(EXPECTED));\n        }\n\n        #[test]\n        fn help_url_with_defaults_works() {\n            let project = setup_project(MIN_MANIFEST);\n            let manifest = crate::manifest(Some(&project.path().join(\"Cargo.toml\"))).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Builder::default().build().help_url(&package);\n            assert!(actual.is_none());\n        }\n\n        #[test]\n        fn help_url_with_documentation_works() {\n            const EXPECTED: &str = \"http://www.example.com\";\n\n            let project = setup_project(DOCUMENTATION_MANIFEST);\n            let manifest = crate::manifest(Some(&project.path().join(\"Cargo.toml\"))).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Builder::default().build().help_url(&package);\n            assert_eq!(actual, Some(String::from(EXPECTED)));\n        }\n\n        #[test]\n        fn help_url_with_homepage_works() {\n            const EXPECTED: &str = \"http://www.example.com\";\n\n            let project = setup_project(HOMEPAGE_MANIFEST);\n            let manifest = crate::manifest(Some(&project.path().join(\"Cargo.toml\"))).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Builder::default().build().help_url(&package);\n            assert_eq!(actual, Some(String::from(EXPECTED)));\n        }\n\n        #[test]\n        fn help_url_with_repository_works() {\n            const EXPECTED: &str = \"http://www.example.com\";\n\n            let project = setup_project(REPOSITORY_MANIFEST);\n            let manifest = crate::manifest(Some(&project.path().join(\"Cargo.toml\"))).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Builder::default().build().help_url(&package);\n            assert_eq!(actual, Some(String::from(EXPECTED)));\n        }\n\n        #[test]\n        fn eula_with_defaults_works() {\n            let project = setup_project(MIN_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Execution::for_test(&input)\n                .licenses(&package)\n                .unwrap()\n                .end_user;\n            assert!(actual.is_none());\n        }\n\n        #[test]\n        fn eula_with_mit_license_field_works() {\n            let project = setup_project(MIT_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let licenses = Execution::for_test(&input).licenses(&package).unwrap();\n            let source = licenses.source.unwrap();\n            let source_path = source.stored_path;\n            let (template_out, source_template) = source.generate.unwrap();\n            let eula_path = licenses.end_user.unwrap().stored_path;\n\n            let expected_rel_path = format!(\"{WIX}\\\\{LICENSE_FILE_NAME}.{RTF_FILE_EXTENSION}\");\n            let expected_abs_path = Utf8Path::from_path(input.parent().unwrap())\n                .unwrap()\n                .join(WIX)\n                .join(format!(\"{LICENSE_FILE_NAME}.{RTF_FILE_EXTENSION}\"));\n            assert_eq!(source_template, Template::Mit);\n            assert_eq!(source_path.as_str(), expected_rel_path);\n            assert_eq!(template_out, expected_abs_path);\n            assert_eq!(eula_path.as_str(), expected_rel_path);\n        }\n\n        #[test]\n        fn eula_with_apache2_license_field_works() {\n            let project = setup_project(APACHE2_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let licenses = Execution::for_test(&input).licenses(&package).unwrap();\n            let source = licenses.source.unwrap();\n            let source_path = source.stored_path;\n            let (template_out, source_template) = source.generate.unwrap();\n            let eula_path = licenses.end_user.unwrap().stored_path;\n\n            let expected_rel_path = format!(\"{WIX}\\\\{LICENSE_FILE_NAME}.{RTF_FILE_EXTENSION}\");\n            let expected_abs_path = Utf8Path::from_path(input.parent().unwrap())\n                .unwrap()\n                .join(WIX)\n                .join(format!(\"{LICENSE_FILE_NAME}.{RTF_FILE_EXTENSION}\"));\n            assert_eq!(source_template, Template::Apache2);\n            assert_eq!(source_path.as_str(), expected_rel_path);\n            assert_eq!(template_out, expected_abs_path);\n            assert_eq!(eula_path.as_str(), expected_rel_path);\n        }\n\n        #[test]\n        fn eula_with_gpl3_license_field_works() {\n            let project = setup_project(GPL3_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let licenses = Execution::for_test(&input).licenses(&package).unwrap();\n            let source = licenses.source.unwrap();\n            let source_path = source.stored_path;\n            let (template_out, source_template) = source.generate.unwrap();\n            let eula_path = licenses.end_user.unwrap().stored_path;\n\n            let expected_rel_path = format!(\"{WIX}\\\\{LICENSE_FILE_NAME}.{RTF_FILE_EXTENSION}\");\n            let expected_abs_path = Utf8Path::from_path(input.parent().unwrap())\n                .unwrap()\n                .join(WIX)\n                .join(format!(\"{LICENSE_FILE_NAME}.{RTF_FILE_EXTENSION}\"));\n            assert_eq!(source_template, Template::Gpl3);\n            assert_eq!(source_path.as_str(), expected_rel_path);\n            assert_eq!(template_out, expected_abs_path);\n            assert_eq!(eula_path.as_str(), expected_rel_path);\n        }\n\n        #[test]\n        fn eula_with_unknown_license_field_works() {\n            let project = setup_project(UNKNOWN_MANIFEST);\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let licenses = Execution::for_test(&input).licenses(&package).unwrap();\n            let source = licenses.source;\n            let eula = licenses.end_user;\n\n            assert_eq!(source, None);\n            assert_eq!(eula, None);\n        }\n\n        #[test]\n        #[cfg(windows)]\n        fn eula_with_override_works() {\n            let project = setup_project(MIT_MANIFEST);\n            let license_file_path = project.path().join(\"Example.rtf\");\n            let _license_file_handle = File::create(&license_file_path).expect(\"Create file\");\n\n            let manifest = crate::manifest(Some(&project.path().join(\"Cargo.toml\"))).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Builder::default()\n                .eula(license_file_path.to_str())\n                .build()\n                .licenses(&package)\n                .unwrap()\n                .end_user\n                .unwrap()\n                .stored_path;\n            assert_eq!(\n                actual,\n                StoredPathBuf::from_std_path(&license_file_path).unwrap(),\n            );\n        }\n\n        #[test]\n        fn eula_with_license_file_field_works() {\n            let project = setup_project(LICENSE_FILE_RTF_MANIFEST);\n            let license_file_path = project.path().join(\"Example.rtf\");\n            let _license_file_handle = File::create(license_file_path).expect(\"Create file\");\n\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Execution::for_test(&input)\n                .licenses(&package)\n                .unwrap()\n                .end_user\n                .unwrap()\n                .stored_path;\n            assert_eq!(actual.as_str(), \"Example.rtf\");\n        }\n\n        #[test]\n        fn eula_with_license_file_extension_works() {\n            let project = setup_project(LICENSE_FILE_TXT_MANIFEST);\n            let license_file_path = project.path().join(\"Example.txt\");\n            let _license_file_handle = File::create(license_file_path).expect(\"Create file\");\n\n            let input = project.path().join(\"Cargo.toml\");\n            let manifest = crate::manifest(Some(&input)).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let licenses = Execution::for_test(&input).licenses(&package).unwrap();\n            let source = licenses.source.unwrap();\n            let eula = licenses.end_user;\n\n            assert_eq!(source.generate, None);\n            assert_eq!(source.name, None);\n            assert_eq!(source.stored_path.as_str(), \"Example.txt\");\n            assert_eq!(eula, None);\n        }\n\n        #[test]\n        fn eula_with_wrong_file_extension_override_works() {\n            let project = setup_project(LICENSE_FILE_TXT_MANIFEST);\n            let license_file_path = project.path().join(\"Example.txt\");\n            let _license_file_handle = File::create(&license_file_path).expect(\"Create file\");\n\n            let manifest = crate::manifest(Some(&project.path().join(\"Cargo.toml\"))).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            // We want to check that if the user hands us an OS-specific path here then we preserve it's format\n            // So we turn the input to a string without escaping.\n            let input = license_file_path.to_str().unwrap();\n            let expected = StoredPathBuf::new(input.to_owned());\n            let licenses = Builder::default()\n                .eula(Some(input))\n                .build()\n                .licenses(&package)\n                .unwrap();\n            let eula = licenses.end_user.unwrap();\n\n            assert_eq!(eula.stored_path, expected);\n        }\n\n        #[test]\n        fn path_guid_with_override_works() {\n            let expected = Uuid::new_v4().as_hyphenated().to_string().to_uppercase();\n\n            let project = setup_project(MIN_MANIFEST);\n            let manifest = crate::manifest(Some(&project.path().join(\"Cargo.toml\"))).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Builder::default()\n                .path_guid(Some(&expected))\n                .build()\n                .path_guid(&package)\n                .unwrap();\n            assert_eq!(actual, expected);\n        }\n\n        #[test]\n        fn path_guid_metadata_works() {\n            let project = setup_project(PATH_GUID_MANIFEST);\n            let manifest = crate::manifest(Some(&project.path().join(\"Cargo.toml\"))).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Builder::default().build().path_guid(&package).unwrap();\n            assert_eq!(actual, PATH_GUID);\n        }\n\n        #[test]\n        fn path_guid_metadata_and_override_works() {\n            let expected = Uuid::new_v4().as_hyphenated().to_string().to_uppercase();\n\n            let project = setup_project(PATH_GUID_MANIFEST);\n            let manifest = crate::manifest(Some(&project.path().join(\"Cargo.toml\"))).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Builder::default()\n                .path_guid(Some(&expected))\n                .build()\n                .path_guid(&package)\n                .unwrap();\n            assert_eq!(actual, expected);\n        }\n\n        #[test]\n        fn upgrade_guid_with_override_works() {\n            let expected = Uuid::new_v4().as_hyphenated().to_string().to_uppercase();\n\n            let project = setup_project(MIN_MANIFEST);\n            let manifest = crate::manifest(Some(&project.path().join(\"Cargo.toml\"))).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Builder::default()\n                .upgrade_guid(Some(&expected))\n                .build()\n                .upgrade_guid(&package)\n                .unwrap();\n            assert_eq!(actual, expected);\n        }\n\n        #[test]\n        fn upgrade_guid_metadata_works() {\n            let project = setup_project(UPGRADE_GUID_MANIFEST);\n            let manifest = crate::manifest(Some(&project.path().join(\"Cargo.toml\"))).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Builder::default().build().upgrade_guid(&package).unwrap();\n            assert_eq!(actual, UPGRADE_GUID);\n        }\n\n        #[test]\n        fn upgrade_guid_metadata_and_override_works() {\n            let expected = Uuid::new_v4().as_hyphenated().to_string().to_uppercase();\n\n            let project = setup_project(UPGRADE_GUID_MANIFEST);\n            let manifest = crate::manifest(Some(&project.path().join(\"Cargo.toml\"))).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Builder::default()\n                .upgrade_guid(Some(&expected))\n                .build()\n                .upgrade_guid(&package)\n                .unwrap();\n            assert_eq!(actual, expected);\n        }\n\n        #[test]\n        fn image_metadata_works() {\n            let project = setup_project(IMAGES_MANIFEST);\n            let manifest = crate::manifest(Some(&project.path().join(\"Cargo.toml\"))).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Builder::default().build();\n            assert_eq!(\n                actual.product_icon(&package).unwrap().as_str(),\n                \"wix/product.ico\"\n            );\n            assert_eq!(\n                actual.dialog_image(&package).unwrap().as_str(),\n                \"wix/dialog.png\"\n            );\n            assert_eq!(\n                actual.banner_image(&package).unwrap().as_str(),\n                \"wix/banner.png\"\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "src/purge.rs",
    "content": "// Copyright (C) 2017 Christopher R. Field.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//! The implementation for the `purge` command. This command is focused on\n//! removing all files associated with `cargo wix` subcommand.\n\nuse crate::CARGO_MANIFEST_FILE;\nuse crate::Error;\nuse crate::Result;\nuse crate::WIX;\nuse crate::clean;\n\nuse log::{debug, info, trace, warn};\n\nuse std::env;\nuse std::ffi::OsStr;\nuse std::fs;\nuse std::path::PathBuf;\n\n/// A builder for creating an execution context to remove all files and folders\n/// related to the `cargo wix` subcommand.\n#[derive(Debug, Clone)]\npub struct Builder<'a> {\n    input: Option<&'a str>,\n}\n\nimpl<'a> Builder<'a> {\n    /// Creates a new `Builder` instance.\n    pub fn new() -> Self {\n        Builder { input: None }\n    }\n\n    /// Sets the path to a package's manifest (Cargo.toml) to be purge.\n    ///\n    /// The default is to use the current working directory if a Cargo.toml file\n    /// is found.\n    pub fn input(&mut self, i: Option<&'a str>) -> &mut Self {\n        self.input = i;\n        self\n    }\n\n    /// Builds an execution context to remove all WiX Toolset-related files and\n    /// folders from a package.\n    pub fn build(&mut self) -> Execution {\n        Execution {\n            input: self.input.map(PathBuf::from),\n        }\n    }\n}\n\nimpl Default for Builder<'_> {\n    fn default() -> Self {\n        Builder::new()\n    }\n}\n\n/// A context for removing WiX Toolset-related source and output files from a\n/// package.\n#[derive(Debug)]\npub struct Execution {\n    input: Option<PathBuf>,\n}\n\nimpl Execution {\n    /// Removes all WiX Toolset-related files and folders, including files and\n    /// folders created with the `cargo wix` subcommand, from a package.\n    ///\n    /// Use with caution! This cannot be undone.\n    pub fn run(self) -> Result<()> {\n        debug!(\"input = {:?}\", self.input);\n        let mut cleaner = clean::Builder::new();\n        cleaner.input(self.input.as_ref().and_then(|p| p.to_str()));\n        cleaner.build().run()?;\n        let wix = self.wix()?;\n        debug!(\"wix = {:?}\", wix);\n        if wix.exists() {\n            trace!(\"The 'wix' folder exists\");\n            warn!(\"Removing the 'wix' folder\");\n            fs::remove_dir_all(wix)?;\n        } else {\n            trace!(\"The 'wix' folder does not exist\");\n            info!(\"Nothing to purge\");\n        }\n        Ok(())\n    }\n\n    fn wix(&self) -> Result<PathBuf> {\n        if let Some(ref input) = self.input {\n            trace!(\"A Cargo.toml file has been explicitly specified\");\n            if input.exists() && input.is_file() {\n                trace!(\"The input path exists and it is a file\");\n                if input.file_name() == Some(OsStr::new(CARGO_MANIFEST_FILE)) {\n                    trace!(\"The input file is a Cargo manifest file\");\n                    Ok(input\n                        .parent()\n                        .map(|p| p.to_path_buf())\n                        .map(|mut p| {\n                            p.push(WIX);\n                            p\n                        })\n                        .unwrap())\n                } else {\n                    Err(Error::Generic(format!(\n                        \"The '{}' path does not appear to be to a '{}' file\",\n                        input.display(),\n                        CARGO_MANIFEST_FILE\n                    )))\n                }\n            } else {\n                Err(Error::Generic(format!(\n                    \"The '{}' path does not exist or it is not a file\",\n                    input.display()\n                )))\n            }\n        } else {\n            trace!(\n                \"An input path has NOT been explicitly specified, implicitly using the current \\\n                 working directory\"\n            );\n            let mut cwd = env::current_dir()?;\n            cwd.push(WIX);\n            Ok(cwd)\n        }\n    }\n}\n\nimpl Default for Execution {\n    fn default() -> Self {\n        Builder::new().build()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    mod builder {\n        use super::*;\n\n        #[test]\n        fn input_works() {\n            const EXPECTED: &str = \"C:\\\\Cargo.toml\";\n            let mut actual = Builder::default();\n            actual.input(Some(EXPECTED));\n            assert_eq!(actual.input, Some(EXPECTED));\n        }\n    }\n\n    mod execution {\n        extern crate assert_fs;\n\n        use super::*;\n        use std::fs::File;\n\n        #[test]\n        fn wix_works() {\n            let actual = Execution::default().wix().unwrap();\n            let cwd = env::current_dir()\n                .expect(\"Current Working Directory\")\n                .join(WIX);\n            assert_eq!(actual, cwd);\n        }\n\n        #[test]\n        fn wix_with_nonexistent_manifest_fails() {\n            let result = Builder::new().input(Some(\"C:\\\\Cargo.toml\")).build().wix();\n            assert!(result.is_err());\n        }\n\n        #[test]\n        fn wix_with_existing_file_but_not_cargo_toml_fails() {\n            let temp_dir = assert_fs::TempDir::new().unwrap();\n            let non_cargo_toml_path = temp_dir.path().join(\"Example.txt\");\n            let _non_cargo_toml_handle = File::create(&non_cargo_toml_path).expect(\"Create file\");\n            let result = Builder::new()\n                .input(non_cargo_toml_path.to_str())\n                .build()\n                .wix();\n            assert!(result.is_err());\n        }\n\n        #[test]\n        fn wix_with_existing_cargo_toml_works() {\n            let temp_dir = assert_fs::TempDir::new().unwrap();\n            let cargo_toml_path = temp_dir.path().join(\"Cargo.toml\");\n            let expected = temp_dir.path().join(WIX);\n            let _non_cargo_toml_handle = File::create(&cargo_toml_path).expect(\"Create file\");\n            let actual = Builder::new()\n                .input(cargo_toml_path.to_str())\n                .build()\n                .wix()\n                .unwrap();\n            assert_eq!(actual, expected);\n        }\n    }\n}\n"
  },
  {
    "path": "src/sign.rs",
    "content": "// Copyright (C) 2017 Christopher R. Field.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//! The implementation for the `sign` command. This command focuses on signing\n//! installers using the Windows SDK `signtool` application.\n\nuse crate::BINARY_FOLDER_NAME;\nuse crate::EXE_FILE_EXTENSION;\nuse crate::Error;\nuse crate::MSI_FILE_EXTENSION;\nuse crate::Result;\nuse crate::SIGNTOOL;\nuse crate::SIGNTOOL_PATH_KEY;\nuse crate::TimestampServer;\nuse crate::WIX;\n\nuse log::{debug, info, trace};\n\nuse std::env;\nuse std::ffi::OsStr;\nuse std::fs;\nuse std::io::ErrorKind;\nuse std::path::{Path, PathBuf};\nuse std::process::{Command, Stdio};\nuse std::str::FromStr;\n\nuse cargo_metadata::Package;\n\n/// A builder for creating an execution context to sign an installer.\n#[derive(Debug, Clone)]\npub struct Builder<'a> {\n    bin_path: Option<&'a str>,\n    capture_output: bool,\n    description: Option<&'a str>,\n    homepage: Option<&'a str>,\n    input: Option<&'a str>,\n    installer: Option<&'a str>,\n    package: Option<&'a str>,\n    product_name: Option<&'a str>,\n    timestamp: Option<&'a str>,\n}\n\nimpl<'a> Builder<'a> {\n    /// Creates a new `Builder` instance.\n    pub fn new() -> Self {\n        Builder {\n            bin_path: None,\n            capture_output: true,\n            description: None,\n            homepage: None,\n            input: None,\n            installer: None,\n            package: None,\n            product_name: None,\n            timestamp: None,\n        }\n    }\n\n    /// Sets the package on which to operate during this build\n    pub fn package(&mut self, p: Option<&'a str>) -> &mut Self {\n        self.package = p;\n        self\n    }\n    /// Sets the path to the folder containing the `signtool.exe` file.\n    ///\n    // Normally the `signtool.exe` is installed in the `bin` folder of the\n    // Windows SDK installation. The default is to use the `PATH` system\n    // environment variable.\n    pub fn bin_path(&mut self, b: Option<&'a str>) -> &mut Self {\n        self.bin_path = b;\n        self\n    }\n\n    /// Enables or disables capturing of the output from the `signtool`\n    /// application.\n    ///\n    /// The default is to capture all output, i.e. display nothing in the\n    /// console but the log statements.\n    pub fn capture_output(&mut self, c: bool) -> &mut Self {\n        self.capture_output = c;\n        self\n    }\n\n    /// Sets the description.\n    ///\n    /// This override the description obtained from the `description` field in\n    /// the package's manifest (Cargo.toml).\n    ///\n    /// The description is displayed in the ACL dialog.\n    pub fn description(&mut self, d: Option<&'a str>) -> &mut Self {\n        self.description = d;\n        self\n    }\n\n    /// Sets the homepage URL that is displayed in the ACL dialog.\n    ///\n    /// The default is to use the value for the `homepage` field in the\n    /// package's manifest (Cargo.toml) if it exists; otherwise, a URL\n    /// is _not_ displayed in the ACL dialog.\n    pub fn homepage(&mut self, h: Option<&'a str>) -> &mut Self {\n        self.homepage = h;\n        self\n    }\n\n    /// Sets the path to a package's manifest (Cargo.toml).\n    pub fn input(&mut self, i: Option<&'a str>) -> &mut Self {\n        self.input = i;\n        self\n    }\n\n    /// Override default installer path.\n    pub fn installer(&mut self, i: Option<&'a str>) -> &mut Self {\n        self.installer = i;\n        self\n    }\n\n    /// Sets the product name.\n    ///\n    /// The default is to use the value for the `name` field in the package's\n    /// manifest (Cargo.toml).\n    pub fn product_name(&mut self, p: Option<&'a str>) -> &mut Self {\n        self.product_name = p;\n        self\n    }\n\n    /// Sets the URL for the timestamp server used when signing an installer.\n    ///\n    /// The default is to _not_ use a timestamp server, even though it is highly\n    /// recommended. Use this method to enable signing with the timestamp.\n    pub fn timestamp(&mut self, t: Option<&'a str>) -> &mut Self {\n        self.timestamp = t;\n        self\n    }\n\n    /// Creates an execution context for signing a package's installer.\n    pub fn build(&mut self) -> Execution {\n        Execution {\n            bin_path: self.bin_path.map(PathBuf::from),\n            capture_output: self.capture_output,\n            description: self.description.map(String::from),\n            homepage: self.homepage.map(String::from),\n            input: self.input.map(PathBuf::from),\n            installer: self.installer.map(PathBuf::from),\n            package: self.package.map(String::from),\n            product_name: self.product_name.map(String::from),\n            timestamp: self.timestamp.map(String::from),\n        }\n    }\n}\n\nimpl Default for Builder<'_> {\n    fn default() -> Self {\n        Builder::new()\n    }\n}\n\n/// A context for signing a package's installer.\n#[derive(Debug)]\npub struct Execution {\n    bin_path: Option<PathBuf>,\n    capture_output: bool,\n    description: Option<String>,\n    homepage: Option<String>,\n    input: Option<PathBuf>,\n    installer: Option<PathBuf>,\n    package: Option<String>,\n    product_name: Option<String>,\n    timestamp: Option<String>,\n}\n\nimpl Execution {\n    /// Signs a package's installer.\n    pub fn run(self) -> Result<()> {\n        info!(\"Signing the installer\");\n        debug!(\"bin_path = {:?}\", self.bin_path);\n        debug!(\"capture_output = {:?}\", self.capture_output);\n        debug!(\"description = {:?}\", self.description);\n        debug!(\"homepage = {:?}\", self.homepage);\n        debug!(\"input = {:?}\", self.input);\n        debug!(\"installer = {:?}\", self.installer);\n        debug!(\"package = {:?}\", self.package);\n        debug!(\"product_name = {:?}\", self.product_name);\n        debug!(\"timestamp = {:?}\", self.timestamp);\n        let manifest = super::manifest(self.input.as_ref())?;\n        debug!(\"target_directory = {:?}\", manifest.target_directory);\n        let package = super::package(&manifest, self.package.as_deref())?;\n        let product_name = super::product_name(self.product_name.as_ref(), &package);\n        let description = if let Some(d) = super::description(self.description.clone(), &package) {\n            trace!(\n                \"A description was provided either at the command line or in the package's manifest (Cargo.toml).\"\n            );\n            format!(\"{product_name} - {d}\")\n        } else {\n            trace!(\n                \"A description was not provided at the command line or in the package's manifest (Cargo.toml).\"\n            );\n            product_name\n        };\n        debug!(\"description = {:?}\", description);\n        let msi = self.msi(manifest.target_directory.as_std_path())?;\n        let mut signer = self.signer()?;\n        debug!(\"signer = {:?}\", signer);\n        if self.capture_output {\n            trace!(\"Capturing the {} output\", SIGNTOOL);\n            signer.stdout(Stdio::null());\n            signer.stderr(Stdio::null());\n        }\n        signer\n            .arg(\"sign\")\n            .arg(\"/a\")\n            .arg(\"/fd\")\n            .arg(\"certHash\")\n            .arg(\"/d\")\n            .arg(description);\n        if let Some(h) = self.homepage(&package) {\n            trace!(\"Using the '{}' URL for the expanded description\", h);\n            signer.arg(\"/du\").arg(h);\n        }\n        if let Some(t) = self.timestamp {\n            let server = TimestampServer::from_str(&t)?;\n            trace!(\n                \"Using the '{}' timestamp server to sign the installer\",\n                server\n            );\n            signer.arg(\"/t\");\n            signer.arg(server.url());\n        }\n        let status = signer.arg(&msi).status().map_err(|err| {\n            if err.kind() == ErrorKind::NotFound {\n                Error::Generic(format!(\n                    \"The {SIGNTOOL} application could not be found. Please check the Windows 10 SDK \\\n                     (https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk) is \\\n                     installed and you are using the x64 or x86 Native Build Tools prompt so the \\\n                     {SIGNTOOL} application is available.\"\n                ))\n            } else {\n                err.into()\n            }\n        })?;\n        if !status.success() {\n            return Err(Error::Command(\n                SIGNTOOL,\n                status.code().unwrap_or(100),\n                self.capture_output,\n            ));\n        }\n        Ok(())\n    }\n\n    fn homepage(&self, manifest: &Package) -> Option<String> {\n        self.homepage\n            .as_ref()\n            .map(String::from)\n            .or_else(|| manifest.homepage.clone())\n    }\n\n    fn msi(&self, target_directory: &Path) -> Result<PathBuf> {\n        if let Some(ref i) = self.installer {\n            trace!(\"The path to an installer to sign has been explicitly set\");\n            let msi = PathBuf::from(i);\n            if msi.exists() {\n                trace!(\"The installer exists\");\n                Ok(msi)\n            } else {\n                Err(Error::Generic(format!(\n                    \"The '{}' path does not exist for the installer\",\n                    msi.display()\n                )))\n            }\n        } else {\n            trace!(\"The path to an installer has not been explicitly set\");\n            let cwd = target_directory.join(WIX);\n            for entry in fs::read_dir(cwd)? {\n                let entry = entry?;\n                let path = entry.path();\n                if path.extension() == Some(OsStr::new(MSI_FILE_EXTENSION)) {\n                    return Ok(path);\n                }\n            }\n            Err(Error::Generic(format!(\n                \"Could not find an installer ({MSI_FILE_EXTENSION}) to sign\"\n            )))\n        }\n    }\n\n    fn signer(&self) -> Result<Command> {\n        if let Some(mut path) = self.bin_path.as_ref().map(|s| {\n            let mut p = PathBuf::from(s);\n            trace!(\n                \"Using the '{}' path to the Windows SDK '{}' folder for the signer\",\n                p.display(),\n                BINARY_FOLDER_NAME\n            );\n            p.push(SIGNTOOL);\n            p.set_extension(EXE_FILE_EXTENSION);\n            p\n        }) {\n            if !path.exists() {\n                path.pop(); // Remove the 'signtool' application from the path\n                Err(Error::Generic(format!(\n                    \"The signer application ('{}') does not exist at the '{}' path specified via \\\n                    the '-S, --sign-path' command line argument. Please check the path is correct and \\\n                    the signer application exists at the path.\",\n                    SIGNTOOL,\n                    path.display()\n                )))\n            } else {\n                Ok(Command::new(path))\n            }\n        } else if let Some(mut path) = env::var_os(SIGNTOOL_PATH_KEY).map(|s| {\n            let mut p = PathBuf::from(s);\n            trace!(\n                \"Using the '{}' path to the Windows SDK '{}' folder for the signer\",\n                p.display(),\n                BINARY_FOLDER_NAME\n            );\n            p.push(SIGNTOOL);\n            p.set_extension(EXE_FILE_EXTENSION);\n            p\n        }) {\n            if !path.exists() {\n                path.pop(); // Remove the `signtool` application from the path\n                Err(Error::Generic(format!(\n                    \"The signer application ('{}') does not exist at the '{}' path specified \\\n                     via the {} environment variable. Please check the path is correct and the \\\n                     signer application exists at the path.\",\n                    SIGNTOOL,\n                    path.display(),\n                    SIGNTOOL_PATH_KEY\n                )))\n            } else {\n                Ok(Command::new(path))\n            }\n        } else {\n            Ok(Command::new(SIGNTOOL))\n        }\n    }\n}\n\nimpl Default for Execution {\n    fn default() -> Self {\n        Builder::new().build()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    mod builder {\n        use super::*;\n\n        #[test]\n        fn bin_path_works() {\n            const EXPECTED: &str = \"C:\\\\signtool.exe\";\n            let mut actual = Builder::new();\n            actual.bin_path(Some(EXPECTED));\n            assert_eq!(actual.bin_path, Some(EXPECTED));\n        }\n\n        #[test]\n        fn capture_output_works() {\n            let mut actual = Builder::new();\n            actual.capture_output(false);\n            assert!(!actual.capture_output);\n        }\n\n        #[test]\n        fn description_works() {\n            const EXPECTED: &str = \"This is a description\";\n            let mut actual = Builder::new();\n            actual.description(Some(EXPECTED));\n            assert_eq!(actual.description, Some(EXPECTED));\n        }\n\n        #[test]\n        fn homepage_works() {\n            const EXPECTED: &str = \"http://www.example.com\";\n            let mut actual = Builder::new();\n            actual.homepage(Some(EXPECTED));\n            assert_eq!(actual.homepage, Some(EXPECTED));\n        }\n\n        #[test]\n        fn input_works() {\n            const EXPECTED: &str = \"C:\\\\Example\";\n            let mut actual = Builder::new();\n            actual.input(Some(EXPECTED));\n            assert_eq!(actual.input, Some(EXPECTED));\n        }\n\n        #[test]\n        fn product_name_works() {\n            const EXPECTED: &str = \"Example\";\n            let mut actual = Builder::new();\n            actual.product_name(Some(EXPECTED));\n            assert_eq!(actual.product_name, Some(EXPECTED));\n        }\n\n        #[test]\n        fn timestamp_works() {\n            const EXPECTED: &str = \"http://www.example.com\";\n            let mut actual = Builder::new();\n            actual.timestamp(Some(EXPECTED));\n            assert_eq!(actual.timestamp, Some(EXPECTED));\n        }\n    }\n\n    mod execution {\n        extern crate assert_fs;\n\n        use std::fs::File;\n\n        use super::*;\n        use crate::tests::setup_project;\n\n        const MIN_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n        \"#;\n\n        const HOMEPAGE_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n            homepage = \"http://www.example.com\"\n        \"#;\n\n        #[test]\n        fn homepage_without_homepage_field_works() {\n            let project = setup_project(MIN_MANIFEST);\n            let manifest = crate::manifest(Some(&project.path().join(\"Cargo.toml\"))).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Execution::default().homepage(&package);\n            assert!(actual.is_none());\n        }\n\n        #[test]\n        fn homepage_with_homepage_field_works() {\n            let project = setup_project(HOMEPAGE_MANIFEST);\n            let manifest = crate::manifest(Some(&project.path().join(\"Cargo.toml\"))).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Execution::default().homepage(&package);\n            assert_eq!(actual, Some(String::from(\"http://www.example.com\")));\n        }\n\n        #[test]\n        fn homepage_with_override_works() {\n            const EXPECTED: &str = \"http://www.another.com\";\n\n            let project = setup_project(HOMEPAGE_MANIFEST);\n            let manifest = crate::manifest(Some(&project.path().join(\"Cargo.toml\"))).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n\n            let actual = Builder::new()\n                .homepage(Some(EXPECTED))\n                .build()\n                .homepage(&package);\n            assert_eq!(actual, Some(String::from(EXPECTED)));\n        }\n\n        #[test]\n        fn msi_with_nonexistent_installer_fails() {\n            let result = Execution::default().msi(Path::new(\"target\"));\n            assert!(result.is_err());\n        }\n\n        #[test]\n        fn msi_with_existing_file_works() {\n            let temp_dir = assert_fs::TempDir::new().unwrap();\n            let msi_path = temp_dir.path().join(\"Example.msi\");\n            let _msi_handle = File::create(&msi_path).expect(\"Create file\");\n            let actual = Builder::new()\n                .installer(msi_path.to_str())\n                .build()\n                .msi(Path::new(\"target\"))\n                .unwrap();\n            assert_eq!(actual, msi_path);\n        }\n\n        #[test]\n        #[cfg(windows)]\n        fn signer_works() {\n            let result = Execution::default().signer();\n            assert!(result.is_ok());\n        }\n\n        #[test]\n        fn signer_with_nonexisting_path_fails() {\n            let result = Builder::new()\n                .bin_path(Some(\"Example.exe\"))\n                .build()\n                .signer();\n            assert!(result.is_err());\n        }\n\n        #[test]\n        fn signer_with_nonexistent_environment_path_fails() {\n            unsafe {\n                env::set_var(SIGNTOOL_PATH_KEY, \"Example\");\n            }\n            let result = Execution::default().signer();\n            unsafe {\n                env::remove_var(SIGNTOOL_PATH_KEY);\n            }\n            assert!(result.is_err());\n        }\n    }\n}\n"
  },
  {
    "path": "src/stored_path.rs",
    "content": "//! Utilities for working with paths that need to be stored in files in a cross-platform way.\n//!\n//! Big picture: work with paths however you want, as long as any path that gets written to\n//! a file (e.g. via a template) always gets converted into a [`StoredPathBuf`][] first,\n//! forcing all the platform-specific differences to get normalized out.\n//!\n//! Although both std and camino have excellent support for working with paths, they're both\n//! designed to have host-platform-specific behaviour (which is good/correct) that makes them\n//! unsuitable for working with paths that need to stored in persistent cross-platform\n//! ways -- like writing them to files. The wxs files we generate contain paths to other files,\n//! and we want to reliably produce the same wxs file on all machines, so we need something else!\n//!\n//! Most notably, std and camino handle path separators differently on different platforms:\n//!\n//! * valid separtors:\n//!   * on windows, `\\` and `/` are both valid path separators\n//!   * on unix, only `/` is a valid separator, and `\\` can appear in file names\n//! * auto separators:\n//!   * on windows, using APIs like `join` will use a `\\` separator\n//!   * on unix, using APIs like `join` will use a `/` separator\n//!\n//! Since cargo-wix is fundamentally concerned with producing things that work on windows, we\n//! can comfortably force unix to be \"like windows\" to normalize the behaviour. This normalization\n//! is handled by the [`StoredPath`][] and [`StoredPathBuf`][] types.\n//!\n//! These types have two flavours of entry-point:\n//!\n//! * When making a StoredPathBuf from a String, the input is assumed to be user-provided and is forwarded\n//!   verbatim without any normalization. Windows is permissive of both kinds of path separator,\n//!   so we never need to \"fix\" things up.\n//!\n//! * When making a StoredPathBuf from a Path or Utf8Path, the input is assumed to be tainted with\n//!   platform-specific behaviour, and all path separators are normalized to `\\`. The net effect is\n//!   that on windows StoredPathBuf usually doesn't do anything, but on unix it forces\n//!   many `/`'s to `\\`'s. See [`StoredPathBuf::from_utf8_path`][] for the implementation.\n//!\n//! A StoredPath is not intended for doing actual i/o, and as such does not expose a way\n//! for it to be turned back into a \"real\" path, and does not expose APIs like `exists`.\n//! However it is useful/necessary to be able to ask questions like \"is this file an RTF\",\n//! so we do need to implement basic path parsing functions like `file_name` and `extension`.\n//!\n//! Notably [`StoredPath::file_name`][]` considers both `\\` and `/` to be path separators.\n//! Ideally it should behave identically to std/camino on windows, making all platforms\n//! behave like windows.\n\nuse std::{fmt, path::Path};\n\nuse camino::{Utf8Component, Utf8Path};\n\n/// A PathBuf that will be in the output of print (and therefore saved to disk)\n///\n/// A proper PathBuf should not be used for that, as we don't want to introduce\n/// platform-specific separators.\n///\n/// This type intentionally lacks some path functionality like `.exists()` because\n/// we don't want to be using it for *actual* path stuff, only for writing it to output.\n/// Most StoredPathBufs are just user-provided strings with no processing applied.\n///\n/// However sometimes we are forced to handle a PathBuf because of things like\n/// cargo-metadata, in which case [`StoredPathBuf::from_utf8_path`][] or\n/// [`StoredPathBuf::from_std_path`][] will convert the path to the windows path style.\n#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub struct StoredPathBuf(String);\n\n/// A borrowed [`StoredPathBuf`][]\n#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]\npub struct StoredPath(str);\n\nimpl StoredPathBuf {\n    /// Make a new StoredPathBuf from a String\n    pub fn new(v: String) -> Self {\n        Self(v)\n    }\n\n    /// Make a new StoredPath from a OS-specific path\n    ///\n    /// This breaks the path into its components and rewrites the slashes to `\\`.\n    ///\n    /// Generally you should avoid this and just try to preserve the user input,\n    /// but it's required when using things like the output of cargo-metadata.\n    pub fn from_std_path(path: &Path) -> Option<Self> {\n        Utf8Path::from_path(path).map(Self::from_utf8_path)\n    }\n\n    /// Make a new StoredPath from a OS-specific Utf8Path\n    ///\n    /// This breaks the path into its components and rewrites the slashes to `\\`.\n    ///\n    /// Generally you should avoid this and just try to preserve the user input,\n    /// but it's required when using things like the output of cargo-metadata.\n    ///\n    /// Also note that this does some handling of absolute paths, but that those\n    /// are kind of nonsensical to store longterm. Still, the user can hand them\n    /// to us, and we have to do our best to deal with it. Mostly this just comes\n    /// up in test code.\n    pub fn from_utf8_path(path: &Utf8Path) -> Self {\n        // The main quirk of this code is handling absolute paths.\n        // `C:\\a\\b\\c` is given to us as `[\"C:\", \"\\\", \"a\", \"b\", \"c\"]`\n        let mut result = String::new();\n        let mut multipart = false;\n        for component in path.components() {\n            // Add separator for every part but the first,\n            // ignoring root prefixes like \"C:\\\" and \"/\" which\n            // provide their own separators.\n            if multipart {\n                result.push('\\\\');\n            }\n            let part = match component {\n                // \"C:\"\n                Utf8Component::Prefix(prefix) => prefix.as_str(),\n                // the root slash\n                // (either the one at the end of \"C:\\\" or the one at the start of \"/a/b/c\")\n                Utf8Component::RootDir => \"\\\\\",\n                other => {\n                    // Ok we're passed the weird root stuff, now should add separators\n                    multipart = true;\n                    other.as_str()\n                }\n            };\n            result.push_str(part);\n        }\n        Self(result)\n    }\n}\n\nimpl StoredPath {\n    /// Make a new StoredPath from a str\n    pub fn new(v: &str) -> &Self {\n        // SAFETY: this is the idiomatic pattern for converting between newtyped slices.\n        // See the impl of std::str::from_utf8_unchecked for an example.\n        unsafe { std::mem::transmute(v) }\n    }\n\n    /// Get the inner string\n    pub fn as_str(&self) -> &str {\n        self\n    }\n\n    /// Extracts the extension part of the `self.file_name`\n    pub fn extension(&self) -> Option<&str> {\n        self.stem_and_extension().1\n    }\n\n    /// Extracts the stem (non-extension) part of the `self.file_name`.\n    pub fn file_stem(&self) -> Option<&str> {\n        self.stem_and_extension().0\n    }\n\n    // Implements `stem` and `extension` together based on the semantics defined by camino/std\n    fn stem_and_extension(&self) -> (Option<&str>, Option<&str>) {\n        let Some(name) = self.file_name() else {\n            // both: None if there's no file name\n            return (None, None);\n        };\n        if let Some((stem, extension)) = name.rsplit_once('.') {\n            if stem.is_empty() {\n                // stem: The entire file name if the file name begins with '.' and has no other '.'s within\n                // extension: None, if the file name begins with '.' and has no other '.'s within;\n                (Some(name), None)\n            } else {\n                // stem: Otherwise, the portion of the file name before the final '.'\n                // extension: Otherwise, the portion of the file name after the final '.'\n                (Some(stem), Some(extension))\n            }\n        } else {\n            // stem: The entire file name if there is no embedded '.'\n            // extension: None, if there is no embedded '.'\n            (Some(name), None)\n        }\n    }\n\n    /// Returns the final component of the path, if there is one.\n    pub fn file_name(&self) -> Option<&str> {\n        let mut path = self.as_str();\n\n        // First repeatedly pop trailing slashes off to get to the actual path\n        // Also pop trailing /. as this is a no-op.\n        // trailing .. is however treated as opaque!\n        while let Some(prefix) = path\n            .strip_suffix('\\\\')\n            .or_else(|| path.strip_suffix('/'))\n            .or_else(|| path.strip_suffix(\"/.\"))\n            .or_else(|| path.strip_suffix(\"\\\\.\"))\n        {\n            path = prefix;\n        }\n\n        // Look for either path separator (windows file names shouldn't include either,\n        // so even though unix file names can have `\\`, it won't work right on the actual\n        // platform that matters, so we can ignore that consideration.)\n        let name1 = path.rsplit_once('\\\\').map(|(_, name)| name);\n        let name2 = path.rsplit_once('/').map(|(_, name)| name);\n\n        // Decide which parse to use\n        let name = match (name1, name2) {\n            // trivial case, only one gave an answer\n            (Some(name), None) | (None, Some(name)) => name,\n            // Both matched, use whichever one came last (shortest file name)\n            (Some(name1), Some(name2)) => {\n                if name1.len() < name2.len() {\n                    name1\n                } else {\n                    name2\n                }\n            }\n            // No slashes left, the entire path is just the filename\n            (None, None) => path,\n        };\n\n        // Several special \"names\" are in fact not names at all:\n        if name.is_empty() || name == \".\" || name == \"..\" {\n            None\n        } else {\n            Some(name)\n        }\n    }\n}\n\nimpl std::ops::Deref for StoredPathBuf {\n    type Target = StoredPath;\n    fn deref(&self) -> &Self::Target {\n        StoredPath::new(&self.0)\n    }\n}\nimpl std::ops::Deref for StoredPath {\n    type Target = str;\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\nimpl std::borrow::Borrow<StoredPath> for StoredPathBuf {\n    fn borrow(&self) -> &StoredPath {\n        self\n    }\n}\nimpl std::borrow::ToOwned for StoredPath {\n    type Owned = StoredPathBuf;\n\n    fn to_owned(&self) -> StoredPathBuf {\n        StoredPathBuf::new(self.0.to_owned())\n    }\n}\nimpl std::convert::From<String> for StoredPathBuf {\n    fn from(v: String) -> Self {\n        Self::new(v)\n    }\n}\nimpl std::convert::From<StoredPathBuf> for String {\n    fn from(v: StoredPathBuf) -> Self {\n        v.0\n    }\n}\nimpl<'a> std::convert::From<&'a StoredPathBuf> for String {\n    fn from(v: &'a StoredPathBuf) -> Self {\n        v.0.clone()\n    }\n}\nimpl<'a> std::convert::From<&'a str> for StoredPathBuf {\n    fn from(v: &'a str) -> Self {\n        StoredPath::new(v).to_owned()\n    }\n}\nimpl std::fmt::Debug for StoredPath {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        std::fmt::Debug::fmt(self.as_str(), f)\n    }\n}\nimpl std::fmt::Display for StoredPath {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        std::fmt::Display::fmt(self.as_str(), f)\n    }\n}\nimpl std::fmt::Debug for StoredPathBuf {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        std::fmt::Debug::fmt(self.as_str(), f)\n    }\n}\nimpl std::fmt::Display for StoredPathBuf {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        std::fmt::Display::fmt(self.as_str(), f)\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::StoredPathBuf;\n    use camino::Utf8Path;\n\n    #[test]\n    fn absolute_windows_path_conversion() {\n        // Absolute native windows format\n        const INPUT: &str = \"C:\\\\Users\\\\test\\\\AppData\\\\Local\\\\Temp\\\\.tmpMh0Mxg\\\\Example.tar.gz\";\n        let path = StoredPathBuf::from_utf8_path(Utf8Path::new(INPUT));\n        assert_eq!(path.as_str(), INPUT);\n        assert_eq!(path.file_name(), Some(\"Example.tar.gz\"));\n        assert_eq!(path.file_stem(), Some(\"Example.tar\"));\n        assert_eq!(path.extension(), Some(\"gz\"));\n    }\n\n    #[test]\n    fn verbatim_absolute_windows_path_conversion() {\n        // Absolute native windows format (verbatim style)\n        const INPUT: &str =\n            \"\\\\\\\\?\\\\C:\\\\Users\\\\test\\\\AppData\\\\Local\\\\Temp\\\\.tmpMh0Mxg\\\\Example.tar.gz\";\n        let path = StoredPathBuf::from_utf8_path(Utf8Path::new(INPUT));\n        assert_eq!(path.as_str(), INPUT);\n        assert_eq!(path.file_name(), Some(\"Example.tar.gz\"));\n        assert_eq!(path.file_stem(), Some(\"Example.tar\"));\n        assert_eq!(path.extension(), Some(\"gz\"));\n    }\n\n    #[test]\n    fn relative_windows_path_conversion() {\n        // relative native windows format\n        const INPUT: &str = \"resource\\\\Example.tar.gz\";\n        let path = StoredPathBuf::from_utf8_path(Utf8Path::new(INPUT));\n        assert_eq!(path.as_str(), INPUT);\n        assert_eq!(path.file_name(), Some(\"Example.tar.gz\"));\n        assert_eq!(path.file_stem(), Some(\"Example.tar\"));\n        assert_eq!(path.extension(), Some(\"gz\"));\n    }\n\n    #[test]\n    fn absolute_unix_path_conversion() {\n        // absolute native unix format\n        const INPUT: &str = \"/users/home/test/Example.tar.gz\";\n        let path = StoredPathBuf::from_utf8_path(Utf8Path::new(INPUT));\n        assert_eq!(path.as_str(), \"\\\\users\\\\home\\\\test\\\\Example.tar.gz\");\n        assert_eq!(path.file_name(), Some(\"Example.tar.gz\"));\n        assert_eq!(path.file_stem(), Some(\"Example.tar\"));\n        assert_eq!(path.extension(), Some(\"gz\"));\n    }\n\n    #[test]\n    fn relative_unix_path_conversion() {\n        // relative native unix format\n        const INPUT: &str = \"resource/Example.tar.gz\";\n        let path = StoredPathBuf::from_utf8_path(Utf8Path::new(INPUT));\n        assert_eq!(path.as_str(), \"resource\\\\Example.tar.gz\");\n        assert_eq!(path.file_name(), Some(\"Example.tar.gz\"));\n        assert_eq!(path.file_stem(), Some(\"Example.tar\"));\n        assert_eq!(path.extension(), Some(\"gz\"));\n    }\n\n    #[test]\n    fn mixed_path_conversion1() {\n        // a mix of both formats (natural when combining user input with OS input)\n        const INPUT: &str = \"resource/blah\\\\Example.tar.gz\";\n        let path = StoredPathBuf::from_utf8_path(Utf8Path::new(INPUT));\n        assert_eq!(path.as_str(), \"resource\\\\blah\\\\Example.tar.gz\");\n        assert_eq!(path.file_name(), Some(\"Example.tar.gz\"));\n        assert_eq!(path.file_stem(), Some(\"Example.tar\"));\n        assert_eq!(path.extension(), Some(\"gz\"));\n    }\n\n    #[test]\n    fn mixed_path_conversion2() {\n        // a mix of both formats (natural when combining user input with OS input)\n        const INPUT: &str = \"resource\\\\blah/Example.tar.gz\";\n        let path = StoredPathBuf::from_utf8_path(Utf8Path::new(INPUT));\n        assert_eq!(path.as_str(), \"resource\\\\blah\\\\Example.tar.gz\");\n        assert_eq!(path.file_name(), Some(\"Example.tar.gz\"));\n        assert_eq!(path.file_stem(), Some(\"Example.tar\"));\n        assert_eq!(path.extension(), Some(\"gz\"));\n    }\n\n    #[test]\n    fn mixed_path_unconverted1() {\n        // a mix of both formats (natural when combining user input with OS input)\n        // here we're testing the verbatim `new` conversion produces a coherent value\n        const INPUT: &str = \"resource\\\\blah/Example.tar.gz\";\n        let path = StoredPathBuf::new(INPUT.to_owned());\n        assert_eq!(path.as_str(), \"resource\\\\blah/Example.tar.gz\");\n        assert_eq!(path.file_name(), Some(\"Example.tar.gz\"));\n        assert_eq!(path.file_stem(), Some(\"Example.tar\"));\n        assert_eq!(path.extension(), Some(\"gz\"));\n    }\n\n    #[test]\n    fn mixed_path_unconverted2() {\n        // a mix of both formats (natural when combining user input with OS input)\n        // here we're testing the verbatim `new` conversion produces a coherent value\n        const INPUT: &str = \"resource/blah\\\\Example.tar.gz\";\n        let path = StoredPathBuf::new(INPUT.to_owned());\n        assert_eq!(path.as_str(), \"resource/blah\\\\Example.tar.gz\");\n        assert_eq!(path.file_name(), Some(\"Example.tar.gz\"));\n        assert_eq!(path.file_stem(), Some(\"Example.tar\"));\n        assert_eq!(path.extension(), Some(\"gz\"));\n    }\n\n    #[test]\n    fn empty_path() {\n        // a mix of both formats (natural when combining user input with OS input)\n        // here we're testing the verbatim `new` conversion produces a coherent value\n        const INPUT: &str = \"\";\n        let path = StoredPathBuf::new(INPUT.to_owned());\n        assert_eq!(path.as_str(), \"\");\n        assert_eq!(path.file_name(), None);\n        assert_eq!(path.file_stem(), None);\n        assert_eq!(path.extension(), None);\n    }\n\n    #[test]\n    fn just_file() {\n        // a mix of both formats (natural when combining user input with OS input)\n        // here we're testing the verbatim `new` conversion produces a coherent value\n        const INPUT: &str = \"abc.txt\";\n        let path = StoredPathBuf::new(INPUT.to_owned());\n        assert_eq!(path.as_str(), \"abc.txt\");\n        assert_eq!(path.file_name(), Some(\"abc.txt\"));\n        assert_eq!(path.file_stem(), Some(\"abc\"));\n        assert_eq!(path.extension(), Some(\"txt\"));\n    }\n\n    #[test]\n    fn trail_slash_1() {\n        // make sure we trim a trailing slash\n        const INPUT: &str = \"abc.txt/\";\n        let path = StoredPathBuf::new(INPUT.to_owned());\n        assert_eq!(path.as_str(), \"abc.txt/\");\n        assert_eq!(path.file_name(), Some(\"abc.txt\"));\n        assert_eq!(path.file_stem(), Some(\"abc\"));\n        assert_eq!(path.extension(), Some(\"txt\"));\n    }\n\n    #[test]\n    fn trail_slash_2() {\n        // make sure we trim a trailing slash\n        const INPUT: &str = \"abc.txt\\\\\";\n        let path = StoredPathBuf::new(INPUT.to_owned());\n        assert_eq!(path.as_str(), \"abc.txt\\\\\");\n        assert_eq!(path.file_name(), Some(\"abc.txt\"));\n        assert_eq!(path.file_stem(), Some(\"abc\"));\n        assert_eq!(path.extension(), Some(\"txt\"));\n    }\n\n    #[test]\n    fn trail_slash_3() {\n        // make sure we trim a trailing slash\n        const INPUT: &str = \"abc.txt\\\\\\\\\\\\\";\n        let path = StoredPathBuf::new(INPUT.to_owned());\n        assert_eq!(path.as_str(), \"abc.txt\\\\\\\\\\\\\");\n        assert_eq!(path.file_name(), Some(\"abc.txt\"));\n        assert_eq!(path.file_stem(), Some(\"abc\"));\n        assert_eq!(path.extension(), Some(\"txt\"));\n    }\n\n    #[test]\n    fn trail_slash_4() {\n        // make sure we trim a trailing slash\n        const INPUT: &str = \"abc.txt/////\";\n        let path = StoredPathBuf::new(INPUT.to_owned());\n        assert_eq!(path.as_str(), \"abc.txt/////\");\n        assert_eq!(path.file_name(), Some(\"abc.txt\"));\n        assert_eq!(path.file_stem(), Some(\"abc\"));\n        assert_eq!(path.extension(), Some(\"txt\"));\n    }\n\n    #[test]\n    fn trail_slash_5() {\n        // make sure we trim a trailing slash\n        const INPUT: &str = \"abc.txt/\\\\//\\\\//\";\n        let path = StoredPathBuf::new(INPUT.to_owned());\n        assert_eq!(path.as_str(), \"abc.txt/\\\\//\\\\//\");\n        assert_eq!(path.file_name(), Some(\"abc.txt\"));\n        assert_eq!(path.file_stem(), Some(\"abc\"));\n        assert_eq!(path.extension(), Some(\"txt\"));\n    }\n\n    #[test]\n    fn trail_slash_6() {\n        /// make sure we trim a trailing slash dot\n        const INPUT: &str = \"abc.txt/.\";\n        let path = StoredPathBuf::new(INPUT.to_owned());\n        assert_eq!(path.as_str(), \"abc.txt/.\");\n        assert_eq!(path.file_name(), Some(\"abc.txt\"));\n        assert_eq!(path.file_stem(), Some(\"abc\"));\n        assert_eq!(path.extension(), Some(\"txt\"));\n    }\n\n    #[test]\n    fn trail_slash_7() {\n        // make sure we trim a trailing slash dot\n        const INPUT: &str = \"abc.txt\\\\.\";\n        let path = StoredPathBuf::new(INPUT.to_owned());\n        assert_eq!(path.as_str(), \"abc.txt\\\\.\");\n        assert_eq!(path.file_name(), Some(\"abc.txt\"));\n        assert_eq!(path.file_stem(), Some(\"abc\"));\n        assert_eq!(path.extension(), Some(\"txt\"));\n    }\n\n    #[test]\n    fn trail_slash_8() {\n        // make sure we trim all kinds of trailing slash dot soup\n        const INPUT: &str = \"abc.txt/./.\\\\\\\\//\\\\././\";\n        let path = StoredPathBuf::new(INPUT.to_owned());\n        assert_eq!(path.as_str(), \"abc.txt/./.\\\\\\\\//\\\\././\");\n        assert_eq!(path.file_name(), Some(\"abc.txt\"));\n        assert_eq!(path.file_stem(), Some(\"abc\"));\n        assert_eq!(path.extension(), Some(\"txt\"));\n    }\n\n    #[test]\n    fn just_dot() {\n        // dot is not a file name, it's a relative-path directive\n        const INPUT: &str = \".\";\n        let path = StoredPathBuf::new(INPUT.to_owned());\n        assert_eq!(path.as_str(), \".\");\n        assert_eq!(path.file_name(), None);\n        assert_eq!(path.file_stem(), None);\n        assert_eq!(path.extension(), None);\n    }\n\n    #[test]\n    fn just_dotfile() {\n        // dotfiles are valid names, and they have no extensions\n        const INPUT: &str = \".abc\";\n        let path = StoredPathBuf::new(INPUT.to_owned());\n        assert_eq!(path.as_str(), \".abc\");\n        assert_eq!(path.file_name(), Some(\".abc\"));\n        assert_eq!(path.file_stem(), Some(\".abc\"));\n        assert_eq!(path.extension(), None);\n    }\n\n    #[test]\n    fn just_dotfile_txt() {\n        // dotfiles with extensions work\n        const INPUT: &str = \".abc.txt\";\n        let path = StoredPathBuf::new(INPUT.to_owned());\n        assert_eq!(path.as_str(), \".abc.txt\");\n        assert_eq!(path.file_name(), Some(\".abc.txt\"));\n        assert_eq!(path.file_stem(), Some(\".abc\"));\n        assert_eq!(path.extension(), Some(\"txt\"));\n    }\n\n    #[test]\n    fn just_dotdot() {\n        // dot dot is not a file name, it's a relative-path directive\n        const INPUT: &str = \"..\";\n        let path = StoredPathBuf::new(INPUT.to_owned());\n        assert_eq!(path.as_str(), \"..\");\n        assert_eq!(path.file_name(), None);\n        assert_eq!(path.file_stem(), None);\n        assert_eq!(path.extension(), None);\n    }\n\n    #[test]\n    fn opaque_dotdot1() {\n        // trailing dot dot is opaque and makes us have no filename\n        const INPUT: &str = \"a/b/..\";\n        let path = StoredPathBuf::new(INPUT.to_owned());\n        assert_eq!(path.as_str(), \"a/b/..\");\n        assert_eq!(path.file_name(), None);\n        assert_eq!(path.file_stem(), None);\n        assert_eq!(path.extension(), None);\n    }\n\n    #[test]\n    fn opaque_dotdot2() {\n        // trailing dot dot is opaque and makes us have no filename\n        const INPUT: &str = \"a/b/../\";\n        let path = StoredPathBuf::new(INPUT.to_owned());\n        assert_eq!(path.as_str(), \"a/b/../\");\n        assert_eq!(path.file_name(), None);\n        assert_eq!(path.file_stem(), None);\n        assert_eq!(path.extension(), None);\n    }\n}\n"
  },
  {
    "path": "src/templates/Apache-2.0.rtf.mustache",
    "content": "{{!\r\nCopyright (C) 2017 Christopher R. Field.\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\nhttp://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n}}\r\n{{=<% %>=}}\r\n{\\rtf1\\ansi\\ansicpg1252\\deff0\\nouicompat{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}{\\f1\\fnil\\fcharset0 Courier New;}}\r\n{\\colortbl ;\\red0\\green0\\blue255;}\r\n{\\*\\generator Riched20 10.0.15063}\\viewkind4\\uc1 \r\n\\pard\\sa180\\qc\\fs24\\lang9 Apache License\\line Version 2.0, January 2004\\line {{\\field{\\*\\fldinst{HYPERLINK http://www.apache.org/licenses/ }}{\\fldrslt{http://www.apache.org/licenses/\\ul0\\cf0}}}}\\f0\\fs24\\par\r\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 1. Definitions.\\par\r\n\r\n\\pard\\sa180 \"License\" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.\\par\r\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.\\par\r\n\"Legal Entity\" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, \"control\" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.\\par\r\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising permissions granted by this License.\\par\r\n\"Source\" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.\\par\r\n\"Object\" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.\\par\r\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).\\par\r\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.\\par\r\n\"Contribution\" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, \"submitted\" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as \"Not a Contribution.\"\\par\r\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 2. Grant of Copyright License.\\par\r\n\r\n\\pard\\sa180\\tx360 Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 3. Grant of Patent License.\\par\r\n\r\n\\pard\\sa180\\tx360 Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 4. Redistribution. \\par\r\n\r\n\\pard\\sa180\\tx360 You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\\par\r\n\r\n\\pard \r\n{\\pntext\\f0 a.\\tab}{\\*\\pn\\pnlvlbody\\pnf0\\pnindent0\\pnstart1\\pnlcltr{\\pntxta.}}\r\n\\fi-360\\li720\\sa180\\tx360 You must give any other recipients of the Work or Derivative Works a copy of this License; and\\par\r\n{\\pntext\\f0 b.\\tab}You must cause any modified files to carry prominent notices stating that You changed the files; and\\par\r\n{\\pntext\\f0 c.\\tab}You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and \\par\r\n{\\pntext\\f0 d.\\tab}If the Work includes a \"NOTICE\" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.\\par\r\n\r\n\\pard\\sa180 You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 5.\\tab Submission of Contributions. \\par\r\n\r\n\\pard\\sa180\\tx360 Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 6.\\tab Trademarks. \\par\r\n\r\n\\pard\\sa180\\tx360 This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 7.\\tab Disclaimer of Warranty. \\par\r\n\r\n\\pard\\sa180\\tx360 Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 8.\\tab Limitation of Liability.\\par\r\n\r\n\\pard\\sa180\\tx360 In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 9.\\tab Accepting Warranty or Additional Liability. \\par\r\n\r\n\\pard\\sa180\\tx360 While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.\\par\r\n\r\n\\pard\\sa180\\qc END OF TERMS AND CONDITIONS\\par\r\nHow to apply the Apache License to your work.\\par\r\n\r\n\\pard\\sa180 To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets \"[]\" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same \"printed page\" as the copyright notice for easier identification within third-party archives.\\par\r\n\\f1 Copyright (C) [YYYY] [Copyright Holder]\\par\r\nLicensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at\\par\r\n{{\\field{\\*\\fldinst{HYPERLINK http://www.apache.org/licenses/LICENSE-2.0 }}{\\fldrslt{http://www.apache.org/licenses/LICENSE-2.0\\ul0\\cf0}}}}\\f1\\fs24\\par\r\nUnless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.\\par\r\n}\r\n\r\n"
  },
  {
    "path": "src/templates/GPL-3.0.rtf.mustache",
    "content": "{{!\r\nCopyright (C) 2017 Christopher R. Field.\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\nhttp://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n}}\r\n{{=<% %>=}}\r\n{\\rtf1\\ansi\\deff0\\nouicompat{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}{\\f1\\fnil\\fcharset0 Courier New;}}\r\n{\\colortbl ;\\red0\\green0\\blue255;}\r\n{\\*\\generator Riched20 10.0.15063}\\viewkind4\\uc1 \r\n\\pard\\sa180\\qc\\fs24\\lang9 GNU GENERAL PUBLIC LICENSE\\line Version 3, 29 June 2007\\par\r\n\r\n\\pard\\sa180 Copyright (C) 2007 Free Software Foundation, Inc. {{\\field{\\*\\fldinst{HYPERLINK http://fsf.org/ }}{\\fldrslt{http://fsf.org/\\ul0\\cf0}}}}\\f0\\fs24  Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.\\par\r\n\r\n\\pard\\sa180\\qc Preamble\\par\r\n\r\n\\pard\\sa180 The GNU General Public License is a free, copyleft license for software and other kinds of works.\\par\r\nThe licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.\\par\r\nWhen we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.\\par\r\nTo protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.\\par\r\nFor example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.\\par\r\nDevelopers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.\\par\r\nFor the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.\\par\r\nSome devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.\\par\r\nFinally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.\\par\r\nThe precise terms and conditions for copying, distribution and modification follow.\\par\r\n\r\n\\pard\\sa180\\qc TERMS AND CONDITIONS\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 0.\\tab Definitions.\\par\r\n\r\n\\pard\\sa180 \"This License\" refers to version 3 of the GNU General Public License.\\par\r\n\"Copyright\" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.\\par\r\n\"The Program\" refers to any copyrightable work licensed under this License. Each licensee is addressed as \"you\". \"Licensees\" and \"recipients\" may be individuals or organizations.\\par\r\nTo \"modify\" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a \"modified version\" of the earlier work or a work \"based on\" the earlier work.\\par\r\nA \"covered work\" means either the unmodified Program or a work based on the Program.\\par\r\nTo \"propagate\" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.\\par\r\nTo \"convey\" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.\\par\r\nAn interactive user interface displays \"Appropriate Legal Notices\" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 1.\\tab Source Code.\\par\r\n\r\n\\pard\\sa180 The \"source code\" for a work means the preferred form of the work for making modifications to it. \"Object code\" means any non-source form of a work.\\par\r\nA \"Standard Interface\" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.\\par\r\nThe \"System Libraries\" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A \"Major Component\", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.\\par\r\nThe \"Corresponding Source\" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.\\par\r\nThe Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.\\par\r\nThe Corresponding Source for a work in source code form is that same work.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 2.\\tab Basic Permissions.\\par\r\n\r\n\\pard\\sa180 All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.\\par\r\nYou may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.\\par\r\nConveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 3.\\tab Protecting Users' Legal Rights From Anti-Circumvention Law.\\par\r\n\r\n\\pard\\sa180 No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.\\par\r\nWhen you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 4.\\tab Conveying Verbatim Copies.\\par\r\n\r\n\\pard\\sa180 You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.\\par\r\nYou may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 5.\\tab Conveying Modified Source Versions.\\par\r\n\r\n\\pard\\sa180 You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:\\par\r\n\r\n\\pard \r\n{\\pntext\\f0 a.\\tab}{\\*\\pn\\pnlvlbody\\pnf0\\pnindent0\\pnstart1\\pnlcltr{\\pntxta.}}\r\n\\fi-360\\li720\\sa180 The work must carry prominent notices stating that you modified\\lang1033  \\lang9 it, and giving a relevant date.\\par\r\n{\\pntext\\f0 b.\\tab}The work must carry prominent notices stating that it is released under this License and any conditions added under section\\lang1033  \\lang9 7.\\lang1033  \\lang9 This requirement modifies the requirement in section 4 to\\lang1033  \\lang9 \"keep intact all notices\".\\par\r\n{\\pntext\\f0 c.\\tab}You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy.  This License will therefore apply, along with any applicable section 7\\lang1033  \\lang9 additional terms, to the whole of the work, and all its parts,\\lang1033  \\lang9 regardless of how they are packaged.  This License gives no\\lang1033  \\lang9 permission to license the work in any other way, but it does not\\lang1033  \\lang9 invalidate such permission if you have separately received it. \\par\r\n{\\pntext\\f0 d.\\tab}If the work has interactive user interfaces, each must display\\lang1033  \\lang9 Appropriate Legal Notices; however, if the Program has interactive\\lang1033  \\lang9 interfaces that do not display Appropriate Legal Notices, your\\lang1033  \\lang9 work need not make them do so.\\par\r\n\r\n\\pard\\sa180 A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an \"aggregate\" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 6.\\tab Conveying Non-Source Forms.\\par\r\n\r\n\\pard\\sa180 You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:\\par\r\n\r\n\\pard \r\n{\\pntext\\f0 a.\\tab}{\\*\\pn\\pnlvlbody\\pnf0\\pnindent0\\pnstart1\\pnlcltr{\\pntxta.}}\r\n\\fi-360\\li720\\sa180 Convey the object code in, or embodied in, a physical product\\line (including a physical distribution medium), accompanied by the\\line Corresponding Source fixed on a durable physical medium\\line customarily used for software interchange.\\par\r\n{\\pntext\\f0 b.\\tab}Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no\\line more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.\\par\r\n{\\pntext\\f0 c.\\tab}Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source.  This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.\\par\r\n{\\pntext\\f0 d.\\tab}Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge.  You need not require recipients to copy the Corresponding Source along with the object code.  If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source.  Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.\\par\r\n{\\pntext\\f0 e.\\tab}Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.\\par\r\n\r\n\\pard\\sa180 A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.\\par\r\nA \"User Product\" is either (1) a \"consumer product\", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, \"normally used\" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.\\par\r\n\"Installation Information\" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.\\par\r\nIf you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).\\par\r\nThe requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.\\par\r\nCorresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 7.\\tab Additional Terms.\\par\r\n\r\n\\pard\\sa180 \"Additional permissions\" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.\\par\r\nWhen you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.\\par\r\nNotwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:\\par\r\n\r\n\\pard \r\n{\\pntext\\f0 a.\\tab}{\\*\\pn\\pnlvlbody\\pnf0\\pnindent0\\pnstart1\\pnlcltr{\\pntxta.}}\r\n\\fi-360\\li720\\sa180 Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or\\par\r\n{\\pntext\\f0 b.\\tab}Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or\\par\r\n{\\pntext\\f0 c.\\tab}Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or\\par\r\n{\\pntext\\f0 d.\\tab}Limiting the use for publicity purposes of names of licensors or authors of the material; or\\par\r\n{\\pntext\\f0 e.\\tab}Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or\\par\r\n{\\pntext\\f0 f.\\tab}Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.\\par\r\n\r\n\\pard\\sa180 All other non-permissive additional terms are considered \"further restrictions\" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.\\par\r\nIf you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.\\par\r\nAdditional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 8.\\tab Termination.\\par\r\n\r\n\\pard\\sa180 You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).\\par\r\nHowever, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.\\par\r\nMoreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.\\par\r\nTermination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 9.\\tab Acceptance Not Required for Having Copies.\\par\r\n\r\n\\pard\\sa180 You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 10.\\tab Automatic Licensing of Downstream Recipients.\\par\r\n\r\n\\pard\\sa180 Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.\\par\r\nAn \"entity transaction\" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.\\par\r\nYou may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 11.\\tab Patents.\\par\r\n\r\n\\pard\\sa180 A \"contributor\" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's \"contributor version\".\\par\r\nA contributor's \"essential patent claims\" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, \"control\" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.\\par\r\nEach contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.\\par\r\nIn the following three paragraphs, a \"patent license\" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To \"grant\" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.\\par\r\nIf you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. \"Knowingly relying\" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.\\par\r\nIf, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.\\par\r\nA patent license is \"discriminatory\" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.\\par\r\nNothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 12.\\tab No Surrender of Others' Freedom.\\par\r\n\r\n\\pard\\sa180 If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 13.\\tab Use with the GNU Affero General Public License.\\par\r\n\r\n\\pard\\sa180 Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 14.\\tab Revised Versions of this License.\\par\r\n\r\n\\pard\\sa180 The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.\\par\r\nEach version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License \"or any later version\" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.\\par\r\nIf the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.\\par\r\nLater license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 15.\\tab Disclaimer of Warranty.\\par\r\n\r\n\\pard\\sa180 THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 16.\\tab Limitation of Liability.\\par\r\n\r\n\\pard\\sa180 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\\par\r\n\r\n\\pard\\fi-360\\li360\\sa180\\tx360 17.\\tab Interpretation of Sections 15 and 16.\\par\r\n\r\n\\pard\\sa180 If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.\\par\r\n\r\n\\pard\\sa180\\qc END OF TERMS AND CONDITIONS\\par\r\n\\line How to Apply These Terms to Your New Programs\\par\r\n\r\n\\pard\\sa180 If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.\\par\r\nTo do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the \"copyright\" line and a pointer to where the full notice is found.\\par\r\n\\f1 <one line to give the program's name and a brief idea of what it does.>\\line Copyright (C) [copyright-year] [copyright-holder]\\par\r\n\\line This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\\line\\line This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.\\line\\line You should have received a copy of the GNU General Public License along with this program.  If not, see <{{\\field{\\*\\fldinst{HYPERLINK \"http://www.gnu.org/licenses/\"}}{\\fldrslt{http://www.gnu.org/licenses/\\ul0\\cf0}}}}\\f1\\fs24 >.\\par\r\n\\f0 Also add information on how to contact you by electronic and paper mail.\\par\r\nIf the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:\\par\r\n\\f1 [product-name] Copyright (C) [copyright-year] [copyright-holder]\\par\r\nThis program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free software, and you are welcome to redistribute it under certain conditions; type 'show c' for details.\\f0\\par\r\nThe hypothetical commands {\\f1 'show w'} and {\\f1 'show c'} should show the appropriate arts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an \"about box\".\\par\r\nYou should also get your employer (if you work as a programmer) or school, if any, to sign a \"copyright disclaimer\" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see {{\\field{\\*\\fldinst{HYPERLINK http://www.gnu.org/licenses/ }}{\\fldrslt{http://www.gnu.org/licenses/\\ul0\\cf0}}}}\\f0\\fs24 .\\par\r\nThe GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read {{\\field{\\*\\fldinst{HYPERLINK http://www.gnu.org/philosophy/why-not-lgpl.html }}{\\fldrslt{http://www.gnu.org/philosophy/why-not-lgpl.html\\ul0\\cf0}}}}\\f0\\fs24 .\\par\r\n}\r\n\u0000\r\n"
  },
  {
    "path": "src/templates/main.v3.wxs.mustache",
    "content": "<?xml version='1.0' encoding='utf-8'?>\n<!--\n  Copyright (C) 2017 Christopher R. Field.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n\n<!--\n  The \"cargo wix\" subcommand provides a variety of predefined variables available\n  for customization of this template. The values for each variable are set at\n  installer creation time. The following variables are available:\n\n  TargetTriple      = The rustc target triple name.\n  TargetEnv         = The rustc target environment. This is typically either\n                      \"msvc\" or \"gnu\" depending on the toolchain downloaded and\n                      installed.\n  TargetVendor      = The rustc target vendor. This is typically \"pc\", but Rust\n                      does support other vendors, like \"uwp\".\n  CargoTargetBinDir = The complete path to the directory containing the\n                      binaries (exes) to include. The default would be\n                      \"target\\release\\\". If an explicit rustc target triple is\n                      used, i.e. cross-compiling, then the default path would\n                      be \"target\\<CARGO_TARGET>\\<CARGO_PROFILE>\",\n                      where \"<CARGO_TARGET>\" is replaced with the \"CargoTarget\"\n                      variable value and \"<CARGO_PROFILE>\" is replaced with the\n                      value from the \"CargoProfile\" variable. This can also\n                      be overridden manually with the \"target-bin-dir\" flag.\n  CargoTargetDir    = The path to the directory for the build artifacts, i.e.\n                      \"target\".\n  CargoProfile      = The cargo profile used to build the binaries\n                      (usually \"debug\" or \"release\").\n  Version           = The version for the installer. The default is the\n                      \"Major.Minor.Fix\" semantic versioning number of the Rust\n                      package.\n-->\n\n<!--\n  Please do not remove these pre-processor If-Else blocks. These are used with\n  the `cargo wix` subcommand to automatically determine the installation\n  destination for 32-bit versus 64-bit installers. Removal of these lines will\n  cause installation errors.\n-->\n<?if $(sys.BUILDARCH) = x64 or $(sys.BUILDARCH) = arm64 ?>\n    <?define PlatformProgramFilesFolder = \"ProgramFiles64Folder\" ?>\n<?else ?>\n    <?define PlatformProgramFilesFolder = \"ProgramFilesFolder\" ?>\n<?endif ?>\n\n<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>\n\n    <Product\n        Id='*'\n        Name='{{product-name}}'\n        UpgradeCode='{{upgrade-code-guid}}'\n        Manufacturer='{{manufacturer}}'\n        Language='1033'\n        Codepage='1252'\n        Version='$(var.Version)'>\n\n        <Package Id='*'\n            Keywords='Installer'\n            {{#description}}\n            Description='{{description}}'\n            {{/description}}\n            Manufacturer='{{manufacturer}}'\n            InstallerVersion='450'\n            Languages='1033'\n            Compressed='yes'\n            InstallScope='perMachine'\n            SummaryCodepage='1252'\n            />\n\n        <MajorUpgrade\n            Schedule='afterInstallInitialize'\n            DowngradeErrorMessage='A newer version of [ProductName] is already installed. Setup will now exit.'/>\n\n        <Media Id='1' Cabinet='media1.cab' EmbedCab='yes' DiskPrompt='CD-ROM #1'/>\n        <Property Id='DiskPrompt' Value='{{product-name}} Installation'/>\n\n        <Directory Id='TARGETDIR' Name='SourceDir'>\n            <Directory Id='$(var.PlatformProgramFilesFolder)' Name='PFiles'>\n                <Directory Id='APPLICATIONFOLDER' Name='{{product-name}}'>\n                    {{#license-source}}\n                    <!--\n                      Disabling the license sidecar file in the installer is a two step process:\n\n                      1. Comment out or remove the `Component` tag along with its contents.\n                      2. Comment out or remove the `ComponentRef` tag with the \"License\" Id\n                         attribute value further down in this file.\n                    -->\n                    <Component Id='License' Guid='*'>\n                        <File Id='LicenseFile'\n                            {{#license-name}}\n                            Name='{{license-name}}'\n                            {{/license-name}}\n                            DiskId='1'\n                            Source='{{license-source}}'\n                            KeyPath='yes'/>\n                    </Component>\n                    {{/license-source}}\n                    {{^license-source}}\n                    <!--\n                      Enabling the license sidecar file in the installer is a four step process:\n\n                      1. Uncomment the `Component` tag and its contents.\n                      2. Change the value for the `Source` attribute in the `File` tag to a path\n                         to the file that should be included as the license sidecar file. The path\n                         can, and probably should be, relative to this file.\n                      3. Change the value for the `Name` attribute in the `File` tag to the\n                         desired name for the file when it is installed alongside the `bin` folder\n                         in the installation directory. This can be omitted if the desired name is\n                         the same as the file name.\n                      4. Uncomment the `ComponentRef` tag with the Id attribute value of \"License\"\n                         further down in this file.\n                    -->\n                    <!--\n                    <Component Id='License' Guid='*'>\n                        <File Id='LicenseFile' Name='ChangeMe' DiskId='1' Source='C:\\Path\\To\\File' KeyPath='yes'/>\n                    </Component>\n                    -->\n                    {{/license-source}}\n\n                    <Directory Id='Bin' Name='bin'>\n                        <Component Id='Path' Guid='{{path-component-guid}}' KeyPath='yes'>\n                            <Environment\n                                Id='PATH'\n                                Name='PATH'\n                                Value='[Bin]'\n                                Permanent='no'\n                                Part='last'\n                                Action='set'\n                                System='yes'/>\n                        </Component>\n                        {{#binaries}}\n                        <Component Id='binary{{binary-index}}' Guid='*'>\n                            <File\n                                Id='exe{{binary-index}}'\n                                Name='{{binary-name}}.exe'\n                                DiskId='1'\n                                Source='{{binary-source}}'\n                                KeyPath='yes'/>\n                        </Component>\n                        {{/binaries}}\n                    </Directory>\n                </Directory>\n            </Directory>\n        </Directory>\n\n        <Feature\n            Id='Binaries'\n            Title='Application'\n            Description='Installs all binaries and the license.'\n            Level='1'\n            ConfigurableDirectory='APPLICATIONFOLDER'\n            AllowAdvertise='no'\n            Display='expand'\n            Absent='disallow'>\n            {{#license-source}}\n            <!--\n              Comment out or remove the following `ComponentRef` tag to remove\n              the license sidecar file from the installer.\n            -->\n            <ComponentRef Id='License'/>\n            {{/license-source}}\n            {{^license-source}}\n            <!--\n              Uncomment the following `ComponentRef` tag to add the license\n              sidecar file to the installer.\n            -->\n            <!--<ComponentRef Id='License'/>-->\n            {{/license-source}}\n\n            {{#binaries}}\n            <ComponentRef Id='binary{{binary-index}}'/>\n            {{/binaries}}\n\n            <Feature\n                Id='Environment'\n                Title='PATH Environment Variable'\n                Description='Add the install location of the [ProductName] executable to the PATH system environment variable. This allows the [ProductName] executable to be called from any location.'\n                Level='1'\n                Absent='allow'>\n                <ComponentRef Id='Path'/>\n            </Feature>\n        </Feature>\n\n        <SetProperty Id='ARPINSTALLLOCATION' Value='[APPLICATIONFOLDER]' After='CostFinalize'/>\n\n        {{#product-icon}}\n        <!--\n          Disabling the custom product icon for the application in the\n          Add/Remove Programs control panel requires commenting out or\n          removing the following `Icon` and `Property` tags.\n        -->\n        <Icon Id='ProductICO' SourceFile='{{product-icon}}'/>\n        <Property Id='ARPPRODUCTICON' Value='ProductICO' />\n        {{/product-icon}}\n        {{^product-icon}}\n        <!--\n          Uncomment the following `Icon` and `Property` tags to change the product icon.\n\n          The product icon is the graphic that appears in the Add/Remove\n          Programs control panel for the application.\n        -->\n        <!--<Icon Id='ProductICO' SourceFile='wix\\Product.ico'/>-->\n        <!--<Property Id='ARPPRODUCTICON' Value='ProductICO' />-->\n        {{/product-icon}}\n\n        {{#help-url}}\n        <Property Id='ARPHELPLINK' Value='{{help-url}}'/>\n        {{/help-url}}\n        {{^help-url}}\n        <!--\n          Adding a URL to Add/Remove Programs control panel listing for the\n          application is a two step process:\n\n          1. Uncomment the following `Property` tag with the \"ARPHELPLINK\" Id\n             attribute value.\n          2. Change the value for `Value` attribute of the following\n             `Property` tag to a valid URL.\n        -->\n        <!--<Property Id='ARPHELPLINK' Value='ChangeMe'/>-->\n        {{/help-url}}\n\n        <UI>\n            <UIRef Id='WixUI_FeatureTree'/>\n            {{#eula}}\n            <!--\n              Disabling the EULA dialog in the installer is a two step process:\n\n                 1. Uncomment the following two `Publish` tags\n                 2. Comment out or remove the `<WiXVariable Id='WixUILicenseRtf'...` tag further down\n\n            -->\n            <!--<Publish Dialog='WelcomeDlg' Control='Next' Event='NewDialog' Value='CustomizeDlg' Order='99'>1</Publish>-->\n            <!--<Publish Dialog='CustomizeDlg' Control='Back' Event='NewDialog' Value='WelcomeDlg' Order='99'>1</Publish>-->\n            {{/eula}}\n            {{^eula}}\n            <!--\n              Enabling the EULA dialog in the installer is a three step process:\n\n                1. Comment out or remove the two `Publish` tags that follow the\n                   `WixVariable` tag.\n                2. Uncomment the `<WixVariable Id='WixUILicenseRtf' Value='Path\\to\\Eula.rft'>` tag further down\n                3. Replace the `Value` attribute of the `WixVariable` tag with\n                   the path to a RTF file that will be used as the EULA and\n                   displayed in the license agreement dialog.\n            -->\n            <Publish Dialog='WelcomeDlg' Control='Next' Event='NewDialog' Value='CustomizeDlg' Order='99'>1</Publish>\n            <Publish Dialog='CustomizeDlg' Control='Back' Event='NewDialog' Value='WelcomeDlg' Order='99'>1</Publish>\n            {{/eula}}\n\n        </UI>\n\n        {{#eula}}\n        <!--\n          Disabling the EULA dialog in the installer requires commenting out\n          or removing the following `WixVariable` tag\n        -->\n        <WixVariable Id='WixUILicenseRtf' Value='{{eula}}'/>\n        {{/eula}}\n        {{^eula}}\n        <!--\n          Enabling the EULA dialog in the installer requires uncommenting\n          the following `WixUILicenseRTF` tag and changing the `Value`\n          attribute.\n        -->\n        <!-- <WixVariable Id='WixUILicenseRtf' Value='Relative\\Path\\to\\Eula.rtf'/> -->\n        {{/eula}}\n\n        {{#banner}}\n        <!--\n          Disabling the banner in the installer requires commenting out or\n          removing the following `WixVariable` tag.\n\n          The banner BMP dimensions are 493 x 58 pixels.\n        -->\n        <WixVariable Id='WixUIBannerBmp' Value='{{banner}}'/>\n        {{/banner}}\n        {{^banner}}\n        <!--\n          Uncomment the next `WixVariable` tag to customize the installer's\n          Graphical User Interface (GUI) and add a custom banner image across\n          the top of each screen. See the WiX Toolset documentation for details\n          about customization.\n\n          The banner BMP dimensions are 493 x 58 pixels.\n        -->\n        <!--<WixVariable Id='WixUIBannerBmp' Value='wix\\Banner.bmp'/>-->\n        {{/banner}}\n\n        {{#dialog}}\n        <!--\n          Disabling the dialog image in the installer requires commenting out or\n          removing the following `WixVariable` tag.\n\n          The dialog BMP dimensions are 493 x 312 pixels.\n        -->\n        <WixVariable Id='WixUIDialogBmp' Value='{{dialog}}'/>\n        {{/dialog}}\n        {{^dialog}}\n        <!--\n          Uncomment the next `WixVariable` tag to customize the installer's\n          Graphical User Interface (GUI) and add a custom image to the first\n          dialog, or screen. See the WiX Toolset documentation for details about\n          customization.\n\n          The dialog BMP dimensions are 493 x 312 pixels.\n        -->\n        <!--<WixVariable Id='WixUIDialogBmp' Value='wix\\Dialog.bmp'/>-->\n        {{/dialog}}\n\n    </Product>\n\n</Wix>\n"
  },
  {
    "path": "src/templates/main.v4.wxs.mustache",
    "content": "<!--\n  Copyright (C) 2017 Christopher R. Field.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n\n<!--\n  The \"cargo wix\" subcommand provides a variety of predefined variables available\n  for customization of this template. The values for each variable are set at\n  installer creation time. The following variables are available:\n\n  TargetTriple      = The rustc target triple name.\n  TargetEnv         = The rustc target environment. This is typically either\n                      \"msvc\" or \"gnu\" depending on the toolchain downloaded and\n                      installed.\n  TargetVendor      = The rustc target vendor. This is typically \"pc\", but Rust\n                      does support other vendors, like \"uwp\".\n  CargoTargetBinDir = The complete path to the directory containing the\n                      binaries (exes) to include. The default would be\n                      \"target\\release\\\". If an explicit rustc target triple is\n                      used, i.e. cross-compiling, then the default path would\n                      be \"target\\<CARGO_TARGET>\\<CARGO_PROFILE>\",\n                      where \"<CARGO_TARGET>\" is replaced with the \"CargoTarget\"\n                      variable value and \"<CARGO_PROFILE>\" is replaced with the\n                      value from the \"CargoProfile\" variable. This can also\n                      be overridden manually with the \"target-bin-dir\" flag.\n  CargoTargetDir    = The path to the directory for the build artifacts, i.e.\n                      \"target\".\n  CargoProfile      = The cargo profile used to build the binaries\n                      (usually \"debug\" or \"release\").\n  Version           = The version for the installer. The default is the\n                      \"Major.Minor.Fix\" semantic versioning number of the Rust\n                      package.\n-->\n\n<!--\n  Please do not remove these pre-processor If-Else blocks. These are used with\n  the `cargo wix` subcommand to automatically determine the installation\n  destination for 32-bit versus 64-bit installers. Removal of these lines will\n  cause installation errors.\n-->\n<?if $(sys.BUILDARCH) = x64 or $(sys.BUILDARCH) = arm64 ?>\n    <?define PlatformProgramFilesFolder = \"ProgramFiles64Folder\" ?>\n<?else?>\n    <?define PlatformProgramFilesFolder = \"ProgramFilesFolder\" ?>\n<?endif?>\n\n<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:ui=\"http://wixtoolset.org/schemas/v4/wxs/ui\">\n    <Package \n        Name=\"{{product-name}}\" \n        UpgradeCode=\"{{upgrade-code-guid}}\"\n        Manufacturer=\"{{manufacturer}}\"\n        Language=\"1033\"\n        Codepage=\"1252\"\n        Version=\"$(var.Version)\"\n        InstallerVersion=\"450\">\n        <SummaryInformation\n            Keywords=\"Installer\"\n            Manufacturer=\"{{manufacturer}}\"\n            {{#description}}\n            Description='{{description}}'\n            {{/description}}\n            />\n\n        <MajorUpgrade\n            Schedule=\"afterInstallInitialize\"\n            DowngradeErrorMessage=\"A newer version of [ProductName] is already installed. Setup will now exit.\" />\n\n        <Media Id=\"1\" Cabinet=\"media1.cab\" EmbedCab=\"yes\" DiskPrompt=\"CD-ROM #1\" />\n        <Property Id=\"DiskPrompt\" Value=\"{{product-name}} Installation\" />\n\n        <Feature \n            Id=\"Binaries\"\n            Title=\"Application\"\n            Description=\"Installs all binaries and the license.\"\n            Level=\"1\"\n            ConfigurableDirectory=\"APPLICATIONFOLDER\"\n            AllowAdvertise=\"no\"\n            Display=\"expand\"\n            AllowAbsent=\"no\">\n            {{#license-source}}\n            <!--\n              Comment out or remove the following `ComponentRef` tag to remove\n              the license sidecar file from the installer.\n            -->\n            <ComponentRef Id='License'/>\n            {{/license-source}}\n            {{^license-source}}\n            <!--\n              Uncomment the following `ComponentRef` tag to add the license\n              sidecar file to the installer.\n            -->\n            <!--<ComponentRef Id='License'/>-->\n            {{/license-source}}\n\n            {{#binaries}}\n            <ComponentRef Id='binary{{binary-index}}'/>\n            {{/binaries}}\n\n            <Feature\n                Id=\"Environment\"\n                Title=\"PATH Environment Variable\"\n                Description=\"Add the install location of the [ProductName] executable to the PATH system environment variable. This allows the [ProductName] executable to be called from any location.\"\n                Level=\"1\"\n                AllowAbsent='yes'>\n                <ComponentRef Id=\"Path\" />\n            </Feature>\n        </Feature>\n\n        <SetProperty Id=\"ARPINSTALLLOCATION\" Value=\"[APPLICATIONFOLDER]\" After=\"CostFinalize\" />\n\n        {{#product-icon}}\n        <!--\n          Disabling the custom product icon for the application in the\n          Add/Remove Programs control panel requires commenting out or\n          removing the following `Icon` and `Property` tags.\n        -->\n        <Icon Id='ProductICO' SourceFile='{{product-icon}}'/>\n        <Property Id='ARPPRODUCTICON' Value='ProductICO' />\n        {{/product-icon}}\n        {{^product-icon}}\n        <!--\n          Uncomment the following `Icon` and `Property` tags to change the product icon.\n\n          The product icon is the graphic that appears in the Add/Remove\n          Programs control panel for the application.\n        -->\n        <!--<Icon Id='ProductICO' SourceFile='wix\\Product.ico'/>-->\n        <!--<Property Id='ARPPRODUCTICON' Value='ProductICO' />-->\n        {{/product-icon}}\n\n        {{#help-url}}\n        <Property Id='ARPHELPLINK' Value='{{help-url}}'/>\n        {{/help-url}}\n        {{^help-url}}\n        <!--\n          Adding a URL to Add/Remove Programs control panel listing for the\n          application is a two step process:\n\n          1. Uncomment the following `Property` tag with the \"ARPHELPLINK\" Id\n             attribute value.\n          2. Change the value for `Value` attribute of the following\n             `Property` tag to a valid URL.\n        -->\n        <!--<Property Id='ARPHELPLINK' Value='ChangeMe'/>-->\n        {{/help-url}}\n       \n        <ui:WixUI Id=\"WixUI_FeatureTree\" />\n        {{#eula}}\n          <!--\n          Disabling the EULA dialog in the installer is a two step process:\n\n          1. Uncomment the following two `Publish` tags\n          2. Comment out or remove the `<WiXVariable Id='WixUILicenseRtf'...` tag further down\n\n          -->\n          <!--<Publish Dialog='WelcomeDlg' Control='Next' Event='NewDialog' Value='CustomizeDlg' Order='99'>1</Publish>-->\n          <!--<Publish Dialog='CustomizeDlg' Control='Back' Event='NewDialog' Value='WelcomeDlg' Order='99'>1</Publish>-->\n          {{/eula}}\n          {{^eula}}\n          <!--\n          Enabling the EULA dialog in the installer is a three step process:\n\n          1. Comment out or remove the two `Publish` tags that follow the\n             `WixVariable` tag.\n          2. Uncomment the `<WixVariable Id='WixUILicenseRtf' Value='Path\\to\\Eula.rft'>` tag further down\n          3. Replace the `Value` attribute of the `WixVariable` tag with\n             the path to a RTF file that will be used as the EULA and\n             displayed in the license agreement dialog.\n          -->\n          <Publish Dialog='WelcomeDlg' Control='Next' Event='NewDialog' Value='CustomizeDlg' Order='99'>1</Publish>\n          <Publish Dialog='CustomizeDlg' Control='Back' Event='NewDialog' Value='WelcomeDlg' Order='99'>1</Publish>\n        {{/eula}}\n            \n        <StandardDirectory Id=\"ProgramFiles6432Folder\">\n            <Directory Id=\"APPLICATIONFOLDER\" Name=\"{{product-name}}\">\n                {{#license-source}}\n                <!--\n                  Disabling the license sidecar file in the installer is a two step process:\n\n                  1. Comment out or remove the `Component` tag along with its contents.\n                  2. Comment out or remove the `ComponentRef` tag with the \"License\" Id\n                     attribute value further down in this file.\n                -->\n                <Component Id='License' Guid='*'>\n                    <File Id='LicenseFile'\n                        {{#license-name}}\n                        Name='{{license-name}}'\n                        {{/license-name}}\n                        DiskId='1'\n                        Source='{{license-source}}'\n                        KeyPath='yes'/>\n                </Component>\n                {{/license-source}}\n                {{^license-source}}\n                <!--\n                  Enabling the license sidecar file in the installer is a four step process:\n\n                  1. Uncomment the `Component` tag and its contents.\n                  2. Change the value for the `Source` attribute in the `File` tag to a path\n                     to the file that should be included as the license sidecar file. The path\n                     can, and probably should be, relative to this file.\n                  3. Change the value for the `Name` attribute in the `File` tag to the\n                     desired name for the file when it is installed alongside the `bin` folder\n                     in the installation directory. This can be omitted if the desired name is\n                     the same as the file name.\n                  4. Uncomment the `ComponentRef` tag with the Id attribute value of \"License\"\n                     further down in this file.\n                -->\n                <!--\n                <Component Id='License' Guid='*'>\n                    <File Id='LicenseFile' Name='ChangeMe' DiskId='1' Source='C:\\Path\\To\\File' KeyPath='yes'/>\n                </Component>\n                -->\n                {{/license-source}}\n\n                <Directory Id=\"Bin\" Name=\"bin\">\n                    <Component Id=\"Path\" Guid=\"{{path-component-guid}}\" KeyPath=\"yes\">\n                        <Environment \n                            Id=\"PATH\"\n                            Name=\"PATH\"\n                            Value=\"[Bin]\"\n                            Permanent=\"no\"\n                            Part=\"last\"\n                            Action=\"set\"\n                            System=\"yes\"/>\n                    </Component>\n                    {{#binaries}}\n                    <Component Id='binary{{binary-index}}' Guid='*'>\n                        <File\n                            Id='exe{{binary-index}}'\n                            Name='{{binary-name}}.exe'\n                            DiskId='1'\n                            Source='{{binary-source}}'\n                            KeyPath='yes'/>\n                    </Component>\n                    {{/binaries}}\n                </Directory>\n            </Directory>\n        </StandardDirectory>\n\n        {{#eula}}\n        <!--\n          Disabling the EULA dialog in the installer requires commenting out\n          or removing the following `WixVariable` tag\n        -->\n        <WixVariable Id='WixUILicenseRtf' Value='{{eula}}'/>\n        {{/eula}}\n        {{^eula}}\n        <!--\n          Enabling the EULA dialog in the installer requires uncommenting\n          the following `WixUILicenseRTF` tag and changing the `Value`\n          attribute.\n        -->\n        <!-- <WixVariable Id='WixUILicenseRtf' Value='Relative\\Path\\to\\Eula.rtf'/> -->\n        {{/eula}}\n\n        {{#banner}}\n        <!--\n          Disabling the banner in the installer requires commenting out or\n          removing the following `WixVariable` tag.\n\n          The banner BMP dimensions are 493 x 58 pixels.\n        -->\n        <WixVariable Id='WixUIBannerBmp' Value='{{banner}}'/>\n        {{/banner}}\n        {{^banner}}\n        <!--\n          Uncomment the next `WixVariable` tag to customize the installer's\n          Graphical User Interface (GUI) and add a custom banner image across\n          the top of each screen. See the WiX Toolset documentation for details\n          about customization.\n\n          The banner BMP dimensions are 493 x 58 pixels.\n        -->\n        <!--<WixVariable Id='WixUIBannerBmp' Value='wix\\Banner.bmp'/>-->\n        {{/banner}}\n\n        {{#dialog}}\n        <!--\n          Disabling the dialog image in the installer requires commenting out or\n          removing the following `WixVariable` tag.\n\n          The dialog BMP dimensions are 493 x 312 pixels.\n        -->\n        <WixVariable Id='WixUIDialogBmp' Value='{{dialog}}'/>\n        {{/dialog}}\n        {{^dialog}}\n        <!--\n          Uncomment the next `WixVariable` tag to customize the installer's\n          Graphical User Interface (GUI) and add a custom image to the first\n          dialog, or screen. See the WiX Toolset documentation for details about\n          customization.\n\n          The dialog BMP dimensions are 493 x 312 pixels.\n        -->\n        <!--<WixVariable Id='WixUIDialogBmp' Value='wix\\Dialog.bmp'/>-->\n        {{/dialog}}\n    </Package>\n\n</Wix>\n"
  },
  {
    "path": "src/templates/mod.rs",
    "content": "// Copyright (C) 2017 Christopher R. Field.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nuse crate::Error;\nuse lazy_static::lazy_static;\nuse std::fmt;\nuse std::str::FromStr;\n\n/// The WiX Source (wxs) template.\nstatic WIX_V3_SOURCE_TEMPLATE: &str = include_str!(\"main.v3.wxs.mustache\");\n\n/// The V4 Schema WiX Source (wxs) template.\n///\n/// Note: Used by both Wix4 and Wix5 toolsets\nstatic WIX_V4_SOURCE_TEMPLATE: &str = include_str!(\"main.v4.wxs.mustache\");\n\n/// The Apache-2.0 Rich Text Format (RTF) license template.\nstatic APACHE2_LICENSE_TEMPLATE: &str = include_str!(\"Apache-2.0.rtf.mustache\");\n\n/// The GPL-3.0 Rich Text Format (RTF) license template.\nstatic GPL3_LICENSE_TEMPLATE: &str = include_str!(\"GPL-3.0.rtf.mustache\");\n\n/// The MIT Rich Text Format (RTF) license template.\nstatic MIT_LICENSE_TEMPLATE: &str = include_str!(\"MIT.rtf.mustache\");\n\n/// The different templates that can be printed or written to a file.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum Template {\n    /// The [Apache-2.0] license.\n    ///\n    /// [Apache-2.0]: https://opensource.org/licenses/Apache-2.0\n    Apache2,\n    /// The [GPL-3.0] license.\n    ///\n    /// [GPL-3.0]: https://opensource.org/licenses/gpl-3.0.html\n    Gpl3,\n    /// The [MIT] license.\n    ///\n    /// [MIT]: https://opensource.org/licenses/MIT\n    Mit,\n    /// A [WiX Source (wxs)] file.\n    ///\n    /// **Note**: This follows teh V3 wxs schema\n    ///\n    /// [Wix Source (wxs)]: http://wixtoolset.org/documentation/manual/v3/overview/files.html\n    WxsV3,\n    /// A [Modern Wix Source (wxs)] file.\n    ///\n    /// [Modern Wix Source (wxs)]: https://wixtoolset.org/docs/schema/wxs/\n    WxsV4,\n}\n\nlazy_static! {\n    static ref POSSIBLE_VALUES: Vec<String> = vec![\n        Template::Apache2.id().to_owned(),\n        Template::Apache2.id().to_lowercase(),\n        Template::Gpl3.id().to_owned(),\n        Template::Gpl3.id().to_lowercase(),\n        Template::Mit.id().to_owned(),\n        Template::Mit.id().to_lowercase(),\n        Template::WxsV3.id().to_owned(),\n        Template::WxsV3.id().to_lowercase(),\n        Template::WxsV4.id().to_lowercase(),\n        // Added for convenience instead of http://wixtoolset.org/schemas/v4/wxs\n        String::from(\"wxs4\"),\n        String::from(\"WXS4\")\n    ];\n}\n\nimpl Template {\n    /// Gets the ID for the template.\n    ///\n    /// In the case of a license template, the ID is the [SPDX ID] which is also used for the\n    /// `license` field in the package's manifest (Cargo.toml). This is also the same value used\n    /// with the `cargo wix print` subcommand.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// use wix::Template;\n    ///\n    /// assert_eq!(Template::Apache2.id(), \"Apache-2.0\");\n    /// assert_eq!(Template::Gpl3.id(), \"GPL-3.0\");\n    /// assert_eq!(Template::Mit.id(), \"MIT\");\n    /// assert_eq!(Template::WxsV3.id(), \"WXS\");\n    /// assert_eq!(Template::WxsV4.id(), wix::toolset::project::V4_NAMESPACE_URI);\n    /// ```\n    ///\n    /// [SPDX ID]: https://spdx.org/licenses/\n    pub fn id(&self) -> &str {\n        match *self {\n            Template::Apache2 => \"Apache-2.0\",\n            Template::Gpl3 => \"GPL-3.0\",\n            Template::Mit => \"MIT\",\n            Template::WxsV3 => \"WXS\",\n            Template::WxsV4 => crate::toolset::project::V4_NAMESPACE_URI,\n        }\n    }\n\n    /// Gets the possible string representations of each variant.\n    ///\n    /// The possibilities are combination of case (upper and lower) for the\n    /// various templates that are available.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// use wix::Template;\n    ///\n    /// assert_eq!(\n    ///     Template::possible_values(),\n    ///     &vec![\n    ///         \"Apache-2.0\".to_owned(),\n    ///         \"apache-2.0\".to_owned(),\n    ///         \"GPL-3.0\".to_owned(),\n    ///         \"gpl-3.0\".to_owned(),\n    ///         \"MIT\".to_owned(),\n    ///         \"mit\".to_owned(),\n    ///         \"WXS\".to_owned(),\n    ///         \"wxs\".to_owned(),\n    ///         wix::toolset::project::V4_NAMESPACE_URI.to_owned(),\n    ///         \"wxs4\".to_owned(),\n    ///         \"WXS4\".to_owned()\n    ///     ]\n    /// );\n    /// ```\n    pub fn possible_values() -> &'static Vec<String> {\n        &POSSIBLE_VALUES\n    }\n\n    /// Gets the IDs of all supported licenses.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// use wix::Template;\n    ///\n    /// assert_eq!(\n    ///     Template::license_ids(),\n    ///     vec![\n    ///         \"Apache-2.0\".to_owned(),\n    ///         \"GPL-3.0\".to_owned(),\n    ///         \"MIT\".to_owned(),\n    ///     ]\n    /// );\n    /// ```\n    pub fn license_ids() -> Vec<String> {\n        vec![\n            Template::Apache2.id().to_owned(),\n            Template::Gpl3.id().to_owned(),\n            Template::Mit.id().to_owned(),\n        ]\n    }\n\n    /// Gets the embedded contents of the template as a string.\n    pub fn to_str(&self) -> &str {\n        match *self {\n            Template::Apache2 => APACHE2_LICENSE_TEMPLATE,\n            Template::Gpl3 => GPL3_LICENSE_TEMPLATE,\n            Template::Mit => MIT_LICENSE_TEMPLATE,\n            Template::WxsV3 => WIX_V3_SOURCE_TEMPLATE,\n            Template::WxsV4 => WIX_V4_SOURCE_TEMPLATE,\n        }\n    }\n}\n\nimpl fmt::Display for Template {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        write!(f, \"{}\", self.id())\n    }\n}\n\nimpl FromStr for Template {\n    type Err = Error;\n\n    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {\n        match s.to_lowercase().trim() {\n            \"apache-2.0\" => Ok(Template::Apache2),\n            \"gpl-3.0\" => Ok(Template::Gpl3),\n            \"mit\" => Ok(Template::Mit),\n            \"wxs\" => Ok(Template::WxsV3),\n            crate::toolset::project::V4_NAMESPACE_URI | \"wxs4\" | \"WXS4\" => Ok(Template::WxsV4),\n            _ => Err(Error::Generic(format!(\n                \"Cannot convert from '{s}' to a Template variant\"\n            ))),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use std::str::FromStr;\n\n    #[test]\n    fn from_str_wxs_returns_v3() {\n        assert_eq!(Template::from_str(\"wxs\").unwrap(), Template::WxsV3);\n    }\n\n    #[test]\n    fn from_str_wxs_uppercase_returns_v3() {\n        assert_eq!(Template::from_str(\"WXS\").unwrap(), Template::WxsV3);\n    }\n\n    #[test]\n    fn from_str_wxs4_returns_v4() {\n        assert_eq!(Template::from_str(\"wxs4\").unwrap(), Template::WxsV4);\n    }\n\n    #[test]\n    fn from_str_wxs4_uppercase_returns_v4() {\n        assert_eq!(Template::from_str(\"WXS4\").unwrap(), Template::WxsV4);\n    }\n\n    #[test]\n    fn from_str_v4_namespace_uri_returns_v4() {\n        assert_eq!(\n            Template::from_str(crate::toolset::project::V4_NAMESPACE_URI).unwrap(),\n            Template::WxsV4\n        );\n    }\n\n    #[test]\n    fn from_str_unknown_fails() {\n        assert!(Template::from_str(\"unknown_template\").is_err());\n    }\n\n    #[test]\n    fn wxs_v3_to_str_is_not_empty() {\n        assert!(!Template::WxsV3.to_str().is_empty());\n    }\n\n    #[test]\n    fn wxs_v4_to_str_is_not_empty() {\n        assert!(!Template::WxsV4.to_str().is_empty());\n    }\n\n    #[test]\n    fn wxs_v4_to_str_contains_v4_namespace() {\n        assert!(\n            Template::WxsV4\n                .to_str()\n                .contains(crate::toolset::project::V4_NAMESPACE_URI),\n            \"V4 template should contain the v4 namespace URI\"\n        );\n    }\n\n    #[test]\n    fn possible_values_contains_wxs4() {\n        let values = Template::possible_values();\n        assert!(\n            values.iter().any(|v| v == \"wxs4\"),\n            \"possible_values should include wxs4\"\n        );\n    }\n}\n"
  },
  {
    "path": "src/toolset/ext.rs",
    "content": "// Copyright (C) 2017 Christopher R. Field.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nuse log::debug;\nuse semver::Version;\nuse std::{\n    collections::{BTreeMap, BTreeSet},\n    path::PathBuf,\n};\n\nuse super::{Toolset, ToolsetAction};\n\n/// Type-alias for .wxs extension dependency\npub type WxsDependency = Box<dyn WixExtension>;\n\n/// Trait to provide wix extension identifiers w/ use the `wix extension` command\npub trait WixExtension {\n    /// Returns the .wixext package name used to identify the extension\n    fn package_name(&self) -> &str;\n    /// Returns the xmlns prefix\n    fn namespace_prefix(&self) -> &str;\n    /// Returns the xmlns uri\n    fn namespace_uri(&self) -> &str;\n}\n\n/// Contains a map of locally/globally installed packages\n#[derive(Debug)]\npub struct PackageCache {\n    /// Installed packages\n    installed: BTreeMap<String, Version>,\n    /// Packages that are indicated as missing from the package cache\n    missing: BTreeSet<String>,\n    /// Toolset in used with this package cache\n    toolset: Toolset,\n}\n\nimpl PackageCache {\n    /// Add an installed package to the cache\n    #[inline]\n    pub fn add(&mut self, name: impl Into<String>, version: Version) {\n        self.installed.insert(name.into(), version);\n    }\n\n    /// Returns true if an ext is installed matching the\n    #[inline]\n    pub fn installed(&self, ext: &impl WixExtension) -> bool {\n        self.installed_package_name(ext.package_name())\n    }\n\n    /// Returns true if an installed package_name exists\n    #[inline]\n    pub fn installed_package_name(&self, package_name: &str) -> bool {\n        self.installed.contains_key(package_name)\n    }\n\n    /// Returns an iterator over missing extensions\n    #[inline]\n    pub fn iter_missing(&self) -> impl Iterator<Item = &String> {\n        self.missing.iter()\n    }\n\n    /// Returns an iterator over installed extensions\n    #[inline]\n    pub fn iter_installed(&self) -> impl Iterator<Item = (&String, &Version)> {\n        self.installed.iter()\n    }\n\n    /// Add's a missing package to the package cache for later installation\n    #[inline]\n    pub fn add_missing(&mut self, name: impl Into<String>) {\n        self.missing.insert(name.into());\n    }\n\n    /// Installs all missing packages\n    #[inline]\n    pub fn install_missing(\n        &self,\n        global_cache: bool,\n        version: Version,\n        work_dir: Option<&PathBuf>,\n    ) -> crate::Result<()> {\n        if self.missing.is_empty() {\n            debug!(\"No missing extensions, skipping extension restore\");\n            return Ok(());\n        }\n        let mut wix = self.toolset.wix(\"extension add\")?;\n\n        if let Some(work_dir) = work_dir {\n            wix.current_dir(work_dir);\n        }\n\n        if global_cache {\n            wix.arg(\"--global\");\n            wix.action = ToolsetAction::AddGlobalExtension;\n        }\n\n        for m in self.missing.iter() {\n            let ext_ref = format!(\"{m}/{}.{}.{}\", version.major, version.minor, version.patch);\n            debug!(\"Installing missing ext {ext_ref}\");\n            wix.arg(ext_ref);\n        }\n\n        let output = wix.output()?;\n        if output.status.success() {\n            Ok(())\n        } else {\n            Err(\"Could not install missing dependencies\".into())\n        }\n    }\n}\n\nimpl From<Toolset> for PackageCache {\n    fn from(toolset: Toolset) -> Self {\n        Self {\n            installed: Default::default(),\n            missing: Default::default(),\n            toolset,\n        }\n    }\n}\n\nimpl WixExtension for WxsDependency {\n    fn package_name(&self) -> &str {\n        self.as_ref().package_name()\n    }\n\n    fn namespace_prefix(&self) -> &str {\n        self.as_ref().namespace_prefix()\n    }\n\n    fn namespace_uri(&self) -> &str {\n        self.as_ref().namespace_uri()\n    }\n}\n\nimpl<'a> From<&sxd_document::dom::Namespace<'a>> for WxsDependency {\n    fn from(value: &sxd_document::dom::Namespace<'a>) -> Self {\n        match (value.prefix(), value.uri()) {\n            (BAL_NS_PREFIX, BAL_NS_URI) => Box::new(WellKnownExtentions::BootstrapperApplications),\n            (COMPLUS_NS_PREFIX, COMPLUS_NS_URI) => Box::new(WellKnownExtentions::ComPlus),\n            (DEPENDENCY_NS_PREFIX, DEPENDENCY_NS_URI) => Box::new(WellKnownExtentions::Dependency),\n            (DIRECTX_NS_PREFIX, DIRECTX_NS_URI) => Box::new(WellKnownExtentions::DirectX),\n            (FIREWALL_NS_PREFIX, FIREWALL_NS_URI) => Box::new(WellKnownExtentions::Firewall),\n            (HTTP_NS_PREFIX, HTTP_NS_URI) => Box::new(WellKnownExtentions::Http),\n            (IIS_NS_PREFIX, IIS_NS_URI) => Box::new(WellKnownExtentions::Iis),\n            (MSMQ_NS_PREFIX, MSMQ_NS_URI) => Box::new(WellKnownExtentions::Msmq),\n            (NETFX_NS_PREFIX, NETFX_NS_URI) => Box::new(WellKnownExtentions::Netfx),\n            (POWERSHELL_NS_PREFIX, POWERSHELL_NS_URI) => Box::new(WellKnownExtentions::Powershell),\n            (SQL_NS_PREFIX, SQL_NS_URI) => Box::new(WellKnownExtentions::Sql),\n            (UI_NS_PREFIX, UI_NS_URI) => Box::new(WellKnownExtentions::UI),\n            (UTIL_NS_PREFIX, UTIL_NS_URI) => Box::new(WellKnownExtentions::Util),\n            (VS_NS_PREFIX, VS_NS_URI) => Box::new(WellKnownExtentions::VS),\n            (prefix, uri) => Box::new(UnknownExtNamespace {\n                prefix: prefix.to_string(),\n                uri: uri.to_string(),\n            }),\n        }\n    }\n}\n/// Struct containing information on an unknown extension found in the `<Wix/>` element\npub struct UnknownExtNamespace {\n    pub(crate) prefix: String,\n    pub(crate) uri: String,\n}\n\nimpl WixExtension for UnknownExtNamespace {\n    fn package_name(&self) -> &str {\n        \"\"\n    }\n\n    fn namespace_prefix(&self) -> &str {\n        &self.prefix\n    }\n\n    fn namespace_uri(&self) -> &str {\n        &self.uri\n    }\n}\n\n/// Enumeration of Well-known extensions documented by the wix toolset org\n///\n/// # Background\n/// Because XML namespaces are intended to be known ahead of time, this is an explicit enuemration of all well known extensions installable by the Wix Toolset.\n/// This enables `cargo-wix` to identify which extensions are required to be installed in order for `wix build` to succeed after a V3 project has been upgraded\n/// to a V4 project.\n///\n/// Source: <https://wixtoolset.org/docs/tools/wixext/>\n#[derive(Clone, Copy)]\npub enum WellKnownExtentions {\n    /// WiX Toolset Bootstrapper Applications Extension\n    /// Docs: <https://wixtoolset.org/docs/schema/bal/>\n    BootstrapperApplications,\n    /// WiX Toolset COM+ Extension\n    /// Docs: <https://wixtoolset.org/docs/schema/complus/>\n    ComPlus,\n    /// WiX Toolset Dependency Extension\n    /// Docs: <https://wixtoolset.org/docs/schema/dependency/>\n    Dependency,\n    /// WiX Toolset DirectX Extension\n    /// Docs: <https://wixtoolset.org/docs/schema/directx/>\n    DirectX,\n    /// WiX Toolset Firewall Extension\n    /// Docs: <https://wixtoolset.org/docs/schema/firewall/>\n    Firewall,\n    /// Windows Installer XML Toolset Http Extension\n    /// Docs: <https://wixtoolset.org/docs/schema/http/>\n    Http,\n    /// WiX Toolset Internet Information Services Extension\n    /// Docs: <https://wixtoolset.org/docs/schema/iis/>\n    Iis,\n    /// WiX Toolset MSMQ Extension\n    /// Docs: <https://wixtoolset.org/docs/schema/msmq/>\n    Msmq,\n    /// WiX Toolset .NET Framework Extension\n    /// Docs: <https://wixtoolset.org/docs/schema/netfx/>\n    Netfx,\n    /// WiX Toolset PowerShell Extension\n    /// Docs: <https://wixtoolset.org/docs/schema/powershell/>\n    Powershell,\n    /// WiX Toolset SQL Server Extension\n    /// Docs: <https://wixtoolset.org/docs/schema/sql/>\n    Sql,\n    /// WiX Toolset UI Extension\n    /// Docs: <https://wixtoolset.org/docs/schema/ui/>\n    UI,\n    /// WiX Toolset Utility Extension\n    /// Docs: <https://wixtoolset.org/docs/schema/util/>\n    Util,\n    /// WiX Toolset Visual Studio Extension\n    /// Docs: <https://wixtoolset.org/docs/schema/vs/>\n    VS,\n}\n\nimpl WixExtension for WellKnownExtentions {\n    fn package_name(&self) -> &str {\n        match self {\n            WellKnownExtentions::BootstrapperApplications => BAL_EXT,\n            WellKnownExtentions::ComPlus => COMPLUS_EXT,\n            WellKnownExtentions::Dependency => DEPENDENCY_EXT,\n            WellKnownExtentions::DirectX => DIRECTX_EXT,\n            WellKnownExtentions::Firewall => FIREWALL_EXT,\n            WellKnownExtentions::Http => HTTP_EXT,\n            WellKnownExtentions::Iis => IIS_EXT,\n            WellKnownExtentions::Msmq => MSMQ_EXT,\n            WellKnownExtentions::Netfx => NETFX_EXT,\n            WellKnownExtentions::Powershell => POWERSHELL_EXT,\n            WellKnownExtentions::Sql => SQL_EXT,\n            WellKnownExtentions::UI => UI_EXT,\n            WellKnownExtentions::Util => UTIL_EXT,\n            WellKnownExtentions::VS => VS_EXT,\n        }\n    }\n\n    /// Returns the xmlns prefix\n    fn namespace_prefix(&self) -> &str {\n        match self {\n            WellKnownExtentions::BootstrapperApplications => BAL_NS_PREFIX,\n            WellKnownExtentions::ComPlus => COMPLUS_NS_PREFIX,\n            WellKnownExtentions::Dependency => DEPENDENCY_NS_PREFIX,\n            WellKnownExtentions::DirectX => DIRECTX_NS_PREFIX,\n            WellKnownExtentions::Firewall => FIREWALL_NS_PREFIX,\n            WellKnownExtentions::Http => HTTP_NS_PREFIX,\n            WellKnownExtentions::Iis => IIS_NS_PREFIX,\n            WellKnownExtentions::Msmq => MSMQ_NS_PREFIX,\n            WellKnownExtentions::Netfx => NETFX_NS_PREFIX,\n            WellKnownExtentions::Powershell => POWERSHELL_NS_PREFIX,\n            WellKnownExtentions::Sql => SQL_NS_PREFIX,\n            WellKnownExtentions::UI => UI_NS_PREFIX,\n            WellKnownExtentions::Util => UTIL_NS_PREFIX,\n            WellKnownExtentions::VS => VS_NS_PREFIX,\n        }\n    }\n\n    fn namespace_uri(&self) -> &str {\n        match self {\n            WellKnownExtentions::BootstrapperApplications => BAL_NS_URI,\n            WellKnownExtentions::ComPlus => COMPLUS_NS_URI,\n            WellKnownExtentions::Dependency => DEPENDENCY_NS_URI,\n            WellKnownExtentions::DirectX => DIRECTX_NS_URI,\n            WellKnownExtentions::Firewall => FIREWALL_NS_URI,\n            WellKnownExtentions::Http => HTTP_NS_URI,\n            WellKnownExtentions::Iis => IIS_NS_URI,\n            WellKnownExtentions::Msmq => MSMQ_NS_URI,\n            WellKnownExtentions::Netfx => NETFX_NS_URI,\n            WellKnownExtentions::Powershell => POWERSHELL_NS_URI,\n            WellKnownExtentions::Sql => SQL_NS_URI,\n            WellKnownExtentions::UI => UI_NS_URI,\n            WellKnownExtentions::Util => UTIL_NS_URI,\n            WellKnownExtentions::VS => VS_NS_URI,\n        }\n    }\n}\n\nconst BAL_EXT: &str = \"WixToolset.BootstrapperApplications.wixext\";\nconst BAL_NS_PREFIX: &str = \"bal\";\nconst BAL_NS_URI: &str = \"http://wixtoolset.org/schemas/v4/wxs/bal\";\n\nconst COMPLUS_EXT: &str = \"WixToolset.ComPlus.wixext\";\nconst COMPLUS_NS_PREFIX: &str = \"complus\";\nconst COMPLUS_NS_URI: &str = \"http://wixtoolset.org/schemas/v4/wxs/complus\";\n\nconst DEPENDENCY_EXT: &str = \"WixToolset.Dependency.wixext\";\n/// Source of namespace prefix:\n/// https://github.com/wixtoolset/wix/blob/dd2fe20d9fe58719445411524bd730495140d02f/src/wix/test/WixToolsetTest.Converters/DependencyFixture.cs#L19\nconst DEPENDENCY_NS_PREFIX: &str = \"dep\";\nconst DEPENDENCY_NS_URI: &str = \"http://wixtoolset.org/schemas/v4/wxs/dependency\";\n\nconst DIRECTX_EXT: &str = \"WixToolset.DirectX.wixext\";\nconst DIRECTX_NS_PREFIX: &str = \"directx\";\nconst DIRECTX_NS_URI: &str = \"http://wixtoolset.org/schemas/v4/wxs/directx\";\n\nconst FIREWALL_EXT: &str = \"WixToolset.Firewall.wixext\";\n/// Source of namespace prefix:\n/// https://github.com/wixtoolset/wix/blob/dd2fe20d9fe58719445411524bd730495140d02f/src/wix/test/WixToolsetTest.Converters/FirewallExtensionFixture.cs#L19\nconst FIREWALL_NS_PREFIX: &str = \"fw\";\nconst FIREWALL_NS_URI: &str = \"http://wixtoolset.org/schemas/v4/wxs/firewall\";\n\nconst HTTP_EXT: &str = \"WixToolset.Http.wixext\";\nconst HTTP_NS_PREFIX: &str = \"http\";\nconst HTTP_NS_URI: &str = \"http://wixtoolset.org/schemas/v4/wxs/http\";\n\nconst IIS_EXT: &str = \"WixToolset.Iis.wixext\";\nconst IIS_NS_PREFIX: &str = \"iis\";\nconst IIS_NS_URI: &str = \"http://wixtoolset.org/schemas/v4/wxs/iis\";\n\nconst MSMQ_EXT: &str = \"WixToolset.Msmq.wixext\";\nconst MSMQ_NS_PREFIX: &str = \"msmq\";\nconst MSMQ_NS_URI: &str = \"http://wixtoolset.org/schemas/v4/wxs/msmq\";\n\nconst NETFX_EXT: &str = \"WixToolset.Netfx.wixext\";\nconst NETFX_NS_PREFIX: &str = \"netfx\";\nconst NETFX_NS_URI: &str = \"http://wixtoolset.org/schemas/v4/wxs/netfx\";\n\nconst POWERSHELL_EXT: &str = \"WixToolset.PowerShell.wixext\";\nconst POWERSHELL_NS_PREFIX: &str = \"powershell\";\nconst POWERSHELL_NS_URI: &str = \"http://wixtoolset.org/schemas/v4/wxs/powershell\";\n\nconst SQL_EXT: &str = \"WixToolset.Sql.wixext\";\nconst SQL_NS_PREFIX: &str = \"sql\";\nconst SQL_NS_URI: &str = \"http://wixtoolset.org/schemas/v4/wxs/sql\";\n\nconst UI_EXT: &str = \"WixToolset.UI.wixext\";\nconst UI_NS_PREFIX: &str = \"ui\";\nconst UI_NS_URI: &str = \"http://wixtoolset.org/schemas/v4/wxs/ui\";\n\nconst UTIL_EXT: &str = \"WixToolset.Util.wixext\";\nconst UTIL_NS_PREFIX: &str = \"util\";\nconst UTIL_NS_URI: &str = \"http://wixtoolset.org/schemas/v4/wxs/util\";\n\nconst VS_EXT: &str = \"WixToolset.VisualStudio.wixext\";\nconst VS_NS_PREFIX: &str = \"vs\";\nconst VS_NS_URI: &str = \"http://wixtoolset.org/schemas/v4/wxs/vs\";\n\n#[cfg(test)]\nmod tests {\n    use std::path::PathBuf;\n\n    use super::PackageCache;\n    use crate::toolset::{ToolsetAction, ext::WellKnownExtentions, test};\n    use semver::Version;\n\n    #[test]\n    fn test_package_cache_installed() {\n        let mut package = PackageCache {\n            installed: Default::default(),\n            missing: Default::default(),\n            toolset: crate::toolset::Toolset::Modern,\n        };\n\n        package.add(\"WixToolset.Util.wixext\", Version::new(0, 0, 0));\n        assert!(package.installed(&WellKnownExtentions::Util));\n    }\n\n    #[test]\n    fn test_package_cache_install_missing_non_global() {\n        let test_shim = test::toolset(|a: &ToolsetAction, cmd: &std::process::Command| match a {\n            ToolsetAction::AddExtension => {\n                let mut expected = std::process::Command::new(\"wix\");\n                expected\n                    .arg(\"extension\")\n                    .arg(\"add\")\n                    .arg(\"Test.wixext/0.0.0\");\n                assert_eq!(format!(\"{:?}\", cmd), format!(\"{:?}\", expected));\n\n                test::ok_stdout(\"\")\n            }\n            _ => {\n                unreachable!(\"Only extension add should be evaluated\")\n            }\n        });\n\n        let mut package = PackageCache::from(test_shim);\n\n        package.add_missing(\"Test.wixext\");\n        package\n            .install_missing(false, Version::new(0, 0, 0), None)\n            .unwrap();\n    }\n\n    #[test]\n    fn test_package_cache_install_missing_global() {\n        let test_shim = test::toolset(|a: &ToolsetAction, cmd: &std::process::Command| match a {\n            ToolsetAction::AddGlobalExtension => {\n                let mut expected = std::process::Command::new(\"wix\");\n                expected\n                    .arg(\"extension\")\n                    .arg(\"add\")\n                    .arg(\"--global\")\n                    .arg(\"Test.wixext/0.0.0\");\n                assert_eq!(format!(\"{:?}\", cmd), format!(\"{:?}\", expected));\n\n                test::ok_stdout(\"\")\n            }\n            a => {\n                unreachable!(\"Only extension add should be evaluated, tried to eval {a:?}\")\n            }\n        });\n\n        let mut package = PackageCache::from(test_shim);\n        package.add_missing(\"Test.wixext\");\n        package\n            .install_missing(true, Version::new(0, 0, 0), None)\n            .unwrap();\n    }\n\n    #[test]\n    fn test_package_cache_install_missing_work_dir() {\n        let test_shim = test::toolset(|a: &ToolsetAction, cmd: &std::process::Command| match a {\n            ToolsetAction::AddGlobalExtension => {\n                let mut expected = std::process::Command::new(\"wix\");\n                expected\n                    .arg(\"extension\")\n                    .arg(\"add\")\n                    .arg(\"--global\")\n                    .arg(\"Test.wixext/0.0.0\")\n                    .current_dir(\"test_work_dir\");\n                assert_eq!(format!(\"{:?}\", cmd), format!(\"{:?}\", expected));\n                assert_eq!(\"test_work_dir\", cmd.get_current_dir().unwrap().as_os_str());\n\n                test::ok_stdout(\"\")\n            }\n            a => {\n                unreachable!(\"Only extension add should be evaluated, tried to eval {a:?}\")\n            }\n        });\n\n        let mut package = PackageCache::from(test_shim);\n        package.add_missing(\"Test.wixext\");\n        package\n            .install_missing(\n                true,\n                Version::new(0, 0, 0),\n                Some(&PathBuf::from(\"test_work_dir\")),\n            )\n            .unwrap();\n    }\n}\n"
  },
  {
    "path": "src/toolset/includes.rs",
    "content": "// Copyright (C) 2017 Christopher R. Field.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nuse crate::{Error, WIX, WIX_SOURCE_FILE_EXTENSION, toolset::Toolset};\nuse cargo_metadata::Package;\nuse log::{debug, trace};\nuse std::path::PathBuf;\n\nuse super::Project;\n\n/// Trait for returning a list of project files to include\npub trait Includes {\n    /// Returns included file paths\n    fn includes(&self) -> Option<&Vec<PathBuf>>;\n\n    /// Returns a list of paths to *.wxs sources\n    fn wxs_sources(&self, package: &Package) -> crate::Result<Vec<PathBuf>> {\n        let project_wix_dir = package\n            .manifest_path\n            .parent()\n            .ok_or_else(|| {\n                Error::Generic(format!(\n                    \"The '{}' path for the package's manifest file is invalid\",\n                    package.manifest_path\n                ))\n            })\n            .map(|d| PathBuf::from(d).join(WIX))?;\n        let mut wix_sources = {\n            if project_wix_dir.exists() {\n                std::fs::read_dir(project_wix_dir)?\n                    .filter_map(|r| r.ok())\n                    .map(|e| e.path())\n                    .filter(|p| {\n                        p.extension().and_then(|s| s.to_str()) == Some(WIX_SOURCE_FILE_EXTENSION)\n                    })\n                    .collect()\n            } else {\n                Vec::new()\n            }\n        };\n        if let Some(paths) = self.includes() {\n            for p in paths {\n                if p.exists() {\n                    if p.is_dir() {\n                        return Err(Error::Generic(format!(\n                            \"The '{}' path is not a file. Please check the path and ensure it is to \\\n                            a WiX Source (wxs) file.\",\n                            p.display()\n                        )));\n                    } else {\n                        trace!(\"Using the '{}' WiX source file\", p.display());\n                    }\n                } else {\n                    return Err(Error::Generic(format!(\n                        \"The '{0}' file does not exist. Consider using the 'cargo \\\n                         wix print WXS > {0}' command to create it.\",\n                        p.display()\n                    )));\n                }\n            }\n            wix_sources.extend(paths.clone());\n        } else if let Some(pkg_meta_wix_include) = package\n            .metadata\n            .get(\"wix\")\n            .and_then(|w| w.as_object())\n            .and_then(|t| t.get(\"include\"))\n            .and_then(|i| i.as_array())\n        {\n            let pkg_meta_wix_sources: Vec<PathBuf> = pkg_meta_wix_include\n                .iter()\n                .map(|s| {\n                    s.as_str().map(PathBuf::from).ok_or_else(|| {\n                        Error::Generic(format!(\n                            \"Invalid value in 'package.metadata.wix.include': \\\n                             expected a string path, found '{s}'\"\n                        ))\n                    })\n                })\n                .collect::<crate::Result<Vec<PathBuf>>>()?;\n            for pkg_meta_wix_source in &pkg_meta_wix_sources {\n                if pkg_meta_wix_source.exists() {\n                    if pkg_meta_wix_source.is_dir() {\n                        return Err(Error::Generic(format!(\n                            \"The '{}' path is not a file. Please check the path and \\\n                             ensure it is to a WiX Source (wxs) file in the \\\n                             'package.metadata.wix' section of the package's manifest \\\n                             (Cargo.toml).\",\n                            pkg_meta_wix_source.display()\n                        )));\n                    } else {\n                        trace!(\n                            \"Using the '{}' WiX source file from the \\\n                             'package.metadata.wix' section in the package's \\\n                             manifest.\",\n                            pkg_meta_wix_source.display()\n                        );\n                    }\n                } else {\n                    return Err(Error::Generic(format!(\n                        \"The '{0}' file does not exist. Consider using the \\\n                         'cargo wix print WXS > {0} command to create it.\",\n                        pkg_meta_wix_source.display()\n                    )));\n                }\n            }\n\n            wix_sources.extend(pkg_meta_wix_sources);\n        }\n        if wix_sources.is_empty() {\n            Err(Error::Generic(String::from(\n                \"There are no WXS files to create an installer\",\n            )))\n        } else {\n            Ok(wix_sources)\n        }\n    }\n}\n\n/// Extension functions for implementations of Include\npub trait ProjectProvider: Includes {\n    /// Returns a working directory override\n    fn work_dir(&self) -> Option<PathBuf> {\n        None\n    }\n\n    /// Return the toolset to use when creating projects\n    fn toolset(&self) -> Toolset;\n\n    /// Derives a project from a Package and the current `impl Includes`\n    ///\n    /// Returns an error if the *.wxs sources cannot be enumerated, if\n    /// a modern WiX toolset is not installed and available from PATH, or\n    /// if a .wxs file has invalid XML\n    fn create_project(&self, package: &Package) -> crate::Result<Project> {\n        debug!(\"Getting wxs files\");\n        let wxs_sources = self.wxs_sources(package)?;\n        debug!(\"wxs_sources = {wxs_sources:#?}\");\n\n        debug!(\"Trying to create new project\");\n        let mut project = Project::try_new(self.toolset())?;\n        debug!(\"project = {project:#?}\");\n\n        for src in wxs_sources {\n            debug!(\"Adding {src:?} to project\");\n            project.add_wxs(src)?;\n        }\n\n        debug!(\"Completed project = {project:#?}\");\n        Ok(project)\n    }\n}\n\nimpl ProjectProvider for crate::create::Execution {\n    fn toolset(&self) -> Toolset {\n        self.toolset.clone()\n    }\n}\nimpl ProjectProvider for crate::migrate::Execution {\n    fn toolset(&self) -> Toolset {\n        Toolset::Modern\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use std::path::PathBuf;\n\n    /// Minimal Includes implementation for testing the default wxs_sources method\n    struct NoIncludes;\n    impl Includes for NoIncludes {\n        fn includes(&self) -> Option<&Vec<PathBuf>> {\n            None\n        }\n    }\n\n    /// Helper to create a Package from JSON\n    fn package_from_json(json: &str) -> cargo_metadata::Package {\n        serde_json::from_str(json).expect(\"valid package JSON\")\n    }\n\n    const PACKAGE_TEMPLATE: &str = r#\"{\n        \"name\": \"Example\",\n        \"version\": \"0.1.0\",\n        \"authors\": [\"First Last <first.last@example.com>\"],\n        \"license\": \"XYZ\",\n        \"id\": \"\",\n        \"dependencies\": [],\n        \"targets\": [],\n        \"features\": {},\n        \"manifest_path\": \"C:\\\\Cargo.toml\"\n    }\"#;\n\n    #[test]\n    fn non_string_metadata_include_errors() {\n        let json = r#\"{\n            \"name\": \"Example\",\n            \"version\": \"0.1.0\",\n            \"authors\": [\"First Last <first.last@example.com>\"],\n            \"license\": \"XYZ\",\n            \"id\": \"\",\n            \"dependencies\": [],\n            \"targets\": [],\n            \"features\": {},\n            \"manifest_path\": \"C:\\\\Cargo.toml\",\n            \"metadata\": {\n                \"wix\": {\n                    \"include\": [42]\n                }\n            }\n        }\"#;\n        let pkg = package_from_json(json);\n        let result = NoIncludes.wxs_sources(&pkg);\n        assert!(\n            result.is_err(),\n            \"Non-string include value should produce an error\"\n        );\n        let err = format!(\"{}\", result.unwrap_err());\n        assert!(\n            err.contains(\"expected a string path\"),\n            \"Error should mention expected string, got: {err}\"\n        );\n    }\n\n    #[test]\n    fn mixed_valid_and_invalid_metadata_include_errors() {\n        let json = r#\"{\n            \"name\": \"Example\",\n            \"version\": \"0.1.0\",\n            \"authors\": [\"First Last <first.last@example.com>\"],\n            \"license\": \"XYZ\",\n            \"id\": \"\",\n            \"dependencies\": [],\n            \"targets\": [],\n            \"features\": {},\n            \"manifest_path\": \"C:\\\\Cargo.toml\",\n            \"metadata\": {\n                \"wix\": {\n                    \"include\": [\"valid.wxs\", null, \"other.wxs\"]\n                }\n            }\n        }\"#;\n        let pkg = package_from_json(json);\n        let result = NoIncludes.wxs_sources(&pkg);\n        assert!(\n            result.is_err(),\n            \"Array with null value should produce an error\"\n        );\n    }\n\n    #[test]\n    fn empty_metadata_include_array_errors_no_sources() {\n        let json = r#\"{\n            \"name\": \"Example\",\n            \"version\": \"0.1.0\",\n            \"authors\": [\"First Last <first.last@example.com>\"],\n            \"license\": \"XYZ\",\n            \"id\": \"\",\n            \"dependencies\": [],\n            \"targets\": [],\n            \"features\": {},\n            \"manifest_path\": \"C:\\\\Cargo.toml\",\n            \"metadata\": {\n                \"wix\": {\n                    \"include\": []\n                }\n            }\n        }\"#;\n        let pkg = package_from_json(json);\n        let result = NoIncludes.wxs_sources(&pkg);\n        // Empty include array means no sources, should error with \"no WXS files\"\n        assert!(result.is_err());\n        let err = format!(\"{}\", result.unwrap_err());\n        assert!(\n            err.contains(\"no WXS files\"),\n            \"Empty include should error about no WXS files, got: {err}\"\n        );\n    }\n\n    #[test]\n    fn no_metadata_no_wix_dir_errors() {\n        let pkg = package_from_json(PACKAGE_TEMPLATE);\n        let result = NoIncludes.wxs_sources(&pkg);\n        assert!(result.is_err(), \"No wix dir and no metadata should error\");\n    }\n}\n"
  },
  {
    "path": "src/toolset/mod.rs",
    "content": "// Copyright (C) 2017 Christopher R. Field.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//! # Toolset Module\n//!\n//! Contains types for integrating legacy and modern WiX toolsets and facilitating various\n//! project setup utilities such as converting from legacy to modern, restoring extension dependencies, or\n//! automatically determining if extension dependencies that are required are currently missing from the environment.\n\npub mod ext;\nmod includes;\npub mod project;\npub mod source;\n#[cfg(test)]\npub(crate) mod test;\nuse cargo_metadata::Package;\npub use includes::Includes;\npub use includes::ProjectProvider;\npub use project::Project;\n\nuse crate::BINARY_FOLDER_NAME;\nuse crate::EXE_FILE_EXTENSION;\nuse crate::WIX_COMPILER;\nuse crate::WIX_PATH_KEY;\nuse clap::ValueEnum;\nuse log::debug;\nuse log::trace;\nuse std::ops::Deref;\nuse std::ops::DerefMut;\nuse std::path::PathBuf;\nuse std::process::Command;\nuse std::process::Output;\nuse std::str::FromStr;\n\n/// Enumeration of wix-toolset options\n///\n/// This controls which wix build binaries are used by cargo-wix\n#[derive(ValueEnum, Clone)]\npub enum Toolset {\n    /// The default wix toolset uses \"candle.exe\" and \"light.exe\" to build the installer\n    Legacy,\n    /// Modern wix toolsets use just \"wix.exe\" to build the installer\n    Modern,\n    /// Test toolset, used to test code without needing any toolkit installed\n    #[cfg(test)]\n    #[clap(skip)]\n    Test {\n        is_legacy: bool,\n        shim: test::SharedTestShim,\n    },\n}\n\nimpl std::fmt::Debug for Toolset {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::Legacy => write!(f, \"Legacy\"),\n            Self::Modern => write!(f, \"Modern\"),\n            #[cfg(test)]\n            Self::Test {\n                is_legacy: allow_legacy,\n                ..\n            } => f.debug_tuple(\"Test\").field(allow_legacy).finish(),\n        }\n    }\n}\n\n/// Wrapper over std::process::Command\n///\n/// Allows for checking args state and centralizes debug logging logic\npub struct ToolsetCommand {\n    pub(crate) inner: Command,\n    action: ToolsetAction,\n    #[cfg(test)]\n    test: Option<test::SharedTestShim>,\n}\n\n/// Enumeration of toolset actions used by this module\n#[derive(Default, Debug)]\npub enum ToolsetAction {\n    /// `candle`\n    Compile { bin_path: Option<PathBuf> },\n    /// `wix convert`\n    Convert,\n    /// `wix build`\n    Build,\n    /// `wix extension add`\n    AddExtension,\n    /// `wix extension add --global`\n    AddGlobalExtension,\n    /// `wix extension list`\n    ListExtension,\n    /// `wix extension list --global`\n    ListGlobalExtension,\n    /// `wix --version`\n    #[default]\n    Version,\n}\n\n/// Enumeration of wix-toolset project setup patterns that can be applied\n///\n/// WiX toolset project setup consists of two major-operations, **upgrade** or **restore**.\n///\n/// - **upgrade**: Convert Wix3 and below *.wxs files to the modern WiX format. Technically only needs to be executed once,\n///   but the setup will try and do the smart thing and upgrade on an as-needed basis\n/// - **restore**: Detect and install wix extensions required by *.wxs files. Requires all source files to be upgraded\n///\n/// The toolset setup mode indicates whether or not to apply `upgrade` and `restore`.\n///\n/// `upgrade` has the same behavior for all setup modes, however for `restore` there is the option of restoring extensions\n/// in the current directory (vendoring) or restoring extensions globally.\n#[derive(ValueEnum, Copy, Clone, Debug, Default)]\npub enum ToolsetSetupMode {\n    /// Do not apply any setup logic. (Default)\n    #[default]\n    None,\n    /// Setup project in \"global\" mode.\n    ///\n    /// Upgrade source files in place and install extensions to the global extension cache\n    ///\n    /// This option is suited to simple one file installer projects, where build pipeline network access will not be\n    /// an issue and wix will likely be installed on a dev machine or during the build pipeline on demand.\n    #[clap(name = \"global\")]\n    Global,\n    /// Setup project in \"vendored\" mode.\n    ///\n    /// Upgrade source files in place and install extensions to the current directory\n    ///\n    /// This option is suited to tracking changes with source version control systems such as git, and also when\n    /// the build will happen in an offline environment\n    #[clap(name = \"vendor\")]\n    Vendor,\n}\n\n#[cfg(test)]\nimpl Toolset {\n    /// Returns true if the toolset in use is legacy\n    pub fn is_legacy(&self) -> bool {\n        matches!(\n            self,\n            Toolset::Test {\n                is_legacy: true,\n                ..\n            } | Toolset::Legacy\n        )\n    }\n\n    pub fn is_modern(&self) -> bool {\n        matches!(\n            self,\n            Toolset::Test {\n                is_legacy: false,\n                ..\n            } | Toolset::Modern\n        )\n    }\n}\n\nimpl Toolset {\n    /// Returns true if the toolset in use is legacy\n    #[cfg(not(test))]\n    pub fn is_legacy(&self) -> bool {\n        matches!(self, Toolset::Legacy)\n    }\n\n    /// Returns true if the toolset in use is modern\n    #[cfg(not(test))]\n    pub fn is_modern(&self) -> bool {\n        matches!(self, Toolset::Modern)\n    }\n\n    /// Returns a new ToolsetCommand for `wix.exe`\n    pub fn wix(&self, action: impl Into<ToolsetAction>) -> crate::Result<ToolsetCommand> {\n        if self.is_modern() {\n            #[cfg(test)]\n            let mut command = ToolsetCommand::try_from(action.into())?;\n\n            #[cfg(test)]\n            if let Toolset::Test { shim, .. } = self {\n                command.test = Some(shim.clone());\n            }\n\n            #[cfg(not(test))]\n            let command = ToolsetCommand::try_from(action.into())?;\n            Ok(command)\n        } else {\n            Err(\"Cannot use modern wix commands with legacy toolset\".into())\n        }\n    }\n\n    /// Returns a new ToolsetCommand for `candle.exe`\n    pub fn compiler(&self, bin_path: Option<PathBuf>) -> crate::Result<ToolsetCommand> {\n        if self.is_legacy() {\n            #[cfg(test)]\n            let mut command: ToolsetCommand = ToolsetAction::Compile { bin_path }.try_into()?;\n\n            #[cfg(test)]\n            if let Toolset::Test { shim, .. } = self {\n                command.test = Some(shim.clone());\n            }\n\n            #[cfg(not(test))]\n            let command: ToolsetCommand = ToolsetAction::Compile { bin_path }.try_into()?;\n            Ok(command)\n        } else {\n            Err(\"Cannot use legacy wix commands with modern toolset\".into())\n        }\n    }\n}\n\nimpl ToolsetSetupMode {\n    /// Returns a project and applies any required project setup required\n    ///\n    /// If vendor mode is enabled, this will ensure a local extension cache folder has been created\n    ///\n    /// If the current toolset setup mode is not `ToolsetSetupMode::None`, this will also apply project migration before returning the project\n    #[inline]\n    pub fn setup(\n        &self,\n        provider: &impl ProjectProvider,\n        package: &Package,\n        work_dir: Option<PathBuf>,\n    ) -> crate::Result<Project> {\n        if matches!(self, ToolsetSetupMode::Vendor) {\n            debug!(\"Vendored mode is enabled, check for the local extension cache\");\n            let work_dir = work_dir.unwrap_or(std::env::current_dir()?);\n\n            let local_ext_dir = work_dir.join(\".wix\").join(\"extensions\");\n            if !local_ext_dir.exists() {\n                debug!(\"Vendored mode is enabled, check for the local extension cache\");\n                std::fs::create_dir_all(local_ext_dir)?;\n            }\n        }\n\n        let mut project = provider.create_project(package)?;\n        match self {\n            ToolsetSetupMode::None => {}\n            _ => {\n                debug!(\"Starting project upgrade\");\n                project.upgrade(None)?;\n                debug!(\"Restoring any missing extension packages\");\n                project.restore(self.use_global(), None)?;\n            }\n        }\n        Ok(project)\n    }\n\n    /// Returns true if setup is enabled\n    #[inline]\n    pub fn is_enabled(&self) -> bool {\n        !matches!(self, ToolsetSetupMode::None)\n    }\n\n    /// Returns true if the global extension package cache should be used\n    #[inline]\n    pub fn use_global(&self) -> bool {\n        matches!(self, ToolsetSetupMode::Global)\n    }\n}\n\nimpl ToolsetCommand {\n    /// Consumes this reference and returns the inner std::process::Command and configured toolset action\n    pub fn split_into_std(self) -> (std::process::Command, ToolsetAction) {\n        (self.inner, self.action)\n    }\n\n    /// Consumes the toolset command and returns the output\n    #[allow(unused_mut)] // For test shim\n    pub fn output(mut self) -> crate::Result<Output> {\n        debug!(\"command.action={:?}\", self.action);\n        #[cfg(not(test))]\n        let output = self.inner.output()?;\n\n        #[cfg(test)]\n        let output = self.test_output();\n\n        if output.status.success() {\n            if log::log_enabled!(log::Level::Debug) && !output.stderr.is_empty() {\n                let std_err = String::from_utf8(output.stderr.clone())?;\n                for line in std_err.lines() {\n                    debug!(\"{line}\");\n                }\n            }\n            Ok(output)\n        } else {\n            if let ToolsetAction::Convert = self.action {\n                // This is expected because `wix convert` will always return a non-zero exit code\n                return Ok(output);\n            }\n            Err(match self.action {\n                ToolsetAction::Convert => \"(wix.exe) Could not convert wxs file\",\n                ToolsetAction::Build => \"(wix.exe) Could not build installer\",\n                ToolsetAction::AddExtension => \"(wix.exe) Could not add extension package to local cache\",\n                ToolsetAction::AddGlobalExtension => {\n                    \"(wix.exe) Could not add extension package to global cache\"\n                }\n                ToolsetAction::ListExtension => {\n                    \"(wix.exe) Could not list installed extensions from local cache\"\n                }\n                ToolsetAction::ListGlobalExtension => {\n                    \"(wix.exe) Could not list installed extensions from global cache\"\n                }\n                ToolsetAction::Version => {\n                    \"(wix.exe) command was not found from PATH env, ensure a WiX4+ toolset is installed\"\n                }\n                ToolsetAction::Compile { .. } => \"(candle.exe) Could not compile wxs files\",\n            }\n            .into())\n        }\n    }\n\n    /// Test shim for converting test parameters into an output\n    #[cfg(test)]\n    fn test_output(&self) -> Output {\n        if let Some(shim) = self.test.as_ref() {\n            let test = shim.on_output(&self.action, &self.inner);\n\n            #[cfg(windows)]\n            use std::os::windows::process::ExitStatusExt;\n\n            #[cfg(unix)]\n            use std::os::unix::process::ExitStatusExt;\n\n            let status = if test.success {\n                std::process::ExitStatus::from_raw(0)\n            } else {\n                std::process::ExitStatus::from_raw(1)\n            };\n\n            Output {\n                status,\n                stdout: test.stdout.as_bytes().to_vec(),\n                stderr: test.stderr.as_bytes().to_vec(),\n            }\n        } else {\n            todo!()\n        }\n    }\n}\n\nimpl Default for ToolsetCommand {\n    fn default() -> Self {\n        Self {\n            inner: Command::new(\"wix\"),\n            action: ToolsetAction::Version,\n            #[cfg(test)]\n            test: None,\n        }\n    }\n}\n\nimpl From<std::process::Command> for ToolsetCommand {\n    fn from(value: std::process::Command) -> Self {\n        let action = ToolsetAction::from(value.get_program().to_string_lossy().as_ref());\n\n        ToolsetCommand {\n            inner: value,\n            action,\n            #[cfg(test)]\n            test: None,\n        }\n    }\n}\n\nimpl FromStr for ToolsetCommand {\n    type Err = crate::Error;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        ToolsetAction::from(s).try_into()\n    }\n}\n\nimpl<'a> From<&'a str> for ToolsetAction {\n    fn from(s: &'a str) -> Self {\n        match s {\n            \"candle.exe\" | \"candle\" | \"compile\" => ToolsetAction::Compile { bin_path: None },\n            \"convert\" => ToolsetAction::Convert,\n            \"build\" => ToolsetAction::Build,\n            \"extension add\" => ToolsetAction::AddExtension,\n            \"extension list\" => ToolsetAction::ListExtension,\n            \"extension add --global\" => ToolsetAction::AddGlobalExtension,\n            \"extension list --global\" => ToolsetAction::ListGlobalExtension,\n            _ => ToolsetAction::Version,\n        }\n    }\n}\n\nimpl TryFrom<ToolsetAction> for ToolsetCommand {\n    type Error = crate::Error;\n\n    fn try_from(value: ToolsetAction) -> crate::Result<Self> {\n        let mut command = ToolsetCommand::default();\n        match value {\n            ToolsetAction::Convert => {\n                command.action = value;\n                command.arg(\"convert\");\n                Ok(command)\n            }\n            ToolsetAction::Build => {\n                command.action = value;\n                command.arg(\"build\");\n                Ok(command)\n            }\n            ToolsetAction::AddExtension => {\n                command.action = value;\n                command.args([\"extension\", \"add\"]);\n                Ok(command)\n            }\n            ToolsetAction::ListExtension => {\n                command.action = value;\n                command.args([\"extension\", \"list\"]);\n                Ok(command)\n            }\n            ToolsetAction::AddGlobalExtension => {\n                command.action = value;\n                command.args([\"extension\", \"add\", \"--global\"]);\n                Ok(command)\n            }\n            ToolsetAction::ListGlobalExtension => {\n                command.action = value;\n                command.args([\"extension\", \"list\", \"--global\"]);\n                Ok(command)\n            }\n            ToolsetAction::Version => {\n                command.action = value;\n                command.arg(\"--version\");\n                Ok(command)\n            }\n            ToolsetAction::Compile { bin_path } => {\n                if let Some(mut path) = bin_path.as_ref().map(|s| {\n                    let mut p = PathBuf::from(s);\n                    trace!(\n                        \"Using the '{}' path to the WiX Toolset's '{}' folder for the compiler\",\n                        p.display(),\n                        BINARY_FOLDER_NAME\n                    );\n                    p.push(WIX_COMPILER);\n                    p.set_extension(EXE_FILE_EXTENSION);\n                    p\n                }) {\n                    if !path.exists() {\n                        path.pop(); // Remove the `candle` application from the path\n                        Err(crate::Error::Generic(format!(\n                            \"The compiler application ('{}') does not exist at the '{}' path specified via \\\n                            the '-b,--bin-path' command line argument. Please check the path is correct and \\\n                            the compiler application exists at the path.\",\n                            WIX_COMPILER,\n                            path.display()\n                        )))\n                    } else {\n                        Ok(ToolsetCommand {\n                            inner: Command::new(path),\n                            action: ToolsetAction::Compile { bin_path },\n                            #[cfg(test)]\n                            test: None,\n                        })\n                    }\n                } else if let Some(mut path) = std::env::var_os(WIX_PATH_KEY).map(|s| {\n                    let mut p = PathBuf::from(s);\n                    trace!(\n                        \"Using the '{}' path to the WiX Toolset's '{}' folder for the compiler\",\n                        p.display(),\n                        BINARY_FOLDER_NAME\n                    );\n                    p.push(BINARY_FOLDER_NAME);\n                    p.push(WIX_COMPILER);\n                    p.set_extension(EXE_FILE_EXTENSION);\n                    p\n                }) {\n                    if !path.exists() {\n                        path.pop(); // Remove the `candle` application from the path\n                        Err(crate::Error::Generic(format!(\n                            \"The compiler application ('{}') does not exist at the '{}' path specified \\\n                             via the {} environment variable. Please check the path is correct and the \\\n                             compiler application exists at the path.\",\n                            WIX_COMPILER,\n                            path.display(),\n                            WIX_PATH_KEY\n                        )))\n                    } else {\n                        Ok(ToolsetCommand {\n                            inner: Command::new(path),\n                            action: ToolsetAction::Compile { bin_path: None },\n                            #[cfg(test)]\n                            test: None,\n                        })\n                    }\n                } else {\n                    Ok(ToolsetCommand {\n                        inner: Command::new(WIX_COMPILER),\n                        action: ToolsetAction::Compile { bin_path: None },\n                        #[cfg(test)]\n                        test: None,\n                    })\n                }\n            }\n        }\n    }\n}\n\nimpl std::fmt::Debug for ToolsetCommand {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"ToolsetCommand\")\n            .field(\"inner\", &self.inner)\n            .field(\"action\", &self.action)\n            .finish()\n    }\n}\n\nimpl Deref for ToolsetCommand {\n    type Target = std::process::Command;\n\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl DerefMut for ToolsetCommand {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.inner\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use serial_test::serial;\n\n    use super::ToolsetAction;\n    use super::test;\n    use crate::toolset::ProjectProvider;\n    use crate::toolset::Toolset;\n    use crate::toolset::ToolsetSetupMode;\n    use std::path::PathBuf;\n\n    #[test]\n    #[cfg(windows)]\n    fn test_toolset_compiler_is_correct_with_defaults() {\n        use std::{path::PathBuf, process::Command};\n\n        use crate::{\n            BINARY_FOLDER_NAME, EXE_FILE_EXTENSION, WIX_COMPILER, WIX_PATH_KEY,\n            toolset::{Toolset, ToolsetAction},\n        };\n\n        let expected = Command::new(\n            std::env::var_os(WIX_PATH_KEY)\n                .map(|s| {\n                    let mut p = PathBuf::from(s);\n                    p.push(BINARY_FOLDER_NAME);\n                    p.push(WIX_COMPILER);\n                    p.set_extension(EXE_FILE_EXTENSION);\n                    p\n                })\n                .unwrap(),\n        );\n        let e = Toolset::Legacy.compiler(None).unwrap();\n        let (actual, action) = e.split_into_std();\n        assert_eq!(format!(\"{actual:?}\"), format!(\"{expected:?}\"));\n        assert!(\n            matches!(action, ToolsetAction::Compile { bin_path: None }),\n            \"{action:?}\"\n        );\n    }\n\n    #[test]\n    fn test_toolset_wix_action() {\n        let toolset =\n            super::test::toolset(\n                |action: &ToolsetAction, _: &std::process::Command| match action {\n                    ToolsetAction::Compile { .. } => test::fail_stdout(\"out\"),\n                    ToolsetAction::Convert => test::ok_stdout(\"converted\"),\n                    ToolsetAction::Build => test::ok_stdout(\"built\"),\n                    ToolsetAction::AddExtension => test::ok_stdout(\"added\"),\n                    ToolsetAction::AddGlobalExtension => test::ok_stdout(\"added_global\"),\n                    ToolsetAction::ListExtension => test::ok_stdout(\"list_ext\"),\n                    ToolsetAction::ListGlobalExtension => test::ok_stdout(\"list_global_ext\"),\n                    ToolsetAction::Version => test::ok_stdout(\"version\"),\n                },\n            );\n\n        let output = toolset.wix(\"convert\").unwrap();\n        assert_eq!(&b\"converted\"[..], &output.output().unwrap().stdout[..]);\n\n        let output = toolset.wix(\"build\").unwrap();\n        assert_eq!(&b\"built\"[..], &output.output().unwrap().stdout[..]);\n\n        let output = toolset.wix(\"extension add\").unwrap();\n        assert_eq!(&b\"added\"[..], &output.output().unwrap().stdout[..]);\n\n        let output = toolset.wix(\"extension add --global\").unwrap();\n        assert_eq!(&b\"added_global\"[..], &output.output().unwrap().stdout[..]);\n\n        let output = toolset.wix(\"extension list\").unwrap();\n        assert_eq!(&b\"list_ext\"[..], &output.output().unwrap().stdout[..]);\n\n        let output = toolset.wix(\"extension list --global\").unwrap();\n        assert_eq!(\n            &b\"list_global_ext\"[..],\n            &output.output().unwrap().stdout[..]\n        );\n\n        let output = toolset.wix(\"--version\").unwrap();\n        assert_eq!(&b\"version\"[..], &output.output().unwrap().stdout[..]);\n\n        let output = toolset.wix(\"--version\").unwrap();\n        assert_eq!(&b\"version\"[..], &output.output().unwrap().stdout[..]);\n\n        toolset.compiler(None).expect_err(\n            \"should not be allowed to create a compiler command if toolset is not legacy\",\n        );\n    }\n\n    #[test]\n    fn test_toolset_legacy_action() {\n        let toolset =\n            super::test::legacy_toolset(|action: &ToolsetAction, _: &std::process::Command| {\n                match action {\n                    ToolsetAction::Compile { bin_path: None } => {\n                        test::ok_stdout(\"compile_bin_path_none\")\n                    }\n                    ToolsetAction::Compile { bin_path: Some(..) } => {\n                        unreachable!(\"should fail before this can be executed\")\n                    }\n                    _ => {\n                        panic!(\"should not execute non-legacy actions\")\n                    }\n                }\n            });\n\n        let output = toolset.compiler(None).unwrap();\n        assert_eq!(\n            &b\"compile_bin_path_none\"[..],\n            &output.output().unwrap().stdout[..]\n        );\n\n        toolset\n            .compiler(Some(PathBuf::new()))\n            .expect_err(\"should not have candle in an empty dir\");\n    }\n\n    #[test]\n    fn test_toolset_fmt_debug() {\n        assert_eq!(\"Legacy\", format!(\"{:?}\", Toolset::Legacy));\n        assert_eq!(\"Modern\", format!(\"{:?}\", Toolset::Modern));\n    }\n\n    #[test]\n    fn test_toolset_wix_command_err_on_modern() {\n        Toolset::Legacy\n            .wix(ToolsetAction::Version)\n            .expect_err(\"should not be able to use modern actions with legacy toolset\");\n    }\n\n    #[test]\n    #[serial]\n    fn test_toolset_migrate_global_mode() {\n        let test_toolset = super::project::tests::TestProject::new(\n            stringify!(test_toolset_setup_mode_migrate),\n            \"well_known_exts\",\n        );\n        let project = ToolsetSetupMode::Global\n            .setup(\n                &test_toolset,\n                &test_toolset.package,\n                test_toolset.work_dir(),\n            )\n            .expect(\"should work\");\n        assert!(\n            !test_toolset\n                .work_dir()\n                .unwrap()\n                .join(\".wix\")\n                .join(\"extensions\")\n                .exists(),\n            \"Ensure local extension cache was not created, since global uses global extension cache\"\n        );\n        for s in project.sources() {\n            // Check source was upgraded\n            assert!(s.is_modern());\n        }\n    }\n\n    #[test]\n    #[serial]\n    fn test_toolset_migrate_vendor_mode() {\n        let test_toolset = super::project::tests::TestProject::new(\n            stringify!(test_toolset_migrate_vendor_mode),\n            \"well_known_exts\",\n        );\n        let project = ToolsetSetupMode::Vendor\n            .setup(\n                &test_toolset,\n                &test_toolset.package,\n                test_toolset.work_dir(),\n            )\n            .expect(\"should work\");\n        assert!(\n            test_toolset\n                .work_dir()\n                .unwrap()\n                .join(\".wix\")\n                .join(\"extensions\")\n                .exists(),\n            \"Must create local wix extension cache in vendor mode\"\n        );\n\n        for s in project.sources() {\n            // Check source was upgraded\n            assert!(s.is_modern());\n        }\n    }\n}\n"
  },
  {
    "path": "src/toolset/project.rs",
    "content": "// Copyright (C) 2017 Christopher R. Field.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//! # Project Module\n//!\n//! This module orchestrates project state hydration by analyzing *.wxs files and\n//! determining the current wix toolset installation state.\n//!\n//! The `Project` type is the entrypoint for all major utilities provided by the toolset module.\n\nuse crate::create::InstallerKind;\nuse crate::toolset::ToolsetCommand;\n\nuse super::source::WixSource;\nuse super::{Toolset, ext::PackageCache};\nuse clap::ValueEnum;\nuse log::debug;\nuse std::collections::BTreeSet;\nuse std::{collections::BTreeMap, path::PathBuf};\nuse sxd_xpath::{Context, Factory, Value, evaluate_xpath};\n\n/// Wix3 XML Namespace URI\nconst LEGACY_NAMESPACE_URI: &str = \"http://schemas.microsoft.com/wix/2006/wi\";\n\n/// Wix4+ XML Namespace URI\npub const V4_NAMESPACE_URI: &str = \"http://wixtoolset.org/schemas/v4/wxs\";\n\n/// XPATH query for the root `<Wix/>` element\nconst WIX_ROOT_ELEMENT_XPATH: &str = \"/*[local-name()='Wix']\";\n\nconst WIX_PACKAGE_ROOT_ELEMENT_XPATH: &str = \"//w:Package\";\n\n/// Enumerations of wix wxs schemas\n#[derive(Debug, Clone, Copy, ValueEnum, PartialEq)]\npub enum WxsSchema {\n    /// Wix3 is not compatible with Wix4 and must always be upgraded\n    #[clap(name = \"2006\")]\n    Legacy,\n    /// Wix4, Wix5 these versions are backwards compatible and share the same namespace\n    ///\n    /// If the V4 namespace is detected, then a wxs format upgrade is not required\n    #[clap(name = \"v4\")]\n    V4,\n    /// Unsupported wxs schema\n    #[clap(skip)]\n    Unsupported,\n}\n\n/// Opens a .wxs source from path and identifies the version\npub fn open_wxs_source(path: PathBuf) -> crate::Result<WixSource> {\n    let source = std::fs::read_to_string(&path)?;\n\n    // If this isn't set correctly, wix convert will **SILENTLY** fail. Throw an error so that the user can go and fix these files manually\n    if source.contains(\"<?xml version='1.0' encoding='windows-1252'?>\") {\n        return Err(format!(\"Source file {path:?} has an xml header with encoding `windows-1252`. This must be changed to `utf-8` otherwise subsequent tooling will silently fail.\").as_str().into());\n    }\n\n    debug!(\"Trying to parse xml document\");\n    let package = sxd_document::parser::parse(source.trim_start_matches('\\u{FEFF}'))?;\n    let document = package.as_document();\n\n    let root = evaluate_xpath(&document, WIX_ROOT_ELEMENT_XPATH)?;\n    match root {\n        Value::Nodeset(ns) => {\n            if let Some((default, exts)) = ns\n                .document_order_first()\n                .and_then(|e| e.element())\n                .map(|e| (e.default_namespace_uri(), e.namespaces_in_scope()))\n            {\n                let exts = exts\n                    .iter()\n                    .filter(|e| e.prefix() != \"xml\")\n                    .map(super::ext::WxsDependency::from)\n                    .collect();\n\n                let wix_version = match default {\n                    Some(LEGACY_NAMESPACE_URI) => WxsSchema::Legacy,\n                    Some(V4_NAMESPACE_URI) => WxsSchema::V4,\n                    _ => WxsSchema::Unsupported,\n                };\n\n                // Check if this .wxs file is a \"package\"\n                let is_package = match default {\n                    Some(ns) => {\n                        let mut context = Context::new();\n                        context.set_namespace(\"w\", ns);\n                        let factory = Factory::new();\n                        if let Ok(Some(xpath)) = factory.build(WIX_PACKAGE_ROOT_ELEMENT_XPATH) {\n                            let value = xpath.evaluate(&context, document.root())?;\n                            matches!(value, Value::Nodeset(ns) if ns.size() > 0)\n                        } else {\n                            false\n                        }\n                    }\n                    _ => false,\n                };\n                Ok(WixSource {\n                    is_package,\n                    wxs_schema: wix_version,\n                    path,\n                    exts,\n                    toolset: if matches!(wix_version, WxsSchema::Legacy) {\n                        Toolset::Legacy\n                    } else {\n                        Toolset::Modern\n                    },\n                })\n            } else {\n                Err(\"Corrupted .wxs file\".into())\n            }\n        }\n        _ => Err(\"Invalid .wxs file\".into()),\n    }\n}\n\n/// Context wix project related information such as the current toolset version, *.wxs files, and installed extension packages in scope\n#[derive(Debug)]\npub struct Project {\n    /// Current version of `wix` command\n    wix_version: semver::Version,\n    /// Paths to all wxs sources\n    wxs_sources: BTreeMap<PathBuf, WixSource>,\n    /// Extension package cache\n    package_cache: PackageCache,\n    /// Toolset\n    toolset: Toolset,\n}\n\nimpl Project {\n    /// Tries to create a new WiX project context\n    ///\n    /// Returns an error if the modern wix toolset is not installed\n    #[inline]\n    pub fn try_new(toolset: Toolset) -> crate::Result<Self> {\n        let wix_version_output = toolset.wix(\"--version\")?.output()?;\n\n        if wix_version_output.status.success() && !wix_version_output.stdout.is_empty() {\n            let output = String::from_utf8(wix_version_output.stdout)?;\n\n            let version = semver::Version::parse(output.trim())?;\n\n            let mut upgrade = Self {\n                wix_version: version,\n                wxs_sources: BTreeMap::new(),\n                package_cache: PackageCache::from(toolset.clone()),\n                toolset,\n            };\n\n            upgrade.load_ext_cache()?;\n            Ok(upgrade)\n        } else {\n            Err(\"wix.exe could not be found from PATH. Ensure that WiX4+ is installed.\".into())\n        }\n    }\n\n    /// Configures a toolset command from project state\n    #[inline]\n    pub fn configure_toolset_extensions(&self, toolset: &mut ToolsetCommand) -> crate::Result<()> {\n        // Find all Wix extensions being used\n        let ext_flags = self\n            .sources()\n            .flat_map(|s| s.exts.iter().map(|e| e.package_name()))\n            .fold(BTreeSet::new(), |mut acc, p| {\n                acc.insert(p);\n                acc\n            });\n\n        // Apply all required `-ext` flags to toolset command\n        for ext in ext_flags.iter() {\n            debug!(\"Adding WiX extension flag: -ext {}\", ext);\n            toolset.args([\"-ext\", ext]);\n        }\n\n        Ok(())\n    }\n\n    /// Adds a *.wxs source to the upgrade context\n    ///\n    /// Analyzes the *.wxs file to determine if the source requires conversion\n    ///\n    /// Returns an error if the *.wxs file is not valid XML\n    #[inline]\n    pub fn add_wxs(&mut self, source: PathBuf) -> crate::Result<()> {\n        if let std::collections::btree_map::Entry::Vacant(e) =\n            self.wxs_sources.entry(source.clone())\n        {\n            debug!(\"Opening and parsing wxs source file to insert into project\");\n            let mut wix_source = open_wxs_source(source)?;\n            wix_source.toolset = self.toolset.clone();\n            debug!(\"Inserting wix_source={wix_source:?}\");\n            e.insert(wix_source);\n        }\n        Ok(())\n    }\n\n    /// Converts all of the source files that are part of the upgrade\n    ///\n    /// If a target directory is provided, none of the original source files will be updated.\n    /// Returns an error if multiple source files share the same file name under different\n    /// directories, since the work_dir copy would cause collisions.\n    #[inline]\n    pub fn upgrade(&mut self, work_dir: Option<&PathBuf>) -> crate::Result<()> {\n        if work_dir.is_some() {\n            // Check for duplicate file names across different directories\n            let mut seen: BTreeMap<std::ffi::OsString, &std::path::Path> = BTreeMap::new();\n            for path in self.wxs_sources.keys() {\n                if let Some(name) = path.file_name() {\n                    if let Some(prev) = seen.get(name) {\n                        return Err(crate::Error::Generic(format!(\n                            \"Multiple WiX source files share the file name '{}' \\\n                             (found in '{}' and '{}'). The migrate command does not support \\\n                             duplicate file names across different directories. Please rename \\\n                             one of the files before running migrate.\",\n                            name.to_string_lossy(),\n                            prev.display(),\n                            path.display(),\n                        )));\n                    }\n                    seen.insert(name.to_os_string(), path.as_path());\n                }\n            }\n        }\n\n        let mut converted = BTreeMap::new();\n        for (path, src) in self.wxs_sources.iter().collect::<Vec<_>>() {\n            if src.can_upgrade() {\n                log::debug!(\"Upgrading {path:?}\");\n                // Upgrade will not modify the original file if a work_dir is provided\n                let converted_src = src.upgrade(work_dir)?;\n\n                // Finds missing dependencies in the package cache\n                converted_src.check_deps(&mut self.package_cache);\n\n                // If target_dir is enabled, conversion will not modify the original files and will instead\n                // convert and copy the files to the target_dir\n                converted.insert(converted_src.path.clone(), converted_src);\n            } else {\n                log::debug!(\"Skipping upgrade for {path:?}\");\n                if src.is_modern() {\n                    // Finds missing dependencies in the package cache\n                    src.check_deps(&mut self.package_cache);\n                }\n            }\n        }\n\n        // Update the state of the current set of sources\n        self.wxs_sources = converted;\n        Ok(())\n    }\n\n    /// Restores any missing extensions\n    #[inline]\n    pub fn restore(&mut self, use_global: bool, work_dir: Option<&PathBuf>) -> crate::Result<()> {\n        self.package_cache\n            .install_missing(use_global, self.wix_version.clone(), work_dir)?;\n        Ok(())\n    }\n\n    /// Returns an iterator over wix sources\n    #[inline]\n    pub fn sources(&self) -> impl Iterator<Item = &WixSource> {\n        self.wxs_sources.values()\n    }\n\n    /// Returns the number of package sources (wxs files that define a `<Package>`)\n    #[inline]\n    pub fn package_count(&self) -> usize {\n        self.wxs_sources.values().filter(|s| s.is_package).count()\n    }\n\n    /// Determines whether the project produces a bundle (.exe) or a package (.msi)\n    ///\n    /// If any source uses the bootstrapper applications (bal) namespace, this is a bundle.\n    /// Otherwise it defaults to an MSI package.\n    #[inline]\n    pub fn is_bundle(&self) -> bool {\n        self.wxs_sources.values().any(|s| s.is_bundle())\n    }\n\n    /// Returns the installer kind for this project (Exe for bundles, Msi otherwise)\n    #[inline]\n    pub fn installer_kind(&self) -> InstallerKind {\n        if self.is_bundle() {\n            InstallerKind::Exe\n        } else {\n            InstallerKind::Msi\n        }\n    }\n\n    /// Load installed ext cache\n    #[inline]\n    fn load_ext_cache(&mut self) -> crate::Result<()> {\n        fn build_package_cache(\n            output: std::process::Output,\n            cache: &mut PackageCache,\n        ) -> crate::Result<()> {\n            if output.status.success() {\n                let std_out = String::from_utf8(output.stdout)?;\n\n                for (package_name, version) in std_out\n                    .lines()\n                    // If the current wix version doesn't match the extension that is installed, it will append \"(damaged)\"\n                    .filter(|l| !l.trim().ends_with(\"(damaged)\"))\n                    .filter_map(|l| l.split_once(' '))\n                {\n                    if let Ok(version) = semver::Version::parse(version) {\n                        cache.add(package_name, version);\n                    }\n                }\n\n                Ok(())\n            } else {\n                Err(\"Could not load installed WiX extensions\".into())\n            }\n        }\n\n        match self.toolset.wix(\"extension list\")?.output() {\n            Ok(wix_ext_list) => {\n                build_package_cache(wix_ext_list, &mut self.package_cache)?;\n            }\n            Err(err) => {\n                // If this returns an error check to see if a local extension cache exists\n                // Docs: https://docs.firegiant.com/wix/development/wips/6184-command-line-extension-acquisition-and-cache/\n                // The path is always .wix\\extensions in the current directory\n                if std::env::current_dir()?\n                    .join(\".wix\")\n                    .join(\"extensions\")\n                    .exists()\n                {\n                    log::error!(\"Could not list extensions {err}\");\n                    return Err(\"Listing local extensions failed\".into());\n                }\n            }\n        }\n\n        let wix_ext_list_global = self.toolset.wix(\"extension list --global\")?.output()?;\n        build_package_cache(wix_ext_list_global, &mut self.package_cache)?;\n\n        Ok(())\n    }\n}\n\n#[cfg(test)]\npub(crate) mod tests {\n    use assert_fs::TempDir;\n    use cargo_metadata::Package;\n\n    use super::Project;\n    use crate::{\n        tests::setup_project,\n        toolset::{\n            Includes, ProjectProvider, Toolset, ToolsetAction,\n            ext::{WellKnownExtentions, WixExtension},\n            project::{WxsSchema, open_wxs_source},\n            source::WixSource,\n            test::{self, ok_stdout},\n        },\n    };\n    use serial_test::serial;\n    use std::{collections::BTreeSet, path::PathBuf};\n\n    #[test]\n    fn test_open_wxs() {\n        let main = open_wxs_source(PathBuf::from(\"./tests/common/post_v4/main.wxs\"))\n            .expect(\"must be able to open wxs file\");\n        assert!(main.is_package);\n\n        let fragment = open_wxs_source(PathBuf::from(\"./tests/common/post_v4/fragment.wxs\"))\n            .expect(\"must be able to open wxs file\");\n        assert!(!fragment.is_package);\n    }\n\n    #[test]\n    fn test_project_create() {\n        let shim = test::toolset(|a: &ToolsetAction, _: &std::process::Command| match a {\n            ToolsetAction::ListExtension => ok_stdout(\"WixToolset.PowerShell.wixext 0.0.0\"),\n            ToolsetAction::ListGlobalExtension => ok_stdout(\"WixToolset.VisualStudio.wixext 0.0.0\"),\n            ToolsetAction::Version => ok_stdout(\"0.0.0\"),\n            _ => {\n                unreachable!(\"Should only be executing version and list actions\")\n            }\n        });\n\n        let project = Project::try_new(shim).unwrap();\n        assert!(\n            project\n                .package_cache\n                .installed(&WellKnownExtentions::Powershell)\n        );\n        assert!(project.package_cache.installed(&WellKnownExtentions::VS));\n    }\n\n    #[test]\n    fn test_project_upgrade() {\n        let test_toolset = TestProject::new(stringify!(test_project_upgrade), \"post_v4\");\n        let mut project = test_toolset.create_project(&test_toolset.package).unwrap();\n        project\n            .upgrade(test_toolset.work_dir().as_ref())\n            .expect(\"should be able to convert\");\n\n        let missing = project\n            .package_cache\n            .iter_missing()\n            .next()\n            .expect(\"should have a missing package\");\n        assert_eq!(\"WixToolset.UI.wixext\", missing);\n    }\n\n    #[test]\n    fn test_project_upgrade_extension_detection() {\n        let test_toolset = TestProject::new(\n            stringify!(test_project_upgrade_extension_detection),\n            \"well_known_exts\",\n        );\n        let mut project = test_toolset.create_project(&test_toolset.package).unwrap();\n        project\n            .upgrade(test_toolset.work_dir().as_ref())\n            .expect(\"should be able to convert\");\n\n        let test_wxs = test_toolset\n            .work_dir()\n            .unwrap()\n            .join(\"main.test_project_upgrade_extension_detection.wxs\");\n        let wxs_source = project\n            .wxs_sources\n            .get(&test_wxs)\n            .expect(\"should have been added to the project\");\n\n        assert_eq!(WxsSchema::V4, wxs_source.wxs_schema);\n        assert_eq!(test_wxs, wxs_source.path);\n        assert!(wxs_source.toolset.is_modern());\n        validate_wxs_ext(wxs_source, WellKnownExtentions::BootstrapperApplications);\n        validate_wxs_ext(wxs_source, WellKnownExtentions::ComPlus);\n        validate_wxs_ext(wxs_source, WellKnownExtentions::Dependency);\n        validate_wxs_ext(wxs_source, WellKnownExtentions::DirectX);\n        validate_wxs_ext(wxs_source, WellKnownExtentions::Firewall);\n        validate_wxs_ext(wxs_source, WellKnownExtentions::Http);\n        validate_wxs_ext(wxs_source, WellKnownExtentions::Iis);\n        validate_wxs_ext(wxs_source, WellKnownExtentions::Msmq);\n        validate_wxs_ext(wxs_source, WellKnownExtentions::Netfx);\n        validate_wxs_ext(wxs_source, WellKnownExtentions::Powershell);\n        validate_wxs_ext(wxs_source, WellKnownExtentions::Sql);\n        validate_wxs_ext(wxs_source, WellKnownExtentions::UI);\n        validate_wxs_ext(wxs_source, WellKnownExtentions::Util);\n        validate_wxs_ext(wxs_source, WellKnownExtentions::VS);\n    }\n\n    pub fn validate_wxs_ext(source: &WixSource, ext: impl WixExtension) {\n        assert!(\n            source\n                .exts\n                .iter()\n                .find(|e| e.package_name() == ext.package_name()\n                    && e.namespace_prefix() == ext.namespace_prefix()\n                    && e.namespace_uri() == ext.namespace_uri())\n                .is_some()\n        );\n    }\n\n    pub struct TestProject {\n        test_dir: TempDir,\n        expected_wxs_name: &'static str,\n        includes: Vec<PathBuf>,\n        pub(crate) package: Package,\n    }\n    const MIN_MANIFEST: &str = r#\"[package]\n            name = \"Example\"\n            version = \"0.1.0\"\n            authors = [\"First Last <first.last@example.com>\"]\n        \"#;\n\n    impl TestProject {\n        /// Creates a new test toolset\n        pub fn new(test_name: &str, expected_wxs_name: &'static str) -> Self {\n            let project = setup_project(MIN_MANIFEST);\n            let manifest = crate::manifest(Some(&project.path().join(\"Cargo.toml\"))).unwrap();\n            let package = crate::package(&manifest, None).unwrap();\n            let test_dir = project.path().to_path_buf();\n            let test_src_file_name = format!(\"main.{test_name}.wxs\");\n            let test_src = test_dir.join(test_src_file_name);\n            std::fs::copy(\n                PathBuf::from(\"tests\")\n                    .join(\"common\")\n                    .join(\"pre_v4\")\n                    .join(\"main.wxs\"),\n                &test_src,\n            )\n            .unwrap();\n            Self {\n                test_dir: project,\n                expected_wxs_name,\n                includes: vec![test_src],\n                package,\n            }\n        }\n    }\n\n    impl Includes for TestProject {\n        fn includes(&self) -> Option<&Vec<PathBuf>> {\n            Some(&self.includes)\n        }\n    }\n\n    impl ProjectProvider for TestProject {\n        fn work_dir(&self) -> Option<PathBuf> {\n            Some(self.test_dir.path().to_path_buf().clone())\n        }\n\n        fn toolset(&self) -> Toolset {\n            const PACKAGES: &[&str] = &[\n                \"WixToolset.BootstrapperApplications.wixext/0.0.0\",\n                \"WixToolset.ComPlus.wixext/0.0.0\",\n                \"WixToolset.Dependency.wixext/0.0.0\",\n                \"WixToolset.DirectX.wixext/0.0.0\",\n                \"WixToolset.Firewall.wixext/0.0.0\",\n                \"WixToolset.Http.wixext/0.0.0\",\n                \"WixToolset.Iis.wixext/0.0.0\",\n                \"WixToolset.Msmq.wixext/0.0.0\",\n                \"WixToolset.Netfx.wixext/0.0.0\",\n                \"WixToolset.PowerShell.wixext/0.0.0\",\n                \"WixToolset.Sql.wixext/0.0.0\",\n                \"WixToolset.UI.wixext/0.0.0\",\n                \"WixToolset.Util.wixext/0.0.0\",\n                \"WixToolset.VisualStudio.wixext/0.0.0\",\n            ];\n\n            let expected_wxs_name = self.expected_wxs_name;\n            // Define test shim to do the \"conversion\" which is copying over a pre-baked converted file\n            test::toolset(\n                move |a: &ToolsetAction, cmd: &std::process::Command| match a {\n                    ToolsetAction::Convert => {\n                        let args = cmd.get_args();\n                        let dest = args.last().expect(\"should be the dest\");\n\n                        std::fs::copy(\n                            PathBuf::from(\"tests\")\n                                .join(\"common\")\n                                .join(expected_wxs_name)\n                                .join(\"main.wxs\"),\n                            PathBuf::from(dest),\n                        )\n                        .unwrap();\n                        ok_stdout(\"\")\n                    }\n                    ToolsetAction::AddGlobalExtension => {\n                        let args = cmd.get_args().map(|a| a.to_string_lossy().to_string());\n                        let args = BTreeSet::from_iter(args);\n                        assert!(PACKAGES.iter().all(|p| args.contains(*p)));\n                        ok_stdout(\"\")\n                    }\n                    ToolsetAction::AddExtension => {\n                        let args = cmd.get_args().map(|a| a.to_string_lossy().to_string());\n                        let args = BTreeSet::from_iter(args);\n                        assert!(PACKAGES.iter().all(|p| args.contains(*p)));\n                        ok_stdout(\"\")\n                    }\n                    ToolsetAction::ListExtension => ok_stdout(\"\"),\n                    ToolsetAction::ListGlobalExtension => ok_stdout(\"\"),\n                    ToolsetAction::Version => ok_stdout(\"0.0.0\"),\n                    a => {\n                        unreachable!(\"Unexpected action, tried to execute {a:?}\")\n                    }\n                },\n            )\n        }\n    }\n\n    #[test]\n    fn test_project_package_count() {\n        let test_toolset = TestProject::new(stringify!(test_project_package_count), \"post_v4\");\n        let project = test_toolset.create_project(&test_toolset.package).unwrap();\n        // The includes list only has the pre_v4 source (no <Package> element)\n        assert_eq!(0, project.package_count());\n    }\n\n    #[test]\n    fn test_project_installer_kind_msi() {\n        let test_toolset = TestProject::new(stringify!(test_project_installer_ext_msi), \"post_v4\");\n        let project = test_toolset.create_project(&test_toolset.package).unwrap();\n        // post_v4 has no bal namespace, so should produce msi\n        assert_eq!(crate::create::InstallerKind::Msi, project.installer_kind());\n        assert!(!project.is_bundle());\n    }\n\n    #[test]\n    fn test_project_installer_extension_from_well_known_exts() {\n        // Open well_known_exts directly (which has bal namespace) and verify is_bundle\n        let source = open_wxs_source(PathBuf::from(\"./tests/common/well_known_exts/main.wxs\"))\n            .expect(\"must be able to open wxs file\");\n        assert!(\n            source.is_bundle(),\n            \"well_known_exts has bal namespace and should be a bundle\"\n        );\n    }\n\n    #[test]\n    #[serial]\n    fn test_upgrade_duplicate_filenames_errors() {\n        let project = setup_project(MIN_MANIFEST);\n        let manifest = crate::manifest(Some(&project.path().join(\"Cargo.toml\"))).unwrap();\n        let package = crate::package(&manifest, None).unwrap();\n        let test_dir = project.path().to_path_buf();\n\n        // Create two wxs files with the same name under different subdirs\n        let sub_a = test_dir.join(\"a\");\n        let sub_b = test_dir.join(\"b\");\n        std::fs::create_dir_all(&sub_a).unwrap();\n        std::fs::create_dir_all(&sub_b).unwrap();\n\n        let src_a = sub_a.join(\"main.wxs\");\n        let src_b = sub_b.join(\"main.wxs\");\n        std::fs::copy(\n            PathBuf::from(\"tests\")\n                .join(\"common\")\n                .join(\"pre_v4\")\n                .join(\"main.wxs\"),\n            &src_a,\n        )\n        .unwrap();\n        std::fs::copy(\n            PathBuf::from(\"tests\")\n                .join(\"common\")\n                .join(\"pre_v4\")\n                .join(\"main.wxs\"),\n            &src_b,\n        )\n        .unwrap();\n\n        struct DupProvider {\n            includes: Vec<PathBuf>,\n            work_dir: PathBuf,\n        }\n        impl Includes for DupProvider {\n            fn includes(&self) -> Option<&Vec<PathBuf>> {\n                Some(&self.includes)\n            }\n        }\n        impl ProjectProvider for DupProvider {\n            fn work_dir(&self) -> Option<PathBuf> {\n                Some(self.work_dir.clone())\n            }\n            fn toolset(&self) -> Toolset {\n                test::toolset(|a: &ToolsetAction, _: &std::process::Command| match a {\n                    ToolsetAction::ListExtension => ok_stdout(\"\"),\n                    ToolsetAction::ListGlobalExtension => ok_stdout(\"\"),\n                    ToolsetAction::Version => ok_stdout(\"0.0.0\"),\n                    _ => ok_stdout(\"\"),\n                })\n            }\n        }\n\n        let provider = DupProvider {\n            includes: vec![src_a, src_b],\n            work_dir: test_dir.clone(),\n        };\n        let mut proj = provider.create_project(&package).unwrap();\n        let result = proj.upgrade(Some(&test_dir));\n        assert!(\n            result.is_err(),\n            \"Should error on duplicate file names with work_dir\"\n        );\n        let err = format!(\"{}\", result.unwrap_err());\n        assert!(\n            err.contains(\"duplicate file names\"),\n            \"Error message should mention duplicate file names, got: {err}\"\n        );\n    }\n\n    #[test]\n    fn test_project_package_count_multiple() {\n        // Create a project with two wxs files that both define <Package>\n        let test_toolset = TestProject::new(stringify!(test_project_pkg_count_multi), \"post_v4\");\n        let mut project = test_toolset.create_project(&test_toolset.package).unwrap();\n\n        // Add two sources that have <Package>\n        let first_pkg = open_wxs_source(PathBuf::from(\"./tests/common/post_v4/main.wxs\"))\n            .expect(\"must be able to open wxs file\");\n        assert!(first_pkg.is_package);\n        let second_pkg = open_wxs_source(PathBuf::from(\"./tests/common/post_v4/main.wxs\"))\n            .expect(\"must be able to open wxs file\");\n        assert!(second_pkg.is_package);\n\n        project\n            .wxs_sources\n            .insert(PathBuf::from(\"first_main.wxs\"), first_pkg);\n        project\n            .wxs_sources\n            .insert(PathBuf::from(\"second_main.wxs\"), second_pkg);\n        assert_eq!(2, project.package_count());\n    }\n\n    #[test]\n    fn test_project_fragment_only_has_zero_packages() {\n        // A project with only fragment sources should have package_count == 0\n        let test_toolset = TestProject::new(stringify!(test_project_fragment_only), \"post_v4\");\n        let mut project = test_toolset.create_project(&test_toolset.package).unwrap();\n\n        // Replace all sources with just a fragment (no <Package>)\n        project.wxs_sources.clear();\n        let fragment = open_wxs_source(PathBuf::from(\"./tests/common/post_v4/fragment.wxs\"))\n            .expect(\"must be able to open wxs file\");\n        assert!(!fragment.is_package, \"fragment.wxs should not be a package\");\n        project\n            .wxs_sources\n            .insert(PathBuf::from(\"fragment.wxs\"), fragment);\n\n        assert_eq!(0, project.package_count());\n        assert!(!project.is_bundle());\n        // This is the condition that triggers the \"no Package or Bundle\" error in create.rs\n        assert_eq!(crate::create::InstallerKind::Msi, project.installer_kind());\n    }\n\n    #[test]\n    fn test_project_bundle_installer_kind() {\n        // A project with a bal-namespace source should return InstallerKind::Exe\n        let test_toolset = TestProject::new(stringify!(test_project_bundle_kind), \"post_v4\");\n        let mut project = test_toolset.create_project(&test_toolset.package).unwrap();\n\n        // Replace sources with well_known_exts which has bal namespace\n        project.wxs_sources.clear();\n        let bundle = open_wxs_source(PathBuf::from(\"./tests/common/well_known_exts/main.wxs\"))\n            .expect(\"must be able to open wxs file\");\n        project\n            .wxs_sources\n            .insert(PathBuf::from(\"bundle.wxs\"), bundle);\n\n        assert!(project.is_bundle());\n        assert_eq!(crate::create::InstallerKind::Exe, project.installer_kind());\n    }\n}\n"
  },
  {
    "path": "src/toolset/source.rs",
    "content": "// Copyright (C) 2017 Christopher R. Field.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nuse log::{debug, error, trace, warn};\n\nuse super::Toolset;\nuse super::ext::{PackageCache, WxsDependency};\nuse super::project::{WxsSchema, open_wxs_source};\nuse std::ffi::OsStr;\nuse std::path::PathBuf;\n\n/// Struct containing information about a wxs source file\npub struct WixSource {\n    /// WiX toolset version\n    pub(super) wxs_schema: WxsSchema,\n    /// Path to this *.wxs file\n    pub(super) path: PathBuf,\n    /// Extensions this wix source is dependent on\n    pub(super) exts: Vec<WxsDependency>,\n    /// Toolset this source is using\n    pub(super) toolset: Toolset,\n    /// True if this source defines a package\n    pub(super) is_package: bool,\n}\n\nimpl WixSource {\n    /// Returns true if the format of this *.wxs source can be upgraded\n    pub fn can_upgrade(&self) -> bool {\n        match (self.wxs_schema, &self.toolset) {\n            (WxsSchema::Legacy, Toolset::Modern) => true,\n            #[cfg(test)]\n            (\n                WxsSchema::Legacy,\n                Toolset::Test {\n                    is_legacy: false, ..\n                },\n            ) => true,\n            _ => false,\n        }\n    }\n\n    /// Returns true if this source is in the modern format\n    ///\n    /// This is relevant because in the modern formats, extensions are namespaced. Knowing\n    /// if the wxs format is \"modern\" indicates that extensions can be derived programatically.\n    pub fn is_modern(&self) -> bool {\n        matches!(self.wxs_schema, WxsSchema::V4)\n    }\n\n    /// Returns true if this source uses the bootstrapper applications (bal) namespace,\n    /// indicating it produces a bundle (.exe) rather than an MSI package.\n    ///\n    /// The `bal` (WixBalExtension) namespace is required for bootstrapper bundles,\n    /// so its presence reliably indicates a bundle.\n    pub fn is_bundle(&self) -> bool {\n        self.exts.iter().any(|e| e.namespace_prefix() == \"bal\")\n    }\n\n    /// Checks that the dependencies required by this *.wxs file exist in the package cache\n    pub fn check_deps(&self, package_cache: &mut PackageCache) {\n        for ext in self\n            .exts\n            .iter()\n            .filter(|e| !package_cache.installed(*e))\n            .collect::<Vec<_>>()\n        {\n            // Package names are known ahead of time because they map to a well known extension uri\n            // If a package name returns as empty, it means that tooling is not aware of it\n            if !ext.package_name().is_empty() {\n                debug!(\n                    \"Missing extension, xmlns:{}='{}'\",\n                    ext.namespace_prefix(),\n                    ext.namespace_uri()\n                );\n                package_cache.add_missing(ext.package_name());\n            } else {\n                warn!(\n                    \"Unknown extension, xmlns:{}='{}'\",\n                    ext.namespace_prefix(),\n                    ext.namespace_uri()\n                );\n            }\n        }\n    }\n\n    /// Upgrades the current wix source file using `wix convert` if applicable\n    ///\n    /// Returns an updated WixSource object if the conversion and dependent ext install is successful\n    pub fn upgrade(&self, work_dir: Option<&PathBuf>) -> crate::Result<Self> {\n        let mut convert = self.toolset.wix(\"convert\")?;\n        let converted_path = if work_dir.is_some() {\n            // If a work dir is specified, do not modify the input file directly\n            let temp = std::env::temp_dir().join(\n                self.path\n                    .file_name()\n                    .expect(\"should have a file name because requires opening to create type\"),\n            );\n            std::fs::copy(&self.path, &temp)?;\n            convert.arg(&temp);\n            temp\n        } else {\n            convert.arg(&self.path);\n            self.path.clone()\n        };\n\n        let content = std::fs::read_to_string(&converted_path)?;\n        let has_bom = content.starts_with('\\u{FEFF}');\n\n        let output = convert.output()?;\n\n        if !output.status.success() {\n            // This is expected, `wix convert`` decided to always return a non-zero exit code because\n            // it will output a log of the changes it made\n            if log::log_enabled!(log::Level::Debug) && !output.stderr.is_empty() {\n                let std_err = String::from_utf8(output.stderr.clone())?;\n                for line in std_err.lines() {\n                    debug!(\"{line}\");\n                }\n            }\n        }\n\n        // The converted_path must be a valid file name\n        let converted_path =\n            if let Some((work_dir, file_name)) = work_dir.zip(converted_path.file_name()) {\n                let dest = work_dir.join(file_name);\n                std::fs::copy(converted_path, &dest)?;\n                dest\n            } else {\n                converted_path\n            };\n        if !has_bom {\n            // Strip the BOM if the previous file did not have a BOM\n            let content = std::fs::read_to_string(&converted_path)?;\n            let has_bom = content.starts_with('\\u{FEFF}');\n            if has_bom {\n                debug!(\n                    \"Detected BOM, previous file did not have a BOM, removing to preserve tooling\"\n                );\n                std::fs::write(&converted_path, content.trim_start_matches('\\u{FEFF}'))?;\n            }\n        }\n        open_wxs_source(converted_path)\n    }\n\n    pub fn try_move_to_installer_destination(\n        &self,\n        name: &str,\n        version: &str,\n        cfg: &rustc_cfg::Cfg,\n        debug_name: bool,\n        target_directory: &std::path::Path,\n        output: Option<&String>,\n    ) -> crate::Result<()> {\n        if !self.is_package {\n            return Ok(());\n        }\n\n        // Won't know the extension until we scan for the file\n        // Capture what we need to do to create the filename\n        // TODO: This would have been easier to just use PathBuf::add_extension, but it requires 1.91\n        let file_name_with_ext = |ext: &str| -> PathBuf {\n            let name = if self.is_main() {\n                name\n            } else {\n                self.path\n                    .file_stem()\n                    .and_then(|f| f.to_str())\n                    .unwrap_or(name)\n            };\n            let filename = if debug_name {\n                format!(\"{}-{}-{}-debug.{}\", name, version, cfg.target_arch, ext)\n            } else {\n                format!(\"{}-{}-{}.{}\", name, version, cfg.target_arch, ext)\n            };\n            if let Some(path_str) = output {\n                trace!(\"Using the explicitly specified output path for the MSI destination\");\n                let path = std::path::Path::new(path_str);\n                if path_str.ends_with('/') || path_str.ends_with('\\\\') || path.is_dir() {\n                    path.join(filename)\n                } else {\n                    path.to_owned()\n                }\n            } else {\n                trace!(\n                    \"Using the package's manifest (Cargo.toml) file path to specify the MSI destination\"\n                );\n                target_directory.join(crate::WIX).join(filename)\n            }\n        };\n\n        let mut path = self.path.clone();\n        for output_type in [\"msi\", \"exe\"] {\n            path.set_extension(output_type);\n            if path.exists() {\n                let filename = file_name_with_ext(output_type);\n                std::fs::rename(&path, &filename)?;\n                debug!(\"Moving {path:?} to {filename:?}\");\n                return Ok(());\n            }\n        }\n\n        error!(\"Expected {:?} to have output files\", self.path);\n        Err(\"Could not find package output for source\".into())\n    }\n\n    fn is_main(&self) -> bool {\n        self.path.file_stem() == Some(OsStr::new(\"main\"))\n    }\n}\n\nimpl std::fmt::Debug for WixSource {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"WixSource\")\n            .field(\"wxs_schema\", &self.wxs_schema)\n            .field(\"path\", &self.path)\n            .finish()\n    }\n}\n\nmod tests {\n\n    #[test]\n    fn test_source_can_upgrade() {\n        use crate::toolset::{project::WxsSchema, source::WixSource};\n        use std::path::PathBuf;\n\n        assert!(\n            !WixSource {\n                is_package: false,\n                wxs_schema: WxsSchema::Legacy,\n                path: PathBuf::new(),\n                exts: vec![],\n                toolset: crate::toolset::Toolset::Legacy\n            }\n            .can_upgrade(),\n            \"should not be able to upgrade legacy w/ legacy toolset\"\n        );\n\n        assert!(\n            WixSource {\n                is_package: false,\n                wxs_schema: WxsSchema::Legacy,\n                path: PathBuf::new(),\n                exts: vec![],\n                toolset: crate::toolset::Toolset::Modern\n            }\n            .can_upgrade(),\n            \"should be able to upgrade legacy w/ modern toolset\"\n        );\n\n        assert!(\n            !WixSource {\n                is_package: false,\n                wxs_schema: WxsSchema::V4,\n                path: PathBuf::new(),\n                exts: vec![],\n                toolset: crate::toolset::Toolset::Modern\n            }\n            .can_upgrade(),\n            \"should not be able to upgrade from v4\"\n        );\n    }\n\n    #[test]\n    fn test_skip_add_unknown_ext_to_package_cache() {\n        use super::WixSource;\n        use crate::toolset::ext::{PackageCache, UnknownExtNamespace};\n        use std::path::PathBuf;\n\n        let source = WixSource {\n            is_package: true,\n            wxs_schema: crate::toolset::project::WxsSchema::V4,\n            path: PathBuf::new(),\n            exts: vec![Box::new(UnknownExtNamespace {\n                prefix: String::from(\"test\"),\n                uri: String::from(\"test_uri\"),\n            })],\n            toolset: crate::toolset::Toolset::Modern,\n        };\n\n        let mut package_cache = PackageCache::from(crate::toolset::Toolset::Modern);\n        source.check_deps(&mut package_cache);\n\n        assert_eq!(0, package_cache.iter_missing().count());\n    }\n\n    #[test]\n    fn test_source_is_bundle() {\n        use super::WixSource;\n        use crate::toolset::ext::WellKnownExtentions;\n        use std::path::PathBuf;\n\n        let source_with_bal = WixSource {\n            is_package: true,\n            wxs_schema: crate::toolset::project::WxsSchema::V4,\n            path: PathBuf::new(),\n            exts: vec![Box::new(WellKnownExtentions::BootstrapperApplications)],\n            toolset: crate::toolset::Toolset::Modern,\n        };\n        assert!(\n            source_with_bal.is_bundle(),\n            \"source with bal namespace should be a bundle\"\n        );\n\n        let source_without_bal = WixSource {\n            is_package: true,\n            wxs_schema: crate::toolset::project::WxsSchema::V4,\n            path: PathBuf::new(),\n            exts: vec![Box::new(WellKnownExtentions::UI)],\n            toolset: crate::toolset::Toolset::Modern,\n        };\n        assert!(\n            !source_without_bal.is_bundle(),\n            \"source without bal namespace should not be a bundle\"\n        );\n\n        let source_no_exts = WixSource {\n            is_package: true,\n            wxs_schema: crate::toolset::project::WxsSchema::V4,\n            path: PathBuf::new(),\n            exts: vec![],\n            toolset: crate::toolset::Toolset::Modern,\n        };\n        assert!(\n            !source_no_exts.is_bundle(),\n            \"source with no exts should not be a bundle\"\n        );\n    }\n}\n"
  },
  {
    "path": "src/toolset/test.rs",
    "content": "// Copyright (C) 2017 Christopher R. Field.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nuse super::{Toolset, ToolsetAction};\nuse std::{fmt::Debug, sync::Arc};\n\n/// Type-alias for a boxed test shim\npub type SharedTestShim = Arc<dyn TestShim + 'static>;\n\n/// Struct containing test output for a toolset command action\n#[cfg(test)]\n#[derive(Clone, Default)]\npub struct ToolsetTest {\n    pub success: bool,\n    pub stdout: String,\n    pub stderr: String,\n}\n\n/// Returns an ok status and the provided string as stdout\npub fn ok_stdout(out: impl Into<String>) -> ToolsetTest {\n    test::<true>(out, \"\")\n}\n\n/// Returns a failure status and the provided string as stdout\npub fn fail_stdout(out: impl Into<String>) -> ToolsetTest {\n    test::<false>(out, \"\")\n}\n\n/// Return a toolset test w/ stdout set\nfn test<const SUCCESS: bool>(out: impl Into<String>, err: impl Into<String>) -> ToolsetTest {\n    ToolsetTest {\n        success: SUCCESS,\n        stdout: out.into(),\n        stderr: err.into(),\n    }\n}\n\n/// This shim allows unit test code without requiring dependencies to be installed on the test machine\npub trait TestShim {\n    /// Called when `.output()` is called\n    fn on_output(&self, action: &ToolsetAction, cmd: &std::process::Command) -> ToolsetTest;\n}\n\n/// Boxes a test shim, is_legacy will return false\npub fn toolset(shim: impl TestShim + 'static) -> Toolset {\n    Toolset::Test {\n        shim: Arc::new(shim),\n        is_legacy: false,\n    }\n}\n\n/// Returns a test toolset with shim, is_legacy will return true\npub fn legacy_toolset(shim: impl TestShim + 'static) -> Toolset {\n    Toolset::Test {\n        shim: Arc::new(shim),\n        is_legacy: true,\n    }\n}\n\nimpl<T> TestShim for T\nwhere\n    T: Fn(&ToolsetAction, &std::process::Command) -> ToolsetTest + Clone + 'static,\n{\n    fn on_output(&self, action: &ToolsetAction, cmd: &std::process::Command) -> ToolsetTest {\n        match action {\n            ToolsetAction::Compile { .. } => {\n                let program = cmd.get_program().to_string_lossy().to_string();\n                assert!(\n                    program == \"candle\" || program.ends_with(\"candle.exe\"),\n                    \"expected program to be 'candle' or end with 'candle.exe', got '{program}'\"\n                );\n            }\n            ToolsetAction::Convert => {\n                assert_eq!(\"wix\", cmd.get_program());\n                assert_eq!([\"convert\"], &cmd.get_args().take(1).collect::<Vec<_>>()[..]);\n            }\n            ToolsetAction::Build => {\n                assert_eq!(\"wix\", cmd.get_program());\n                assert_eq!([\"build\"], &cmd.get_args().take(1).collect::<Vec<_>>()[..]);\n            }\n            ToolsetAction::AddExtension => {\n                assert_eq!(\"wix\", cmd.get_program());\n                assert_eq!(\n                    [\"extension\", \"add\"],\n                    &cmd.get_args().take(2).collect::<Vec<_>>()[..]\n                );\n            }\n            ToolsetAction::AddGlobalExtension => {\n                assert_eq!(\"wix\", cmd.get_program());\n                assert_eq!(\n                    [\"extension\", \"add\", \"--global\"],\n                    &cmd.get_args().take(3).collect::<Vec<_>>()[..]\n                );\n            }\n            ToolsetAction::ListExtension => {\n                assert_eq!(\"wix\", cmd.get_program());\n                assert_eq!(\n                    [\"extension\", \"list\"],\n                    &cmd.get_args().collect::<Vec<_>>()[..]\n                );\n            }\n            ToolsetAction::ListGlobalExtension => {\n                assert_eq!(\"wix\", cmd.get_program());\n                assert_eq!(\n                    [\"extension\", \"list\", \"--global\"],\n                    &cmd.get_args().collect::<Vec<_>>()[..]\n                );\n            }\n            ToolsetAction::Version => {\n                assert_eq!(\"wix\", cmd.get_program());\n                assert_eq!([\"--version\"], &cmd.get_args().collect::<Vec<_>>()[..]);\n            }\n        }\n        self(action, cmd)\n    }\n}\n\nimpl Debug for ToolsetTest {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"ToolsetTest\")\n            .field(\"success\", &self.success)\n            .field(\"stdout\", &self.stdout)\n            .field(\"stderr\", &self.stderr)\n            .finish()\n    }\n}\n"
  },
  {
    "path": "tests/common/mod.rs",
    "content": "#![allow(dead_code)]\n\nextern crate assert_fs;\nextern crate env_logger;\nextern crate log;\nextern crate sxd_document;\nextern crate sxd_xpath;\n\nuse assert_fs::prelude::*;\n\nuse self::sxd_document::parser;\nuse self::sxd_xpath::{Context, Factory};\n\nuse assert_fs::TempDir;\n\nuse env_logger::Builder;\nuse env_logger::fmt::style::{AnsiColor as LogColor, Style};\n\nuse log::{Level, LevelFilter};\n\nuse std::env;\nuse std::fs;\nuse std::fs::{File, OpenOptions};\nuse std::io::{Read, Write};\nuse std::path::Path;\nuse std::process::Command;\n\nuse toml::{Table, Value};\n\npub const TARGET_NAME: &str = \"target\";\n\n// Cannot use dashes. WiX Toolset only allows A-Z, a-z, digits, underscores (_), or periods (.)\n// for attribute IDs.\npub const PACKAGE_NAME: &str = \"cargowixtest\";\n\npub const NO_CAPTURE_VAR_NAME: &str = \"CARGO_WIX_TEST_NO_CAPTURE\";\n\npub const PERSIST_VAR_NAME: &str = \"CARGO_WIX_TEST_PERSIST\";\n\npub const MISC_NAME: &str = \"misc\";\n\npub const SUBPACKAGE1_NAME: &str = \"subproject1\";\npub const SUBPACKAGE2_NAME: &str = \"subproject2\";\n\nfn create_test_package_at_path(path: &Path, package_name: &str) {\n    let cargo_init_status = Command::new(\"cargo\")\n        .arg(\"init\")\n        .arg(\"--bin\")\n        .arg(\"--quiet\")\n        .arg(\"--vcs\")\n        .arg(\"none\")\n        .arg(\"--name\")\n        .arg(package_name)\n        .arg(path)\n        .status()\n        .expect(\"Creation of test Cargo package\");\n    assert!(cargo_init_status.success());\n    let cargo_path = path.join(\"Cargo.toml\");\n\n    let mut toml = {\n        let mut cargo_toml_handle = File::open(&cargo_path).unwrap();\n        let mut cargo_toml_content = String::new();\n        cargo_toml_handle\n            .read_to_string(&mut cargo_toml_content)\n            .unwrap();\n        cargo_toml_content.parse::<Table>().unwrap()\n    };\n    {\n        toml.get_mut(\"package\")\n            .map(|p| {\n                match p {\n                    Value::Table(t) => t.insert(\n                        String::from(\"authors\"),\n                        Value::Array(vec![Value::from(\"author1\"), Value::from(\"author2\")]),\n                    ),\n                    _ => panic!(\"The 'package' section is not a table\"),\n                };\n                Some(p)\n            })\n            .expect(\"A package section for the Cargo.toml\");\n        let toml_string = toml.to_string();\n        let mut cargo_toml_handle = File::create(cargo_path).unwrap();\n        cargo_toml_handle.write_all(toml_string.as_bytes()).unwrap();\n    }\n}\n\npub fn add_license_to_package(path: &Path, license: &str) {\n    let cargo_path = path.join(\"Cargo.toml\");\n\n    let mut toml = {\n        let mut cargo_toml_handle = File::open(&cargo_path).unwrap();\n        let mut cargo_toml_content = String::new();\n        cargo_toml_handle\n            .read_to_string(&mut cargo_toml_content)\n            .unwrap();\n        cargo_toml_content.parse::<Table>().unwrap()\n    };\n    {\n        toml.get_mut(\"package\")\n            .map(|p| {\n                match p {\n                    Value::Table(t) => t.insert(String::from(\"license\"), Value::from(license)),\n                    _ => panic!(\"The 'package' section is not a table\"),\n                };\n                Some(p)\n            })\n            .expect(\"A package section for the Cargo.toml\");\n        let toml_string = toml.to_string();\n        let mut cargo_toml_handle = File::create(cargo_path).unwrap();\n        cargo_toml_handle.write_all(toml_string.as_bytes()).unwrap();\n    }\n}\n\n/// Create a new cargo project/package for a binary project in a temporary\n/// directory.\n///\n/// This provides a unique, isolated Cargo project/package for testing. A\n/// temporary directory is created. Then, a cargo project is initialized within\n/// the temporary directory. The package/project is initialized without any\n/// version control system (vcs). The command that is ultimately executed to\n/// create the cargo project in the temporary directory is:\n///\n/// ```\n/// > cargo init --bin --quiet --vcs none --name cargowixtest \"C:\\Users\\<username>\\AppData\\Local\\Temp\\cargo_wix_text_######\"\n/// ```\n///\n/// where `<username>` is replaced with the current logged in user for the\n/// Windows Operating System (OS) and `######` is a hash ID that guarantees the\n/// folder is unique.\n///\n/// # Panics\n///\n/// This will panic if a temporary directory fails to be created or if cargo\n/// fails to create the project/package.\npub fn create_test_package() -> TempDir {\n    let temp_dir = TempDir::new().unwrap();\n    create_test_package_at_path(temp_dir.path(), PACKAGE_NAME);\n    temp_dir.into_persistent_if(env::var(PERSIST_VAR_NAME).is_ok())\n}\n\n/// Create a new cargo project/package for a project with multiple binaries in a\n/// temporary directory. See the [create_test_package] function for more\n/// information.\n///\n/// Following creation of the project, the manifest file (Cargo.toml) is\n/// modified to include multiple `[[bin]]` sections for multiple binaries. The\n/// original `main.rs` file that is created for the first binary is copied for\n/// each of the other binaries. A total of three (3) binaries will be created\n/// and added to the manifest file.\n///\n/// [create_test_package]: fn.create_test_package.html\n///\n/// # Panics\n///\n/// This will panic if a temporary directory fails to be created or if cargo\n/// fails to create the project/package.\n///\n/// It will also panic if it cannot modify the manifest file (Cargo.toml) or the\n/// project layout for multiple binaries.\npub fn create_test_package_multiple_binaries() -> TempDir {\n    let package = create_test_package();\n    let package_manifest = package.child(\"Cargo.toml\");\n    let package_src = package.child(\"src\");\n    {\n        let mut cargo_toml_handle = OpenOptions::new()\n            .read(true)\n            .append(true)\n            .open(package_manifest.path())\n            .unwrap();\n        cargo_toml_handle\n            .write_all(\n                r#\"[[bin]]\nname = \"main1\"\npath = \"src/main1.rs\"\n\n[[bin]]\nname = \"main2\"\npath = \"src/main2.rs\"\n\n[[bin]]\nname = \"main3\"\npath = \"src/main3.rs\"\n\"#\n                .as_bytes(),\n            )\n            .unwrap();\n    }\n    let package_original_main = package_src.child(\"main.rs\");\n    fs::copy(\n        package_original_main.path(),\n        package_src.child(\"main1.rs\").path(),\n    )\n    .unwrap();\n    fs::copy(\n        package_original_main.path(),\n        package_src.child(\"main2.rs\").path(),\n    )\n    .unwrap();\n    fs::copy(\n        package_original_main.path(),\n        package_src.child(\"main3.rs\").path(),\n    )\n    .unwrap();\n    fs::remove_file(package_original_main.path()).unwrap();\n    package\n}\n\n/// Create a new cargo project/package for a project with a\n/// `[package.metadata.wix]` section.\n///\n/// Following creation of the project, the manifest file (Cargo.toml) is\n/// modified to include a `[package.metadata.wix]` section.\n///\n/// # Panics\n///\n/// This will panic if a temporary directory fails to be created or if cargo\n/// fails to create the project/package.\n///\n/// It will also panic if it cannot modify the manifest file (Cargo.toml) or the\n/// project layout for multiple binaries.\npub fn create_test_package_metadata() -> TempDir {\n    let package = create_test_package();\n    let package_manifest = package.child(\"Cargo.toml\");\n    let mut cargo_toml_handle = OpenOptions::new()\n        .read(true)\n        .append(true)\n        .open(package_manifest.path())\n        .unwrap();\n    cargo_toml_handle\n        .write_all(\n            r#\"[package.metadata.wix]\nname = \"Metadata\"\nversion = \"2.1.0\"\ncompiler-args = [\"-nologo\", \"-wx\", \"-arch\", \"x64\"]\nlinker-args = [\"-nologo\"]\n\"#\n            .as_bytes(),\n        )\n        .unwrap();\n    package\n}\n\n/// Create a new cargo project/package for a project with a\n/// `[profile.{profile}]` section.\n///\n/// Following creation of the project, the manifest file (Cargo.toml) is\n/// modified to include a `[profile.{profile}]` section.\n///\n/// # Panics\n///\n/// This will panic if a temporary directory fails to be created or if cargo\n/// fails to create the project/package.\n///\n/// It will also panic if it cannot modify the manifest file (Cargo.toml) or the\n/// project layout for multiple binaries.\npub fn create_test_package_profile(profile: &str) -> TempDir {\n    let package = create_test_package();\n    let package_manifest = package.child(\"Cargo.toml\");\n    let mut cargo_toml_handle = OpenOptions::new()\n        .read(true)\n        .append(true)\n        .open(package_manifest.path())\n        .unwrap();\n    cargo_toml_handle\n        .write_all(\n            format!(\n                r#\"\n[profile.{profile}]\ninherits = \"release\"\nlto = \"thin\"\n\"#\n            )\n            .as_bytes(),\n        )\n        .unwrap();\n    package\n}\n\n/// Create a new cargo project/package for a project with multiple WXS files.\n///\n/// # Panics\n///\n/// This will panic if a temporary directory fails to be created or if cargo\n/// fails to create the project/package.\n///\n/// It will also panic if it cannot modify the manifest file (Cargo.toml) or the\n/// project layout for multiple binaries.\n///\n/// This function will panic if the `wix` sub-folder could not be created.\npub fn create_test_package_multiple_wxs_sources() -> TempDir {\n    let one_wxs = include_str!(\"one.wxs\");\n    let two_wxs = include_str!(\"two.wxs\");\n    let three_wxs = include_str!(\"three.wxs\");\n    let package = create_test_package();\n    let mut misc_dir = package.path().join(MISC_NAME);\n    fs::create_dir(&misc_dir).unwrap();\n    misc_dir.push(\"one.wxs\");\n    let mut one_wxs_handle = File::create(&misc_dir).unwrap();\n    one_wxs_handle.write_all(one_wxs.as_bytes()).unwrap();\n    misc_dir.pop();\n    misc_dir.push(\"two.wxs\");\n    let mut two_wxs_handle = File::create(&misc_dir).unwrap();\n    two_wxs_handle.write_all(two_wxs.as_bytes()).unwrap();\n    misc_dir.pop();\n    misc_dir.push(\"three.wxs\");\n    let mut three_wxs_handle = File::create(&misc_dir).unwrap();\n    three_wxs_handle.write_all(three_wxs.as_bytes()).unwrap();\n    package\n}\n\n/// Create a new cargo workspace with multiple sub-projects in a\n/// temporary directory. See the [create_test_package] function for more\n/// information.\npub fn create_test_workspace() -> TempDir {\n    let temp_dir = TempDir::new().unwrap();\n    fs::create_dir(temp_dir.path().join(SUBPACKAGE1_NAME)).unwrap();\n    fs::create_dir(temp_dir.path().join(SUBPACKAGE2_NAME)).unwrap();\n    create_test_package_at_path(&temp_dir.path().join(SUBPACKAGE1_NAME), SUBPACKAGE1_NAME);\n    create_test_package_at_path(&temp_dir.path().join(SUBPACKAGE2_NAME), SUBPACKAGE2_NAME);\n    fs::write(\n        temp_dir.path().join(\"Cargo.toml\"),\n        format!(\n            r#\"[workspace]\n            members = [{SUBPACKAGE1_NAME:?}, {SUBPACKAGE2_NAME:?}]\"#\n        ),\n    )\n    .unwrap();\n    temp_dir.into_persistent_if(env::var(PERSIST_VAR_NAME).is_ok())\n}\n\n/// Evaluates an XPath expression for a WiX Source file.\n///\n/// This registers the WiX XML namespace with the `wix` prefix. So, XPath\n/// expressions should use `/wix:Wix/` as the start and prefix all element/node\n/// names with the `wix:` prefix. Note, attributes should _not_ have the `wix:`\n/// prefix.\n///\n/// All values are currently returned as strings.\npub fn evaluate_xpath(wxs: &Path, xpath: &str) -> String {\n    let mut wxs = File::open(wxs).expect(\"Open Wix Source file\");\n    let mut wxs_content = String::new();\n    wxs.read_to_string(&mut wxs_content)\n        .expect(\"Read WiX Source file\");\n    let wxs_package = parser::parse(&wxs_content).expect(\"Parsing WiX Source file\");\n    let wxs_document = wxs_package.as_document();\n    let mut context = Context::new();\n    context.set_namespace(\"wix\", \"http://schemas.microsoft.com/wix/2006/wi\");\n    let xpath = Factory::new().build(xpath).unwrap().unwrap();\n    xpath\n        .evaluate(&context, wxs_document.root())\n        .unwrap()\n        .string()\n}\n\n/// Initializes the logging for the integration tests.\n///\n/// When a test fails, it is useful to re-run the tests with logging statements\n/// enabled to debug the failed test. This initializes the logging based on the\n/// `CARGO_WIX_TEST_LOG` environment variable, which takes an integer as a\n/// value. A `0` value, or not setting the environment variable, turns off\n/// logging. Each increment of the integer value will increase the number of\n/// statements that are logged up to 5 (Trace).\n///\n/// If the `CARGO_WIX_TEST_LOG` value is greater than zero (0), then log\n/// statements will be emitted to the terminal/console regardless of the\n/// `--nocapture` option for cargo tests. In other words, log statements are\n/// *not* captured by cargo's testing framework with this implementation. Thus,\n/// it is recommended to *not* activate logging if running all of the tests.\n/// Logging should be done for isolated tests. Not capturing the log statements\n/// by cargo's test framework keeps the formatting and coloring. There might be\n/// a decrease in performance as well.\n///\n/// Log statements are formatted the same as the verbosity format for the CLI.\n///\n/// # Examples\n///\n/// Enabling logging for tests in Powershell requires two commands and an\n/// optional third command to undo:\n///\n/// ```powershell\n/// PS C:\\Path\\to\\Cargo\\Wix> $env:CARGO_WIX_TEST_LOG=5\n/// PS C:\\Path\\to\\Cargo\\Wix> cargo test\n/// PS C:\\Path\\to\\Cargo\\Wix> Remove-Item Env:\\CARGO_WIX_TEST_LOG\n/// ```\n///\n/// This can be collapsed into a single line as:\n///\n/// ```powershell\n/// PS C:\\Path\\to\\Cargo\\Wix> $env:CARGO_WIX_TEST_LOG=5; cargo test; Remove-Item Env:\\CARGO_WIX_TEST_LOG\n/// ```\n///\n/// But again, logging should only be activated for isolated tests to avoid\n/// relatively large number of statements being written:\n///\n/// ```powershell\n/// PS C:\\Path\\to\\Cargo\\Wix> $env:CARGO_WIX_TEST_LOG=5; cargo test <TEST_NAME>; Remove-Item Env:\\CARGO_WIX_TEST_LOG\n/// ```\n///\n/// where `<TEST_NAME>` is the name of a test, a.k.a. function name with the `#[test]` attribute.\npub fn init_logging() {\n    let log_level = match std::env::var(\"CARGO_WIX_TEST_LOG\") {\n        Ok(level) => level\n            .parse::<i32>()\n            .expect(\"Integer for CARGO_WIX_TEST_LOG value\"),\n        Err(_) => 0,\n    };\n    let mut builder = Builder::new();\n    builder\n        .format(|buf, record| {\n            let level = record.level();\n            // This implementation for a format is copied from the default format implemented for the\n            // `env_logger` crate but modified to use a colon, `:`, to separate the level from the\n            // message and change the colors to match the previous colors used by the `loggerv` crate.\n            let style = match level {\n                // Light Gray, or just Gray, is not a supported color for non-ANSI enabled Windows\n                // consoles, so TRACE and DEBUG statements are differentiated by boldness but use the\n                // same white color.\n                Level::Trace => Style::new().fg_color(Some(LogColor::White.into())),\n                Level::Debug => Style::new().fg_color(Some(LogColor::White.into())).bold(),\n                Level::Info => Style::new().fg_color(Some(LogColor::Green.into())).bold(),\n                Level::Warn => Style::new().fg_color(Some(LogColor::Yellow.into())).bold(),\n                Level::Error => Style::new().fg_color(Some(LogColor::Red.into())).bold(),\n            };\n            let write_level = write!(buf, \"{style}{level:>5}{style:#}: \");\n            let write_args = writeln!(buf, \"{}\", record.args());\n            write_level.and(write_args)\n        })\n        .filter(\n            Some(\"wix\"),\n            match log_level {\n                0 => LevelFilter::Off,\n                1 => LevelFilter::Error,\n                2 => LevelFilter::Warn,\n                3 => LevelFilter::Info,\n                4 => LevelFilter::Debug,\n                _ => LevelFilter::Trace,\n            },\n        )\n        .try_init()\n        .ok();\n}\n"
  },
  {
    "path": "tests/common/one.wxs",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Wix xmlns=\"http://schemas.microsoft.com/wix/2006/wi\">\n  <Product Id=\"*\" \n    Name=\"My Software\"\n    Language=\"1033\"\n    Manufacturer=\"My Company\"\n    Version=\"1.0.0.0\" \n    UpgradeCode=\"8c7d85db-b0d1-4a9a-85ea-130836aeef67\">\n    \n    <Package InstallerVersion=\"200\" Compressed=\"yes\" InstallScope=\"perMachine\" />\n\n    <MajorUpgrade DowngradeErrorMessage=\"A newer version of [ProductName] is already installed.\" />\n    <MediaTemplate EmbedCab=\"yes\" />\n\n    <Feature Id=\"ProductFeature\" Title=\"The main feature\" Level=\"1\">\n      <ComponentGroupRef Id=\"ProductComponents\" />\n    </Feature>\n  </Product>\n</Wix>\n"
  },
  {
    "path": "tests/common/post_v4/fragment.wxs",
    "content": "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:ui=\"http://wixtoolset.org/schemas/v4/wxs/ui\">\n    <Fragment test=\"1\"></Fragment>\n</Wix>"
  },
  {
    "path": "tests/common/post_v4/main.wxs",
    "content": "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:ui=\"http://wixtoolset.org/schemas/v4/wxs/ui\">\n    <Package></Package>\n</Wix>"
  },
  {
    "path": "tests/common/pre_v4/main.wxs",
    "content": "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'></Wix>"
  },
  {
    "path": "tests/common/three.wxs",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Wix xmlns=\"http://schemas.microsoft.com/wix/2006/wi\">\n  <Fragment>\n    <Feature Id=\"ProductFeature\" Title=\"The main feature\" Level=\"1\">\n      <ComponentGroupRef Id=\"ProductComponents\" />\n    </Feature>\n  </Fragment>\n</Wix>\n"
  },
  {
    "path": "tests/common/two.wxs",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<?if $(sys.BUILDARCH) = x64 or $(sys.BUILDARCH) = arm64 ?>\n    <?define PlatformProgramFilesFolder = \"ProgramFiles64Folder\" ?>\n<?else ?>\n    <?define PlatformProgramFilesFolder = \"ProgramFilesFolder\" ?>\n<?endif ?>\n<Wix xmlns=\"http://schemas.microsoft.com/wix/2006/wi\">\n  <Fragment>\n    <Directory Id=\"TARGETDIR\" Name=\"SourceDir\">\n      <Directory Id=\"$(var.PlatformProgramFilesFolder)\">\n        <Directory Id=\"INSTALLFOLDER\" Name=\"My Software\" />\n      </Directory>\n    </Directory>\n  </Fragment>\n\n  <Fragment>\n    <ComponentGroup Id=\"ProductComponents\" Directory=\"INSTALLFOLDER\">\n      <Component Id=\"cmpCargoTOML\" Guid=\"*\">\n        <File Source=\"Cargo.toml\" />\n      </Component>\n    </ComponentGroup>\n  </Fragment>\n</Wix>\n"
  },
  {
    "path": "tests/common/well_known_exts/main.wxs",
    "content": "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\"\n    xmlns:ui=\"http://wixtoolset.org/schemas/v4/wxs/ui\"\n    xmlns:bal=\"http://wixtoolset.org/schemas/v4/wxs/bal\"\n    xmlns:complus=\"http://wixtoolset.org/schemas/v4/wxs/complus\"\n    xmlns:dep=\"http://wixtoolset.org/schemas/v4/wxs/dependency\"\n    xmlns:directx=\"http://wixtoolset.org/schemas/v4/wxs/directx\"\n    xmlns:fw=\"http://wixtoolset.org/schemas/v4/wxs/firewall\"\n    xmlns:http=\"http://wixtoolset.org/schemas/v4/wxs/http\"\n    xmlns:iis=\"http://wixtoolset.org/schemas/v4/wxs/iis\"\n    xmlns:msmq=\"http://wixtoolset.org/schemas/v4/wxs/msmq\"\n    xmlns:netfx=\"http://wixtoolset.org/schemas/v4/wxs/netfx\"\n    xmlns:powershell=\"http://wixtoolset.org/schemas/v4/wxs/powershell\"\n    xmlns:sql=\"http://wixtoolset.org/schemas/v4/wxs/sql\"\n    xmlns:util=\"http://wixtoolset.org/schemas/v4/wxs/util\"\n    xmlns:vs=\"http://wixtoolset.org/schemas/v4/wxs/vs\"\n    ></Wix>"
  },
  {
    "path": "tests/create.rs",
    "content": "#![cfg(windows)]\n\n// Copyright (C) 2017 Christopher R. Field.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nmod common;\n\nuse assert_fs::prelude::*;\n\nuse predicates::prelude::*;\n\nuse crate::common::init_logging;\nuse crate::common::{\n    MISC_NAME, NO_CAPTURE_VAR_NAME, PACKAGE_NAME, PERSIST_VAR_NAME, SUBPACKAGE1_NAME,\n};\n\nuse assert_fs::TempDir;\n\nuse lazy_static::lazy_static;\n\nuse serial_test::serial;\n\nuse std::env;\nuse std::fs::{self, File};\nuse std::io::{Read, Write};\nuse std::path::{Path, PathBuf};\n\nuse toml::{Table, Value};\n\nuse wix::create::Builder;\nuse wix::initialize;\nuse wix::{CARGO_MANIFEST_FILE, Result, WIX};\n\nlazy_static! {\n    static ref TARGET_WIX_DIR: PathBuf = {\n        let mut p = TARGET_NAME.clone();\n        p.push(WIX);\n        p\n    };\n}\n\nlazy_static! {\n    static ref TARGET_NAME: PathBuf = PathBuf::from(\"target\");\n}\n\n/// Run the _create_ subcommand with the output capture toggled by the\n/// `CARGO_WIX_TEST_NO_CAPTURE` environment variable.\nfn run(b: &mut Builder) -> Result<()> {\n    run_with_package(b, &std::env::current_dir()?)\n}\n\n/// Run the _create_ subcommand with the output capture toggled by the\n/// `CARGO_WIX_TEST_NO_CAPTURE` environment variable, and the CARGO_TARGET_DIR\n/// env variable set to <package path>/target.\nfn run_with_package(b: &mut Builder, package_path: &Path) -> Result<()> {\n    // Forcefully set the target dir to its default location\n    unsafe {\n        env::set_var(\"CARGO_TARGET_DIR\", package_path.join(\"target\"));\n    }\n    b.capture_output(env::var(NO_CAPTURE_VAR_NAME).is_err())\n        .build()\n        .run()\n}\n\n#[test]\n#[serial]\nfn default_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Execution::default().run().unwrap();\n    let result = run(&mut Builder::default());\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn russian_culture_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Execution::default().run().unwrap();\n    let result = run(Builder::default().culture(Some(\"ru-ru\")));\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn debug_build_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Execution::default().run().unwrap();\n    let result = run(Builder::default().debug_build(true));\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn profile_build_works() {\n    init_logging();\n    const PROFILE: &str = \"dist\";\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package_profile(PROFILE);\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    eprintln!(\"{}\", expected_msi_file.display());\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Execution::default().run().unwrap();\n    let result = run(Builder::default().profile(Some(PROFILE)));\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn debug_name_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64-debug.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Execution::default().run().unwrap();\n    let result = run(Builder::default().debug_name(true));\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn metadata_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package_metadata();\n    let expected_msi_file = TARGET_WIX_DIR.join(\"Metadata-2.1.0-x86_64.msi\");\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Execution::default().run().unwrap();\n    let result = run(&mut Builder::default());\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn output_trailing_forwardslash_works() {\n    init_logging();\n    let output_dir = TARGET_NAME.join(\"output_dir\");\n    let output_dir_str = format!(\"{}/\", output_dir.to_str().unwrap());\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = output_dir.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Execution::default().run().unwrap();\n    let result = run(Builder::default().output(Some(output_dir_str.as_str())));\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn output_trailing_backslash_works() {\n    init_logging();\n    let output_dir = TARGET_NAME.join(\"output_dir\");\n    let output_dir_str = format!(\"{}\\\\\", output_dir.to_str().unwrap());\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = output_dir.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Execution::default().run().unwrap();\n    let result = run(Builder::default().output(Some(output_dir_str.as_str())));\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn output_existing_dir_works() {\n    init_logging();\n    let output_dir = PathBuf::from(\"output_dir\");\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = output_dir.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    fs::create_dir(&output_dir).unwrap();\n    initialize::Execution::default().run().unwrap();\n    let result = run(Builder::default().output(output_dir.to_str()));\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn output_file_without_extension_works() {\n    init_logging();\n    let output_dir = TARGET_NAME.join(\"output_dir\");\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let output_file = output_dir.join(PACKAGE_NAME);\n    let expected_msi_file = output_dir.join(format!(\"{PACKAGE_NAME}.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Execution::default().run().unwrap();\n    let result = run(Builder::default().output(output_file.to_str()));\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn output_file_with_extension_works() {\n    init_logging();\n    let output_dir = TARGET_NAME.join(\"output_dir\");\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = output_dir.join(format!(\"{PACKAGE_NAME}.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Execution::default().run().unwrap();\n    let result = run(Builder::default().output(expected_msi_file.to_str()));\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn init_with_package_section_fields_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    let package_manifest = package.child(\"Cargo.toml\");\n    let mut toml: Table = {\n        let mut cargo_toml_handle = File::open(package_manifest.path()).unwrap();\n        let mut cargo_toml_content = String::new();\n        cargo_toml_handle\n            .read_to_string(&mut cargo_toml_content)\n            .unwrap();\n        toml::from_str(&cargo_toml_content).unwrap()\n    };\n    {\n        toml.get_mut(\"package\")\n            .map(|p| {\n                match p {\n                    Value::Table(t) => {\n                        t.insert(\n                            String::from(\"description\"),\n                            Value::from(\"This is a description\"),\n                        );\n                        t.insert(\n                            String::from(\"documentation\"),\n                            Value::from(\"https://www.example.com/docs\"),\n                        );\n                        t.insert(\n                            String::from(\"homepage\"),\n                            Value::from(\"https://www.example.com\"),\n                        );\n                        t.insert(String::from(\"license\"), Value::from(\"MIT\"));\n                        t.insert(\n                            String::from(\"repository\"),\n                            Value::from(\"https://www.example.com/repo\"),\n                        );\n                    }\n                    _ => panic!(\"The 'package' section is not a table\"),\n                };\n                Some(p)\n            })\n            .expect(\"A package section for the Cargo.toml\");\n        let toml_string = toml.to_string();\n        let mut cargo_toml_handle = File::create(package_manifest.path()).unwrap();\n        cargo_toml_handle.write_all(toml_string.as_bytes()).unwrap();\n    }\n    initialize::Execution::default().run().unwrap();\n    let result = run(&mut Builder::default());\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn init_with_all_options_works() {\n    init_logging();\n    const LICENSE_FILE: &str = \"License_Example.txt\";\n    const EULA_FILE: &str = \"Eula_Example.rtf\";\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    let bin_example_path = package.path().join(\"bin\").join(\"Example.exe\");\n    fs::create_dir(bin_example_path.parent().unwrap()).unwrap();\n    {\n        let _bin_example_handle = File::create(&bin_example_path).unwrap();\n    }\n    let banner_path = package.path().join(\"img\").join(\"Banner.bmp\");\n    fs::create_dir(banner_path.parent().unwrap()).unwrap();\n    {\n        let _banner_handle = File::create(&banner_path).unwrap();\n    }\n    let dialog_path = package.path().join(\"img\").join(\"Dialog.bmp\");\n    {\n        let _dialog_handle = File::create(&dialog_path).unwrap();\n    }\n    let package_license = package.child(LICENSE_FILE);\n    {\n        let _license_handle = File::create(package_license.path()).unwrap();\n    }\n    let package_eula = package.child(EULA_FILE);\n    {\n        let _eula_handle = File::create(package_eula.path()).unwrap();\n    }\n    let product_icon_path = package.path().join(\"img\").join(\"Product.ico\");\n    {\n        let _product_icon_handle = File::create(&product_icon_path).unwrap();\n    }\n    initialize::Builder::new()\n        .banner(banner_path.to_str())\n        .binaries(bin_example_path.to_str().map(|b| vec![b]))\n        .description(Some(\"This is a description\"))\n        .dialog(dialog_path.to_str())\n        .eula(package_eula.path().to_str())\n        .help_url(Some(\"http://www.example.com\"))\n        .license(package_license.path().to_str())\n        .manufacturer(Some(\"Example Manufacturer\"))\n        .product_icon(product_icon_path.to_str())\n        .product_name(Some(\"Example Product Name\"))\n        .build()\n        .run()\n        .unwrap();\n    let result = run(&mut Builder::default());\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn init_with_banner_option_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    let banner_path = package.path().join(\"img\").join(\"Banner.bmp\");\n    fs::create_dir(banner_path.parent().unwrap()).unwrap();\n    {\n        let _banner_handle = File::create(&banner_path).unwrap();\n    }\n    initialize::Builder::new()\n        .banner(banner_path.to_str())\n        .build()\n        .run()\n        .unwrap();\n    let result = run(&mut Builder::default());\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn init_with_binaries_option_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    let bin_example_path = package.path().join(\"bin\").join(\"Example.exe\");\n    fs::create_dir(bin_example_path.parent().unwrap()).unwrap();\n    {\n        let _bin_example_handle = File::create(&bin_example_path).unwrap();\n    }\n    initialize::Builder::new()\n        .binaries(bin_example_path.to_str().map(|b| vec![b]))\n        .build()\n        .run()\n        .unwrap();\n    let result = run(&mut Builder::default());\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn init_with_multiple_binaries_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package_multiple_binaries();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Builder::new().build().run().unwrap();\n    let result = run(&mut Builder::default());\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn init_with_description_option_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Builder::new()\n        .description(Some(\"This is a description\"))\n        .build()\n        .run()\n        .unwrap();\n    let result = run(&mut Builder::default());\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn init_with_dialog_option_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    let dialog_path = package.path().join(\"img\").join(\"Dialog.bmp\");\n    fs::create_dir(dialog_path.parent().unwrap()).unwrap();\n    {\n        let _dialog_handle = File::create(&dialog_path).unwrap();\n    }\n    initialize::Builder::new()\n        .dialog(dialog_path.to_str())\n        .build()\n        .run()\n        .unwrap();\n    let result = run(&mut Builder::default());\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn init_with_eula_in_cwd_works() {\n    init_logging();\n    const EULA_FILE: &str = \"Eula_Example.rtf\";\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    let package_eula = package.child(EULA_FILE);\n    {\n        let _eula_handle = File::create(package_eula.path()).unwrap();\n    }\n    initialize::Builder::new()\n        .eula(package_eula.path().to_str())\n        .build()\n        .run()\n        .expect(\"Initialization\");\n    let result = run(&mut Builder::default());\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn init_with_eula_in_docs_works() {\n    init_logging();\n    const EULA_FILE: &str = \"Eula_Example.rtf\";\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    let package_docs = package.child(\"docs\");\n    fs::create_dir(package_docs.path()).unwrap();\n    let package_eula = package_docs.path().join(EULA_FILE);\n    {\n        let _eula_handle = File::create(&package_eula).unwrap();\n    }\n    initialize::Builder::new()\n        .eula(package_eula.to_str())\n        .build()\n        .run()\n        .expect(\"Initialization\");\n    let result = run(&mut Builder::default());\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn init_with_help_url_option_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Builder::new()\n        .help_url(Some(\"http://www.example.com\"))\n        .build()\n        .run()\n        .unwrap();\n    let result = run(&mut Builder::default());\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn init_with_license_in_cwd_works() {\n    init_logging();\n    const LICENSE_FILE: &str = \"License_Example.txt\";\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    let package_license = package.child(LICENSE_FILE);\n    {\n        let _license_handle = File::create(package_license.path()).unwrap();\n    }\n    initialize::Builder::new()\n        .license(package_license.path().to_str())\n        .build()\n        .run()\n        .unwrap();\n    let result = run(&mut Builder::default());\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn init_with_license_in_docs_works() {\n    init_logging();\n    const EULA_FILE: &str = \"License_Example.txt\";\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    let package_docs = package.child(\"docs\");\n    fs::create_dir(package_docs.path()).unwrap();\n    let package_license = package_docs.path().join(EULA_FILE);\n    {\n        let _license_handle = File::create(&package_license).unwrap();\n    }\n    initialize::Builder::new()\n        .license(package_license.to_str())\n        .build()\n        .run()\n        .expect(\"Initialization\");\n    let result = run(&mut Builder::default());\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn init_with_manufacturer_option_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Builder::new()\n        .manufacturer(Some(\"Example Manufacturer\"))\n        .build()\n        .run()\n        .unwrap();\n    let result = run(&mut Builder::default());\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn init_with_product_icon_option_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    let product_icon_path = package.path().join(\"img\").join(\"Product.ico\");\n    fs::create_dir(product_icon_path.parent().unwrap()).unwrap();\n    {\n        let _product_icon_handle = File::create(&product_icon_path).unwrap();\n    }\n    initialize::Builder::new()\n        .product_icon(product_icon_path.to_str())\n        .build()\n        .run()\n        .unwrap();\n    let result = run(&mut Builder::default());\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn init_with_product_name_option_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Builder::new()\n        .product_name(Some(\"Example Product Name\"))\n        .build()\n        .run()\n        .unwrap();\n    let mut wxs_handle =\n        File::open(package.child(PathBuf::from(WIX).join(\"main.wxs\")).path()).unwrap();\n    let mut wxs_content = String::new();\n    wxs_handle.read_to_string(&mut wxs_content).unwrap();\n    println!(\"{wxs_content}\");\n    let result = run(&mut Builder::default());\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn input_works_inside_cwd() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let package_manifest = package.child(CARGO_MANIFEST_FILE);\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Builder::default().build().run().unwrap();\n    let result = run(Builder::default().input(package_manifest.path().to_str()));\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn input_works_outside_cwd() {\n    init_logging();\n    let package = common::create_test_package();\n    let package_manifest = package.child(CARGO_MANIFEST_FILE);\n    let expected_msi_file =\n        package.child(TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\")));\n    initialize::Builder::default()\n        .input(package_manifest.path().to_str())\n        .build()\n        .run()\n        .unwrap();\n    let result = run_with_package(\n        Builder::default().input(package_manifest.path().to_str()),\n        package.path(),\n    );\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file.path())\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn includes_works_with_wix_dir() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package_multiple_wxs_sources();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    let two_wxs = package.path().join(MISC_NAME).join(\"two.wxs\");\n    let three_wxs = package.path().join(MISC_NAME).join(\"three.wxs\");\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Builder::default().build().run().unwrap();\n    let result = run(Builder::default().includes(Some(vec![\n        two_wxs.to_str().unwrap(),\n        three_wxs.to_str().unwrap(),\n    ])));\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn includes_works_without_wix_dir() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package_multiple_wxs_sources();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    let one_wxs = package.path().join(MISC_NAME).join(\"one.wxs\");\n    let two_wxs = package.path().join(MISC_NAME).join(\"two.wxs\");\n    env::set_current_dir(package.path()).unwrap();\n    let result = run(Builder::default().includes(Some(vec![\n        one_wxs.to_str().unwrap(),\n        two_wxs.to_str().unwrap(),\n    ])));\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn includes_works_with_input_outside_cwd() {\n    init_logging();\n    let package = common::create_test_package_multiple_wxs_sources();\n    let package_manifest = package.child(CARGO_MANIFEST_FILE);\n    let expected_msi_file =\n        package.child(TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\")));\n    let two_wxs = package.path().join(MISC_NAME).join(\"two.wxs\");\n    let three_wxs = package.path().join(MISC_NAME).join(\"three.wxs\");\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Builder::default()\n        .input(package_manifest.path().to_str())\n        .build()\n        .run()\n        .unwrap();\n    let result = run(Builder::default()\n        .input(package_manifest.path().to_str())\n        .includes(Some(vec![\n            two_wxs.to_str().unwrap(),\n            three_wxs.to_str().unwrap(),\n        ])));\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file.path())\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn compiler_args_flags_only_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Builder::default().build().run().unwrap();\n    let result = run(Builder::default().compiler_args(Some(vec![\"-nologo\", \"-wx\"])));\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn compiler_args_options_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Builder::default().build().run().unwrap();\n    let result = run(Builder::default().compiler_args(Some(vec![\"-arch\", \"x64\"])));\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[ignore = \"currently requires a binary to be installed\"]\n#[test]\n#[serial]\nfn linker_args_flags_only_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Builder::default().build().run().unwrap();\n    let result = run(Builder::default().linker_args(Some(vec![\"-nologo\", \"-wx\"])));\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn compiler_and_linker_args_works_with_metadata() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package_metadata();\n    let expected_msi_file = TARGET_WIX_DIR.join(\"Metadata-2.1.0-x86_64.msi\");\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Builder::default().build().run().unwrap();\n    let result = run(&mut Builder::default());\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn custom_target_dir_works() {\n    init_logging();\n    let target_tmpdir = TempDir::new()\n        .unwrap()\n        .into_persistent_if(env::var(PERSIST_VAR_NAME).is_ok());\n\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = Path::new(WIX).join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Execution::default().run().unwrap();\n    unsafe {\n        env::set_var(\"CARGO_TARGET_DIR\", target_tmpdir.path());\n    }\n    let result = Builder::default()\n        .capture_output(env::var(NO_CAPTURE_VAR_NAME).is_err())\n        .build()\n        .run();\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    target_tmpdir.child(WIX).assert(predicate::path::exists());\n    target_tmpdir\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn workspace_package_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_workspace();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{SUBPACKAGE1_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Builder::new()\n        .package(Some(SUBPACKAGE1_NAME))\n        .build()\n        .run()\n        .unwrap();\n    let result = run(Builder::default().package(Some(SUBPACKAGE1_NAME)));\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn cross_compilation_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Execution::default().run().unwrap();\n    let result = run(Builder::default().target(Some(\"x86_64-pc-windows-msvc\")));\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn profile_cross_compilation_works() {\n    // Makes sure we handle customizing profile+target, in terms of target paths\n    init_logging();\n    const PROFILE: &str = \"dist\";\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package_profile(PROFILE);\n    let expected_msi_file = TARGET_WIX_DIR.join(format!(\"{PACKAGE_NAME}-0.1.0-x86_64.msi\"));\n    eprintln!(\"{}\", expected_msi_file.display());\n    env::set_current_dir(package.path()).unwrap();\n    initialize::Execution::default().run().unwrap();\n    let result = run(Builder::default()\n        .profile(Some(PROFILE))\n        .target(Some(\"x86_64-pc-windows-msvc\")));\n    env::set_current_dir(original_working_directory).unwrap();\n    result.expect(\"OK result\");\n    package\n        .child(TARGET_WIX_DIR.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(expected_msi_file)\n        .assert(predicate::path::exists());\n}\n"
  },
  {
    "path": "tests/initialize.rs",
    "content": "// Copyright (C) 2017 Christopher R. Field.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nmod common;\n\nuse assert_fs::prelude::*;\nuse predicates::prelude::*;\n\nuse assert_fs::TempDir;\n\nuse lazy_static::lazy_static;\n\nuse serial_test::serial;\n\nuse std::env;\nuse std::fs::{self, File};\nuse std::io::{Read, Write};\nuse std::path::PathBuf;\n\nuse toml::{Table, Value};\n\nuse wix::initialize::{Builder, Execution};\nuse wix::stored_path::StoredPathBuf;\nuse wix::{\n    CARGO_MANIFEST_FILE, LICENSE_FILE_NAME, RTF_FILE_EXTENSION, WIX, WIX_SOURCE_FILE_EXTENSION,\n    WIX_SOURCE_FILE_NAME,\n};\n\nuse crate::common::{SUBPACKAGE1_NAME, SUBPACKAGE2_NAME, add_license_to_package, init_logging};\n\nlazy_static! {\n    static ref MAIN_WXS: String = WIX_SOURCE_FILE_NAME.to_owned() + \".\" + WIX_SOURCE_FILE_EXTENSION;\n    static ref LICENSE_RTF: String = LICENSE_FILE_NAME.to_owned() + \".\" + RTF_FILE_EXTENSION;\n    static ref WIX_PATH: PathBuf = PathBuf::from(WIX);\n    static ref MAIN_WXS_PATH: PathBuf = PathBuf::from(WIX).join(MAIN_WXS.as_str());\n    static ref LICENSE_RTF_PATH: PathBuf = PathBuf::from(WIX).join(LICENSE_RTF.as_str());\n}\n\n#[test]\n#[serial]\nfn default_works() {\n    // Save the current working directory so that we can change back to it at\n    // the end of the test. This avoids polluting the `tests` folder for the\n    // source code with test artifacts.\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    env::set_current_dir(package.path()).unwrap();\n    let result = Execution::default().run();\n    env::set_current_dir(original_working_directory).unwrap();\n    println!(\"{result:?}\");\n    assert!(result.is_ok());\n    package\n        .child(WIX_PATH.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(MAIN_WXS_PATH.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(LICENSE_RTF_PATH.as_path())\n        .assert(predicate::path::missing());\n}\n\n#[test]\n#[serial]\nfn description_works() {\n    const EXPECTED: &str = \"This is a description\";\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    env::set_current_dir(package.path()).unwrap();\n    let result = Builder::default().description(Some(EXPECTED)).build().run();\n    env::set_current_dir(original_working_directory).unwrap();\n    assert!(result.is_ok());\n    let actual = common::evaluate_xpath(\n        package.child(MAIN_WXS_PATH.as_path()).path(),\n        \"/wix:Wix/wix:Product/wix:Package/@Description\",\n    );\n    assert_eq!(actual, EXPECTED);\n}\n\n#[test]\n#[serial]\nfn help_url_works() {\n    const EXPECTED: &str = \"http://www.example.com\";\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    env::set_current_dir(package.path()).unwrap();\n    let result = Builder::default().help_url(Some(EXPECTED)).build().run();\n    env::set_current_dir(original_working_directory).unwrap();\n    assert!(result.is_ok());\n    let actual = common::evaluate_xpath(\n        package.child(MAIN_WXS_PATH.as_path()).path(),\n        \"/wix:Wix/wix:Product/wix:Property[@Id='ARPHELPLINK']/@Value\",\n    );\n    assert_eq!(actual, EXPECTED);\n}\n\n#[test]\n#[serial]\nfn manufacturer_works() {\n    const EXPECTED: &str = \"Example Manufacturer\";\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    env::set_current_dir(package.path()).unwrap();\n    let result = Builder::default()\n        .manufacturer(Some(EXPECTED))\n        .build()\n        .run();\n    env::set_current_dir(original_working_directory).unwrap();\n    assert!(result.is_ok());\n    let actual = common::evaluate_xpath(\n        package.child(MAIN_WXS_PATH.as_path()).path(),\n        \"/wix:Wix/wix:Product/wix:Package/@Manufacturer\",\n    );\n    assert_eq!(actual, EXPECTED);\n}\n\n#[test]\n#[serial]\nfn product_name_works() {\n    const EXPECTED: &str = \"Example Product Name\";\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    env::set_current_dir(package.path()).unwrap();\n    let result = Builder::default()\n        .product_name(Some(EXPECTED))\n        .build()\n        .run();\n    env::set_current_dir(original_working_directory).unwrap();\n    assert!(result.is_ok());\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"/wix:Wix/wix:Product/@Name\"\n        ),\n        EXPECTED\n    );\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"/wix:Wix/wix:Product/wix:Property[@Id='DiskPrompt']/@Value\"\n        ),\n        EXPECTED.to_string() + \" Installation\"\n    );\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:Directory[@Id='APPLICATIONFOLDER']/@Name\"\n        ),\n        EXPECTED\n    );\n}\n\n#[test]\n#[serial]\nfn binaries_works() {\n    const EXPECTED: &str = \"bin\\\\Example.exe\";\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    env::set_current_dir(package.path()).unwrap();\n    let result = Builder::default()\n        .binaries(Some(vec![EXPECTED]))\n        .build()\n        .run();\n    env::set_current_dir(original_working_directory).unwrap();\n    assert!(result.is_ok());\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='exe0']/@Name\"\n        ),\n        \"Example.exe\"\n    );\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='exe0']/@Source\"\n        ),\n        EXPECTED\n    );\n}\n\n#[test]\nfn input_works() {\n    let package = common::create_test_package();\n    Builder::default()\n        .input(package.child(CARGO_MANIFEST_FILE).path().to_str())\n        .build()\n        .run()\n        .expect(\"OK result\");\n    package.child(WIX).assert(predicate::path::exists());\n    package\n        .child(MAIN_WXS_PATH.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(LICENSE_RTF_PATH.as_path())\n        .assert(predicate::path::missing());\n}\n\n#[test]\n#[serial]\nfn output_works() {\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let output = TempDir::new().unwrap();\n    env::set_current_dir(package.path()).unwrap();\n    let result = Builder::default()\n        .output(output.path().to_str())\n        .build()\n        .run();\n    env::set_current_dir(original_working_directory).unwrap();\n    assert!(result.is_ok());\n    output\n        .child(MAIN_WXS.as_str())\n        .assert(predicate::path::exists());\n}\n\n#[test]\n#[serial]\nfn input_with_output_works() {\n    let package = common::create_test_package();\n    let package_manifest = package.child(CARGO_MANIFEST_FILE);\n    let output = package.path().join(\"assets\").join(\"windows\");\n    fs::create_dir(output.parent().unwrap()).unwrap();\n    fs::create_dir(&output).unwrap();\n    let mut toml: Table = {\n        let mut cargo_toml_handle = File::open(package_manifest.path()).unwrap();\n        let mut cargo_toml_content = String::new();\n        cargo_toml_handle\n            .read_to_string(&mut cargo_toml_content)\n            .unwrap();\n        toml::from_str(&cargo_toml_content).unwrap()\n    };\n    {\n        toml.get_mut(\"package\")\n            .map(|p| {\n                match p {\n                    Value::Table(t) => t.insert(String::from(\"license\"), Value::from(\"MIT\")),\n                    _ => panic!(\"The 'package' section is not a table\"),\n                };\n                Some(p)\n            })\n            .expect(\"A package section for the Cargo.toml\");\n        let toml_string = toml.to_string();\n        let mut cargo_toml_handle = File::create(package_manifest.path()).unwrap();\n        cargo_toml_handle.write_all(toml_string.as_bytes()).unwrap();\n    }\n    Builder::default()\n        .input(package.child(CARGO_MANIFEST_FILE).path().to_str())\n        .output(output.to_str())\n        .build()\n        .run()\n        .expect(\"OK result\");\n    assert!(output.join(MAIN_WXS.as_str()).exists());\n    assert!(output.join(LICENSE_RTF.as_str()).exists());\n}\n\n#[test]\n#[serial]\nfn license_with_txt_file_works() {\n    const EXPECTED: &str = \"License_Example.txt\";\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let package_license = package.child(EXPECTED);\n    env::set_current_dir(package.path()).unwrap();\n    let _license_handle = File::create(package_license.path()).unwrap();\n    let result = Builder::default()\n        .license(package_license.path().to_str())\n        .build()\n        .run();\n    env::set_current_dir(original_working_directory).unwrap();\n    assert!(result.is_ok());\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='LicenseFile']/@Name\"\n        ),\n        \"\"\n    );\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='LicenseFile']/@Source\"\n        ),\n        package_license.path().to_str().unwrap()\n    );\n}\n\n#[test]\n#[serial]\nfn license_with_rtf_file_works() {\n    const EXPECTED: &str = \"License_Example.rtf\";\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let package_license = package.child(EXPECTED);\n    env::set_current_dir(package.path()).unwrap();\n    let _license_handle = File::create(package_license.path()).unwrap();\n    let result = Builder::default()\n        .license(package_license.path().to_str())\n        .build()\n        .run();\n    env::set_current_dir(original_working_directory).unwrap();\n    assert!(result.is_ok());\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='LicenseFile']/@Name\"\n        ),\n        \"\"\n    );\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='LicenseFile']/@Source\"\n        ),\n        package_license.path().to_str().unwrap()\n    );\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:WixVariable[@Id='WixUILicenseRtf']/@Value\"\n        ),\n        package_license.path().to_str().unwrap()\n    );\n}\n\n#[test]\n#[serial]\nfn eula_works() {\n    const EXPECTED: &str = \"EULA_Example.rtf\";\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let package_eula = package.child(EXPECTED);\n    env::set_current_dir(package.path()).unwrap();\n    let _eula_handle = File::create(package_eula.path()).unwrap();\n    let result = Builder::default()\n        .eula(package_eula.path().to_str())\n        .build()\n        .run();\n    env::set_current_dir(original_working_directory).unwrap();\n    assert!(result.is_ok());\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:WixVariable[@Id='WixUILicenseRtf']/@Value\"\n        ),\n        package_eula.path().to_str().unwrap()\n    );\n}\n\n#[test]\n#[serial]\nfn mit_license_id_works() {\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    env::set_current_dir(package.path()).unwrap();\n    let package_manifest = package.child(CARGO_MANIFEST_FILE);\n    let mut toml: Table = {\n        let mut cargo_toml_handle = File::open(package_manifest.path()).unwrap();\n        let mut cargo_toml_content = String::new();\n        cargo_toml_handle\n            .read_to_string(&mut cargo_toml_content)\n            .unwrap();\n        toml::from_str(&cargo_toml_content).unwrap()\n    };\n    {\n        toml.get_mut(\"package\")\n            .map(|p| {\n                match p {\n                    Value::Table(t) => t.insert(String::from(\"license\"), Value::from(\"MIT\")),\n                    _ => panic!(\"The 'package' section is not a table\"),\n                };\n                Some(p)\n            })\n            .expect(\"A package section for the Cargo.toml\");\n        let toml_string = toml.to_string();\n        let mut cargo_toml_handle = File::create(package_manifest.path()).unwrap();\n        cargo_toml_handle.write_all(toml_string.as_bytes()).unwrap();\n    }\n    let result = Execution::default().run();\n    env::set_current_dir(original_working_directory).unwrap();\n    assert!(result.is_ok());\n    package\n        .child(WIX_PATH.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(MAIN_WXS_PATH.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(LICENSE_RTF_PATH.as_path())\n        .assert(predicate::path::exists());\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='LicenseFile']/@Name\"\n        ),\n        \"\"\n    );\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='LicenseFile']/@Source\"\n        ),\n        StoredPathBuf::from_std_path(LICENSE_RTF_PATH.as_path())\n            .unwrap()\n            .to_string()\n    );\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:WixVariable[@Id='WixUILicenseRtf']/@Value\"\n        ),\n        StoredPathBuf::from_std_path(LICENSE_RTF_PATH.as_path())\n            .unwrap()\n            .to_string()\n    );\n}\n\n#[test]\n#[serial]\nfn apache2_license_id_works() {\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    env::set_current_dir(package.path()).unwrap();\n    let package_manifest = package.child(CARGO_MANIFEST_FILE);\n    let mut toml: Table = {\n        let mut cargo_toml_handle = File::open(package_manifest.path()).unwrap();\n        let mut cargo_toml_content = String::new();\n        cargo_toml_handle\n            .read_to_string(&mut cargo_toml_content)\n            .unwrap();\n        toml::from_str(&cargo_toml_content).unwrap()\n    };\n    {\n        toml.get_mut(\"package\")\n            .map(|p| {\n                match p {\n                    Value::Table(t) => t.insert(String::from(\"license\"), Value::from(\"Apache-2.0\")),\n                    _ => panic!(\"The 'package' section is not a table\"),\n                };\n                Some(p)\n            })\n            .expect(\"A package section for the Cargo.toml\");\n        let toml_string = toml.to_string();\n        let mut cargo_toml_handle = File::create(package_manifest.path()).unwrap();\n        cargo_toml_handle.write_all(toml_string.as_bytes()).unwrap();\n    }\n    let result = Execution::default().run();\n    env::set_current_dir(original_working_directory).unwrap();\n    assert!(result.is_ok());\n    package\n        .child(WIX_PATH.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(MAIN_WXS_PATH.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(LICENSE_RTF_PATH.as_path())\n        .assert(predicate::path::exists());\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='LicenseFile']/@Name\"\n        ),\n        \"\"\n    );\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='LicenseFile']/@Source\"\n        ),\n        StoredPathBuf::from_std_path(LICENSE_RTF_PATH.as_path())\n            .unwrap()\n            .to_string()\n    );\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:WixVariable[@Id='WixUILicenseRtf']/@Value\"\n        ),\n        StoredPathBuf::from_std_path(LICENSE_RTF_PATH.as_path())\n            .unwrap()\n            .to_string()\n    );\n}\n\n#[test]\n#[serial]\nfn gpl3_license_id_works() {\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    env::set_current_dir(package.path()).unwrap();\n    let package_manifest = package.child(CARGO_MANIFEST_FILE);\n    let mut toml: Table = {\n        let mut cargo_toml_handle = File::open(package_manifest.path()).unwrap();\n        let mut cargo_toml_content = String::new();\n        cargo_toml_handle\n            .read_to_string(&mut cargo_toml_content)\n            .unwrap();\n        toml::from_str(&cargo_toml_content).unwrap()\n    };\n    {\n        toml.get_mut(\"package\")\n            .map(|p| {\n                match p {\n                    Value::Table(t) => t.insert(String::from(\"license\"), Value::from(\"GPL-3.0\")),\n                    _ => panic!(\"The 'package' section is not a table\"),\n                };\n                Some(p)\n            })\n            .expect(\"A package section for the Cargo.toml\");\n        let toml_string = toml.to_string();\n        let mut cargo_toml_handle = File::create(package_manifest.path()).unwrap();\n        cargo_toml_handle.write_all(toml_string.as_bytes()).unwrap();\n    }\n    let result = Execution::default().run();\n    env::set_current_dir(original_working_directory).unwrap();\n    assert!(result.is_ok());\n    package\n        .child(WIX_PATH.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(MAIN_WXS_PATH.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(LICENSE_RTF_PATH.as_path())\n        .assert(predicate::path::exists());\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='LicenseFile']/@Name\"\n        ),\n        \"\"\n    );\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='LicenseFile']/@Source\"\n        ),\n        StoredPathBuf::from_std_path(LICENSE_RTF_PATH.as_path())\n            .unwrap()\n            .to_string()\n    );\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:WixVariable[@Id='WixUILicenseRtf']/@Value\"\n        ),\n        StoredPathBuf::from_std_path(LICENSE_RTF_PATH.as_path())\n            .unwrap()\n            .to_string()\n    );\n}\n\n#[test]\n#[serial]\nfn license_file_field_with_rtf_file_works() {\n    const EXPECTED: &str = \"License_Example.rtf\";\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let package_license = package.child(EXPECTED);\n    env::set_current_dir(package.path()).unwrap();\n    let _license_handle = File::create(package_license.path()).unwrap();\n    let package_manifest = package.child(CARGO_MANIFEST_FILE);\n    let mut toml: Table = {\n        let mut cargo_toml_handle = File::open(package_manifest.path()).unwrap();\n        let mut cargo_toml_content = String::new();\n        cargo_toml_handle\n            .read_to_string(&mut cargo_toml_content)\n            .unwrap();\n        toml::from_str(&cargo_toml_content).unwrap()\n    };\n    {\n        toml.get_mut(\"package\")\n            .map(|p| {\n                match p {\n                    Value::Table(t) => t.insert(\n                        String::from(\"license-file\"),\n                        Value::from(package_license.path().to_str().unwrap()),\n                    ),\n                    _ => panic!(\"The 'package' section is not a table\"),\n                };\n                Some(p)\n            })\n            .expect(\"A package section for the Cargo.toml\");\n        let toml_string = toml.to_string();\n        let mut cargo_toml_handle = File::create(package_manifest.path()).unwrap();\n        cargo_toml_handle.write_all(toml_string.as_bytes()).unwrap();\n    }\n    let result = Execution::default().run();\n    env::set_current_dir(original_working_directory).unwrap();\n    assert!(result.is_ok());\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='LicenseFile']/@Name\"\n        ),\n        \"\"\n    );\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='LicenseFile']/@Source\"\n        ),\n        StoredPathBuf::from_std_path(package_license.path())\n            .unwrap()\n            .to_string()\n    );\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:WixVariable[@Id='WixUILicenseRtf']/@Value\"\n        ),\n        StoredPathBuf::from_std_path(package_license.path())\n            .unwrap()\n            .to_string()\n    );\n}\n\n#[test]\n#[serial]\nfn license_file_field_with_txt_file_works() {\n    const EXPECTED: &str = \"License_Example.txt\";\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let package_license = package.child(EXPECTED);\n    env::set_current_dir(package.path()).unwrap();\n    let _license_handle = File::create(package_license.path()).unwrap();\n    let package_manifest = package.child(CARGO_MANIFEST_FILE);\n    let mut toml: Table = {\n        let mut cargo_toml_handle = File::open(package_manifest.path()).unwrap();\n        let mut cargo_toml_content = String::new();\n        cargo_toml_handle\n            .read_to_string(&mut cargo_toml_content)\n            .unwrap();\n        toml::from_str(&cargo_toml_content).unwrap()\n    };\n    {\n        toml.get_mut(\"package\")\n            .map(|p| {\n                match p {\n                    Value::Table(t) => t.insert(\n                        String::from(\"license-file\"),\n                        Value::from(package_license.path().to_str().unwrap()),\n                    ),\n                    _ => panic!(\"The 'package' section is not a table\"),\n                };\n                Some(p)\n            })\n            .expect(\"A package section for the Cargo.toml\");\n        let toml_string = toml.to_string();\n        let mut cargo_toml_handle = File::create(package_manifest.path()).unwrap();\n        cargo_toml_handle.write_all(toml_string.as_bytes()).unwrap();\n    }\n    let result = Execution::default().run();\n    env::set_current_dir(original_working_directory).unwrap();\n    assert!(result.is_ok());\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='LicenseFile']/@Name\"\n        ),\n        \"\"\n    );\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='LicenseFile']/@Source\"\n        ),\n        StoredPathBuf::from_std_path(package_license.path())\n            .unwrap()\n            .to_string()\n    );\n}\n\n#[test]\n#[serial]\nfn banner_works() {\n    const EXPECTED: &str = \"img\\\\Banner.bmp\";\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let package_banner = package.child(EXPECTED);\n    env::set_current_dir(package.path()).unwrap();\n    fs::create_dir(\"img\").unwrap();\n    let _banner_handle = File::create(package_banner.path()).unwrap();\n    let result = Builder::default()\n        .banner(package_banner.path().to_str())\n        .build()\n        .run();\n    env::set_current_dir(original_working_directory).unwrap();\n    assert!(result.is_ok());\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:WixVariable[@Id='WixUIBannerBmp']/@Value\"\n        ),\n        package_banner.path().to_str().unwrap()\n    );\n}\n\n#[test]\n#[serial]\nfn dialog_works() {\n    const EXPECTED: &str = \"img\\\\Dialog.bmp\";\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let package_dialog = package.child(EXPECTED);\n    env::set_current_dir(package.path()).unwrap();\n    fs::create_dir(\"img\").unwrap();\n    let _dialog_handle = File::create(package_dialog.path()).unwrap();\n    let result = Builder::default()\n        .dialog(package_dialog.path().to_str())\n        .build()\n        .run();\n    env::set_current_dir(original_working_directory).unwrap();\n    assert!(result.is_ok());\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:WixVariable[@Id='WixUIDialogBmp']/@Value\"\n        ),\n        package_dialog.path().to_str().unwrap()\n    );\n}\n\n#[test]\n#[serial]\nfn product_icon_works() {\n    const EXPECTED: &str = \"img\\\\Product.ico\";\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package();\n    let package_product_icon = package.child(EXPECTED);\n    env::set_current_dir(package.path()).unwrap();\n    fs::create_dir(\"img\").unwrap();\n    let _product_icon_handle = File::create(package_product_icon.path()).unwrap();\n    let result = Builder::default()\n        .product_icon(package_product_icon.path().to_str())\n        .build()\n        .run();\n    env::set_current_dir(original_working_directory).unwrap();\n    assert!(result.is_ok());\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:Icon[@Id='ProductICO']/@SourceFile\"\n        ),\n        package_product_icon.path().to_str().unwrap()\n    );\n}\n\n#[test]\n#[serial]\nfn multiple_binaries_works() {\n    const EXPECTED_NAME_1: &str = \"main1\";\n    const EXPECTED_SOURCE_1: &str = \"$(var.CargoTargetBinDir)\\\\main1.exe\";\n    const EXPECTED_NAME_2: &str = \"main2\";\n    const EXPECTED_SOURCE_2: &str = \"$(var.CargoTargetBinDir)\\\\main2.exe\";\n    const EXPECTED_NAME_3: &str = \"main3\";\n    const EXPECTED_SOURCE_3: &str = \"$(var.CargoTargetBinDir)\\\\main3.exe\";\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_package_multiple_binaries();\n    env::set_current_dir(package.path()).unwrap();\n    let result = Builder::default().build().run();\n    env::set_current_dir(original_working_directory).unwrap();\n    assert!(result.is_ok());\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='exe0']/@Name\"\n        ),\n        format!(\"{EXPECTED_NAME_1}.exe\")\n    );\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='exe0']/@Source\"\n        ),\n        EXPECTED_SOURCE_1\n    );\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='exe1']/@Name\"\n        ),\n        format!(\"{EXPECTED_NAME_2}.exe\")\n    );\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='exe1']/@Source\"\n        ),\n        EXPECTED_SOURCE_2\n    );\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='exe2']/@Name\"\n        ),\n        format!(\"{EXPECTED_NAME_3}.exe\")\n    );\n    assert_eq!(\n        common::evaluate_xpath(\n            package.child(MAIN_WXS_PATH.as_path()).path(),\n            \"//*/wix:File[@Id='exe2']/@Source\"\n        ),\n        EXPECTED_SOURCE_3\n    );\n}\n\n#[test]\n#[serial]\nfn workspace_no_package_fails() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_workspace();\n    env::set_current_dir(package.path()).unwrap();\n    let result = Builder::default().build().run();\n    env::set_current_dir(original_working_directory).unwrap();\n    assert!(result.is_err());\n}\n\n#[test]\n#[serial]\nfn workspace_package_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_workspace();\n    env::set_current_dir(package.path()).unwrap();\n    let result = Builder::default()\n        .package(Some(SUBPACKAGE1_NAME))\n        .build()\n        .run();\n    env::set_current_dir(original_working_directory).unwrap();\n    assert!(result.is_ok());\n    package\n        .child(SUBPACKAGE1_NAME)\n        .child(WIX_PATH.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(SUBPACKAGE1_NAME)\n        .child(MAIN_WXS_PATH.as_path())\n        .assert(predicate::path::exists());\n    package\n        .child(SUBPACKAGE1_NAME)\n        .child(LICENSE_RTF_PATH.as_path())\n        .assert(predicate::path::missing());\n}\n\n#[test]\n#[serial]\nfn workspace_package_with_license_works() {\n    init_logging();\n    let original_working_directory = env::current_dir().unwrap();\n    let package = common::create_test_workspace();\n    add_license_to_package(&package.path().join(SUBPACKAGE1_NAME), \"GPL-3.0\");\n    add_license_to_package(&package.path().join(SUBPACKAGE2_NAME), \"GPL-3.0\");\n\n    env::set_current_dir(package.path()).unwrap();\n    let result = Builder::default()\n        .package(Some(SUBPACKAGE1_NAME))\n        .license(Some(\"license\"))\n        .build()\n        .run();\n    env::set_current_dir(original_working_directory).unwrap();\n    assert!(result.is_ok());\n}\n"
  },
  {
    "path": "xtask/Cargo.toml",
    "content": "[package]\nname = \"xtask\"\nversion = \"0.1.0\"\nauthors = [\"Chris Field <cfield2@gmail.com>\"]\nedition = \"2018\"\npublish = false\n\n[dependencies]\nanyhow = \"1\"\nstructopt = \"0.3\"\n"
  },
  {
    "path": "xtask/src/main.rs",
    "content": "use anyhow::{bail, Result};\nuse structopt::StructOpt;\n\nuse std::env;\nuse std::fs;\nuse std::path::{Path, PathBuf};\nuse std::process::Command;\n\n#[derive(Debug, StructOpt)]\nenum XTask {\n    /// Upload documentation to Github Pages (ghp) via the `gh-pages` branch.\n    Ghp,\n}\n\nfn copy<U: AsRef<Path>, V: AsRef<Path>>(from: U, to: V) -> Result<()> {\n    let mut stack = Vec::new();\n    stack.push(PathBuf::from(from.as_ref()));\n\n    let output_root = PathBuf::from(to.as_ref());\n    let input_root = PathBuf::from(from.as_ref()).components().count();\n\n    while let Some(working_path) = stack.pop() {\n        println!(\"process: {}\", &working_path.display());\n\n        // Generate a relative path\n        let src: PathBuf = working_path.components().skip(input_root).collect();\n\n        // Create a destination if missing\n        let dest = if src.components().count() == 0 {\n            output_root.clone()\n        } else {\n            output_root.join(&src)\n        };\n        if fs::metadata(&dest).is_err() {\n            println!(\"mkdir: {}\", dest.display());\n            fs::create_dir_all(&dest)?;\n        }\n\n        for entry in fs::read_dir(working_path)? {\n            let entry = entry?;\n            let path = entry.path();\n            if path.is_dir() {\n                stack.push(path);\n            } else {\n                match path.file_name() {\n                    Some(filename) => {\n                        let dest_path = dest.join(filename);\n                        println!(\"copy: {} -> {}\", &path.display(), &dest_path.display());\n                        fs::copy(&path, &dest_path)?;\n                    }\n                    None => {\n                        println!(\"failed: {}\", path.display());\n                    }\n                }\n            }\n        }\n    }\n\n    Ok(())\n}\n\nfn main() -> Result<()> {\n    let args = XTask::from_args();\n    match args {\n        XTask::Ghp => {\n            let cargo = env::var(\"CARGO\")\n                .map(PathBuf::from)\n                .ok()\n                .unwrap_or_else(|| PathBuf::from(\"cargo\"));\n            if !Command::new(cargo)\n                .arg(\"doc\")\n                .arg(\"--no-deps\")\n                .status()?\n                .success()\n            {\n                bail!(\"The 'cargo doc --no-deps' command failed\");\n            }\n            if !Command::new(\"git\")\n                .arg(\"checkout\")\n                .arg(\"gh-pages\")\n                .status()?\n                .success()\n            {\n                bail!(\"The 'git checkout gh-pages' command failed\");\n            }\n            let mut target_doc_dir = PathBuf::from(\"target\");\n            target_doc_dir.push(\"doc\");\n            copy(target_doc_dir, env::current_dir()?)?;\n            if !Command::new(\"git\")\n                .arg(\"add\")\n                .arg(\"--verbose\")\n                .arg(\"--all\")\n                .status()?\n                .success()\n            {\n                bail!(\"The 'git add --verbose --all' command failed\")\n            }\n            if Command::new(\"git\")\n                .arg(\"commit\")\n                .arg(\"--verbose\")\n                .arg(\"-m\")\n                .arg(\"Change content to match latest revision\")\n                .status()?\n                .success()\n            {\n                if !Command::new(\"git\").arg(\"push\").status()?.success() {\n                    bail!(\"The 'git push' command failed\");\n                }\n            }\n            if !Command::new(\"git\")\n                .arg(\"checkout\")\n                .arg(\"-\")\n                .status()?\n                .success()\n            {\n                bail!(\"The 'git checkout main' command failed\");\n            }\n        }\n    }\n    Ok(())\n}\n"
  }
]