1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
mod create;
mod default;
mod delete;
mod list;
mod show;

use colorful::Colorful;
pub use create::CreateCommand;
pub(crate) use delete::DeleteCommand;
pub(crate) use list::ListCommand;
pub(crate) use show::ShowCommand;

use crate::identity::default::DefaultCommand;
use crate::terminal::OckamColor;
use crate::{docs, fmt_log, fmt_ok, CommandGlobalOpts, PARSER_LOGS};
use clap::{Args, Subcommand};
use ockam_api::cli_state::traits::StateDirTrait;
use ockam_api::cli_state::CliState;

const LONG_ABOUT: &str = include_str!("./static/long_about.txt");

/// Manage identities
#[derive(Clone, Debug, Args)]
#[command(
arg_required_else_help = true,
subcommand_required = true,
long_about = docs::about(LONG_ABOUT),
)]
pub struct IdentityCommand {
    #[command(subcommand)]
    subcommand: IdentitySubcommand,
}

#[derive(Clone, Debug, Subcommand)]
pub enum IdentitySubcommand {
    Create(CreateCommand),
    Show(ShowCommand),
    List(ListCommand),
    Default(DefaultCommand),
    Delete(DeleteCommand),
}

impl IdentityCommand {
    pub fn run(self, options: CommandGlobalOpts) {
        match self.subcommand {
            IdentitySubcommand::Create(c) => c.run(options),
            IdentitySubcommand::Show(c) => c.run(options),
            IdentitySubcommand::List(c) => c.run(options),
            IdentitySubcommand::Delete(c) => c.run(options),
            IdentitySubcommand::Default(c) => c.run(options),
        }
    }
}

/// If the required identity is the default identity but if it has not been initialized yet
/// then initialize it
pub fn initialize_identity_if_default(opts: &CommandGlobalOpts, name: &Option<String>) {
    let name = get_identity_name(&opts.state, name);
    if name == "default" && opts.state.identities.default().is_err() {
        create_default_identity(opts);
    }
}

/// Return the name if identity_name is Some otherwise return the name of the default identity
pub fn get_identity_name(cli_state: &CliState, identity_name: &Option<String>) -> String {
    identity_name
        .clone()
        .unwrap_or_else(|| get_default_identity_name(cli_state))
}

/// Return the name of the default identity
pub fn get_default_identity_name(cli_state: &CliState) -> String {
    cli_state
        .identities
        .default()
        .map(|i| i.name().to_string())
        .unwrap_or_else(|_| "default".to_string())
}

/// Create the default identity
pub fn create_default_identity(opts: &CommandGlobalOpts) {
    let default = "default";
    let create_command = CreateCommand::new(default.into(), None);
    create_command.run(opts.clone().set_quiet());

    // Retrieve the identifier if available
    // Otherwise, use the name of the identity
    let identifier = match opts.state.identities.get(default) {
        Ok(i) => i.identifier().to_string(),
        Err(_) => default.to_string(),
    };

    if let Ok(mut logs) = PARSER_LOGS.lock() {
        logs.push(fmt_log!(
            "There is no identity, on this machine, marked as your default."
        ));
        logs.push(fmt_log!("Creating a new Ockam identity for you..."));
        logs.push(fmt_ok!(
            "Created: {}",
            identifier.color(OckamColor::PrimaryResource.color())
        ));
        logs.push(fmt_log!(
            "Marked this new identity as your default, on this machine.\n"
        ));
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::GlobalArgs;
    use ockam_api::cli_state::StateItemTrait;

    #[test]
    fn test_initialize() {
        let state = CliState::test().unwrap();
        let opts = CommandGlobalOpts::new_for_test(GlobalArgs::default(), state);

        // on start-up there is no default identity
        assert!(opts.state.identities.default().is_err());

        // if no name is given then the default identity is initialized
        initialize_identity_if_default(&opts, &None);
        assert!(opts.state.identities.default().is_ok());

        // if "default" is given as a name the default identity is initialized
        opts.state.identities.default().unwrap().delete().unwrap();
        initialize_identity_if_default(&opts, &Some("default".into()));
        assert!(opts.state.identities.default().is_ok());

        // if the name of another identity is given then the default identity is not initialized
        opts.state.identities.default().unwrap().delete().unwrap();
        initialize_identity_if_default(&opts, &Some("other".into()));
        assert!(opts.state.identities.default().is_err());
    }
}