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
use std::{fs::DirBuilder, path::PathBuf};

use anyhow::Result;
use tokio::{
    net::UnixListener,
    sync::mpsc::{UnboundedReceiver, UnboundedSender},
};
use tokio_stream::wrappers::UnixListenerStream;

use crate::IpcStream;

/// The directory in which IPC sockets are created
pub const IPC_DIR: &str = "/tmp/lazybar-ipc/";

/// Initialize IPC for a given bar
pub fn init(enabled: bool, bar_name: &str) -> (Result<IpcStream>, String) {
    let mut final_name = bar_name.to_string();
    (
        if enabled && DirBuilder::new().recursive(true).create(IPC_DIR).is_ok()
        {
            let (path, idx) = find_path(bar_name);

            if idx > 0 {
                final_name = format!("{bar_name}({idx})");
            }

            // map_or_else is invalid here due to type coercion issues
            #[allow(clippy::option_if_let_else)]
            if let Ok(listener) = UnixListener::bind(path) {
                let stream = UnixListenerStream::new(listener);

                Ok(Box::pin(stream))
            } else {
                Ok(Box::pin(tokio_stream::pending()))
            }
        } else {
            Ok(Box::pin(tokio_stream::pending()))
        },
        final_name,
    )
}

fn find_path(bar_name: &str) -> (PathBuf, i32) {
    let mut fmt = format!("{IPC_DIR}{bar_name}");
    let mut path = PathBuf::from(fmt);
    let mut idx = 0;
    while path.exists() {
        idx += 1;
        fmt = format!("{IPC_DIR}{bar_name}({idx})");
        path = PathBuf::from(fmt.as_str());
    }

    (path, idx)
}

/// A sender and a receiver bundled together for two-way communication
#[derive(Debug)]
pub struct ChannelEndpoint<T, U> {
    /// The sender
    pub send: UnboundedSender<T>,
    /// The receiver
    pub recv: UnboundedReceiver<U>,
}

impl<T, U> ChannelEndpoint<T, U> {
    /// create a new endpoint from a sender and a receiver
    #[must_use]
    pub const fn new(
        send: UnboundedSender<T>,
        recv: UnboundedReceiver<U>,
    ) -> Self {
        Self { send, recv }
    }
}

impl<T, U> AsRef<UnboundedSender<T>> for ChannelEndpoint<T, U> {
    fn as_ref(&self) -> &UnboundedSender<T> {
        &self.send
    }
}

impl<T, U> AsMut<UnboundedSender<T>> for ChannelEndpoint<T, U> {
    fn as_mut(&mut self) -> &mut UnboundedSender<T> {
        &mut self.send
    }
}

impl<T, U> AsRef<UnboundedReceiver<U>> for ChannelEndpoint<T, U> {
    fn as_ref(&self) -> &UnboundedReceiver<U> {
        &self.recv
    }
}

impl<T, U> AsMut<UnboundedReceiver<U>> for ChannelEndpoint<T, U> {
    fn as_mut(&mut self) -> &mut UnboundedReceiver<U> {
        &mut self.recv
    }
}