Fork of daniellemaywood.uk/gleam — Wasm codegen work
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}