mantra_lancer/
lib.rs

1//! A currency tracker for the LANCER TTRPG system, combination of Manna and Tracker.
2//! Provides summarizing, filtering, and multi-pilot support
3
4use std::{fmt::Display, ops::Deref};
5
6use crossterm::event::KeyModifiers;
7use xdg::BaseDirectories;
8
9pub mod app;
10pub mod config;
11pub mod storage;
12
13/// Grabs the XDG dirs
14pub fn base_dirs() -> Result<BaseDirectories, xdg::BaseDirectoriesError> {
15    BaseDirectories::with_prefix("mantra")
16}
17
18/// Returns an appropriatly scaled value given the held modifier keys
19pub fn value_from_modifiers(modifiers: KeyModifiers) -> i32 {
20    let mut value = 10;
21    if modifiers.contains(KeyModifiers::SHIFT) {
22        value = 1;
23    }
24    if modifiers.contains(KeyModifiers::CONTROL) {
25        value *= 5;
26    }
27    if modifiers.contains(KeyModifiers::ALT) {
28        value *= 20;
29    }
30
31    value
32}
33
34/// A String with a cursor character based position for editing
35/// The cursor is always considered 'in front' of the character with the same index
36#[derive(Default)]
37pub struct CursoredString {
38    buf: String,
39    index: usize,
40    pub inserting: bool,
41}
42
43impl CursoredString {
44    /// Creates a new empty CursoredString
45    pub fn new() -> Self {
46        Self::default()
47    }
48
49    /// Gets the text from the internal buffer
50    pub fn as_str(&self) -> &str {
51        self
52    }
53
54    /// Gets the current index for the cursor
55    pub fn cursor_index(&self) -> usize {
56        self.index
57    }
58
59    /// Move the cursor to the right
60    pub fn next(&mut self) {
61        self.index = self.index.saturating_add(1).clamp(0, self.buf.len())
62    }
63
64    /// Move the cursor to the left
65    pub fn prev(&mut self) {
66        self.index = self.index.saturating_sub(1).clamp(0, self.buf.len())
67    }
68
69    /// Remove a character from behind the cursor
70    pub fn remove_behind(&mut self) {
71        // can't delete behind index 0
72        if self.index > 0 {
73            let old_len = self.buf.len();
74            let mut index = 0;
75            // retain is used to modify in place
76            self.buf.retain(|_| {
77                index += 1;
78                index != self.index
79            });
80            // length change indicates successful deletion
81            if self.buf.len() < old_len {
82                self.index -= 1;
83            };
84        }
85    }
86
87    /// Removes a character ahead (same index) of the cursor
88    pub fn remove_ahead(&mut self) {
89        if self.index < self.buf.chars().count() {
90            let mut index = 0;
91            self.buf.retain(|_| {
92                index += 1;
93                if index - 1 == self.index {
94                    return false;
95                }
96                true
97            })
98        }
99    }
100
101    /// Inserts a character at the current position, moving existing characters after the cursor ahead.
102    /// Replaces the current character if insert mode is enabled.
103    pub fn insert(&mut self, value: char) {
104        if self.inserting {
105            self.remove_ahead();
106        }
107        // String.insert is indexed by byte so we get the byte index from the char index
108        let byte_index = self
109            .buf
110            .char_indices()
111            .map(|(i, _)| i)
112            .nth(self.index)
113            .unwrap_or(self.buf.len());
114
115        self.buf.insert(byte_index, value);
116        self.index += 1
117    }
118}
119
120impl Display for CursoredString {
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        self.buf.fmt(f)
123    }
124}
125
126impl From<CursoredString> for String {
127    fn from(value: CursoredString) -> Self {
128        value.buf
129    }
130}
131
132impl Deref for CursoredString {
133    type Target = str;
134
135    fn deref(&self) -> &Self::Target {
136        self.buf.as_str()
137    }
138}
139
140impl From<String> for CursoredString {
141    fn from(value: String) -> Self {
142        Self {
143            buf: value,
144            index: 0,
145            inserting: false,
146        }
147    }
148}