Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/cli/src/bin/code/main.rs
3320 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
mod legacy_args;
6
7
use std::process::Command;
8
9
use clap::Parser;
10
use cli::{
11
commands::{args, serve_web, tunnels, update, version, CommandContext},
12
constants::get_default_user_agent,
13
desktop, log,
14
state::LauncherPaths,
15
util::{
16
errors::{wrap, AnyError},
17
is_integrated_cli,
18
prereqs::PreReqChecker,
19
},
20
};
21
use legacy_args::try_parse_legacy;
22
use opentelemetry::sdk::trace::TracerProvider as SdkTracerProvider;
23
use opentelemetry::trace::TracerProvider;
24
25
#[tokio::main]
26
async fn main() -> Result<(), std::convert::Infallible> {
27
let raw_args = std::env::args_os().collect::<Vec<_>>();
28
let parsed = try_parse_legacy(&raw_args)
29
.map(|core| args::AnyCli::Integrated(args::IntegratedCli { core }))
30
.unwrap_or_else(|| {
31
if let Ok(true) = is_integrated_cli() {
32
args::AnyCli::Integrated(args::IntegratedCli::parse_from(&raw_args))
33
} else {
34
args::AnyCli::Standalone(args::StandaloneCli::parse_from(&raw_args))
35
}
36
});
37
38
let core = parsed.core();
39
let context_paths = LauncherPaths::migrate(core.global_options.cli_data_dir.clone()).unwrap();
40
let context_args = core.clone();
41
42
// gets a command context without installing the global logger
43
let context_no_logger = || CommandContext {
44
http: reqwest::ClientBuilder::new()
45
.user_agent(get_default_user_agent())
46
.build()
47
.unwrap(),
48
paths: context_paths,
49
log: make_logger(&context_args),
50
args: context_args,
51
};
52
53
// gets a command context with the global logger installer. Usually what most commands want.
54
macro_rules! context {
55
() => {{
56
let context = context_no_logger();
57
log::install_global_logger(context.log.clone());
58
context
59
}};
60
}
61
62
let result = match parsed {
63
args::AnyCli::Standalone(args::StandaloneCli {
64
subcommand: Some(cmd),
65
..
66
}) => match cmd {
67
args::StandaloneCommands::Update(args) => update::update(context!(), args).await,
68
},
69
args::AnyCli::Standalone(args::StandaloneCli { core: c, .. })
70
| args::AnyCli::Integrated(args::IntegratedCli { core: c, .. }) => match c.subcommand {
71
None => {
72
let context = context!();
73
let ca = context.args.get_base_code_args();
74
start_code(context, ca).await
75
}
76
77
Some(args::Commands::Extension(extension_args)) => {
78
let context = context!();
79
let mut ca = context.args.get_base_code_args();
80
extension_args.add_code_args(&mut ca);
81
start_code(context, ca).await
82
}
83
84
Some(args::Commands::Status) => {
85
let context = context!();
86
let mut ca = context.args.get_base_code_args();
87
ca.push("--status".to_string());
88
start_code(context, ca).await
89
}
90
91
Some(args::Commands::Version(version_args)) => match version_args.subcommand {
92
args::VersionSubcommand::Use(use_version_args) => {
93
version::switch_to(context!(), use_version_args).await
94
}
95
args::VersionSubcommand::Show => version::show(context!()).await,
96
},
97
98
Some(args::Commands::CommandShell(cs_args)) => {
99
tunnels::command_shell(context!(), cs_args).await
100
}
101
102
Some(args::Commands::ServeWeb(sw_args)) => {
103
serve_web::serve_web(context!(), sw_args).await
104
}
105
106
Some(args::Commands::Tunnel(mut tunnel_args)) => match tunnel_args.subcommand.take() {
107
Some(args::TunnelSubcommand::Prune) => tunnels::prune(context!()).await,
108
Some(args::TunnelSubcommand::Unregister) => tunnels::unregister(context!()).await,
109
Some(args::TunnelSubcommand::Kill) => tunnels::kill(context!()).await,
110
Some(args::TunnelSubcommand::Restart) => tunnels::restart(context!()).await,
111
Some(args::TunnelSubcommand::Status) => tunnels::status(context!()).await,
112
Some(args::TunnelSubcommand::Rename(rename_args)) => {
113
tunnels::rename(context!(), rename_args).await
114
}
115
Some(args::TunnelSubcommand::User(user_command)) => {
116
tunnels::user(context!(), user_command).await
117
}
118
Some(args::TunnelSubcommand::Service(service_args)) => {
119
tunnels::service(context_no_logger(), tunnel_args, service_args).await
120
}
121
Some(args::TunnelSubcommand::ForwardInternal(forward_args)) => {
122
tunnels::forward(context_no_logger(), forward_args).await
123
}
124
None => tunnels::serve(context_no_logger(), tunnel_args.serve_args).await,
125
},
126
},
127
};
128
129
match result {
130
Err(e) => print_and_exit(e),
131
Ok(code) => std::process::exit(code),
132
}
133
}
134
135
fn make_logger(core: &args::CliCore) -> log::Logger {
136
let log_level = if core.global_options.verbose {
137
log::Level::Trace
138
} else {
139
core.global_options.log.unwrap_or(log::Level::Info)
140
};
141
142
let tracer = SdkTracerProvider::builder().build().tracer("codecli");
143
let mut log = log::Logger::new(tracer, log_level);
144
if let Some(f) = &core.global_options.log_to_file {
145
log = log
146
.with_sink(log::FileLogSink::new(log_level, f).expect("expected to make file logger"))
147
}
148
149
log
150
}
151
152
fn print_and_exit<E>(err: E) -> !
153
where
154
E: std::fmt::Display,
155
{
156
log::emit(log::Level::Error, "", &format!("{err}"));
157
std::process::exit(1);
158
}
159
160
async fn start_code(context: CommandContext, args: Vec<String>) -> Result<i32, AnyError> {
161
// todo: once the integrated CLI takes the place of the Node.js CLI, this should
162
// redirect to the current installation without using the CodeVersionManager.
163
164
let platform = PreReqChecker::new().verify().await?;
165
let version_manager =
166
desktop::CodeVersionManager::new(context.log.clone(), &context.paths, platform);
167
let version = match &context.args.editor_options.code_options.use_version {
168
Some(v) => desktop::RequestedVersion::try_from(v.as_str())?,
169
None => version_manager.get_preferred_version(),
170
};
171
172
let binary = match version_manager.try_get_entrypoint(&version).await {
173
Some(ep) => ep,
174
None => {
175
desktop::prompt_to_install(&version);
176
return Ok(1);
177
}
178
};
179
180
let code = Command::new(&binary)
181
.args(args)
182
.status()
183
.map(|s| s.code().unwrap_or(1))
184
.map_err(|e| wrap(e, format!("error running editor from {}", binary.display())))?;
185
186
Ok(code)
187
}
188
189