Skip to main content

lanflare_cli/
lib.rs

1pub mod commands;
2pub mod tui;
3
4use clap::{Args, Parser, Subcommand};
5use std::path::PathBuf;
6
7#[derive(Parser, Debug)]
8#[command(
9    name = "lanflare",
10    version,
11    about = "Fast, cross-platform local network file sharing (LocalSend v2.2 compatible)"
12)]
13pub struct Cli {
14    /// Device alias advertised on the network
15    #[arg(long, global = true)]
16    pub alias: Option<String>,
17
18    /// Port to bind services on
19    #[arg(long, short, global = true)]
20    pub port: Option<u16>,
21
22    #[command(subcommand)]
23    pub command: Option<Commands>,
24}
25
26#[derive(Subcommand, Debug)]
27pub enum Commands {
28    /// Scan and list nearby peers on the local network
29    Scan(ScanArgs),
30
31    /// Send files to a peer
32    Send(SendArgs),
33
34    /// Run receiver daemon
35    Receive(ReceiveArgs),
36
37    /// Print local status and identity
38    Status,
39}
40
41#[derive(Args, Debug)]
42pub struct ScanArgs {
43    /// Scan duration in seconds
44    #[arg(long, short = 't', default_value = "3")]
45    pub duration: u64,
46
47    /// Disable UDP Multicast discovery
48    #[arg(long)]
49    pub no_multicast: bool,
50
51    /// Disable mDNS discovery
52    #[arg(long)]
53    pub no_mdns: bool,
54
55    /// Force active /24 subnet IP scan
56    #[arg(long, short = 's')]
57    pub subnet: bool,
58
59    /// Disable fallback /24 subnet IP scan
60    #[arg(long)]
61    pub no_subnet: bool,
62}
63
64#[derive(Args, Debug)]
65pub struct SendArgs {
66    /// File path(s) to send
67    #[arg(required = true)]
68    pub files: Vec<PathBuf>,
69
70    /// Destination IP, alias, or fingerprint
71    #[arg(long, short)]
72    pub to: Option<String>,
73
74    /// Security PIN if required by receiver
75    #[arg(long)]
76    pub pin: Option<String>,
77}
78
79#[derive(Args, Debug)]
80pub struct ReceiveArgs {
81    /// Directory where received files will be saved
82    #[arg(long, short)]
83    pub dir: Option<PathBuf>,
84
85    /// Port to listen on (defaults to configured port)
86    #[arg(long, short)]
87    pub port: Option<u16>,
88
89    /// Automatically accept transfers without interactive prompt
90    #[arg(long)]
91    pub auto_accept: bool,
92
93    /// Require incoming connections to provide this PIN
94    #[arg(long)]
95    pub pin: Option<String>,
96}
97
98/// Executes the CLI application lifecycle based on parsed command line arguments.
99pub async fn run() -> anyhow::Result<()> {
100    let cli = Cli::parse();
101
102    let mut config = lanflare_core::config::AppConfig::default();
103    if let Some(alias) = cli.alias {
104        config.alias = alias;
105    }
106    if let Some(port) = cli.port {
107        config.port = port;
108    }
109
110    // Generate or load TLS identity for this session
111    let identity = lanflare_core::crypto::cert::generate_self_signed()?;
112
113    match cli.command {
114        Some(Commands::Scan(args)) => {
115            let scan_opts = commands::ScanOptions {
116                duration_secs: args.duration,
117                multicast: !args.no_multicast,
118                mdns: !args.no_mdns,
119                subnet: args.subnet,
120                fallback_subnet: !args.no_subnet,
121            };
122            commands::scan(&config, &identity, &scan_opts).await?;
123        }
124        Some(Commands::Send(args)) => {
125            let send_opts = commands::SendOptions {
126                files: args.files,
127                to: args.to,
128                pin: args.pin,
129            };
130            commands::send(&config, &identity, &send_opts).await?;
131        }
132        Some(Commands::Receive(args)) => {
133            let receive_opts = commands::ReceiveOptions {
134                dir: args.dir,
135                port: args.port,
136                auto_accept: args.auto_accept,
137                pin: args.pin,
138            };
139            commands::receive(&config, &identity, &receive_opts).await?;
140        }
141        Some(Commands::Status) | None => {
142            commands::status(&config, &identity).await?;
143        }
144    }
145
146    Ok(())
147}