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