Skip to content

Commit 81efee5

Browse files
authored
Merge pull request #527 from AppFlowy-IO/fix/grid_number_format
fix: disable set decimal scale
2 parents 8b40244 + 72e26ab commit 81efee5

File tree

2 files changed

+92
-97
lines changed

2 files changed

+92
-97
lines changed

frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,7 @@ impl CellDataOperation<String> for DateTypeOption {
150150
(true, Some(time)) => {
151151
let time = Some(time.trim().to_uppercase());
152152
let utc = self.utc_date_time_from_timestamp(date_timestamp);
153-
let timestamp = self.timestamp_from_utc_with_time(&utc, &time)?;
154-
timestamp
153+
self.timestamp_from_utc_with_time(&utc, &time)?
155154
}
156155
_ => date_timestamp,
157156
},
@@ -548,7 +547,7 @@ mod tests {
548547
assert_changeset_result(
549548
&type_option,
550549
DateCellContentChangeset {
551-
date: Some(date_timestamp.clone()),
550+
date: Some(date_timestamp),
552551
time: Some("1:00".to_owned()),
553552
},
554553
&type_option.field_type(),
@@ -568,7 +567,7 @@ mod tests {
568567
assert_changeset_result(
569568
&type_option,
570569
DateCellContentChangeset {
571-
date: Some(date_timestamp.clone()),
570+
date: Some(date_timestamp),
572571
time: Some("1:00 am".to_owned()),
573572
},
574573
&type_option.field_type(),
@@ -622,7 +621,7 @@ mod tests {
622621
if type_option.include_time {
623622
format!("{}{}", decoded_data.date, decoded_data.time)
624623
} else {
625-
format!("{}", decoded_data.date)
624+
decoded_data.date
626625
}
627626
}
628627
}

frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs

Lines changed: 88 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,76 @@ pub struct NumberTypeOption {
7171
}
7272
impl_type_option!(NumberTypeOption, FieldType::Number);
7373

74+
impl NumberTypeOption {
75+
pub fn new() -> Self {
76+
Self::default()
77+
}
78+
79+
fn cell_content_from_number_str(&self, s: &str) -> FlowyResult<String> {
80+
return match self.format {
81+
NumberFormat::Number => {
82+
if let Ok(v) = s.parse::<f64>() {
83+
return Ok(v.to_string());
84+
}
85+
86+
if let Ok(v) = s.parse::<i64>() {
87+
return Ok(v.to_string());
88+
}
89+
90+
Ok("".to_string())
91+
}
92+
NumberFormat::Percent => {
93+
let content = s.parse::<f64>().map_or(String::new(), |v| v.to_string());
94+
Ok(content)
95+
}
96+
_ => self.money_from_number_str(s),
97+
};
98+
}
99+
100+
pub fn set_format(&mut self, format: NumberFormat) {
101+
self.format = format;
102+
self.symbol = format.symbol();
103+
}
104+
105+
fn money_from_number_str(&self, s: &str) -> FlowyResult<String> {
106+
let mut number = self.strip_currency_symbol(s);
107+
108+
if s.is_empty() {
109+
return Ok("".to_string());
110+
}
111+
112+
match Decimal::from_str(&number) {
113+
Ok(mut decimal) => {
114+
decimal.set_sign_positive(self.sign_positive);
115+
let money = rusty_money::Money::from_decimal(decimal, self.format.currency()).to_string();
116+
Ok(money)
117+
}
118+
Err(_) => match rusty_money::Money::from_str(&number, self.format.currency()) {
119+
Ok(money) => Ok(money.to_string()),
120+
Err(_) => {
121+
number.retain(|c| !STRIP_SYMBOL.contains(&c.to_string()));
122+
if number.chars().all(char::is_numeric) {
123+
self.money_from_number_str(&number)
124+
} else {
125+
Err(FlowyError::invalid_data().context("Should only contain numbers"))
126+
}
127+
}
128+
},
129+
}
130+
}
131+
132+
fn strip_currency_symbol<T: ToString>(&self, s: T) -> String {
133+
let mut s = s.to_string();
134+
for symbol in CURRENCY_SYMBOL.iter() {
135+
if s.starts_with(symbol) {
136+
s = s.strip_prefix(symbol).unwrap_or("").to_string();
137+
break;
138+
}
139+
}
140+
s
141+
}
142+
}
143+
74144
impl CellDataOperation<String> for NumberTypeOption {
75145
fn decode_cell_data<T>(
76146
&self,
@@ -103,7 +173,7 @@ impl CellDataOperation<String> for NumberTypeOption {
103173
Ok(DecodedCellData::new(content))
104174
}
105175
_ => {
106-
let content = self.number_from_str(&cell_data);
176+
let content = self.money_from_number_str(&cell_data).unwrap_or("".to_string());
107177
Ok(DecodedCellData::new(content))
108178
}
109179
}
@@ -114,15 +184,8 @@ impl CellDataOperation<String> for NumberTypeOption {
114184
C: Into<CellContentChangeset>,
115185
{
116186
let changeset = changeset.into();
117-
let mut data = changeset.trim().to_string();
118-
119-
if self.format != NumberFormat::Number {
120-
data = self.strip_symbol(data);
121-
if !data.chars().all(char::is_numeric) {
122-
return Err(FlowyError::invalid_data().context("Should only contain numbers"));
123-
}
124-
}
125-
187+
let data = changeset.trim().to_string();
188+
let _ = self.cell_content_from_number_str(&data)?;
126189
Ok(data)
127190
}
128191
}
@@ -141,54 +204,6 @@ impl std::default::Default for NumberTypeOption {
141204
}
142205
}
143206

144-
impl NumberTypeOption {
145-
pub fn set_format(&mut self, format: NumberFormat) {
146-
self.format = format;
147-
self.symbol = format.symbol();
148-
}
149-
150-
fn number_from_str(&self, s: &str) -> String {
151-
match Decimal::from_str(s) {
152-
Ok(mut decimal) => {
153-
match decimal.set_scale(self.scale) {
154-
Ok(_) => {}
155-
Err(e) => {
156-
tracing::error!("Set decimal scale failed: {:?}", e);
157-
}
158-
}
159-
160-
decimal.set_sign_positive(self.sign_positive);
161-
let money = rusty_money::Money::from_decimal(decimal, self.format.currency());
162-
money.to_string()
163-
}
164-
Err(_) => {
165-
let s = self.strip_symbol(s);
166-
if !s.is_empty() && s.chars().all(char::is_numeric) {
167-
self.number_from_str(&s)
168-
} else {
169-
"".to_owned()
170-
}
171-
}
172-
}
173-
}
174-
175-
fn strip_symbol<T: ToString>(&self, s: T) -> String {
176-
let mut s = s.to_string();
177-
178-
for symbol in CURRENCY_SYMBOL.iter() {
179-
if s.starts_with(symbol) {
180-
s = s.strip_prefix(symbol).unwrap_or("").to_string();
181-
break;
182-
}
183-
}
184-
185-
if !s.chars().all(char::is_numeric) {
186-
s.retain(|c| !STRIP_SYMBOL.contains(&c.to_string()));
187-
}
188-
s
189-
}
190-
}
191-
192207
#[cfg(test)]
193208
mod tests {
194209
use crate::services::field::FieldBuilder;
@@ -206,14 +221,21 @@ mod tests {
206221
assert_equal(&type_option, "abc", "", &field_type, &field_meta);
207222
}
208223

224+
#[test]
225+
fn number_type_option_strip_symbol_test() {
226+
let mut type_option = NumberTypeOption::new();
227+
type_option.format = NumberFormat::USD;
228+
assert_eq!(type_option.strip_currency_symbol("$18,443"), "18,443".to_owned());
229+
230+
type_option.format = NumberFormat::Yuan;
231+
assert_eq!(type_option.strip_currency_symbol("$0.2"), "0.2".to_owned());
232+
}
233+
209234
#[test]
210235
fn number_type_option_format_number_test() {
211236
let mut type_option = NumberTypeOption::default();
212237
let field_type = FieldType::Number;
213238
let field_meta = FieldBuilder::from_field_type(&field_type).build();
214-
assert_eq!(type_option.strip_symbol("¥18,443"), "18443".to_owned());
215-
assert_eq!(type_option.strip_symbol("$18,443"), "18443".to_owned());
216-
assert_eq!(type_option.strip_symbol("€18.443"), "18443".to_owned());
217239

218240
for format in NumberFormat::iter() {
219241
type_option.format = format;
@@ -248,10 +270,12 @@ mod tests {
248270
type_option.format = format;
249271
match format {
250272
NumberFormat::Number => {
251-
// assert_equal(&type_option, "18443", "18443", &field_type, &field_meta);
273+
assert_equal(&type_option, "18443", "18443", &field_type, &field_meta);
274+
assert_equal(&type_option, "0.2", "0.2", &field_type, &field_meta);
252275
}
253276
NumberFormat::USD => {
254277
assert_equal(&type_option, "$18,44", "$1,844", &field_type, &field_meta);
278+
assert_equal(&type_option, "$0.2", "$0.2", &field_type, &field_meta);
255279
assert_equal(&type_option, "", "", &field_type, &field_meta);
256280
assert_equal(&type_option, "abc", "", &field_type, &field_meta);
257281
}
@@ -264,43 +288,15 @@ mod tests {
264288
assert_equal(&type_option, "CN¥1844", "CN¥1,844", &field_type, &field_meta);
265289
}
266290
NumberFormat::EUR => {
267-
assert_equal(&type_option, "€18.44", "€1.844", &field_type, &field_meta);
291+
assert_equal(&type_option, "€18.44", "€18,44", &field_type, &field_meta);
292+
assert_equal(&type_option, "€0.5", "€0,5", &field_type, &field_meta);
268293
assert_equal(&type_option, "€1844", "€1.844", &field_type, &field_meta);
269294
}
270295
_ => {}
271296
}
272297
}
273298
}
274299

275-
#[test]
276-
fn number_type_option_scale_test() {
277-
let mut type_option = NumberTypeOption {
278-
scale: 1,
279-
..Default::default()
280-
};
281-
let field_type = FieldType::Number;
282-
let field_meta = FieldBuilder::from_field_type(&field_type).build();
283-
284-
for format in NumberFormat::iter() {
285-
type_option.format = format;
286-
match format {
287-
NumberFormat::Number => {
288-
assert_equal(&type_option, "18443", "18443", &field_type, &field_meta);
289-
}
290-
NumberFormat::USD => {
291-
assert_equal(&type_option, "18443", "$1,844.3", &field_type, &field_meta);
292-
}
293-
NumberFormat::Yen => {
294-
assert_equal(&type_option, "18443", "¥1,844.3", &field_type, &field_meta);
295-
}
296-
NumberFormat::EUR => {
297-
assert_equal(&type_option, "18443", "€1.844,3", &field_type, &field_meta);
298-
}
299-
_ => {}
300-
}
301-
}
302-
}
303-
304300
#[test]
305301
fn number_description_sign_test() {
306302
let mut type_option = NumberTypeOption {

0 commit comments

Comments
 (0)