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