about summary refs log tree commit diff stats
path: root/pkgs/by-name/ri/river-mk-keymap/src/key_map/mod.rs
blob: 5c89c2e22cd20b7f64ca9cfd76be52c8f25b8d6a (plain) (blame)
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// nixos-config - My current NixOS configuration
//
// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This file is part of my nixos-config.
//
// You should have received a copy of the License along with this program.
// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.

use std::{fmt::Display, ops::Deref, str::FromStr};

use anyhow::{anyhow, bail, Context, Result};
use keymaps::{
    key_repr::{Key, Keys},
    map_tree::MapTrie,
};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};

pub mod commands;

#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, PartialOrd)]
/// What values to use for: `riverctl <command..>`
#[serde(deny_unknown_fields)]
pub struct KeyConfig {
    command: Vec<String>,

    /// Whether to allow this key mapping in the “locked” mode.
    #[serde(default)]
    allow_locked: bool,

    /// Use a different description to display this command, instead of the `command`.
    description: Option<String>,
}

impl FromStr for KeyMap {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        fn decode_value(
            output: &mut MapTrie<KeyConfig>,
            current_key: Vec<Key>,
            value: &Value,
        ) -> Result<()> {
            let key_config = if let Some(value) = value.as_array() {
                KeyConfig {
                    command: value
                        .iter()
                        .map(|v| v.as_str().map(ToOwned::to_owned))
                        .collect::<Option<_>>()
                        .ok_or(anyhow!("A array contained a non-string value: {value:#?}"))?,
                    allow_locked: false,
                    description: None,
                }
            } else if let Some(object) = value.as_object() {
                if object.contains_key("command") {
                    serde_json::from_value(value.to_owned())
                        .with_context(|| format!("Failed to parse key config: {value:#?}"))?
                } else {
                    for (key, value) in object {
                        let mut local_current_key = current_key.clone();
                        local_current_key.push(
                            Key::from_str(key)
                                .with_context(|| format!("Failed to parse key '{key}'"))?,
                        );

                        decode_value(output, local_current_key, value)?;
                    }
                    return Ok(());
                }
            } else {
                bail!("Value ({}) is invalid (not array or object).", value)
            };

            output
                .insert(&current_key, key_config.clone())
                .with_context(|| {
                    format!(
                        "Failed to insert mapping {} -> {key_config}",
                        Keys::from(current_key)
                    )
                })?;

            Ok(())
        }

        let mut out = MapTrie::<KeyConfig>::new();

        let raw: Map<String, Value> =
            serde_json::from_str(s).context("Failed to parse the keymap config file as json.")?;

        for (key, value) in raw {
            decode_value(
                &mut out,
                vec![Key::from_str(&key)
                    .with_context(|| format!("Failed to parse key ('{key}')"))?],
                &value,
            )?;
        }

        Ok(Self(out))
    }
}
impl Display for KeyConfig {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if let Some(desc) = &self.description {
            f.write_str(desc)
        } else {
            f.write_str(self.command.join(" ").as_str())
        }
    }
}

#[derive(Debug)]
pub struct KeyMap(MapTrie<KeyConfig>);

impl Display for KeyMap {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}

impl Deref for KeyMap {
    type Target = MapTrie<KeyConfig>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}