bw/
main.rs

1#![doc = include_str!("../README.md")]
2
3use base64::{Engine, engine::general_purpose::STANDARD};
4use bitwarden_cli::install_color_eyre;
5use clap::{CommandFactory, Parser};
6use clap_complete::Shell;
7use color_eyre::eyre::Result;
8use env_logger::Target;
9
10use crate::{command::*, render::CommandResult};
11
12mod admin_console;
13mod auth;
14mod command;
15mod platform;
16mod render;
17mod tools;
18mod vault;
19
20#[tokio::main(flavor = "current_thread")]
21async fn main() -> Result<()> {
22    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info"))
23        .target(Target::Stderr)
24        .init();
25
26    let cli = Cli::parse();
27    install_color_eyre(cli.color)?;
28    let render_config = render::RenderConfig::new(&cli);
29
30    let Some(command) = cli.command else {
31        let mut cmd = Cli::command();
32        cmd.print_help()?;
33        return Ok(());
34    };
35
36    let result = process_commands(command, cli.session).await;
37
38    // Render the result of the command
39    render_config.render_result(result)
40}
41
42async fn process_commands(command: Commands, _session: Option<String>) -> CommandResult {
43    // Try to initialize the client with the session if provided
44    // Ideally we'd have separate clients and this would be an enum, something like:
45    // enum CliClient {
46    //   Unlocked(_),  // If the user already logged in and the provided session is valid
47    //   Locked(_),    // If the user is logged in, but the session hasn't been provided
48    //   LoggedOut(_), // If the user is not logged in
49    // }
50    // If the session was invalid, we'd just return an error immediately
51    // This would allow each command to match on the client type that they need, and we don't need
52    // to do two matches over the whole command tree
53    let client = bitwarden_pm::PasswordManagerClient::new(None);
54
55    match command {
56        // Auth commands
57        Commands::Login(args) => args.run().await,
58        Commands::Logout => todo!(),
59        Commands::Register(register) => register.run().await,
60
61        // KM commands
62        Commands::Unlock(_args) => todo!(),
63
64        // Platform commands
65        Commands::Sync { .. } => todo!(),
66
67        Commands::Encode => {
68            let input = std::io::read_to_string(std::io::stdin())?;
69            let encoded = STANDARD.encode(input);
70            Ok(encoded.into())
71        }
72
73        Commands::Config { command } => command.run().await,
74
75        Commands::Update { .. } => todo!(),
76
77        Commands::Completion { shell } => {
78            let Some(shell) = shell.or_else(Shell::from_env) else {
79                return Ok(
80                    "Couldn't autodetect a valid shell. Run `bw completion --help` for more info."
81                        .into(),
82                );
83            };
84
85            let mut cmd = Cli::command();
86            let name = cmd.get_name().to_string();
87            clap_complete::generate(shell, &mut cmd, name, &mut std::io::stdout());
88            Ok(().into())
89        }
90
91        Commands::Status => todo!(),
92
93        // Vault commands
94        Commands::Item { command: _ } => todo!(),
95        Commands::Template { command } => command.run(),
96
97        Commands::List => todo!(),
98        Commands::Get => todo!(),
99        Commands::Create => todo!(),
100        Commands::Edit => todo!(),
101        Commands::Delete => todo!(),
102        Commands::Restore => todo!(),
103        Commands::Move => todo!(),
104
105        // Admin console commands
106        Commands::Confirm { .. } => todo!(),
107
108        // Tools commands
109        Commands::Generate(arg) => arg.run(&client),
110        Commands::Import => todo!(),
111        Commands::Export => todo!(),
112        Commands::Share => todo!(),
113        Commands::Send => todo!(),
114        Commands::Receive => todo!(),
115    }
116}