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