1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use std::{
    fs::File,
    io::{Error, Read},
    path::{Path, PathBuf},
    sync::mpsc::Receiver,
};

use colored::{Color, Colorize};
use lazy_static::lazy_static;
use notify::{Config, Event, PollWatcher, RecursiveMode, Watcher};
use parking_lot::RwLock;
use regex::{Captures, Regex};

pub mod config;
pub mod provider;

pub fn watch<P: AsRef<Path>>(path: P) -> notify::Result<Receiver<PathBuf>> {
    let (tx_a, tx_b, rx) = {
        let (tx, rx) = std::sync::mpsc::channel();
        (tx.clone(), tx, rx)
    };

    let mut watcher = PollWatcher::with_initial_scan(
        move |watch_event: notify::Result<Event>| {
            let event = watch_event.unwrap();
            for path in event.paths {
                tx_a.send(path).unwrap();
            }
        },
        Config::default(),
        move |scan_event: notify::Result<PathBuf>| {
            let path = scan_event.unwrap();
            tx_b.send(path).unwrap();
        },
    )?;

    watcher.watch(path.as_ref(), RecursiveMode::Recursive)?;

    Ok(rx)
}

pub fn parse_path_env(haystack: &str) -> Result<PathBuf, Error> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"%(\w+)%").unwrap();
    }

    let str = RE.replace_all(haystack, |captures: &Captures| {
        let key = &captures[1];
        std::env::var(key).unwrap()
    });
    let path = std::fs::canonicalize(str.as_ref())?;

    Ok(path)
}

pub fn parse_avatar_ids(path: PathBuf) -> Result<Vec<String>, Error> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"avtr_\w{8}-\w{4}-\w{4}-\w{4}-\w{12}").unwrap();
    }

    let mut file = File::open(path)?;
    let mut buf = vec![];
    file.read_to_end(&mut buf)?;

    let haystack = String::from_utf8_lossy(&buf);
    let avatar_ids = RE
        .find_iter(&haystack)
        .map(|m| m.as_str().into())
        .collect::<Vec<_>>();

    Ok(avatar_ids)
}

pub fn print_colorized(avatar_id: &str) {
    lazy_static! {
        static ref INDEX: RwLock<usize> = RwLock::new(0);
        static ref COLORS: [Color; 12] = [
            Color::Red,
            Color::BrightRed,
            Color::Yellow,
            Color::BrightYellow,
            Color::Green,
            Color::BrightGreen,
            Color::Blue,
            Color::BrightBlue,
            Color::Cyan,
            Color::BrightCyan,
            Color::Magenta,
            Color::BrightMagenta,
        ];
    }

    let index = *INDEX.read();
    let color = COLORS[index];
    *INDEX.write() = (index + 1) % COLORS.len();

    let text = format!("vrcx://avatar/{avatar_id}").color(color);
    println!("{text}");
}