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
31 kB 1034 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 } 663 } 664} 665 666fn template_doc() -> &'static str { 667 "The template to use" 668} 669 670fn target_doc() -> &'static str { 671 "The platform to target" 672} 673 674fn no_print_progress_doc() -> &'static str { 675 "Don't print progress information" 676} 677 678fn runtime_doc() -> &'static str { 679 "The JavaScript runtime to target. This is only available on the \ 680 JavaScript target" 681} 682 683#[derive(Subcommand, Debug, Clone)] 684pub enum ExportTarget { 685 /// Precompiled Erlang in a single file, suitable for CLIs 686 Escript, 687 /// Precompiled Erlang, suitable for deployment 688 ErlangShipment, 689 /// The package bundled into a tarball, suitable for publishing to Hex 690 HexTarball, 691 /// The JavaScript prelude module 692 JavascriptPrelude, 693 /// The TypeScript prelude module 694 TypescriptPrelude, 695 /// Information on the modules, functions, and types in the project in JSON format 696 PackageInterface { 697 /// The path to write the JSON file to 698 #[arg(long = "out", required = true)] 699 output: Utf8PathBuf, 700 }, 701 /// Package information (gleam.toml) in JSON format 702 PackageInformation { 703 /// The path to write the JSON file to 704 #[arg(long = "out", required = true)] 705 output: Utf8PathBuf, 706 }, 707} 708 709#[derive(Args, Debug, Clone)] 710pub struct NewOptions { 711 /// Location of the project root 712 pub project_root: String, 713 714 /// Name of the project 715 #[arg(long)] 716 pub name: Option<String>, 717 718 #[arg(long, ignore_case = true, default_value = "erlang", help = template_doc())] 719 pub template: new::Template, 720 721 /// Skip git initialization and creation of .gitignore, .git/* and .github/* files 722 #[arg(long)] 723 pub skip_git: bool, 724 725 /// Skip creation of .github/* files 726 #[arg(long)] 727 pub skip_github: bool, 728} 729 730#[derive(Args, Debug)] 731pub struct CompilePackage { 732 /// The compilation target for the generated project 733 #[arg(long, ignore_case = true, help = target_doc())] 734 target: Target, 735 736 /// The directory of the Gleam package 737 #[arg(long = "package")] 738 package_directory: Utf8PathBuf, 739 740 /// A directory to write compiled package to 741 #[arg(long = "out")] 742 output_directory: Utf8PathBuf, 743 744 /// A directories of precompiled Gleam projects 745 #[arg(long = "lib")] 746 libraries_directory: Utf8PathBuf, 747 748 /// The location of the JavaScript prelude module, relative to the `out` 749 /// directory. 750 /// 751 /// Required when compiling to JavaScript. 752 /// 753 /// This likely wants to be a `.mjs` file as NodeJS does not permit 754 /// importing of other JavaScript file extensions. 755 /// 756 #[arg(verbatim_doc_comment, long = "javascript-prelude")] 757 javascript_prelude: Option<Utf8PathBuf>, 758 759 /// Skip Erlang to BEAM bytecode compilation 760 #[arg(long = "no-beam")] 761 skip_beam_compilation: bool, 762} 763 764#[derive(Subcommand, Debug)] 765pub enum Dependencies { 766 /// List all dependency packages 767 List, 768 769 /// Download all dependency packages 770 Download, 771 772 /// List all outdated dependencies 773 Outdated, 774 775 /// Update dependency packages to their latest versions 776 Update(UpdateOptions), 777 778 /// Tree of all the dependency packages 779 Tree(TreeOptions), 780} 781 782#[derive(Subcommand, Debug)] 783pub enum Hex { 784 /// Retire a release from Hex 785 /// 786 /// This command uses the environment variable: 787 /// 788 /// - HEXPM_API_KEY: (optional) A Hex API key to authenticate with the Hex package manager. 789 /// 790 #[command(verbatim_doc_comment)] 791 Retire { 792 /// The name of the package to retire 793 #[arg(long)] 794 package: String, 795 /// The version to retire 796 #[arg(long)] 797 version: String, 798 /// The reason for the retirement 799 #[arg(long)] 800 reason: RetirementReason, 801 message: Option<String>, 802 }, 803 804 /// Un-retire a release from Hex 805 /// 806 /// This command uses this environment variable: 807 /// 808 /// - HEXPM_API_KEY: (optional) A Hex API key to authenticate with the Hex package manager. 809 /// 810 #[command(verbatim_doc_comment)] 811 Unretire { 812 /// The name of the package to unretire 813 #[arg(long)] 814 package: String, 815 /// The version to unretire 816 #[arg(long)] 817 version: String, 818 }, 819 820 /// Revert a release, removing it from Hex. 821 /// 822 /// Releases can only be reverted within 24 hours since they were published. 823 /// 824 /// This command uses this environment variable: 825 /// 826 /// - HEXPM_API_KEY: (optional) A Hex API key to authenticate with the Hex package manager. 827 /// 828 #[command(verbatim_doc_comment)] 829 Revert { 830 /// The name of the package to revert 831 #[arg(long)] 832 package: Option<String>, 833 834 /// The version to revert 835 #[arg(long)] 836 version: Option<String>, 837 }, 838 839 /// Deal with package ownership 840 #[command(subcommand)] 841 Owner(Owner), 842 843 /// Log in to Hex. Replaces the credentials with new ones if already logged in. 844 Authenticate, 845} 846 847#[derive(Subcommand, Debug)] 848pub enum Owner { 849 /// Adds a new owner to the given package on Hex 850 /// 851 /// This command uses this environment variable: 852 /// 853 /// - HEXPM_API_KEY: (optional) A Hex API key to authenticate against the Hex package manager. 854 /// 855 #[command(verbatim_doc_comment)] 856 Add { 857 #[arg(long)] 858 package: String, 859 860 /// The username or email of the additional owner 861 #[arg(long = "user")] 862 username_or_email: String, 863 864 /// The ownership level 865 #[arg(long, default_value = "maintainer")] 866 level: hexpm::OwnerLevel, 867 }, 868 869 /// Transfers ownership of the given package to a new Hex user 870 /// 871 /// This command uses this environment variable: 872 /// 873 /// - HEXPM_API_KEY: (optional) A Hex API key to authenticate against the Hex package manager. 874 /// 875 #[command(verbatim_doc_comment)] 876 Transfer { 877 /// The name of the package 878 #[arg(long)] 879 package: String, 880 881 /// The username or email of the new owner 882 #[arg(long = "user")] 883 username_or_email: String, 884 }, 885} 886 887#[derive(Subcommand, Debug)] 888pub enum Docs { 889 /// Render HTML docs locally 890 Build { 891 /// Opens the docs in a browser after rendering 892 #[arg(long)] 893 open: bool, 894 895 /// Which compilation target to use 896 #[arg(short, long, ignore_case = true, help = target_doc())] 897 target: Option<Target>, 898 }, 899 900 /// Publish HTML docs to HexDocs 901 /// 902 /// This command uses this environment variable: 903 /// 904 /// - HEXPM_API_KEY: (optional) A Hex API key to authenticate with the Hex package manager. 905 /// 906 #[command(verbatim_doc_comment)] 907 Publish, 908 909 /// Remove HTML docs from HexDocs 910 /// 911 /// This command uses this environment variable: 912 /// 913 /// - HEXPM_API_KEY: (optional) A Hex API key to authenticate with the Hex package manager. 914 /// 915 #[command(verbatim_doc_comment)] 916 Remove { 917 /// The name of the package 918 #[arg(long)] 919 package: String, 920 921 /// The version of the docs to remove 922 #[arg(long)] 923 version: String, 924 }, 925} 926 927pub fn main() { 928 initialise_logger(); 929 panic::add_handler(); 930 let stderr = cli::stderr_buffer_writer(); 931 let result = get_current_directory() 932 .and_then(|working_directory| Command::parse().run(working_directory)); 933 match result { 934 Ok(_) => { 935 tracing::info!("Successfully completed"); 936 } 937 Err(error) => { 938 tracing::error!(error = ?error, "Failed"); 939 let mut buffer = stderr.buffer(); 940 error.pretty(&mut buffer); 941 stderr.print(&buffer).expect("Final result error writing"); 942 std::process::exit(1); 943 } 944 } 945} 946 947fn command_check(paths: &ProjectPaths, target: Option<Target>) -> Result<()> { 948 let _ = build::main( 949 paths, 950 Options { 951 root_target_support: TargetSupport::Enforced, 952 warnings_as_errors: false, 953 codegen: Codegen::DepsOnly, 954 compile: Compile::All, 955 mode: Mode::Dev, 956 target, 957 no_print_progress: false, 958 }, 959 build::download_dependencies(paths, cli::Reporter::new())?, 960 )?; 961 Ok(()) 962} 963 964fn command_build( 965 paths: &ProjectPaths, 966 target: Option<Target>, 967 warnings_as_errors: bool, 968 no_print_progress: bool, 969) -> Result<()> { 970 let manifest = if no_print_progress { 971 build::download_dependencies(paths, NullTelemetry)? 972 } else { 973 build::download_dependencies(paths, cli::Reporter::new())? 974 }; 975 let _ = build::main( 976 paths, 977 Options { 978 root_target_support: TargetSupport::Enforced, 979 warnings_as_errors, 980 codegen: Codegen::All, 981 compile: Compile::All, 982 mode: Mode::Dev, 983 target, 984 no_print_progress, 985 }, 986 manifest, 987 )?; 988 Ok(()) 989} 990 991fn print_config(paths: &ProjectPaths) -> Result<()> { 992 let config = root_config(paths)?; 993 println!("{config:#?}"); 994 Ok(()) 995} 996 997fn clean(paths: &ProjectPaths) -> Result<()> { 998 fs::delete_directory(&paths.build_directory()) 999} 1000 1001fn initialise_logger() { 1002 let enable_colours = std::env::var("GLEAM_LOG_NOCOLOUR").is_err(); 1003 tracing_subscriber::fmt() 1004 .with_writer(std::io::stderr) 1005 .with_env_filter(std::env::var("GLEAM_LOG").unwrap_or_else(|_| "off".into())) 1006 .with_target(false) 1007 .with_ansi(enable_colours) 1008 .without_time() 1009 .init(); 1010} 1011 1012fn find_project_paths(current_dir: Utf8PathBuf) -> Result<ProjectPaths> { 1013 get_project_root(current_dir).map(ProjectPaths::new) 1014} 1015 1016#[cfg(test)] 1017fn project_paths_at_current_directory_without_toml() -> ProjectPaths { 1018 let current_dir = get_current_directory().expect("Failed to get current directory"); 1019 ProjectPaths::new(current_dir) 1020} 1021 1022fn download_dependencies(paths: &ProjectPaths) -> Result<()> { 1023 _ = dependencies::resolve_and_download( 1024 paths, 1025 cli::Reporter::new(), 1026 None, 1027 Vec::new(), 1028 dependencies::DependencyManagerConfig { 1029 use_manifest: dependencies::UseManifest::Yes, 1030 check_major_versions: dependencies::CheckMajorVersions::No, 1031 }, 1032 )?; 1033 Ok(()) 1034}