Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

gleam / compiler-cli / src / lib.rs
32 kB 1045 lines
1// SPDX-License-Identifier: Apache-2.0 2// SPDX-FileCopyrightText: 2018 The Gleam contributors 3 4#![warn( 5 clippy::all, 6 clippy::redundant_clone, 7 clippy::dbg_macro, 8 clippy::todo, 9 clippy::mem_forget, 10 clippy::use_self, 11 clippy::filter_map_next, 12 clippy::needless_continue, 13 clippy::needless_borrow, 14 clippy::match_wildcard_for_single_variants, 15 clippy::imprecise_flops, 16 clippy::suboptimal_flops, 17 clippy::lossy_float_literal, 18 clippy::rest_pat_in_fully_bound_structs, 19 clippy::fn_params_excessive_bools, 20 clippy::inefficient_to_string, 21 clippy::linkedlist, 22 clippy::macro_use_imports, 23 clippy::option_option, 24 clippy::verbose_file_reads, 25 clippy::unnested_or_patterns, 26 clippy::default_trait_access, 27 clippy::format_push_string, 28 rust_2018_idioms, 29 missing_debug_implementations, 30 missing_copy_implementations, 31 trivial_casts, 32 trivial_numeric_casts, 33 nonstandard_style, 34 unexpected_cfgs, 35 unused_import_braces, 36 unused_qualifications 37)] 38#![deny( 39 clippy::await_holding_lock, 40 clippy::if_let_mutex, 41 clippy::indexing_slicing, 42 clippy::mem_forget, 43 clippy::ok_expect, 44 clippy::unimplemented, 45 clippy::unwrap_used, 46 unsafe_code, 47 unstable_features, 48 unused_results 49)] 50#![allow( 51 clippy::match_single_binding, 52 clippy::inconsistent_struct_constructor, 53 clippy::assign_op_pattern, 54 clippy::len_without_is_empty, 55 clippy::let_unit_value 56)] 57 58#[cfg(test)] 59#[macro_use] 60extern crate pretty_assertions; 61 62mod add; 63mod beam_compiler; 64mod build; 65mod build_lock; 66mod cli; 67mod compile_package; 68mod config; 69mod dependencies; 70mod docs; 71mod export; 72mod fix; 73mod format; 74pub mod fs; 75mod hex; 76mod http; 77mod lsp; 78mod new; 79mod owner; 80mod panic; 81mod publish; 82mod remove; 83pub mod run; 84mod shell; 85mod text_layout; 86 87use config::root_config; 88use fs::{get_current_directory, get_project_root}; 89pub use gleam_core::error::{Error, Result}; 90 91use camino::Utf8PathBuf; 92use clap::{ 93 Args, Parser, Subcommand, 94 builder::{Styles, styling}, 95}; 96use gleam_core::{ 97 analyse::TargetSupport, 98 build::{Codegen, Compile, Mode, NullTelemetry, Options, Runtime, Target}, 99 hex::RetirementReason, 100 paths::ProjectPaths, 101 version::COMPILER_VERSION, 102}; 103 104#[derive(Args, Debug, Clone)] 105pub struct UpdateOptions { 106 /// (optional) Names of the packages to update 107 /// If omitted, all dependencies will be updated 108 #[arg(verbatim_doc_comment)] 109 packages: Vec<String>, 110} 111 112#[derive(Args, Debug, Clone)] 113pub struct TreeOptions { 114 /// Name of the package to get the dependency tree for 115 #[arg( 116 short, 117 long, 118 ignore_case = true, 119 conflicts_with = "invert", 120 help = "Package to be used as the root of the tree" 121 )] 122 package: Option<String>, 123 /// Name of the package to get the inverted dependency tree for 124 #[arg( 125 short, 126 long, 127 ignore_case = true, 128 conflicts_with = "package", 129 help = "Invert the tree direction and focus on the given package", 130 value_name = "PACKAGE" 131 )] 132 invert: Option<String>, 133} 134 135#[derive(Parser, Debug)] 136#[command( 137 version, 138 name = "gleam", 139 next_display_order = None, 140 help_template = "\ 141{before-help}{name} {version} 142 143{usage-heading} {usage} 144 145{all-args}{after-help}", 146 styles = Styles::styled() 147 .header(styling::AnsiColor::Yellow.on_default()) 148 .usage(styling::AnsiColor::Yellow.on_default()) 149 .literal(styling::AnsiColor::Green.on_default()) 150)] 151pub enum Command { 152 /// Build the project 153 /// 154 /// This command optionally accepts the environment variable 155 /// `HEXPM_READ_API_KEY`, which can hold a Hex API key to authenticate 156 /// with Hex with a higher rate limit. 157 /// 158 #[command(verbatim_doc_comment)] 159 Build { 160 /// Consider the build failed if the package contains any warnings 161 #[arg(long)] 162 warnings_as_errors: bool, 163 164 /// Which compilation target to use 165 #[arg(short, long, ignore_case = true, help = target_doc())] 166 target: Option<Target>, 167 168 #[arg(long, help = no_print_progress_doc())] 169 no_print_progress: bool, 170 }, 171 172 /// Type check the project 173 Check { 174 /// Which compilation target to use 175 #[arg(short, long, ignore_case = true, help = target_doc())] 176 target: Option<Target>, 177 }, 178 179 /// Publish the project to the Hex package repository 180 /// 181 /// Please ensure your package is suitable for production use before 182 /// publishing. If you have a prototype package that you wish to use 183 /// in another project then use git dependencies instead of publishing 184 /// to the package repository. 185 /// 186 /// This command optionally accepts the environment variable 187 /// `HEXPM_API_KEY`, which can hold a Hex API key to authenticate 188 /// with Hex. 189 /// 190 #[command(verbatim_doc_comment)] 191 Publish { 192 /// Replace the latest release with this one 193 #[arg(long)] 194 replace: bool, 195 /// Automatically accept confirmation prompts 196 #[arg(short, long)] 197 yes: bool, 198 }, 199 200 /// Render HTML documentation for the package 201 /// 202 /// There are several options in `gleam.toml` that can be used to 203 /// configure the output. 204 /// 205 /// repository = { type = "github", user = "lpil", repo = "wibble" } 206 /// 207 /// Specify the location of the source code repository for the package. 208 /// This will add a link to the side bar, and add "view code" links for 209 /// the documented types and values. 210 /// 211 /// links = [ 212 /// { title = "Home page", href = "https://example.com" }, 213 /// { title = "Other site", href = "https://another.example.com" }, 214 /// ] 215 /// 216 /// Specify some additional links to include in the sidebar. 217 /// 218 /// [documentation] 219 /// pages = [ 220 /// { title = "My Page", path = "my-page.html", source = "./path/to/my-page.md" }, 221 /// ] 222 /// 223 /// Specify additional markdown pages to include in the documentation, 224 /// with links for each added to the sidebar. 225 /// 226 #[command(subcommand, verbatim_doc_comment)] 227 Docs(Docs), 228 229 /// Work with dependency packages 230 /// 231 /// The packages and the acceptable version ranges are specified in 232 /// `gleam.toml`. You can edit this file manually, or use the `gleam add` 233 /// and `gleam remove` commands. 234 /// 235 /// Package versions follow semantic versioning: MAJOR.MINOR.PATCH. 236 /// - Major updates include breaking changes, either type changes or 237 /// semantic changes. 238 /// - Minor updates include new functionality, but no breaking changes. 239 /// - Patch updates include only bug fixes. 240 /// 241 /// Dependency resolution will be performed automatically by any command 242 /// that builds your project. Once versions have been selected they are 243 /// written to `manifest.toml`, which locks the package to those versions, 244 /// making your build deterministic. You should not edit this file manually. 245 /// 246 /// To upgrade the dependencies you can use the `gleam update` command, 247 /// which will select the newest versions compatible with the requirements 248 /// in `gleam.toml`. 249 /// 250 /// The `[dependencies]` section of `gleam.toml` holds the production 251 /// dependencies. These are included in your production application, or 252 /// are used as the dependencies when published as a library. The 253 /// `[dev_dependencies]` section holds dependencies that are only used 254 /// during development, e.g. code used for testing the package. 255 /// 256 /// ## Syntax examples: 257 /// 258 /// [dependencies] 259 /// wibble = ">= 1.2.0 and < 2.0.0" 260 /// 261 /// Require the package `wibble`, permitting versions greater than or 262 /// equal to 1.2.0, but lower than 2.0.0. 263 /// 264 /// [dependencies] 265 /// wibble = ">= 1.2.0 and < 2.0.0 and != 1.4.1" 266 /// 267 /// Permit a range, but deny a specific version within that range. This 268 /// could be useful if there is a version known to have a bug. 269 /// 270 /// [dependencies] 271 /// wibble = { git = "https://example.com/wibble.git", ref = "a8b3c5d82" } 272 /// 273 /// A dependency fetched from Git instead of from Hex. This is useful 274 /// for using packages of yours that are not-yet production-ready, or 275 /// for bug fixes that have not yet been published to Hex. 276 /// 277 /// [dependencies] 278 /// wibble = { path = "../wibble" } 279 /// 280 /// A local dependency, on your computer. This is useful for testing and 281 /// and for applications made of multiple packages in a single version 282 /// control repository. 283 /// 284 /// This command optionally accepts the environment variable 285 /// `HEXPM_READ_API_KEY`, which can hold a Hex API key to authenticate 286 /// with Hex with a higher rate limit. 287 /// 288 #[command(subcommand, verbatim_doc_comment)] 289 Deps(Dependencies), 290 291 /// Update dependency packages to their latest versions 292 /// 293 /// This command optionally accepts the environment variable 294 /// `HEXPM_READ_API_KEY`, which can hold a Hex API key to authenticate 295 /// with Hex with a higher rate limit. 296 /// 297 #[command(verbatim_doc_comment)] 298 Update(UpdateOptions), 299 300 /// Work with the Hex package manager 301 #[command(subcommand)] 302 Hex(Hex), 303 304 /// Create a new project 305 New(NewOptions), 306 307 /// Format source code 308 Format { 309 /// The files or directories to format 310 #[arg(default_value = ".")] 311 files: Vec<String>, 312 313 /// Read source from standard-input 314 #[arg(long)] 315 stdin: bool, 316 317 /// Only check if inputs are formatted correctly, erroring if they are not 318 #[arg(long)] 319 check: bool, 320 }, 321 322 /// Rewrite deprecated Gleam code 323 Fix, 324 325 /// Start an Erlang REPL with the Gleam code loaded 326 Shell, 327 328 /// Run the project 329 /// 330 /// This command runs the `main` function from the `<PROJECT_NAME>` module. 331 #[command(trailing_var_arg = true)] 332 Run { 333 /// Which compilation target to use 334 #[arg(short, long, ignore_case = true, help = target_doc())] 335 target: Option<Target>, 336 337 /// Which runtime to use 338 #[arg(long, ignore_case = true, help = runtime_doc())] 339 runtime: Option<Runtime>, 340 341 /// The module to run 342 #[arg(short, long)] 343 module: Option<String>, 344 345 #[arg(long, help = no_print_progress_doc())] 346 no_print_progress: bool, 347 348 arguments: Vec<String>, 349 }, 350 351 /// Run the project tests 352 /// 353 /// This command runs the `main` function from the `<PROJECT_NAME>_test` module. 354 #[command(trailing_var_arg = true)] 355 Test { 356 /// Which compilation target to use 357 #[arg(short, long, ignore_case = true, help = target_doc())] 358 target: Option<Target>, 359 360 /// Which runtime to use 361 #[arg(long, ignore_case = true, help = runtime_doc())] 362 runtime: Option<Runtime>, 363 364 arguments: Vec<String>, 365 }, 366 367 /// Run the project development entrypoint 368 /// 369 /// This command runs the `main` function from the `<PROJECT_NAME>_dev` module. 370 #[command(trailing_var_arg = true)] 371 Dev { 372 /// Which compilation target to use 373 #[arg(short, long, ignore_case = true, help = target_doc())] 374 target: Option<Target>, 375 376 /// Which runtime to use 377 #[arg(long, ignore_case = true, help = runtime_doc())] 378 runtime: Option<Runtime>, 379 380 #[arg(long, help = no_print_progress_doc())] 381 no_print_progress: bool, 382 383 arguments: Vec<String>, 384 }, 385 386 /// A low-level API for compiling a single Gleam package 387 /// 388 /// This is to be used by other build tools to implement support for Gleam 389 /// code. It is not used directly by humans. 390 /// 391 #[command(verbatim_doc_comment)] 392 CompilePackage(CompilePackage), 393 394 /// Read and print gleam.toml for debugging 395 #[command(hide = true)] 396 PrintConfig, 397 398 /// Add new dependencies 399 /// 400 /// The newest compatible version of the package is determined, and then 401 /// `gleam.toml` is updated to require at least that version, with a range 402 /// permitting future patch and minor updates. 403 /// 404 /// Add the package "wibble": 405 /// gleam add wibble 406 /// 407 /// Add the package "wibble", requiring version >= v2.0.0 and < v3.0.0: 408 /// gleam add wibble@2 409 /// 410 /// Add the package "wibble", requiring version >= v2.5.1 and < v3.0.0: 411 /// gleam add wibble@2.5.1 412 /// 413 /// Add multiple packages: 414 /// gleam add wibble@2 warble@1 415 /// 416 /// Add a package as a non-production dependency: 417 /// gleam add --dev wibble 418 /// 419 /// You can also edit `gleam.toml` directly, for further control over your 420 /// package dependencies. Run `gleam help deps` for documentation on the 421 /// format. 422 /// 423 #[command(verbatim_doc_comment)] 424 Add { 425 /// The names of Hex packages to add 426 #[arg(required = true)] 427 packages: Vec<String>, 428 429 /// Add the packages as dev-only dependencies 430 #[arg(long)] 431 dev: bool, 432 }, 433 434 /// Remove project dependencies 435 Remove { 436 /// The names of packages to remove 437 #[arg(required = true)] 438 packages: Vec<String>, 439 }, 440 441 /// Delete any build artefacts for this project 442 Clean, 443 444 /// Run the language server, to be used by editors 445 #[command(name = "lsp")] 446 LanguageServer, 447 448 /// Export something useful from the Gleam project 449 #[command(subcommand)] 450 Export(ExportTarget), 451} 452 453impl Command { 454 pub fn run(self, directory: Utf8PathBuf) -> Result<(), Error> { 455 match self { 456 Self::Build { 457 target, 458 warnings_as_errors, 459 no_print_progress, 460 } => { 461 let paths = find_project_paths(directory)?; 462 command_build(&paths, target, warnings_as_errors, no_print_progress) 463 } 464 465 Self::Check { target } => { 466 let paths = find_project_paths(directory)?; 467 command_check(&paths, target) 468 } 469 470 Self::Docs(Docs::Build { open, target }) => { 471 let paths = find_project_paths(directory)?; 472 docs::build(&paths, docs::BuildOptions { open, target }) 473 } 474 475 Self::Docs(Docs::Publish) => { 476 let paths = find_project_paths(directory)?; 477 docs::publish(&paths) 478 } 479 480 Self::Docs(Docs::Remove { package, version }) => docs::remove(package, version), 481 482 Self::Format { 483 stdin, 484 files, 485 check, 486 } => format::run(stdin, check, files), 487 488 Self::Fix => { 489 let paths = find_project_paths(directory)?; 490 fix::run(&paths) 491 } 492 493 Self::Deps(Dependencies::List) => { 494 let paths = find_project_paths(directory)?; 495 dependencies::list(&paths) 496 } 497 498 Self::Deps(Dependencies::Download) => { 499 let paths = find_project_paths(directory)?; 500 download_dependencies(&paths) 501 } 502 503 Self::Deps(Dependencies::Outdated) => { 504 let paths = find_project_paths(directory)?; 505 dependencies::outdated(&paths) 506 } 507 508 Self::Deps(Dependencies::Update(options)) => { 509 let paths = find_project_paths(directory)?; 510 dependencies::update(&paths, options.packages) 511 } 512 513 Self::Deps(Dependencies::Tree(options)) => { 514 let paths = find_project_paths(directory)?; 515 dependencies::tree(&paths, options) 516 } 517 518 Self::Hex(Hex::Authenticate) => hex::authenticate(), 519 520 Self::New(options) => new::create(options, COMPILER_VERSION), 521 522 Self::Shell => { 523 let paths = find_project_paths(directory)?; 524 shell::command(&paths) 525 } 526 527 Self::Run { 528 target, 529 arguments, 530 runtime, 531 module, 532 no_print_progress, 533 } => { 534 let paths = find_project_paths(directory)?; 535 run::command( 536 &paths, 537 arguments, 538 target, 539 runtime, 540 module, 541 run::Which::Src, 542 no_print_progress, 543 ) 544 } 545 546 Self::Test { 547 target, 548 arguments, 549 runtime, 550 } => { 551 let paths = find_project_paths(directory)?; 552 run::command( 553 &paths, 554 arguments, 555 target, 556 runtime, 557 None, 558 run::Which::Test, 559 false, 560 ) 561 } 562 563 Self::Dev { 564 target, 565 arguments, 566 runtime, 567 no_print_progress, 568 } => { 569 let paths = find_project_paths(directory)?; 570 run::command( 571 &paths, 572 arguments, 573 target, 574 runtime, 575 None, 576 run::Which::Dev, 577 no_print_progress, 578 ) 579 } 580 581 Self::CompilePackage(opts) => compile_package::command(opts), 582 583 Self::Publish { replace, yes } => { 584 let paths = find_project_paths(directory)?; 585 publish::command(&paths, replace, yes) 586 } 587 588 Self::PrintConfig => { 589 let paths = find_project_paths(directory)?; 590 print_config(&paths) 591 } 592 593 Self::Hex(Hex::Retire { 594 package, 595 version, 596 reason, 597 message, 598 }) => hex::retire(package, version, reason, message), 599 600 Self::Hex(Hex::Unretire { package, version }) => hex::unretire(package, version), 601 602 Self::Hex(Hex::Revert { package, version }) => { 603 let paths = find_project_paths(directory)?; 604 hex::revert(&paths, package, version) 605 } 606 607 Self::Hex(Hex::Owner(Owner::Add { 608 package, 609 username_or_email, 610 level, 611 })) => owner::add(package, username_or_email, level), 612 613 Self::Hex(Hex::Owner(Owner::Transfer { 614 package, 615 username_or_email, 616 })) => owner::transfer(package, username_or_email), 617 618 Self::Add { packages, dev } => { 619 let paths = find_project_paths(directory)?; 620 add::command(&paths, packages, dev) 621 } 622 623 Self::Remove { packages } => { 624 let paths = find_project_paths(directory)?; 625 remove::command(&paths, packages) 626 } 627 628 Self::Update(options) => { 629 let paths = find_project_paths(directory)?; 630 dependencies::update(&paths, options.packages) 631 } 632 633 Self::Clean => { 634 let paths = find_project_paths(directory)?; 635 clean(&paths) 636 } 637 638 Self::LanguageServer => lsp::main(), 639 640 Self::Export(ExportTarget::ErlangShipment) => { 641 let paths = find_project_paths(directory)?; 642 export::erlang_shipment(&paths) 643 } 644 Self::Export(ExportTarget::Escript) => { 645 let paths = find_project_paths(directory)?; 646 export::escript(&paths) 647 } 648 Self::Export(ExportTarget::HexTarball) => { 649 let paths = find_project_paths(directory)?; 650 export::hex_tarball(&paths) 651 } 652 Self::Export(ExportTarget::JavascriptPrelude) => export::javascript_prelude(), 653 Self::Export(ExportTarget::TypescriptPrelude) => export::typescript_prelude(), 654 Self::Export(ExportTarget::PackageInterface { output }) => { 655 let paths = find_project_paths(directory)?; 656 export::package_interface(&paths, output) 657 } 658 Self::Export(ExportTarget::PackageInformation { output }) => { 659 let paths = find_project_paths(directory)?; 660 export::package_information(&paths, output) 661 } 662 Self::Export(ExportTarget::ZedExtension) => { 663 let paths = find_project_paths(directory)?; 664 export::zed_extension(&paths) 665 } 666 } 667 } 668} 669 670fn template_doc() -> &'static str { 671 "The template to use" 672} 673 674fn target_doc() -> &'static str { 675 "The platform to target" 676} 677 678fn no_print_progress_doc() -> &'static str { 679 "Don't print progress information" 680} 681 682fn runtime_doc() -> &'static str { 683 "The JavaScript runtime to target. This is only available on the \ 684 JavaScript target" 685} 686 687#[derive(Subcommand, Debug, Clone)] 688pub enum ExportTarget { 689 /// Precompiled Erlang in a single file, suitable for CLIs 690 Escript, 691 /// Precompiled Erlang, suitable for deployment 692 ErlangShipment, 693 /// The package bundled into a tarball, suitable for publishing to Hex 694 HexTarball, 695 /// The JavaScript prelude module 696 JavascriptPrelude, 697 /// The TypeScript prelude module 698 TypescriptPrelude, 699 /// Information on the modules, functions, and types in the project in JSON format 700 PackageInterface { 701 /// The path to write the JSON file to 702 #[arg(long = "out", required = true)] 703 output: Utf8PathBuf, 704 }, 705 /// Package information (gleam.toml) in JSON format 706 PackageInformation { 707 /// The path to write the JSON file to 708 #[arg(long = "out", required = true)] 709 output: Utf8PathBuf, 710 }, 711 /// Build a Zed editor extension component (`extension.wasm`) 712 /// 713 /// Compiles the package for the Wasm target and packages a full 714 /// `zed:extension` world guest (API 0.7.0) with `zed:api-version`. 715 /// Writes `extension.wasm` at the package root. 716 #[command(name = "zed-extension")] 717 ZedExtension, 718} 719 720#[derive(Args, Debug, Clone)] 721pub struct NewOptions { 722 /// Location of the project root 723 pub project_root: String, 724 725 /// Name of the project 726 #[arg(long)] 727 pub name: Option<String>, 728 729 #[arg(long, ignore_case = true, default_value = "erlang", help = template_doc())] 730 pub template: new::Template, 731 732 /// Skip git initialization and creation of .gitignore, .git/* and .github/* files 733 #[arg(long)] 734 pub skip_git: bool, 735 736 /// Skip creation of .github/* files 737 #[arg(long)] 738 pub skip_github: bool, 739} 740 741#[derive(Args, Debug)] 742pub struct CompilePackage { 743 /// The compilation target for the generated project 744 #[arg(long, ignore_case = true, help = target_doc())] 745 target: Target, 746 747 /// The directory of the Gleam package 748 #[arg(long = "package")] 749 package_directory: Utf8PathBuf, 750 751 /// A directory to write compiled package to 752 #[arg(long = "out")] 753 output_directory: Utf8PathBuf, 754 755 /// A directories of precompiled Gleam projects 756 #[arg(long = "lib")] 757 libraries_directory: Utf8PathBuf, 758 759 /// The location of the JavaScript prelude module, relative to the `out` 760 /// directory. 761 /// 762 /// Required when compiling to JavaScript. 763 /// 764 /// This likely wants to be a `.mjs` file as NodeJS does not permit 765 /// importing of other JavaScript file extensions. 766 /// 767 #[arg(verbatim_doc_comment, long = "javascript-prelude")] 768 javascript_prelude: Option<Utf8PathBuf>, 769 770 /// Skip Erlang to BEAM bytecode compilation 771 #[arg(long = "no-beam")] 772 skip_beam_compilation: bool, 773} 774 775#[derive(Subcommand, Debug)] 776pub enum Dependencies { 777 /// List all dependency packages 778 List, 779 780 /// Download all dependency packages 781 Download, 782 783 /// List all outdated dependencies 784 Outdated, 785 786 /// Update dependency packages to their latest versions 787 Update(UpdateOptions), 788 789 /// Tree of all the dependency packages 790 Tree(TreeOptions), 791} 792 793#[derive(Subcommand, Debug)] 794pub enum Hex { 795 /// Retire a release from Hex 796 /// 797 /// This command uses the environment variable: 798 /// 799 /// - HEXPM_API_KEY: (optional) A Hex API key to authenticate with the Hex package manager. 800 /// 801 #[command(verbatim_doc_comment)] 802 Retire { 803 /// The name of the package to retire 804 #[arg(long)] 805 package: String, 806 /// The version to retire 807 #[arg(long)] 808 version: String, 809 /// The reason for the retirement 810 #[arg(long)] 811 reason: RetirementReason, 812 message: Option<String>, 813 }, 814 815 /// Un-retire a release from Hex 816 /// 817 /// This command uses this environment variable: 818 /// 819 /// - HEXPM_API_KEY: (optional) A Hex API key to authenticate with the Hex package manager. 820 /// 821 #[command(verbatim_doc_comment)] 822 Unretire { 823 /// The name of the package to unretire 824 #[arg(long)] 825 package: String, 826 /// The version to unretire 827 #[arg(long)] 828 version: String, 829 }, 830 831 /// Revert a release, removing it from Hex. 832 /// 833 /// Releases can only be reverted within 24 hours since they were published. 834 /// 835 /// This command uses this environment variable: 836 /// 837 /// - HEXPM_API_KEY: (optional) A Hex API key to authenticate with the Hex package manager. 838 /// 839 #[command(verbatim_doc_comment)] 840 Revert { 841 /// The name of the package to revert 842 #[arg(long)] 843 package: Option<String>, 844 845 /// The version to revert 846 #[arg(long)] 847 version: Option<String>, 848 }, 849 850 /// Deal with package ownership 851 #[command(subcommand)] 852 Owner(Owner), 853 854 /// Log in to Hex. Replaces the credentials with new ones if already logged in. 855 Authenticate, 856} 857 858#[derive(Subcommand, Debug)] 859pub enum Owner { 860 /// Adds a new owner to the given package on Hex 861 /// 862 /// This command uses this environment variable: 863 /// 864 /// - HEXPM_API_KEY: (optional) A Hex API key to authenticate against the Hex package manager. 865 /// 866 #[command(verbatim_doc_comment)] 867 Add { 868 #[arg(long)] 869 package: String, 870 871 /// The username or email of the additional owner 872 #[arg(long = "user")] 873 username_or_email: String, 874 875 /// The ownership level 876 #[arg(long, default_value = "maintainer")] 877 level: hexpm::OwnerLevel, 878 }, 879 880 /// Transfers ownership of the given package to a new Hex user 881 /// 882 /// This command uses this environment variable: 883 /// 884 /// - HEXPM_API_KEY: (optional) A Hex API key to authenticate against the Hex package manager. 885 /// 886 #[command(verbatim_doc_comment)] 887 Transfer { 888 /// The name of the package 889 #[arg(long)] 890 package: String, 891 892 /// The username or email of the new owner 893 #[arg(long = "user")] 894 username_or_email: String, 895 }, 896} 897 898#[derive(Subcommand, Debug)] 899pub enum Docs { 900 /// Render HTML docs locally 901 Build { 902 /// Opens the docs in a browser after rendering 903 #[arg(long)] 904 open: bool, 905 906 /// Which compilation target to use 907 #[arg(short, long, ignore_case = true, help = target_doc())] 908 target: Option<Target>, 909 }, 910 911 /// Publish HTML docs to HexDocs 912 /// 913 /// This command uses this environment variable: 914 /// 915 /// - HEXPM_API_KEY: (optional) A Hex API key to authenticate with the Hex package manager. 916 /// 917 #[command(verbatim_doc_comment)] 918 Publish, 919 920 /// Remove HTML docs from HexDocs 921 /// 922 /// This command uses this environment variable: 923 /// 924 /// - HEXPM_API_KEY: (optional) A Hex API key to authenticate with the Hex package manager. 925 /// 926 #[command(verbatim_doc_comment)] 927 Remove { 928 /// The name of the package 929 #[arg(long)] 930 package: String, 931 932 /// The version of the docs to remove 933 #[arg(long)] 934 version: String, 935 }, 936} 937 938pub fn main() { 939 initialise_logger(); 940 panic::add_handler(); 941 let stderr = cli::stderr_buffer_writer(); 942 let result = get_current_directory() 943 .and_then(|working_directory| Command::parse().run(working_directory)); 944 match result { 945 Ok(_) => { 946 tracing::info!("Successfully completed"); 947 } 948 Err(error) => { 949 tracing::error!(error = ?error, "Failed"); 950 let mut buffer = stderr.buffer(); 951 error.pretty(&mut buffer); 952 stderr.print(&buffer).expect("Final result error writing"); 953 std::process::exit(1); 954 } 955 } 956} 957 958fn command_check(paths: &ProjectPaths, target: Option<Target>) -> Result<()> { 959 let _ = build::main( 960 paths, 961 Options { 962 root_target_support: TargetSupport::Enforced, 963 warnings_as_errors: false, 964 codegen: Codegen::DepsOnly, 965 compile: Compile::All, 966 mode: Mode::Dev, 967 target, 968 no_print_progress: false, 969 }, 970 build::download_dependencies(paths, cli::Reporter::new())?, 971 )?; 972 Ok(()) 973} 974 975fn command_build( 976 paths: &ProjectPaths, 977 target: Option<Target>, 978 warnings_as_errors: bool, 979 no_print_progress: bool, 980) -> Result<()> { 981 let manifest = if no_print_progress { 982 build::download_dependencies(paths, NullTelemetry)? 983 } else { 984 build::download_dependencies(paths, cli::Reporter::new())? 985 }; 986 let _ = build::main( 987 paths, 988 Options { 989 root_target_support: TargetSupport::Enforced, 990 warnings_as_errors, 991 codegen: Codegen::All, 992 compile: Compile::All, 993 mode: Mode::Dev, 994 target, 995 no_print_progress, 996 }, 997 manifest, 998 )?; 999 Ok(()) 1000} 1001 1002fn print_config(paths: &ProjectPaths) -> Result<()> { 1003 let config = root_config(paths)?; 1004 println!("{config:#?}"); 1005 Ok(()) 1006} 1007 1008fn clean(paths: &ProjectPaths) -> Result<()> { 1009 fs::delete_directory(&paths.build_directory()) 1010} 1011 1012fn initialise_logger() { 1013 let enable_colours = std::env::var("GLEAM_LOG_NOCOLOUR").is_err(); 1014 tracing_subscriber::fmt() 1015 .with_writer(std::io::stderr) 1016 .with_env_filter(std::env::var("GLEAM_LOG").unwrap_or_else(|_| "off".into())) 1017 .with_target(false) 1018 .with_ansi(enable_colours) 1019 .without_time() 1020 .init(); 1021} 1022 1023fn find_project_paths(current_dir: Utf8PathBuf) -> Result<ProjectPaths> { 1024 get_project_root(current_dir).map(ProjectPaths::new) 1025} 1026 1027#[cfg(test)] 1028fn project_paths_at_current_directory_without_toml() -> ProjectPaths { 1029 let current_dir = get_current_directory().expect("Failed to get current directory"); 1030 ProjectPaths::new(current_dir) 1031} 1032 1033fn download_dependencies(paths: &ProjectPaths) -> Result<()> { 1034 _ = dependencies::resolve_and_download( 1035 paths, 1036 cli::Reporter::new(), 1037 None, 1038 Vec::new(), 1039 dependencies::DependencyManagerConfig { 1040 use_manifest: dependencies::UseManifest::Yes, 1041 check_major_versions: dependencies::CheckMajorVersions::No, 1042 }, 1043 )?; 1044 Ok(()) 1045}