about summary refs log tree commit diff stats
path: root/pkgs/by-name/ri/river-mk-keymap/src/key_map/mod.rs
blob: 60ed41b8ce11f37d397209524136c19bd5bfa778 (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
// 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,
}

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,
                }
            } 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 {
        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
    }
}