klavier_core/
text_input.rs1use std::{num::{ParseFloatError, ParseIntError}, str::FromStr};
2
3use crate::{duration, location::{parse_location, Location}, percent::PercentU16, rhythm, tempo::TempoValue, velocity::Velocity};
4
5#[derive(Debug)]
6pub struct Validated<T>(pub T);
7
8#[inline]
9pub fn from_str<T, E>(s: &str) -> Result<Validated<T>, E>
10 where Validated<T>: FromStr<Err = E>
11{
12 Validated::<T>::from_str(s)
13}
14
15#[derive(Debug)]
16pub struct TextInput<T, E> {
17 validated: Result<Validated<T>, E>,
18 buffer: String,
19}
20
21impl<T, E> TextInput<T, E>
22 where Validated<T>: FromStr<Err = E>
23{
24 pub fn from_string(s: String) -> Self {
25 let validated: Result<Validated<T>, E> = from_str(&s);
26 Self { validated, buffer: s }
27 }
28
29 pub fn mutate<R, M>(&mut self, mutator: M) -> R
30 where M: FnOnce(&mut String) -> R
31 {
32 let r = (mutator)(&mut self.buffer);
33 self.validated = from_str(&self.buffer);
34 r
35 }
36
37 #[inline]
38 pub fn input(&self) -> &str {
39 &self.buffer
40 }
41
42 #[inline]
43 pub fn validate(&self) -> &Result<Validated<T>, E> {
44 &self.validated
45 }
46}
47
48pub enum LocationParseError {
49 InvalidFormat,
50}
51
52impl FromStr for Validated<Option<Location>> {
53 type Err = LocationParseError;
54
55 fn from_str(s: &str) -> Result<Self, Self::Err> {
56 if s.is_empty() {
57 Ok(Validated(None))
58 } else {
59 match parse_location(s) {
60 Some(loc) => Ok(Validated(Some(loc))),
61 None => Err(LocationParseError::InvalidFormat),
62 }
63 }
64 }
65}
66
67impl FromStr for Validated<Option<i16>> {
68 type Err = ParseIntError;
69
70 fn from_str(s: &str) -> Result<Self, Self::Err> {
71 if s.is_empty() {
72 Ok(Validated(None))
73 } else {
74 s.parse::<i16>().map(|i| Validated(Some(i)))
75 }
76 }
77}
78
79impl FromStr for Validated<Option<TempoValue>> {
80 type Err = ParseIntError;
81
82 fn from_str(s: &str) -> Result<Self, Self::Err> {
83 if s.is_empty() {
84 Ok(Validated(None))
85 } else {
86 s.parse::<u16>().map(|u| Validated(Some(TempoValue::new(u))))
87 }
88 }
89}
90
91impl FromStr for Validated<Option<Velocity>> {
92 type Err = ParseIntError;
93
94 fn from_str(s: &str) -> Result<Self, Self::Err> {
95 if s.is_empty() {
96 Ok(Validated(None))
97 } else {
98 s.parse::<u8>().map(|u| Validated(Some(Velocity::new(u))))
99 }
100 }
101}
102
103impl FromStr for Validated<Option<rhythm::Numerator>> {
104 type Err = rhythm::NumeratorError;
105
106 fn from_str(s: &str) -> Result<Self, Self::Err> {
107 if s.is_empty() {
108 Ok(Validated(None))
109 } else {
110 rhythm::Numerator::from_str(s).map(|n| Validated(Some(n)))
111 }
112 }
113}
114
115#[derive(Debug)]
116pub enum DurationDenominatorError {
117 ParseIntError,
118 InvalidValue,
119}
120
121impl FromStr for Validated<Option<duration::Denominator>> {
122 type Err = DurationDenominatorError;
123
124 fn from_str(s: &str) -> Result<Self, Self::Err> {
125 if s.is_empty() {
126 Ok(Validated(None))
127 } else {
128 match s.parse::<u8>() {
129 Ok(u) => match duration::Denominator::from_value(u) {
130 Some(d) => Ok(Validated(Some(d))),
131 None => Err(DurationDenominatorError::InvalidValue),
132 }
133 Err(_) => Err(DurationDenominatorError::ParseIntError)
134 }
135 }
136 }
137}
138
139impl FromStr for Validated<Option<PercentU16>> {
140 type Err = ParseFloatError;
141
142 fn from_str(s: &str) -> Result<Self, Self::Err> {
143 if s.is_empty() {
144 Ok(Validated(None))
145 } else {
146 match s.parse::<f32>() {
147 Ok(pct) => Ok(Validated(Some(pct.into()))),
148 Err(e) => Err(e),
149 }
150 }
151 }
152}