1mod ace;
10mod benches;
11mod check;
12mod doc;
13mod format;
14mod languageserver;
15mod loader;
16mod naming;
17mod out;
18mod run;
19mod tests;
20mod visitor;
21
22use std::fmt;
23use std::io::{self, IsTerminal, Write};
24use std::path::{Path, PathBuf};
25
26use rust_alloc::string::String;
27use rust_alloc::vec::Vec;
28
29use crate as rune;
30use crate::alloc;
31use crate::alloc::prelude::*;
32use crate::workspace::{self, WorkspaceFilter};
33
34use anyhow::{bail, Context as _, Error, Result};
35use clap::{Parser, Subcommand, ValueEnum};
36use tracing_subscriber::filter::EnvFilter;
37
38use crate::compile::ParseOptionError;
39use crate::modules::capture_io::CaptureIo;
40use crate::termcolor::{ColorChoice, StandardStream};
41use crate::{Context, ContextError, Hash, ItemBuf, Options};
42
43use self::out::{Color, Io, Stream};
44
45const DEFAULT_ABOUT: &str = "The Rune Language Interpreter";
47
48#[non_exhaustive]
50pub struct ContextOptions<'a> {
51 pub capture: Option<&'a CaptureIo>,
54 pub test: bool,
56}
57
58pub type ContextBuilder = dyn FnMut(ContextOptions<'_>) -> Result<Context, ContextError>;
60
61#[derive(Default)]
66pub struct Entry<'a> {
67 about: Option<alloc::String>,
68 context: Option<&'a mut ContextBuilder>,
69}
70
71impl<'a> Entry<'a> {
72 pub fn new() -> Self {
74 Self::default()
75 }
76
77 pub fn about(mut self, about: impl fmt::Display) -> Self {
90 self.about = Some(
91 about
92 .try_to_string()
93 .expect("Failed to format about string"),
94 );
95 self
96 }
97
98 pub fn context(mut self, context: &'a mut ContextBuilder) -> Self {
121 self.context = Some(context);
122 self
123 }
124
125 pub fn run(self) -> ! {
129 let runtime = tokio::runtime::Builder::new_current_thread()
130 .enable_all()
131 .build()
132 .expect("Failed to build runtime");
133
134 match runtime.block_on(self.inner()) {
135 Ok(exit_code) => {
136 std::process::exit(exit_code as i32);
137 }
138 Err(error) => {
139 let o = std::io::stderr();
140 let _ = format_errors(&mut o.lock(), &error);
142 std::process::exit(ExitCode::Failure as i32);
143 }
144 }
145 }
146
147 pub async fn run_async(self) -> ! {
151 match self.inner().await {
152 Ok(exit_code) => {
153 std::process::exit(exit_code as i32);
154 }
155 Err(error) => {
156 let o = std::io::stderr();
157 let _ = format_errors(&mut o.lock(), &error);
159 std::process::exit(ExitCode::Failure as i32);
160 }
161 }
162 }
163
164 async fn inner(mut self) -> Result<ExitCode> {
165 let args = match Args::try_parse() {
166 Ok(args) => args,
167 Err(e) => {
168 let about = self.about.as_deref().unwrap_or(DEFAULT_ABOUT);
169
170 let code = if e.use_stderr() {
171 let o = std::io::stderr();
172 let mut o = o.lock();
173 o.write_all(about.as_bytes())?;
174 writeln!(o)?;
175 writeln!(o)?;
176 writeln!(o, "{}", e)?;
177 o.flush()?;
178 ExitCode::Failure
179 } else {
180 let o = std::io::stdout();
181 let mut o = o.lock();
182 o.write_all(about.as_bytes())?;
183 writeln!(o)?;
184 writeln!(o)?;
185 writeln!(o, "{}", e)?;
186 o.flush()?;
187 ExitCode::Success
188 };
189
190 return Ok(code);
191 }
192 };
193
194 if args.version {
195 let o = std::io::stdout();
196 let mut o = o.lock();
197 let about = self.about.as_deref().unwrap_or(DEFAULT_ABOUT);
198 o.write_all(about.as_bytes())?;
199 o.flush()?;
200 return Ok(ExitCode::Success);
201 }
202
203 let choice = match args.color {
204 ColorArgument::Always => ColorChoice::Always,
205 ColorArgument::Ansi => ColorChoice::AlwaysAnsi,
206 ColorArgument::Auto => {
207 if std::io::stdin().is_terminal() {
208 ColorChoice::Auto
209 } else {
210 ColorChoice::Never
211 }
212 }
213 ColorArgument::Never => ColorChoice::Never,
214 };
215
216 let mut stdout = StandardStream::stdout(choice);
217 let mut stderr = StandardStream::stderr(choice);
218
219 let mut io = Io::new(&mut stdout, &mut stderr);
220
221 tracing_subscriber::fmt()
222 .with_env_filter(EnvFilter::from_default_env())
223 .init();
224
225 match main_with_out(&mut io, &mut self, args).await {
226 Ok(code) => Ok(code),
227 Err(error) => {
228 let o = io.with_color(Stream::Stdout, Color::Error)?;
229 format_errors(o, &error)?;
230 o.close()?;
231 Ok(ExitCode::Failure)
232 }
233 }
234 }
235}
236
237#[derive(TryClone)]
239pub(crate) enum EntryPoint<'a> {
240 Path(PathBuf, bool),
242 Package(workspace::FoundPackage<'a>),
244}
245
246impl EntryPoint<'_> {
247 pub(crate) fn path(&self) -> &Path {
249 match self {
250 EntryPoint::Path(path, _) => path,
251 EntryPoint::Package(p) => &p.found.path,
252 }
253 }
254
255 pub(crate) fn is_argument(&self) -> bool {
257 match self {
258 EntryPoint::Path(_, explicit) => *explicit,
259 EntryPoint::Package(..) => false,
260 }
261 }
262}
263
264impl fmt::Display for EntryPoint<'_> {
265 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266 match self {
267 EntryPoint::Path(path, false) => {
268 write!(f, "path in {}", path.display())
269 }
270 EntryPoint::Path(path, true) => {
271 write!(f, "path in {} (argument)", path.display())
272 }
273 EntryPoint::Package(package) => {
274 write!(
275 f,
276 "package `{}` in {} ({})",
277 package.package.name,
278 package.found.path.display(),
279 package.found.kind
280 )
281 }
282 }
283 }
284}
285
286#[derive(Parser, Debug, Clone)]
287#[command(rename_all = "kebab-case")]
288struct CommandShared<T>
289where
290 T: clap::Args,
291{
292 #[command(flatten)]
293 shared: SharedFlags,
294 #[command(flatten)]
295 command: T,
296}
297
298impl<T> CommandShared<T>
299where
300 T: CommandBase + clap::Args,
301{
302 fn options(&self) -> Result<Options, ParseOptionError> {
304 let mut options = Options::from_default_env()?;
305
306 if self.command.is_debug() {
308 options.debug_info(true);
309 options.test(true);
310 options.bytecode(false);
311 }
312
313 for option in &self.shared.compiler_option {
314 options.parse_option(option)?;
315 }
316
317 Ok(options)
318 }
319}
320
321#[derive(Clone, Copy)]
322struct CommandSharedRef<'a> {
323 shared: &'a SharedFlags,
324 command: &'a dyn CommandBase,
325}
326
327impl<'a> CommandSharedRef<'a> {
328 fn find(
329 &self,
330 all_targets: bool,
331 kind: AssetKind,
332 name: Option<&'a str>,
333 ) -> Option<WorkspaceFilter<'a>> {
334 if !all_targets && !self.command.is_workspace(kind) {
335 return None;
336 }
337
338 if let Some(name) = name {
339 return Some(WorkspaceFilter::Name(name));
340 }
341
342 self.shared.is_unfiltered().then_some(WorkspaceFilter::All)
343 }
344
345 #[inline]
346 fn find_bins(&self, all_targets: bool) -> Option<WorkspaceFilter<'a>> {
347 self.find(all_targets, AssetKind::Bin, self.shared.bin.as_deref())
348 }
349
350 #[inline]
351 fn find_tests(&self, all_targets: bool) -> Option<WorkspaceFilter<'a>> {
352 self.find(all_targets, AssetKind::Test, self.shared.test.as_deref())
353 }
354
355 #[inline]
356 fn find_examples(&self, all_targets: bool) -> Option<WorkspaceFilter<'a>> {
357 self.find(all_targets, AssetKind::Bin, self.shared.example.as_deref())
358 }
359
360 #[inline]
361 fn find_benches(&self, all_targets: bool) -> Option<WorkspaceFilter<'a>> {
362 self.find(all_targets, AssetKind::Bench, self.shared.bench.as_deref())
363 }
364}
365
366#[derive(Parser, Debug)]
367#[command(rename_all = "kebab-case")]
368struct HashFlags {
369 #[arg(long)]
371 random: bool,
372 #[arg(long)]
374 count: Option<usize>,
375 item: Vec<String>,
377}
378
379enum AssetKind {
380 Bin,
381 Test,
382 Bench,
383}
384
385trait CommandBase {
386 #[inline]
388 fn is_debug(&self) -> bool {
389 false
390 }
391
392 #[inline]
394 fn is_workspace(&self, _: AssetKind) -> bool {
395 false
396 }
397
398 #[inline]
400 fn describe(&self) -> &str {
401 "Running"
402 }
403
404 #[inline]
406 fn propagate(&mut self, _: &mut Config, _: &mut SharedFlags) {}
407
408 #[inline]
410 fn paths(&self) -> &[PathBuf] {
411 &[]
412 }
413}
414
415#[derive(Subcommand, Debug)]
416enum Command {
417 Check(CommandShared<check::Flags>),
419 Doc(CommandShared<doc::Flags>),
421 Ace(CommandShared<ace::Flags>),
423 Test(CommandShared<tests::Flags>),
425 Bench(CommandShared<benches::Flags>),
427 Run(CommandShared<run::Flags>),
429 Fmt(CommandShared<format::Flags>),
431 LanguageServer(SharedFlags),
433 Hash(HashFlags),
435}
436
437impl Command {
438 const ALL: [&'static str; 9] = [
439 "check",
440 "doc",
441 "ace",
442 "test",
443 "bench",
444 "run",
445 "fmt",
446 "languageserver",
447 "hash",
448 ];
449
450 fn as_command_base_mut(&mut self) -> Option<(&mut SharedFlags, &mut dyn CommandBase)> {
451 let (shared, command): (_, &mut dyn CommandBase) = match self {
452 Command::Check(shared) => (&mut shared.shared, &mut shared.command),
453 Command::Doc(shared) => (&mut shared.shared, &mut shared.command),
454 Command::Ace(shared) => (&mut shared.shared, &mut shared.command),
455 Command::Test(shared) => (&mut shared.shared, &mut shared.command),
456 Command::Bench(shared) => (&mut shared.shared, &mut shared.command),
457 Command::Run(shared) => (&mut shared.shared, &mut shared.command),
458 Command::Fmt(shared) => (&mut shared.shared, &mut shared.command),
459 Command::LanguageServer(..) => return None,
460 Command::Hash(..) => return None,
461 };
462
463 Some((shared, command))
464 }
465
466 fn as_command_shared_ref(&self) -> Option<CommandSharedRef<'_>> {
467 let (shared, command): (_, &dyn CommandBase) = match self {
468 Command::Check(shared) => (&shared.shared, &shared.command),
469 Command::Doc(shared) => (&shared.shared, &shared.command),
470 Command::Ace(shared) => (&shared.shared, &shared.command),
471 Command::Test(shared) => (&shared.shared, &shared.command),
472 Command::Bench(shared) => (&shared.shared, &shared.command),
473 Command::Run(shared) => (&shared.shared, &shared.command),
474 Command::Fmt(shared) => (&shared.shared, &shared.command),
475 Command::LanguageServer(..) => return None,
476 Command::Hash(..) => return None,
477 };
478
479 Some(CommandSharedRef { shared, command })
480 }
481}
482
483enum BuildPath<'a> {
484 Path(&'a Path, bool),
486 Package(workspace::FoundPackage<'a>),
488}
489
490#[derive(Default)]
491struct Config {
492 filtered: bool,
494 test: bool,
496 verbose: bool,
498 all_targets: bool,
500 manifest_root: Option<PathBuf>,
502}
503
504#[derive(Default)]
505struct Inputs {
506 manifest: workspace::Manifest,
508 found_paths: alloc::Vec<(PathBuf, bool)>,
510}
511
512impl Inputs {
513 fn build_paths<'m>(
515 &'m self,
516 cmd: CommandSharedRef<'_>,
517 c: &mut Config,
518 ) -> Result<alloc::Vec<BuildPath<'m>>> {
519 let mut build_paths = alloc::Vec::new();
520
521 if !self.found_paths.is_empty() {
522 build_paths.try_extend(self.found_paths.iter().map(|(p, e)| BuildPath::Path(p, *e)))?;
523
524 if !cmd.shared.workspace {
525 return Ok(build_paths);
526 }
527 }
528
529 if let Some(filter) = cmd.find_bins(c.all_targets) {
530 c.filtered |= !matches!(filter, WorkspaceFilter::All);
531
532 for p in self.manifest.find_bins(filter)? {
533 build_paths.try_push(BuildPath::Package(p))?;
534 }
535 }
536
537 if let Some(filter) = cmd.find_tests(c.all_targets) {
538 c.filtered |= !matches!(filter, WorkspaceFilter::All);
539
540 for p in self.manifest.find_tests(filter)? {
541 build_paths.try_push(BuildPath::Package(p))?;
542 }
543 }
544
545 if let Some(filter) = cmd.find_examples(c.all_targets) {
546 c.filtered |= !matches!(filter, WorkspaceFilter::All);
547
548 for p in self.manifest.find_examples(filter)? {
549 build_paths.try_push(BuildPath::Package(p))?;
550 }
551 }
552
553 if let Some(filter) = cmd.find_benches(c.all_targets) {
554 c.filtered |= !matches!(filter, WorkspaceFilter::All);
555
556 for p in self.manifest.find_benches(filter)? {
557 build_paths.try_push(BuildPath::Package(p))?;
558 }
559 }
560
561 Ok(build_paths)
562 }
563}
564
565impl SharedFlags {
566 fn context(
568 &self,
569 entry: &mut Entry<'_>,
570 c: &Config,
571 capture: Option<&CaptureIo>,
572 ) -> Result<Context> {
573 let opts = ContextOptions {
574 capture,
575 test: c.test,
576 };
577
578 let mut context =
579 entry
580 .context
581 .as_mut()
582 .context("Context builder not configured with Entry::context")?(opts)?;
583
584 if let Some(capture) = capture {
585 context.install(crate::modules::capture_io::module(capture)?)?;
586 }
587
588 Ok(context)
589 }
590}
591
592#[derive(Default, Debug, Clone, Copy, ValueEnum)]
593enum ColorArgument {
594 #[default]
595 Auto,
597 Ansi,
599 Always,
601 Never,
603}
604
605#[derive(Parser, Debug)]
606#[command(name = "rune", about = None)]
607struct Args {
608 #[arg(long)]
610 version: bool,
611
612 #[arg(short = 'C', long, default_value = "auto")]
614 color: ColorArgument,
615
616 #[command(subcommand)]
618 cmd: Option<Command>,
619}
620
621#[derive(Parser, Debug, Clone)]
622#[command(rename_all = "kebab-case")]
623struct SharedFlags {
624 #[arg(long, short = 'R')]
626 recursive: bool,
627
628 #[arg(long)]
630 warnings: bool,
631
632 #[arg(long)]
634 verbose: bool,
635
636 #[arg(long)]
641 workspace: bool,
642
643 #[arg(short = 'O', num_args = 1)]
645 compiler_option: Vec<String>,
646
647 #[arg(long)]
649 list_options: bool,
650
651 #[arg(long)]
654 bin: Option<String>,
655
656 #[arg(long)]
659 test: Option<String>,
660
661 #[arg(long)]
664 example: Option<String>,
665
666 #[arg(long)]
669 bench: Option<String>,
670
671 #[arg(long)]
674 all_targets: bool,
675
676 #[arg(long)]
686 path: Vec<PathBuf>,
687}
688
689impl SharedFlags {
690 fn is_unfiltered(&self) -> bool {
691 self.bin.is_none()
692 && self.test.is_none()
693 && self.example.is_none()
694 && self.bench.is_none()
695 && self.path.is_empty()
696 }
697}
698
699const SPECIAL_FILES: &[&str] = &[
700 "main.rn",
701 "lib.rn",
702 "src/main.rn",
703 "src/lib.rn",
704 "script/main.rn",
705 "script/lib.rn",
706];
707
708#[repr(i32)]
711enum ExitCode {
712 Success = 0,
713 Failure = 1,
714 VmError = 2,
715}
716
717fn format_errors<O>(o: &mut O, error: &Error) -> io::Result<()>
719where
720 O: ?Sized + io::Write,
721{
722 writeln!(o, "Error: {}", error)?;
723
724 for error in error.chain().skip(1) {
725 writeln!(o, "Caused by: {}", error)?;
726 }
727
728 Ok(())
729}
730
731fn find_manifest() -> Option<(PathBuf, PathBuf)> {
732 let mut path = PathBuf::new();
733
734 loop {
735 let manifest_path = path.join(workspace::MANIFEST_FILE);
736
737 if manifest_path.is_file() {
738 return Some((path, manifest_path));
739 }
740
741 path.push("..");
742
743 if !path.is_dir() {
744 return None;
745 }
746 }
747}
748
749fn populate_config(
750 io: &mut Io<'_>,
751 c: &mut Config,
752 inputs: &mut Inputs,
753 cmd: CommandSharedRef<'_>,
754) -> Result<()> {
755 c.all_targets = cmd.shared.all_targets;
756
757 inputs
758 .found_paths
759 .try_extend(cmd.shared.path.iter().map(|p| (p.clone(), false)))?;
760
761 inputs
762 .found_paths
763 .try_extend(cmd.command.paths().iter().map(|p| (p.clone(), true)))?;
764
765 if !inputs.found_paths.is_empty() && !cmd.shared.workspace {
766 return Ok(());
767 }
768
769 let Some((manifest_root, manifest_path)) = find_manifest() else {
770 for file in SPECIAL_FILES {
771 let path = Path::new(file);
772
773 if path.is_file() {
774 inputs.found_paths.try_push((path.try_to_owned()?, false))?;
775 return Ok(());
776 }
777 }
778
779 let special = SPECIAL_FILES.join(", ");
780
781 bail!(
782 "Could not find `{}` in this or parent directories nor any of the special files: {special}",
783 workspace::MANIFEST_FILE
784 )
785 };
786
787 c.verbose = true;
790 c.manifest_root = Some(manifest_root);
791
792 let mut sources = crate::Sources::new();
793 sources.insert(crate::Source::from_path(manifest_path)?)?;
794
795 let mut diagnostics = workspace::Diagnostics::new();
796
797 let result = workspace::prepare(&mut sources)
798 .with_diagnostics(&mut diagnostics)
799 .build();
800
801 diagnostics.emit(io.stdout, &sources)?;
802 inputs.manifest = result?;
803 Ok(())
804}
805
806async fn main_with_out(io: &mut Io<'_>, entry: &mut Entry<'_>, mut args: Args) -> Result<ExitCode> {
807 let mut c = Config::default();
808 let mut inputs = Inputs::default();
809
810 if let Some((shared, base)) = args.cmd.as_mut().and_then(|c| c.as_command_base_mut()) {
811 base.propagate(&mut c, shared);
812 }
813
814 let Some(cmd) = &args.cmd else {
815 let commands: alloc::String = Command::ALL.into_iter().try_join(", ")?;
816 writeln!(io.stdout, "Expected a subcommand: {commands}")?;
817 return Ok(ExitCode::Failure);
818 };
819
820 let mut entries = alloc::Vec::new();
821
822 if let Some(cmd) = cmd.as_command_shared_ref() {
823 if cmd.shared.list_options {
824 writeln!(
825 io.stdout,
826 "Available compiler options (set with -O <option>=<value>):"
827 )?;
828 writeln!(io.stdout)?;
829
830 for (i, option) in Options::available().iter().enumerate() {
831 if i > 0 {
832 writeln!(io.stdout)?;
833 }
834
835 io.write(
836 format_args!("{}", option.key),
837 Stream::Stdout,
838 Color::Highlight,
839 )?;
840
841 write!(io.stdout, "={}", option.default)?;
842
843 if option.unstable {
844 io.write(" (unstable)", Stream::Stdout, Color::Error)?;
845 }
846
847 writeln!(io.stdout, ":")?;
848 writeln!(io.stdout, " Options: {}", option.options)?;
849 writeln!(io.stdout)?;
850
851 for &line in option.doc {
852 let line = line.strip_prefix(' ').unwrap_or(line);
853 writeln!(io.stdout, " {line}")?;
854 }
855 }
856
857 return Ok(ExitCode::Success);
858 }
859
860 populate_config(io, &mut c, &mut inputs, cmd)?;
861
862 let build_paths = inputs.build_paths(cmd, &mut c)?;
863
864 let what = cmd.command.describe();
865 let verbose = c.verbose;
866 let recursive = cmd.shared.recursive;
867
868 for build_path in build_paths {
869 match build_path {
870 BuildPath::Path(path, explicit) => {
871 for path in loader::recurse_paths(recursive, path.try_to_owned()?) {
872 entries.try_push(EntryPoint::Path(path?, explicit))?;
873 }
874 }
875 BuildPath::Package(p) => {
876 if verbose {
877 let mut section = io.section(what, Stream::Stderr, Color::Highlight)?;
878
879 section.append(format_args!(
880 " {} `{}` (from {})",
881 p.found.kind,
882 p.found.path.display(),
883 p.package.name
884 ))?;
885
886 section.close()?;
887 }
888
889 entries.try_push(EntryPoint::Package(p))?;
890 }
891 }
892 }
893 }
894
895 match run_path(io, &c, cmd, entry, entries).await? {
896 ExitCode::Success => (),
897 other => {
898 return Ok(other);
899 }
900 }
901
902 Ok(ExitCode::Success)
903}
904
905async fn run_path<'p, I>(
907 io: &mut Io<'_>,
908 c: &Config,
909 cmd: &Command,
910 entry: &mut Entry<'_>,
911 entries: I,
912) -> Result<ExitCode>
913where
914 I: IntoIterator<Item = EntryPoint<'p>>,
915{
916 match cmd {
917 Command::Check(f) => {
918 let options = f.options()?;
919
920 for e in entries {
921 let mut options = options.clone();
922
923 if e.is_argument() {
924 options.function_body = true;
925 }
926
927 match check::run(io, entry, c, &f.command, &f.shared, &options, e.path())? {
928 ExitCode::Success => (),
929 other => return Ok(other),
930 }
931 }
932 }
933 Command::Doc(f) => {
934 let options = f.options()?;
935 return doc::run(io, entry, c, &f.command, &f.shared, &options, entries);
936 }
937 Command::Ace(f) => {
938 let options = f.options()?;
939 return ace::run(io, entry, c, &f.command, &f.shared, &options, entries);
940 }
941 Command::Fmt(f) => {
942 let options = f.options()?;
943 return format::run(io, entry, c, entries, &f.command, &f.shared, &options);
944 }
945 Command::Test(f) => {
946 let options = f.options()?;
947
948 match tests::run(io, c, &f.command, &f.shared, &options, entry, entries).await? {
949 ExitCode::Success => (),
950 other => return Ok(other),
951 }
952 }
953 Command::Bench(f) => {
954 let options = f.options()?;
955
956 for e in entries {
957 let mut options = options.clone();
958
959 if e.is_argument() {
960 options.function_body = true;
961 }
962
963 let capture_io = crate::modules::capture_io::CaptureIo::new();
964 let context = f.shared.context(entry, c, Some(&capture_io))?;
965
966 let load = loader::load(
967 io,
968 &context,
969 &f.shared,
970 &options,
971 e.path(),
972 visitor::Attribute::Bench,
973 )?;
974
975 match benches::run(
976 io,
977 &f.command,
978 &context,
979 Some(&capture_io),
980 load.unit,
981 &load.sources,
982 &load.functions,
983 )
984 .await?
985 {
986 ExitCode::Success => (),
987 other => return Ok(other),
988 }
989 }
990 }
991 Command::Run(f) => {
992 let options = f.options()?;
993 let context = f.shared.context(entry, c, None)?;
994
995 for e in entries {
996 let mut options = options.clone();
997
998 if e.is_argument() {
999 options.function_body = true;
1000 }
1001
1002 let load = loader::load(
1003 io,
1004 &context,
1005 &f.shared,
1006 &options,
1007 e.path(),
1008 visitor::Attribute::None,
1009 )?;
1010
1011 let entry = if e.is_argument() {
1012 Hash::EMPTY
1013 } else {
1014 Hash::type_hash(["main"])
1015 };
1016
1017 match run::run(io, c, &f.command, &context, load.unit, &load.sources, entry).await?
1018 {
1019 ExitCode::Success => (),
1020 other => return Ok(other),
1021 }
1022 }
1023 }
1024 Command::LanguageServer(shared) => {
1025 let context = shared.context(entry, c, None)?;
1026 languageserver::run(context).await?;
1027 }
1028 Command::Hash(args) => {
1029 use rand::prelude::*;
1030
1031 if args.random {
1032 for _ in 0..args.count.unwrap_or(1) {
1033 let mut rand = rand::thread_rng();
1034 writeln!(io.stdout, "{}", Hash::new(rand.gen::<u64>()))?;
1035 }
1036 }
1037
1038 for item in &args.item {
1039 let item: ItemBuf = item.parse()?;
1040 let hash = Hash::type_hash(&item);
1041 writeln!(io.stdout, "{item} => {hash}")?;
1042 }
1043 }
1044 }
1045
1046 Ok(ExitCode::Success)
1047}