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
20pub 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
53pub 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#[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}