1#[doc(hidden)]
4pub mod env_vars;
5#[doc(hidden)]
6pub mod models;
7#[doc(hidden)]
8pub mod process;
9
10mod cargo_toml;
11mod io;
12mod messages;
13mod options;
14
15pub use process::NO_EXIT_CODE;
16
17use crate::models::{EditorData, Location};
18use anyhow::{Context, Result};
19use cargo_metadata::{Message, MetadataCommand};
20use io::Buffers;
21use messages::{Messages, transform_and_process_messages};
22use options::Options;
23use process::{CargoProcess, failed_to_execute_error_text};
24use std::{
25 io::Write,
26 path::Path,
27 process::{Command, Stdio},
28};
29
30pub const NVIM: &str = "nvim";
31
32const ADDITIONAL_ENVIRONMENT_VARIABLES: &str =
33 include_str!("../additional_environment_variables.txt");
34
35#[doc(hidden)]
36pub fn run_cargo_filtered(current_exe: String) -> Result<i32> {
37 let workspace_root = MetadataCommand::new()
38 .no_deps()
39 .exec()
40 .ok()
41 .map(|m| m.workspace_root);
42 let workspace_root = workspace_root.as_ref().map(|w| w.as_ref());
43 let options = Options::from_os_env(current_exe, workspace_root)?;
44
45 let mut cargo_process = CargoProcess::run(&options)?;
46 let mut buffers = cargo_process.buffers()?;
47
48 let process_messages = |buffers: &mut Buffers,
49 messages: Vec<Message>,
50 locations_in_consistent_order: Vec<Location>,
51 workspace_root: &Path|
52 -> Result<()> {
53 let messages = messages.into_iter();
54 if options.json_message_format {
55 for message in messages {
56 buffers.writeln_to_stdout(&serde_json::to_string(&message)?)?;
57 }
58 } else {
59 for message in messages.filter_map(|message| match message {
60 Message::CompilerMessage(compiler_message) => compiler_message.message.rendered,
61 _ => None,
62 }) {
63 buffers.write_to_stderr(message)?;
64 }
65 }
66 open_affected_files_in_external_app(
67 buffers,
68 locations_in_consistent_order,
69 &options,
70 workspace_root,
71 )
72 };
73
74 let mut parsed_messages =
75 Messages::parse_with_timeout_on_error(&mut buffers, Some(&cargo_process), &options)?;
76
77 let exit_code = if parsed_messages.child_killed {
78 buffers.writeln_to_stdout("")?;
79 let exit_code = cargo_process.wait()?;
80 parsed_messages.merge(Messages::parse_with_timeout_on_error(
81 &mut buffers,
82 None,
83 &options,
84 )?);
85 transform_and_process_messages(
86 &mut buffers,
87 parsed_messages,
88 &options,
89 workspace_root,
90 process_messages,
91 )?;
92 buffers.copy_from_child_stdout_reader_to_stdout_writer()?;
93
94 exit_code
95 } else {
96 transform_and_process_messages(
97 &mut buffers,
98 parsed_messages,
99 &options,
100 workspace_root,
101 process_messages,
102 )?;
103 buffers.copy_from_child_stdout_reader_to_stdout_writer()?;
104 cargo_process.wait()?
105 };
106
107 if options.help {
108 buffers.writeln_to_stdout("")?;
109 buffers.write_to_stdout(ADDITIONAL_ENVIRONMENT_VARIABLES)?;
110 }
111
112 Ok(exit_code)
113}
114
115fn open_affected_files_in_external_app(
116 buffers: &mut Buffers,
117 locations_in_consistent_order: Vec<Location>,
118 options: &Options,
119 workspace_root: &Path,
120) -> Result<()> {
121 let app = &options.open_in_external_app;
122 if !app.is_empty() {
123 let editor_data = EditorData::new(workspace_root, locations_in_consistent_order);
124 let mut child = Command::new(app).stdin(Stdio::piped()).spawn()?;
125 child
126 .stdin
127 .take()
128 .context("no stdin")?
129 .write_all(serde_json::to_string(&editor_data)?.as_bytes())?;
130
131 let error_text = failed_to_execute_error_text(app);
132 let output = child.wait_with_output().context(error_text)?;
133
134 buffers.write_all_to_stderr(&output.stdout)?;
135 buffers.write_all_to_stderr(&output.stderr)?;
136 }
137 Ok(())
138}
139
140#[doc(hidden)]
141#[macro_export]
142macro_rules! run_subcommand {
143 () => {
144 #[doc(hidden)]
145 fn main() -> anyhow::Result<()> {
146 use anyhow::Context;
147 let current_exe = std::env::current_exe()?
148 .file_stem()
149 .context("invalid executable")?
150 .to_string_lossy()
151 .to_string();
152 std::process::exit(cargo_limit::run_cargo_filtered(current_exe)?);
153 }
154 };
155}