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