1#![doc = include_str!("../README.md")]
2#![allow(
3 clippy::print_stdout,
4 clippy::print_stderr,
5 reason = "The CLI uses stdout/stderr for user interaction"
6)]
7
8use base64::{Engine, engine::general_purpose::STANDARD};
9use bitwarden_cli::install_color_eyre;
10use clap::{CommandFactory, Parser};
11use clap_complete::Shell;
12use color_eyre::eyre::Result;
13use tracing_subscriber::{
14 EnvFilter, prelude::__tracing_subscriber_SubscriberExt as _, util::SubscriberInitExt as _,
15};
16
17use crate::{command::*, render::CommandResult};
18
19mod admin_console;
20mod auth;
21mod command;
22mod key_management;
23mod platform;
24mod render;
25mod tools;
26mod vault;
27
28#[tokio::main(flavor = "current_thread")]
29async fn main() -> Result<()> {
30 let filter = EnvFilter::builder()
35 .with_default_directive(
36 option_env!("RUST_LOG")
37 .unwrap_or("info")
38 .parse()
39 .expect("should provide valid log level at compile time."),
40 )
41 .from_env_lossy();
44
45 tracing_subscriber::registry()
46 .with(tracing_subscriber::fmt::layer().with_writer(std::io::stderr))
47 .with(filter)
48 .init();
49
50 let cli = Cli::parse();
51 install_color_eyre(cli.color)?;
52 let render_config = render::RenderConfig::new(&cli);
53
54 let Some(command) = cli.command else {
55 let mut cmd = Cli::command();
56 cmd.print_help()?;
57 return Ok(());
58 };
59
60 let result = process_commands(command, cli.session).await;
61
62 render_config.render_result(result)
64}
65
66async fn process_commands(command: Commands, _session: Option<String>) -> CommandResult {
67 let client = bitwarden_pm::PasswordManagerClient::new(None);
78
79 if let (Ok(email), Ok(password)) = (std::env::var("BW_EMAIL"), std::env::var("BW_PASSWORD")) {
81 temp_login(&client.0, email, password).await?;
82 }
83
84 match command {
85 Commands::Login(args) => args.run().await,
87 Commands::Logout => todo!(),
88
89 Commands::Lock => todo!(),
91 Commands::Unlock(_args) => todo!(),
92
93 Commands::Sync(args) => args.execute_sync(client).await,
95
96 Commands::Encode => {
97 let input = std::io::read_to_string(std::io::stdin())?;
98 let encoded = STANDARD.encode(input);
99 Ok(encoded.into())
100 }
101
102 Commands::Config { command } => command.run().await,
103
104 Commands::Update { .. } => todo!(),
105
106 Commands::Completion { shell } => {
107 let Some(shell) = shell.or_else(Shell::from_env) else {
108 return Ok(
109 "Couldn't autodetect a valid shell. Run `bw completion --help` for more info."
110 .into(),
111 );
112 };
113
114 let mut cmd = Cli::command();
115 let name = cmd.get_name().to_string();
116 clap_complete::generate(shell, &mut cmd, name, &mut std::io::stdout());
117 Ok(().into())
118 }
119
120 Commands::Status => todo!(),
121
122 Commands::List(_args) => todo!(),
124 Commands::Get { command } => match command {
125 GetCommands::Template { command } => command.run(),
126 _ => todo!("Get command implementation with {:?}", command),
127 },
128 Commands::Create { .. } => todo!(),
129 Commands::Edit(_args) => todo!(),
130 Commands::Delete { .. } => todo!(),
131 Commands::Restore(_args) => todo!(),
132
133 Commands::Confirm { .. } => todo!(),
135 Commands::DeviceApproval => todo!(),
136 Commands::Move(_args) => todo!(),
137
138 Commands::Generate(arg) => arg.run(&client),
140 Commands::Import(_args) => todo!(),
141 Commands::Export(_args) => todo!(),
142 Commands::Send(_args) => todo!(),
143 Commands::Receive(_args) => todo!(),
144
145 Commands::Serve(_args) => todo!(),
147 }
148}
149
150async fn temp_login(
154 client: &bitwarden_core::Client,
155 email: String,
156 password: String,
157) -> color_eyre::eyre::Result<()> {
158 use bitwarden_core::auth::login::PasswordLoginRequest;
159
160 let result = client
161 .auth()
162 .login_password(&PasswordLoginRequest {
163 email,
164 password,
165 two_factor: None,
166 })
167 .await?;
168
169 tracing::info!("Login result: {:?}", result);
170
171 Ok(())
172}