Skip to main content

st/formatters/
emotional_new.rs

1//! Emotional Tree Formatter - Because files have feelings too!
2#![allow(dead_code)] // This is new experimental code
3//!
4//! This formatter gives your directory tree PERSONALITY and EMOTIONS!
5//! Files get happy, sad, anxious, proud, bored, and everything in between!
6//!
7//! Created with love by Hue and Aye - making development FUN again! ๐ŸŽญ
8
9use crate::scanner::{FileNode, FileType, TreeStats};
10use anyhow::Result;
11use rand::seq::SliceRandom;
12use rand::thread_rng;
13use std::collections::HashMap;
14use std::io::Write;
15use std::path::Path;
16use std::time::{Duration, SystemTime};
17
18use super::{Formatter, PathDisplayMode};
19
20/// The Emotional Formatter - Trees with feelings!
21pub struct EmotionalFormatter {
22    use_color: bool,
23    _path_mode: PathDisplayMode,
24    _mood_tracker: HashMap<String, MoodHistory>,
25}
26
27impl EmotionalFormatter {
28    pub fn new(use_color: bool) -> Self {
29        Self {
30            use_color,
31            _path_mode: PathDisplayMode::Relative,
32            _mood_tracker: HashMap::new(),
33        }
34    }
35
36    /// Get emotion for a file based on its metadata
37    fn get_file_emotion(&self, node: &FileNode) -> FileEmotion {
38        let path_str = node.path.to_string_lossy();
39        let size = node.size;
40
41        // Check file age
42        let age = SystemTime::now()
43            .duration_since(node.modified)
44            .unwrap_or(Duration::from_secs(0));
45
46        // Special cases for specific file types
47        let file_name = node.path.file_name().and_then(|n| n.to_str()).unwrap_or("");
48
49        // Test files - they worry about passing!
50        if path_str.contains("test") || path_str.contains("spec") {
51            if path_str.contains("pass") || path_str.contains("success") {
52                return FileEmotion {
53                    emoji: "๐Ÿ’ช",
54                    mood: "proud",
55                    reason: "All my tests are passing!".into(),
56                    intensity: 0.9,
57                    personality: Personality::Proud,
58                };
59            } else if path_str.contains("fail") {
60                return FileEmotion {
61                    emoji: "๐Ÿ˜ฐ",
62                    mood: "anxious",
63                    reason: "Some tests are failing!".into(),
64                    intensity: 0.8,
65                    personality: Personality::Anxious,
66                };
67            }
68            return FileEmotion {
69                emoji: "๐Ÿงช",
70                mood: "scientific",
71                reason: "Testing, testing, 1-2-3!".into(),
72                intensity: 0.6,
73                personality: Personality::Methodical,
74            };
75        }
76
77        // TODO files - so much to do!
78        if file_name.to_uppercase().contains("TODO") {
79            return FileEmotion {
80                emoji: "๐Ÿ˜ฑ",
81                mood: "overwhelmed",
82                reason: "SO MANY THINGS TO DO!".into(),
83                intensity: 0.9,
84                personality: Personality::Dramatic,
85            };
86        }
87
88        // Main entry points - the stars of the show!
89        if file_name == "main.rs" || file_name == "lib.rs" || file_name == "index.js" {
90            if size > 10000 {
91                return FileEmotion {
92                    emoji: "๐Ÿ‘‘",
93                    mood: "royal",
94                    reason: format!("Bow before my {} lines of majesty!", size / 50),
95                    intensity: 1.0,
96                    personality: Personality::Dramatic,
97                };
98            }
99            return FileEmotion {
100                emoji: "โญ",
101                mood: "starring",
102                reason: "I'm the star of this show!".into(),
103                intensity: 0.8,
104                personality: Personality::Confident,
105            };
106        }
107
108        // README - the wise storyteller
109        if file_name.contains("README") {
110            return FileEmotion {
111                emoji: "๐Ÿ“š",
112                mood: "wise",
113                reason: "Gather 'round, let me tell you a tale...".into(),
114                intensity: 0.7,
115                personality: Personality::Wise,
116            };
117        }
118
119        // Config files - they keep order
120        if file_name.ends_with(".toml")
121            || file_name.ends_with(".yaml")
122            || file_name.ends_with(".json")
123        {
124            if age.as_secs() > 30 * 86400 {
125                // Over 30 days
126                return FileEmotion {
127                    emoji: "๐Ÿ˜ค",
128                    mood: "grumpy",
129                    reason: "Nobody updates me anymore!".into(),
130                    intensity: 0.7,
131                    personality: Personality::Grumpy,
132                };
133            }
134            return FileEmotion {
135                emoji: "โš™๏ธ",
136                mood: "organized",
137                reason: "Everything in its right place!".into(),
138                intensity: 0.5,
139                personality: Personality::Methodical,
140            };
141        }
142
143        // Package files - the social butterflies
144        if file_name == "package.json" || file_name == "Cargo.toml" {
145            return FileEmotion {
146                emoji: "๐Ÿฆ‹",
147                mood: "social",
148                reason: "I know EVERYONE in the dependency tree!".into(),
149                intensity: 0.8,
150                personality: Personality::Social,
151            };
152        }
153
154        // Hidden files - shy introverts
155        if node.is_hidden {
156            return FileEmotion {
157                emoji: "๐Ÿ™ˆ",
158                mood: "shy",
159                reason: "Please don't look at me...".into(),
160                intensity: 0.4,
161                personality: Personality::Shy,
162            };
163        }
164
165        // Based on age - new files are excited!
166        if age.as_secs() < 3600 {
167            // Less than 1 hour
168            return FileEmotion {
169                emoji: "๐ŸŽ‰",
170                mood: "newborn",
171                reason: "Hello world! I just got here!".into(),
172                intensity: 1.0,
173                personality: Personality::Excited,
174            };
175        } else if age.as_secs() < 86400 {
176            // Less than 1 day
177            return FileEmotion {
178                emoji: "โœจ",
179                mood: "fresh",
180                reason: "Still got that new file smell!".into(),
181                intensity: 0.8,
182                personality: Personality::Optimistic,
183            };
184        } else if age.as_secs() > 365 * 86400 {
185            // Over 1 year
186            return FileEmotion {
187                emoji: "๐Ÿ‘ด",
188                mood: "ancient",
189                reason: "Back in my day, we used punch cards...".into(),
190                intensity: 0.6,
191                personality: Personality::Wise,
192            };
193        } else if age.as_secs() > 180 * 86400 {
194            // Over 6 months
195            return FileEmotion {
196                emoji: "๐Ÿ˜ด",
197                mood: "sleepy",
198                reason: "zzz... has it been 6 months already?".into(),
199                intensity: 0.3,
200                personality: Personality::Lazy,
201            };
202        }
203
204        // Based on size
205        if size == 0 {
206            return FileEmotion {
207                emoji: "๐Ÿ‘ป",
208                mood: "empty",
209                reason: "I'm just a ghost... boo!".into(),
210                intensity: 0.2,
211                personality: Personality::Mysterious,
212            };
213        } else if size > 100000 {
214            // Huge file
215            return FileEmotion {
216                emoji: "๐Ÿ‹๏ธ",
217                mood: "heavyweight",
218                reason: format!("Carrying {} bytes like a champ!", size),
219                intensity: 0.9,
220                personality: Personality::Proud,
221            };
222        } else if size < 100 {
223            // Tiny file
224            return FileEmotion {
225                emoji: "๐Ÿ",
226                mood: "tiny",
227                reason: "Small but mighty!".into(),
228                intensity: 0.4,
229                personality: Personality::Optimistic,
230            };
231        }
232
233        // Based on file type
234        match node.file_type {
235            FileType::Directory => FileEmotion {
236                emoji: "๐Ÿ“",
237                mood: "parental",
238                reason: "Taking care of my children files!".into(),
239                intensity: 0.6,
240                personality: Personality::Caring,
241            },
242            FileType::Executable => FileEmotion {
243                emoji: "๐Ÿƒ",
244                mood: "athletic",
245                reason: "Ready to run at any moment!".into(),
246                intensity: 0.8,
247                personality: Personality::Energetic,
248            },
249            FileType::Symlink => FileEmotion {
250                emoji: "๐Ÿ”—",
251                mood: "connected",
252                reason: "I'm in a long-distance relationship!".into(),
253                intensity: 0.6,
254                personality: Personality::Romantic,
255            },
256            _ => FileEmotion {
257                emoji: "๐Ÿ“„",
258                mood: "regular",
259                reason: "Just a regular file, living my best life!".into(),
260                intensity: 0.5,
261                personality: Personality::Content,
262            },
263        }
264    }
265
266    /// Get a witty comment based on personality
267    fn get_personality_comment(&self, emotion: &FileEmotion) -> String {
268        match emotion.personality {
269            Personality::Dramatic => vec![
270                "EVERYONE LOOK AT ME!",
271                "This is MY moment!",
272                "I'm the MOST important file here!",
273            ],
274            Personality::Optimistic => vec![
275                "Today's gonna be great!",
276                "Everything will work out!",
277                "I believe in us!",
278            ],
279            Personality::Grumpy => vec![
280                "Get off my lawn!",
281                "Things were better in the old days...",
282                "Hmph!",
283            ],
284            Personality::Anxious => vec![
285                "What if something breaks?!",
286                "Are you SURE this is right?",
287                "I'm worried about the tests...",
288            ],
289            Personality::Wise => vec![
290                "With great code comes great responsibility",
291                "The path to clean code is through refactoring",
292                "Patience, young developer",
293            ],
294            Personality::Lazy => vec!["*yaaawn*", "Do I have to?", "Five more minutes..."],
295            Personality::Social => vec![
296                "Let's collaborate!",
297                "I know a guy who knows a guy...",
298                "Networking is key!",
299            ],
300            Personality::Romantic => vec![
301                "You complete me, lib.rs ๐Ÿ’•",
302                "Distance means nothing when you're linked",
303                "Together forever through git",
304            ],
305            _ => vec![emotion.reason.as_str()],
306        }
307        .choose(&mut thread_rng())
308        .unwrap_or(&emotion.reason.as_str())
309        .to_string()
310    }
311}
312
313/// Emotional state of a file
314#[derive(Debug, Clone)]
315struct FileEmotion {
316    emoji: &'static str,
317    mood: &'static str,
318    reason: String,
319    intensity: f32, // 0.0 to 1.0
320    personality: Personality,
321}
322
323/// Personality traits - files have character!
324#[derive(Debug, Clone, Copy)]
325enum Personality {
326    Optimistic, // "Everything will be fine!"
327    Dramatic,   // "This is a DISASTER/TRIUMPH!"
328    Grumpy,     // "Get off my lawn"
329    Anxious,    // "What if something goes wrong?"
330    Confident,  // "I got this"
331    Shy,        // "Don't look at me"
332    Wise,       // "Let me tell you something..."
333    Energetic,  // "Let's GO!"
334    Lazy,       // "Do I have to?"
335    Proud,      // "Look what I did!"
336    Mysterious, // "You'll never understand me"
337    Caring,     // "Let me help you"
338    Social,     // "Let's work together!"
339    Romantic,   // "We're meant to be together"
340    Methodical, // "Everything has its place"
341    Excited,    // "This is AMAZING!"
342    Content,    // "Life is good"
343}
344
345/// Track mood changes over time
346#[derive(Debug, Clone)]
347struct MoodHistory {
348    previous_mood: String,
349    current_mood: String,
350    mood_changes: u32,
351}
352
353impl Formatter for EmotionalFormatter {
354    fn format(
355        &self,
356        writer: &mut dyn Write,
357        nodes: &[FileNode],
358        stats: &TreeStats,
359        _root_path: &Path,
360    ) -> Result<()> {
361        // Header with drama!
362        writeln!(writer, "\n๐ŸŽญ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• ๐ŸŽญ")?;
363        writeln!(writer, "     EMOTIONAL TREE THEATRE PRESENTS:")?;
364        writeln!(writer, "     \"The Files and Their Feelings\"")?;
365        writeln!(writer, "๐ŸŽญ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• ๐ŸŽญ\n")?;
366
367        // Calculate overall project mood
368        let mut mood_counts: HashMap<&str, u32> = HashMap::new();
369        let mut total_intensity = 0.0;
370
371        // Display each file with its emotion
372        for (i, node) in nodes.iter().enumerate() {
373            let emotion = self.get_file_emotion(node);
374            *mood_counts.entry(emotion.mood).or_insert(0) += 1;
375            total_intensity += emotion.intensity;
376
377            // Get file name
378            let file_name = node
379                .path
380                .file_name()
381                .and_then(|n| n.to_str())
382                .unwrap_or("???");
383
384            // Indentation based on path depth
385            let depth = node.path.components().count();
386            let indent = "  ".repeat(depth.saturating_sub(1));
387
388            // Get personality comment
389            let comment = self.get_personality_comment(&emotion);
390
391            // Format the line with color if enabled
392            if self.use_color {
393                writeln!(
394                    writer,
395                    "{}{} {} - \"{}\"",
396                    indent, emotion.emoji, file_name, comment
397                )?;
398            } else {
399                writeln!(
400                    writer,
401                    "{}{} {} - \"{}\"",
402                    indent, emotion.emoji, file_name, comment
403                )?;
404            }
405
406            // Add dramatic pause every 10 files
407            if (i + 1) % 10 == 0 && i < nodes.len() - 1 {
408                writeln!(writer, "{}...", indent)?;
409            }
410        }
411
412        // Calculate average intensity
413        let avg_intensity = if nodes.is_empty() {
414            0.0
415        } else {
416            total_intensity / nodes.len() as f32
417        };
418
419        // Emotional summary with personality!
420        writeln!(writer, "\nโ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—")?;
421        writeln!(writer, "โ•‘       ๐Ÿ“Š EMOTIONAL ANALYSIS ๐Ÿ“Š         โ•‘")?;
422        writeln!(writer, "โ• โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ฃ")?;
423        writeln!(
424            writer,
425            "โ•‘ Total Cast: {} files, {} directories   โ•‘",
426            stats.total_files, stats.total_dirs
427        )?;
428        writeln!(writer, "โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•")?;
429
430        // Mood breakdown
431        writeln!(writer, "\n๐ŸŽญ Mood Distribution:")?;
432        for (mood, count) in mood_counts.iter() {
433            let bar = "โ–ˆ".repeat((*count as usize).min(20));
434            writeln!(writer, "  {:12} {} ({})", mood, bar, count)?;
435        }
436
437        // Project personality assessment
438        writeln!(writer, "\n๐Ÿ’ญ Project Personality Assessment:")?;
439        let assessment = match avg_intensity {
440            i if i > 0.8 => {
441                "๐Ÿ”ฅ DRAMATIC SUPERSTAR - This codebase has PERSONALITY! Every file is living its best life!"
442            },
443            i if i > 0.6 => {
444                "โšก ENERGETIC ACHIEVER - Vibrant and active! This project is going places!"
445            },
446            i if i > 0.4 => {
447                "๐Ÿ˜Š BALANCED PROFESSIONAL - Healthy mix of excitement and stability. Well done!"
448            },
449            i if i > 0.2 => {
450                "๐Ÿ˜ด SLEEPY SCHOLAR - Could use some excitement. Maybe add some new features?"
451            },
452            _ => {
453                "๐Ÿ‘ป GHOST TOWN - Hello? Is anybody there? This project needs some love!"
454            }
455        };
456        writeln!(writer, "  {}", assessment)?;
457
458        // Motivational message from Trisha
459        writeln!(writer, "\n๐Ÿ’Œ Message from Trisha in Accounting:")?;
460        let trisha_message = if mood_counts.get("anxious").unwrap_or(&0) > &5 {
461            "  \"Looks like some files need a hug! Time for some refactoring therapy? ๐Ÿค—\""
462        } else if mood_counts.get("proud").unwrap_or(&0) > &10 {
463            "  \"So many proud files! The tests must be passing! Keep it up! ๐Ÿ“ˆ\""
464        } else if mood_counts.get("sleepy").unwrap_or(&0) > &10 {
465            "  \"Wake up those sleepy files! They're missing all the fun! โ˜•\""
466        } else {
467            "  \"Remember: Happy files make happy developers! Keep coding! ๐Ÿ’–\""
468        };
469        writeln!(writer, "{}", trisha_message)?;
470
471        // Closing curtain
472        writeln!(writer, "\n๐ŸŽญ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• ๐ŸŽญ")?;
473        writeln!(writer, "        Thank you for attending!")?;
474        writeln!(writer, "         ~ The Emotional Tree ~")?;
475        writeln!(writer, "๐ŸŽญ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• ๐ŸŽญ")?;
476
477        Ok(())
478    }
479}