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