Skip to main content

lanflare_core/
config.rs

1use std::path::PathBuf;
2use serde::{Deserialize, Serialize};
3
4fn default_save_to_history() -> bool {
5    true
6}
7
8fn default_multicast_address() -> String {
9    "224.0.0.167".to_string()
10}
11
12fn default_share_link_auto_accept() -> bool {
13    false
14}
15
16fn default_share_link_require_pin() -> bool {
17    true
18}
19
20/// Generates a friendly, two-word random device alias (e.g. "Golden Mango", "Sweet Apple").
21/// Avoids awkward, funny, or ambiguous words.
22pub fn generate_random_alias() -> String {
23    use rand::Rng;
24    const ADJECTIVES: &[&str] = &[
25        "Amber", "Azure", "Brave", "Bright", "Brisk", "Calm", "Clever", "Cool",
26        "Cosmic", "Crispy", "Crystal", "Dapper", "Eager", "Emerald", "Frosty", "Gentle",
27        "Golden", "Grand", "Green", "Happy", "Jolly", "Kind", "Lucky", "Lunar",
28        "Mellow", "Misty", "Noble", "Pacific", "Peaceful", "Placid", "Proud", "Quiet",
29        "Radiant", "Rosy", "Ruby", "Sapphire", "Serene", "Silent", "Silver", "Simple",
30        "Solar", "Starry", "Sturdy", "Sunny", "Sweet", "Swift", "Tender", "Tranquil",
31        "Valiant", "Velvet", "Vibrant", "Warm", "Wild", "Wise",
32    ];
33
34    const NOUNS: &[&str] = &[
35        "Apple", "Aurora", "Autumn", "Bamboo", "Beacon", "Berry", "Blossom", "Breeze",
36        "Brook", "Canyon", "Cedar", "Cherry", "Clover", "Comet", "Coral", "Cottage",
37        "Creek", "Cypress", "Dawn", "Dolphin", "Echo", "Falcon", "Fern", "Finch",
38        "Forest", "Garden", "Glade", "Grove", "Harbor", "Haven", "Hazel", "Island",
39        "Lagoon", "Lark", "Leaf", "Lemon", "Lotus", "Mango", "Maple", "Meadow",
40        "Melon", "Moon", "Mountain", "Oasis", "Ocean", "Orchard", "Orchid", "Otter",
41        "Peach", "Peak", "Pebble", "Penguin", "Pine", "Planet", "Plum", "Prairie",
42        "Rainbow", "Raven", "Ray", "Reef", "River", "Robin", "Rose", "Sail",
43        "Sequoia", "Sky", "Sparrow", "Spring", "Star", "Stream", "Summit", "Sunrise",
44        "Sunset", "Swallow", "Tea", "Tulip", "Valley", "Willow", "Wind",
45    ];
46
47    let mut rng = rand::rng();
48    let adj = ADJECTIVES[rng.random_range(0..ADJECTIVES.len())];
49    let noun = NOUNS[rng.random_range(0..NOUNS.len())];
50    format!("{adj} {noun}")
51}
52
53/// Generates a strong random 10-character password containing no '@', ':', or '/'
54pub fn generate_share_link_pin() -> String {
55    use rand::Rng;
56    const CHARSET: &[u8] = b"23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
57    let mut rng = rand::rng();
58    (0..10)
59        .map(|_| {
60            let idx = rng.random_range(0..CHARSET.len());
61            CHARSET[idx] as char
62        })
63        .collect()
64}
65
66fn default_share_link_pin() -> String {
67    generate_share_link_pin()
68}
69
70/// Global runtime configuration for LanFlare.
71#[derive(Clone, Debug, Serialize, Deserialize)]
72pub struct AppConfig {
73    pub alias: String,
74    pub port: u16,
75    pub download_dir: PathBuf,
76    pub auto_accept: bool,
77    pub pin: Option<String>,
78    #[serde(default = "default_save_to_history")]
79    pub save_to_history: bool,
80    #[serde(default = "default_multicast_address")]
81    pub multicast_address: String,
82    #[serde(default = "default_share_link_auto_accept")]
83    pub share_link_auto_accept: bool,
84    #[serde(default = "default_share_link_require_pin")]
85    pub share_link_require_pin: bool,
86    #[serde(default = "default_share_link_pin")]
87    pub share_link_pin: String,
88}
89
90impl Default for AppConfig {
91    fn default() -> Self {
92        #[cfg(target_os = "android")]
93        let download_dir = PathBuf::from("/storage/emulated/0/Download");
94        #[cfg(not(target_os = "android"))]
95        let download_dir = dirs::download_dir().unwrap_or_else(|| PathBuf::from("."));
96
97        Self {
98            alias: generate_random_alias(),
99            port: 53317,
100            download_dir,
101            auto_accept: false,
102            pin: None,
103            save_to_history: true,
104            multicast_address: default_multicast_address(),
105            share_link_auto_accept: false,
106            share_link_require_pin: true,
107            share_link_pin: generate_share_link_pin(),
108        }
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115
116    #[test]
117    fn test_generate_random_alias_structure() {
118        for _ in 0..20 {
119            let alias = generate_random_alias();
120            let parts: Vec<&str> = alias.split_whitespace().collect();
121            assert_eq!(parts.len(), 2, "Alias must consist of exactly two words: {alias}");
122            assert!(!parts[0].is_empty());
123            assert!(!parts[1].is_empty());
124        }
125    }
126}