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