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, FoundKind, 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_libs(&self, all_targets: bool) -> Option<WorkspaceFilter<'a>> {
352 self.find(all_targets, AssetKind::Lib, self.shared.lib.as_deref())
353 }
354
355 #[inline]
356 fn find_tests(&self, all_targets: bool) -> Option<WorkspaceFilter<'a>> {
357 self.find(all_targets, AssetKind::Test, self.shared.test.as_deref())
358 }
359
360 #[inline]
361 fn find_examples(&self, all_targets: bool) -> Option<WorkspaceFilter<'a>> {
362 self.find(all_targets, AssetKind::Bin, self.shared.example.as_deref())
363 }
364
365 #[inline]
366 fn find_benches(&self, all_targets: bool) -> Option<WorkspaceFilter<'a>> {
367 self.find(all_targets, AssetKind::Bench, self.shared.bench.as_deref())
368 }
369}
370
371#[derive(Parser, Debug)]
372#[command(rename_all = "kebab-case")]
373struct HashFlags {
374 #[arg(long)]
376 random: bool,
377 #[arg(long)]
379 count: Option<usize>,
380 item: Vec<String>,
382}
383
384enum AssetKind {
385 Bin,
386 Lib,
387 Test,
388 Bench,
389}
390
391trait CommandBase {
392 #[inline]
394 fn is_debug(&self) -> bool {
395 false
396 }
397
398 #[inline]
400 fn is_workspace(&self, _: AssetKind) -> bool {
401 false
402 }
403
404 #[inline]
406 fn describe(&self) -> &str {
407 "Running"
408 }
409
410 #[inline]
412 fn propagate(&mut self, _: &mut Config, _: &mut SharedFlags) {}
413
414 #[inline]
416 fn paths(&self) -> &[PathBuf] {
417 &[]
418 }
419}
420
421#[derive(Subcommand, Debug)]
422enum Command {
423 Check(CommandShared<check::Flags>),
425 Doc(CommandShared<doc::Flags>),
427 Ace(CommandShared<ace::Flags>),
429 Test(CommandShared<tests::Flags>),
431 Bench(CommandShared<benches::Flags>),
433 Run(CommandShared<run::Flags>),
435 Fmt(CommandShared<format::Flags>),
437 LanguageServer(SharedFlags),
439 Hash(HashFlags),
441}
442
443impl Command {
444 const ALL: [&'static str; 9] = [
445 "check",
446 "doc",
447 "ace",
448 "test",
449 "bench",
450 "run",
451 "fmt",
452 "languageserver",
453 "hash",
454 ];
455
456 fn as_command_base_mut(&mut self) -> Option<(&mut SharedFlags, &mut dyn CommandBase)> {
457 let (shared, command): (_, &mut dyn CommandBase) = match self {
458 Command::Check(shared) => (&mut shared.shared, &mut shared.command),
459 Command::Doc(shared) => (&mut shared.shared, &mut shared.command),
460 Command::Ace(shared) => (&mut shared.shared, &mut shared.command),
461 Command::Test(shared) => (&mut shared.shared, &mut shared.command),
462 Command::Bench(shared) => (&mut shared.shared, &mut shared.command),
463 Command::Run(shared) => (&mut shared.shared, &mut shared.command),
464 Command::Fmt(shared) => (&mut shared.shared, &mut shared.command),
465 Command::LanguageServer(..) => return None,
466 Command::Hash(..) => return None,
467 };
468
469 Some((shared, command))
470 }
471
472 fn as_command_shared_ref(&self) -> Option<CommandSharedRef<'_>> {
473 let (shared, command): (_, &dyn CommandBase) = match self {
474 Command::Check(shared) => (&shared.shared, &shared.command),
475 Command::Doc(shared) => (&shared.shared, &shared.command),
476 Command::Ace(shared) => (&shared.shared, &shared.command),
477 Command::Test(shared) => (&shared.shared, &shared.command),
478 Command::Bench(shared) => (&shared.shared, &shared.command),
479 Command::Run(shared) => (&shared.shared, &shared.command),
480 Command::Fmt(shared) => (&shared.shared, &shared.command),
481 Command::LanguageServer(..) => return None,
482 Command::Hash(..) => return None,
483 };
484
485 Some(CommandSharedRef { shared, command })
486 }
487}
488
489enum BuildPath<'a> {
490 Path(&'a Path, bool),
492 Package(workspace::FoundPackage<'a>),
494}
495
496#[derive(Default)]
497struct Config {
498 filtered: bool,
500 test: bool,
502 verbose: bool,
504 all_targets: bool,
506 manifest_root: Option<PathBuf>,
508}
509
510#[derive(Default)]
511struct Inputs {
512 manifest: workspace::Manifest,
514 found_paths: alloc::Vec<(PathBuf, bool)>,
516}
517
518impl Inputs {
519 fn build_paths<'m>(
521 &'m self,
522 cmd: CommandSharedRef<'_>,
523 c: &mut Config,
524 ) -> Result<alloc::Vec<BuildPath<'m>>> {
525 let mut build_paths = alloc::Vec::new();
526
527 if !self.found_paths.is_empty() {
528 build_paths.try_extend(self.found_paths.iter().map(|(p, e)| BuildPath::Path(p, *e)))?;
529
530 if !cmd.shared.workspace {
531 return Ok(build_paths);
532 }
533 }
534
535 if let Some(filter) = cmd.find_bins(c.all_targets) {
536 c.filtered |= !matches!(filter, WorkspaceFilter::All);
537
538 for p in self.manifest.find_by_kind(filter, FoundKind::Binary)? {
539 build_paths.try_push(BuildPath::Package(p))?;
540 }
541 }
542
543 if let Some(filter) = cmd.find_libs(c.all_targets) {
544 c.filtered |= !matches!(filter, WorkspaceFilter::All);
545
546 for p in self.manifest.find_by_kind(filter, FoundKind::Library)? {
547 build_paths.try_push(BuildPath::Package(p))?;
548 }
549 }
550
551 if let Some(filter) = cmd.find_tests(c.all_targets) {
552 c.filtered |= !matches!(filter, WorkspaceFilter::All);
553
554 for p in self.manifest.find_by_kind(filter, FoundKind::Test)? {
555 build_paths.try_push(BuildPath::Package(p))?;
556 }
557 }
558
559 if let Some(filter) = cmd.find_examples(c.all_targets) {
560 c.filtered |= !matches!(filter, WorkspaceFilter::All);
561
562 for p in self.manifest.find_by_kind(filter, FoundKind::Example)? {
563 build_paths.try_push(BuildPath::Package(p))?;
564 }
565 }
566
567 if let Some(filter) = cmd.find_benches(c.all_targets) {
568 c.filtered |= !matches!(filter, WorkspaceFilter::All);
569
570 for p in self.manifest.find_by_kind(filter, FoundKind::Bench)? {
571 build_paths.try_push(BuildPath::Package(p))?;
572 }
573 }
574
575 if let Some(package) = &cmd.shared.package {
576 build_paths.retain(|path| match path {
577 BuildPath::Path(_, _) => true,
578 BuildPath::Package(found_package) => found_package.package.name.as_str() == package,
579 })
580 }
581
582 Ok(build_paths)
583 }
584}
585
586impl SharedFlags {
587 fn context(
589 &self,
590 entry: &mut Entry<'_>,
591 c: &Config,
592 capture: Option<&CaptureIo>,
593 ) -> Result<Context> {
594 let opts = ContextOptions {
595 capture,
596 test: c.test,
597 };
598
599 let mut context =
600 entry
601 .context
602 .as_mut()
603 .context("Context builder not configured with Entry::context")?(opts)?;
604
605 if let Some(capture) = capture {
606 context.install(crate::modules::capture_io::module(capture)?)?;
607 }
608
609 Ok(context)
610 }
611}
612
613#[derive(Default, Debug, Clone, Copy, ValueEnum)]
614enum ColorArgument {
615 #[default]
616 Auto,
618 Ansi,
620 Always,
622 Never,
624}
625
626#[derive(Parser, Debug)]
627#[command(name = "rune", about = None)]
628struct Args {
629 #[arg(long)]
631 version: bool,
632
633 #[arg(short = 'C', long, default_value = "auto")]
635 color: ColorArgument,
636
637 #[command(subcommand)]
639 cmd: Option<Command>,
640}
641
642#[derive(Parser, Debug, Clone)]
643#[command(rename_all = "kebab-case")]
644struct SharedFlags {
645 #[arg(long, short = 'R')]
647 recursive: bool,
648
649 #[arg(long)]
651 warnings: bool,
652
653 #[arg(long)]
655 verbose: bool,
656
657 #[arg(long)]
662 workspace: bool,
663
664 #[arg(short = 'O', num_args = 1)]
666 compiler_option: Vec<String>,
667
668 #[arg(long)]
670 list_options: bool,
671
672 #[arg(long)]
675 bin: Option<String>,
676
677 #[arg(long)]
680 lib: Option<String>,
681
682 #[arg(long)]
685 test: Option<String>,
686
687 #[arg(long)]
690 example: Option<String>,
691
692 #[arg(long)]
695 bench: Option<String>,
696
697 #[arg(long, short = 'p')]
700 package: Option<String>,
701
702 #[arg(long)]
705 all_targets: bool,
706
707 #[arg(long)]
717 path: Vec<PathBuf>,
718}
719
720impl SharedFlags {
721 fn is_unfiltered(&self) -> bool {
722 self.bin.is_none()
723 && self.test.is_none()
724 && self.example.is_none()
725 && self.bench.is_none()
726 && self.path.is_empty()
727 }
728}
729
730const SPECIAL_FILES: &[&str] = &[
731 "main.rn",
732 "lib.rn",
733 "src/main.rn",
734 "src/lib.rn",
735 "script/main.rn",
736 "script/lib.rn",
737];
738
739#[repr(i32)]
742enum ExitCode {
743 Success = 0,
744 Failure = 1,
745 VmError = 2,
746}
747
748fn format_errors<O>(o: &mut O, error: &Error) -> io::Result<()>
750where
751 O: ?Sized + io::Write,
752{
753 writeln!(o, "Error: {error}")?;
754
755 for error in error.chain().skip(1) {
756 writeln!(o, "Caused by: {error}")?;
757 }
758
759 Ok(())
760}
761
762fn find_manifest() -> Option<(PathBuf, PathBuf)> {
763 let mut path = PathBuf::new();
764
765 loop {
766 let manifest_path = path.join(workspace::MANIFEST_FILE);
767
768 if manifest_path.is_file() {
769 return Some((path, manifest_path));
770 }
771
772 path.push("..");
773
774 if !path.is_dir() {
775 return None;
776 }
777 }
778}
779
780fn populate_config(
781 io: &mut Io<'_>,
782 c: &mut Config,
783 inputs: &mut Inputs,
784 cmd: CommandSharedRef<'_>,
785) -> Result<()> {
786 c.all_targets = cmd.shared.all_targets;
787
788 inputs
789 .found_paths
790 .try_extend(cmd.shared.path.iter().map(|p| (p.clone(), false)))?;
791
792 inputs
793 .found_paths
794 .try_extend(cmd.command.paths().iter().map(|p| (p.clone(), true)))?;
795
796 if !inputs.found_paths.is_empty() && !cmd.shared.workspace {
797 return Ok(());
798 }
799
800 let Some((manifest_root, manifest_path)) = find_manifest() else {
801 for file in SPECIAL_FILES {
802 let path = Path::new(file);
803
804 if path.is_file() {
805 inputs.found_paths.try_push((path.try_to_owned()?, false))?;
806 return Ok(());
807 }
808 }
809
810 let special = SPECIAL_FILES.join(", ");
811
812 bail!(
813 "Could not find `{}` in this or parent directories nor any of the special files: {special}",
814 workspace::MANIFEST_FILE
815 )
816 };
817
818 c.verbose = true;
821 c.manifest_root = Some(manifest_root);
822
823 let mut sources = crate::Sources::new();
824 sources.insert(crate::Source::from_path(manifest_path)?)?;
825
826 let mut diagnostics = workspace::Diagnostics::new();
827
828 let result = workspace::prepare(&mut sources)
829 .with_diagnostics(&mut diagnostics)
830 .build();
831
832 diagnostics.emit(io.stdout, &sources)?;
833 inputs.manifest = result?;
834 Ok(())
835}
836
837async fn main_with_out(io: &mut Io<'_>, entry: &mut Entry<'_>, mut args: Args) -> Result<ExitCode> {
838 let mut c = Config::default();
839 let mut inputs = Inputs::default();
840
841 if let Some((shared, base)) = args.cmd.as_mut().and_then(|c| c.as_command_base_mut()) {
842 base.propagate(&mut c, shared);
843 }
844
845 let Some(cmd) = &args.cmd else {
846 let commands: alloc::String = Command::ALL.into_iter().try_join(", ")?;
847 writeln!(io.stdout, "Expected a subcommand: {commands}")?;
848 return Ok(ExitCode::Failure);
849 };
850
851 let mut entries = alloc::Vec::new();
852
853 if let Some(cmd) = cmd.as_command_shared_ref() {
854 if cmd.shared.list_options {
855 writeln!(
856 io.stdout,
857 "Available compiler options (set with -O <option>=<value>):"
858 )?;
859 writeln!(io.stdout)?;
860
861 for (i, option) in Options::available().iter().enumerate() {
862 if i > 0 {
863 writeln!(io.stdout)?;
864 }
865
866 io.write(
867 format_args!("{}", option.key),
868 Stream::Stdout,
869 Color::Highlight,
870 )?;
871
872 write!(io.stdout, "={}", option.default)?;
873
874 if option.unstable {
875 io.write(" (unstable)", Stream::Stdout, Color::Error)?;
876 }
877
878 writeln!(io.stdout, ":")?;
879 writeln!(io.stdout, " Options: {}", option.options)?;
880 writeln!(io.stdout)?;
881
882 for &line in option.doc {
883 let line = line.strip_prefix(' ').unwrap_or(line);
884 writeln!(io.stdout, " {line}")?;
885 }
886 }
887
888 return Ok(ExitCode::Success);
889 }
890
891 populate_config(io, &mut c, &mut inputs, cmd)?;
892
893 let build_paths = inputs.build_paths(cmd, &mut c)?;
894
895 let what = cmd.command.describe();
896 let verbose = c.verbose;
897 let recursive = cmd.shared.recursive;
898
899 for build_path in build_paths {
900 match build_path {
901 BuildPath::Path(path, explicit) => {
902 for path in loader::recurse_paths(recursive, path.try_to_owned()?) {
903 entries.try_push(EntryPoint::Path(path?, explicit))?;
904 }
905 }
906 BuildPath::Package(p) => {
907 if verbose {
908 let mut section = io.section(what, Stream::Stderr, Color::Highlight)?;
909
910 section.append(format_args!(
911 " {} `{}` (from {})",
912 p.found.kind,
913 p.found.path.display(),
914 p.package.name
915 ))?;
916
917 section.close()?;
918 }
919
920 entries.try_push(EntryPoint::Package(p))?;
921 }
922 }
923 }
924 }
925
926 match run_path(io, &c, cmd, entry, entries).await? {
927 ExitCode::Success => (),
928 other => {
929 return Ok(other);
930 }
931 }
932
933 Ok(ExitCode::Success)
934}
935
936async fn run_path<'p, I>(
938 io: &mut Io<'_>,
939 c: &Config,
940 cmd: &Command,
941 entry: &mut Entry<'_>,
942 entries: I,
943) -> Result<ExitCode>
944where
945 I: IntoIterator<Item = EntryPoint<'p>>,
946{
947 match cmd {
948 Command::Check(f) => {
949 let options = f.options()?;
950
951 for e in entries {
952 let mut options = options.clone();
953
954 if e.is_argument() {
955 options.script = true;
956 }
957
958 match check::run(io, entry, c, &f.command, &f.shared, &options, e.path())? {
959 ExitCode::Success => (),
960 other => return Ok(other),
961 }
962 }
963 }
964 Command::Doc(f) => {
965 let options = f.options()?;
966 return doc::run(io, entry, c, &f.command, &f.shared, &options, entries);
967 }
968 Command::Ace(f) => {
969 let options = f.options()?;
970 return ace::run(io, entry, c, &f.command, &f.shared, &options, entries);
971 }
972 Command::Fmt(f) => {
973 let options = f.options()?;
974 return format::run(io, entry, c, entries, &f.command, &f.shared, &options);
975 }
976 Command::Test(f) => {
977 let options = f.options()?;
978
979 match tests::run(io, c, &f.command, &f.shared, &options, entry, entries).await? {
980 ExitCode::Success => (),
981 other => return Ok(other),
982 }
983 }
984 Command::Bench(f) => {
985 let options = f.options()?;
986
987 for e in entries {
988 let mut options = options.clone();
989
990 if e.is_argument() {
991 options.script = true;
992 }
993
994 let capture_io = crate::modules::capture_io::CaptureIo::new();
995 let context = f.shared.context(entry, c, Some(&capture_io))?;
996
997 let load = loader::load(
998 io,
999 &context,
1000 &f.shared,
1001 &options,
1002 e.path(),
1003 visitor::Attribute::Bench,
1004 )?;
1005
1006 match benches::run(
1007 io,
1008 &f.command,
1009 &context,
1010 Some(&capture_io),
1011 load.unit,
1012 &load.sources,
1013 &load.functions,
1014 )
1015 .await?
1016 {
1017 ExitCode::Success => (),
1018 other => return Ok(other),
1019 }
1020 }
1021 }
1022 Command::Run(f) => {
1023 let options = f.options()?;
1024 let context = f.shared.context(entry, c, None)?;
1025
1026 for e in entries {
1027 let mut options = options.clone();
1028
1029 if e.is_argument() {
1030 options.script = true;
1031 }
1032
1033 let load = loader::load(
1034 io,
1035 &context,
1036 &f.shared,
1037 &options,
1038 e.path(),
1039 visitor::Attribute::None,
1040 )?;
1041
1042 let entry = if e.is_argument() {
1043 Hash::EMPTY
1044 } else {
1045 Hash::type_hash(["main"])
1046 };
1047
1048 match run::run(io, c, &f.command, &context, load.unit, &load.sources, entry).await?
1049 {
1050 ExitCode::Success => (),
1051 other => return Ok(other),
1052 }
1053 }
1054 }
1055 Command::LanguageServer(shared) => {
1056 let context = shared.context(entry, c, None)?;
1057 languageserver::run(context).await?;
1058 }
1059 Command::Hash(args) => {
1060 use rand::prelude::*;
1061
1062 if args.random {
1063 let mut rand = rand::rng();
1064
1065 for _ in 0..args.count.unwrap_or(1) {
1066 writeln!(io.stdout, "{}", Hash::new(rand.random::<u64>()))?;
1067 }
1068 }
1069
1070 for item in &args.item {
1071 let item: ItemBuf = item.parse()?;
1072 let hash = Hash::type_hash(&item);
1073 writeln!(io.stdout, "{item} => {hash}")?;
1074 }
1075 }
1076 }
1077
1078 Ok(ExitCode::Success)
1079}