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