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