p2p_chat/ui/runner/actions/commands/
friends.rs

1//! This module contains command handlers related to managing friends.
2use anyhow::Result;
3use base64::prelude::BASE64_STANDARD;
4use base64::Engine;
5use libp2p::PeerId;
6use std::str::FromStr;
7
8use crate::types::Friend;
9
10use super::super::context::CommandContext;
11
12/// Adds a new friend to the application's friend list.
13///
14/// This command requires the friend's Peer ID, their E2E public key, and optionally a nickname.
15///
16/// Usage: `friend <peer_id> <e2e_key> [nickname]`
17///
18/// # Arguments
19///
20/// * `parts` - A slice of strings representing the command arguments.
21/// * `context` - The `CommandContext` providing access to the application's state and node.
22///
23/// # Errors
24///
25/// This function returns an error if adding the friend to storage fails.
26pub async fn add_friend(parts: &[&str], context: &CommandContext) -> Result<()> {
27    if !(3..=4).contains(&parts.len()) {
28        context.emit_chat("Usage: friend <peer_id> <e2e_key> [nickname]");
29        return Ok(());
30    }
31
32    let peer_id = match PeerId::from_str(parts[1]) {
33        Ok(id) => id,
34        Err(e) => {
35            context.emit_chat(format!("❌ Invalid peer ID: {}", e));
36            return Ok(());
37        }
38    };
39
40    let e2e_public_key = match BASE64_STANDARD.decode(parts[2]) {
41        Ok(key) => key,
42        Err(e) => {
43            context.emit_chat(format!("❌ Invalid base64 key: {}", e));
44            return Ok(());
45        }
46    };
47
48    let nickname = parts.get(3).map(|s| s.to_string());
49
50    let friend = Friend {
51        peer_id,
52        e2e_public_key,
53        nickname: nickname.clone(),
54    };
55
56    match context.node().friends.add_friend(friend).await {
57        Ok(()) => {
58            context.emit_chat(format!(
59                "✅ Added friend: {} ({})",
60                peer_id,
61                nickname.unwrap_or_else(|| "no nickname".to_string())
62            ));
63        }
64        Err(e) => {
65            context.emit_chat(format!("❌ Failed to add friend: {}", e));
66        }
67    }
68
69    Ok(())
70}
71
72/// Lists all friends currently stored in the application.
73///
74/// The output includes the friend's Peer ID and their nickname (if available).
75///
76/// # Arguments
77///
78/// * `context` - The `CommandContext` providing access to the application's state and node.
79///
80/// # Errors
81///
82/// This function returns an error if retrieving the friend list from storage fails.
83pub async fn list_friends(context: &CommandContext) -> Result<()> {
84    match context.node().friends.list_friends().await {
85        Ok(friends) => {
86            if friends.is_empty() {
87                context.emit_chat("No friends added yet.");
88            } else {
89                let mut output = format!("Friends ({}):", friends.len());
90                for friend in friends {
91                    let nickname = friend.nickname.as_deref().unwrap_or("(no nickname)");
92                    output.push_str(&format!("\n  {} - {}", friend.peer_id, nickname));
93                }
94                context.emit_chat(output);
95            }
96        }
97        Err(e) => {
98            context.emit_chat(format!("❌ Failed to list friends: {}", e));
99        }
100    }
101
102    Ok(())
103}