rust_persian_tools/digits/
mod.rs

1//! Conversion between Persian, Arabic, and English numbers. (`digits` feature)
2//!
3//! ### Example
4//! ```rust
5//! use rust_persian_tools::digits::{DigitsFa2Ar, DigitsAr2En, DigitsEn2Fa, DigitsEn2ArMut};
6//!
7//! assert_eq!("۴۵".digits_fa_to_ar(), "٤٥");
8//! assert_eq!("۴۵".digits_fa_to_ar().digits_ar_to_en(), "45");
9//!
10//! assert_eq!(3.14.digits_en_to_fa(), "۳.۱۴");
11//! let mut n = (-6).to_string();
12//! n.digits_en_to_ar_mut();
13//! assert_eq!(n, "-٦");
14//! ```
15
16use std::borrow::Cow;
17use std::ops::Range;
18
19/// Convert Persian digits to English digits.
20pub fn fa_to_en(input: impl AsRef<str>) -> String {
21    // UTF-8 code points for Persian numbers: 1776..1785
22    // UTF-8 code points for English numbers: 48..57
23    // 48 - 1776 = -1728
24    convert(input, 1776..1786, -1728)
25}
26
27/// Convert Persian digits to English  digits in-place.
28pub fn fa_to_en_mut(input: &mut String) {
29    convert_mut(input, 1776..1786, -1728)
30}
31
32/// convert English digits to Persian digits.
33pub fn en_to_fa(input: impl AsRef<str>) -> String {
34    // UTF-8 code points for English numbers: 48..57
35    // UTF-8 code points for Persian numbers: 1776..1785
36    // 1776 - 48 = 1728
37    convert(input, 48..58, 1728)
38}
39
40/// Convert English digits to Persian digits in-place.
41pub fn en_to_fa_mut(input: &mut String) {
42    convert_mut(input, 48..58, 1728)
43}
44
45/// convert English digits to Arabic digits.
46pub fn en_to_ar(input: impl AsRef<str>) -> String {
47    // UTF-8 code points for English numbers: 48..57
48    // UTF-8 code points for Arabic numbers: 1632..1641
49    // 1632 - 48 = 1584
50    convert(input, 48..58, 1584)
51}
52
53/// convert English digits to Arabic digits in-place.
54pub fn en_to_ar_mut(input: &mut String) {
55    convert_mut(input, 48..58, 1584)
56}
57
58/// convert Arabic digits to English digits.
59pub fn ar_to_en(input: impl AsRef<str>) -> String {
60    // UTF-8 code points for Arabic numbers: 1632..1641
61    // UTF-8 code points for English numbers: 48..57
62    // 48 - 1632 = -1584
63    convert(input, 1632..1642, -1584)
64}
65
66/// convert Arabic digits to English digits in-place.
67pub fn ar_to_en_mut(input: &mut String) {
68    convert_mut(input, 1632..1642, -1584)
69}
70
71/// convert Persian digits to Arabic digits.
72pub fn fa_to_ar(input: impl AsRef<str>) -> String {
73    // UTF-8 code points for Persian numbers: 1776..1785
74    // UTF-8 code points for Arabic numbers: 1632..1641
75    // 1632 - 1776 = -144
76    convert(input, 1776..1786, -144)
77}
78
79/// convert Persian digits to Arabic digits in-place.
80pub fn fa_to_ar_mut(input: &mut String) {
81    convert_mut(input, 1776..1786, -144)
82}
83
84/// convert Arabic digits to Persian digits.
85pub fn ar_to_fa(input: impl AsRef<str>) -> String {
86    // UTF-8 code points for Arabic numbers: 1632..1641
87    // UTF-8 code points for Persian numbers: 1776..1785
88    // 1776 - 1632 = 144
89    convert(input, 1632..1642, 144)
90}
91
92/// convert Arabic digits to Persian digits in-place.
93pub fn ar_to_fa_mut(input: &mut String) {
94    convert_mut(input, 1632..1642, 144)
95}
96
97/// Conversion for Arabic to Persian digits.
98pub trait DigitsAr2Fa {
99    fn digits_ar_to_fa(&self) -> String;
100}
101
102/// Mutable conversion for Arabic to Persian digits.
103pub trait DigitsAr2FaMut {
104    fn digits_ar_to_fa_mut(&mut self);
105}
106
107/// Conversion for Arabic to English digits.
108pub trait DigitsAr2En {
109    fn digits_ar_to_en(&self) -> String;
110}
111
112/// Mutable conversion for Arabic to English digits.
113pub trait DigitsAr2EnMut {
114    fn digits_ar_to_en_mut(&mut self);
115}
116
117/// Conversion for Persian to Arabic digits.
118pub trait DigitsFa2Ar {
119    fn digits_fa_to_ar(&self) -> String;
120}
121
122/// Mutable conversion for Persian to Arabic digits.
123pub trait DigitsFa2ArMut {
124    fn digits_fa_to_ar_mut(&mut self);
125}
126
127/// Conversion for Persian to English digits.
128pub trait DigitsFa2En {
129    fn digits_fa_to_en(&self) -> String;
130}
131
132/// Mutable conversion for Persian to English digits.
133pub trait DigitsFa2EnMut {
134    fn digits_fa_to_en_mut(&mut self);
135}
136
137/// Conversion for English to Arabic digits.
138pub trait DigitsEn2Ar {
139    fn digits_en_to_ar(&self) -> String;
140}
141
142/// Mutable conversion for English to Arabic digits.
143pub trait DigitsEn2ArMut {
144    fn digits_en_to_ar_mut(&mut self);
145}
146
147/// Conversion for English to Persian digits.
148pub trait DigitsEn2Fa {
149    fn digits_en_to_fa(&self) -> String;
150}
151
152/// Mutable conversion for English to Persian digits.
153pub trait DigitsEn2FaMut {
154    fn digits_en_to_fa_mut(&mut self);
155}
156
157impl DigitsAr2Fa for str {
158    fn digits_ar_to_fa(&self) -> String {
159        ar_to_fa(self)
160    }
161}
162
163impl DigitsAr2Fa for String {
164    fn digits_ar_to_fa(&self) -> String {
165        ar_to_fa(self)
166    }
167}
168
169impl DigitsAr2Fa for Cow<'_, str> {
170    fn digits_ar_to_fa(&self) -> String {
171        ar_to_fa(self)
172    }
173}
174
175impl DigitsAr2FaMut for String {
176    fn digits_ar_to_fa_mut(&mut self) {
177        ar_to_fa_mut(self)
178    }
179}
180
181impl DigitsAr2FaMut for Cow<'_, str> {
182    fn digits_ar_to_fa_mut(&mut self) {
183        ar_to_fa_mut(self.to_mut())
184    }
185}
186
187impl DigitsAr2En for str {
188    fn digits_ar_to_en(&self) -> String {
189        ar_to_en(self)
190    }
191}
192
193impl DigitsAr2En for String {
194    fn digits_ar_to_en(&self) -> String {
195        ar_to_en(self)
196    }
197}
198
199impl DigitsAr2En for Cow<'_, str> {
200    fn digits_ar_to_en(&self) -> String {
201        ar_to_en(self)
202    }
203}
204
205impl DigitsAr2EnMut for String {
206    fn digits_ar_to_en_mut(&mut self) {
207        ar_to_en_mut(self)
208    }
209}
210
211impl DigitsAr2EnMut for Cow<'_, str> {
212    fn digits_ar_to_en_mut(&mut self) {
213        ar_to_en_mut(self.to_mut())
214    }
215}
216
217impl DigitsFa2Ar for str {
218    fn digits_fa_to_ar(&self) -> String {
219        fa_to_ar(self)
220    }
221}
222
223impl DigitsFa2Ar for String {
224    fn digits_fa_to_ar(&self) -> String {
225        fa_to_ar(self)
226    }
227}
228
229impl DigitsFa2Ar for Cow<'_, str> {
230    fn digits_fa_to_ar(&self) -> String {
231        fa_to_ar(self)
232    }
233}
234
235impl DigitsFa2ArMut for String {
236    fn digits_fa_to_ar_mut(&mut self) {
237        fa_to_ar_mut(self)
238    }
239}
240
241impl DigitsFa2ArMut for Cow<'_, str> {
242    fn digits_fa_to_ar_mut(&mut self) {
243        fa_to_ar_mut(self.to_mut())
244    }
245}
246
247impl DigitsFa2En for str {
248    fn digits_fa_to_en(&self) -> String {
249        fa_to_en(self)
250    }
251}
252
253impl DigitsFa2En for String {
254    fn digits_fa_to_en(&self) -> String {
255        fa_to_en(self)
256    }
257}
258
259impl DigitsFa2En for Cow<'_, str> {
260    fn digits_fa_to_en(&self) -> String {
261        fa_to_en(self)
262    }
263}
264
265impl DigitsFa2EnMut for String {
266    fn digits_fa_to_en_mut(&mut self) {
267        fa_to_en_mut(self)
268    }
269}
270
271impl DigitsFa2EnMut for Cow<'_, str> {
272    fn digits_fa_to_en_mut(&mut self) {
273        fa_to_en_mut(self.to_mut())
274    }
275}
276
277impl DigitsEn2Ar for str {
278    fn digits_en_to_ar(&self) -> String {
279        en_to_ar(self)
280    }
281}
282
283impl DigitsEn2Ar for String {
284    fn digits_en_to_ar(&self) -> String {
285        en_to_ar(self)
286    }
287}
288
289impl DigitsEn2Ar for Cow<'_, str> {
290    fn digits_en_to_ar(&self) -> String {
291        en_to_ar(self)
292    }
293}
294
295impl DigitsEn2ArMut for String {
296    fn digits_en_to_ar_mut(&mut self) {
297        en_to_ar_mut(self)
298    }
299}
300
301impl DigitsEn2ArMut for Cow<'_, str> {
302    fn digits_en_to_ar_mut(&mut self) {
303        en_to_ar_mut(self.to_mut())
304    }
305}
306
307impl DigitsEn2Ar for u8 {
308    fn digits_en_to_ar(&self) -> String {
309        let mut string = self.to_string();
310        string.digits_en_to_ar_mut();
311        string
312    }
313}
314
315impl DigitsEn2Ar for i8 {
316    fn digits_en_to_ar(&self) -> String {
317        let mut string = self.to_string();
318        string.digits_en_to_ar_mut();
319        string
320    }
321}
322
323impl DigitsEn2Ar for u16 {
324    fn digits_en_to_ar(&self) -> String {
325        let mut string = self.to_string();
326        string.digits_en_to_ar_mut();
327        string
328    }
329}
330
331impl DigitsEn2Ar for i16 {
332    fn digits_en_to_ar(&self) -> String {
333        let mut string = self.to_string();
334        string.digits_en_to_ar_mut();
335        string
336    }
337}
338
339impl DigitsEn2Ar for u32 {
340    fn digits_en_to_ar(&self) -> String {
341        let mut string = self.to_string();
342        string.digits_en_to_ar_mut();
343        string
344    }
345}
346
347impl DigitsEn2Ar for i32 {
348    fn digits_en_to_ar(&self) -> String {
349        let mut string = self.to_string();
350        string.digits_en_to_ar_mut();
351        string
352    }
353}
354
355impl DigitsEn2Ar for u64 {
356    fn digits_en_to_ar(&self) -> String {
357        let mut string = self.to_string();
358        string.digits_en_to_ar_mut();
359        string
360    }
361}
362
363impl DigitsEn2Ar for i64 {
364    fn digits_en_to_ar(&self) -> String {
365        let mut string = self.to_string();
366        string.digits_en_to_ar_mut();
367        string
368    }
369}
370
371impl DigitsEn2Ar for u128 {
372    fn digits_en_to_ar(&self) -> String {
373        let mut string = self.to_string();
374        string.digits_en_to_ar_mut();
375        string
376    }
377}
378
379impl DigitsEn2Ar for i128 {
380    fn digits_en_to_ar(&self) -> String {
381        let mut string = self.to_string();
382        string.digits_en_to_ar_mut();
383        string
384    }
385}
386
387impl DigitsEn2Ar for usize {
388    fn digits_en_to_ar(&self) -> String {
389        let mut string = self.to_string();
390        string.digits_en_to_ar_mut();
391        string
392    }
393}
394
395impl DigitsEn2Ar for isize {
396    fn digits_en_to_ar(&self) -> String {
397        let mut string = self.to_string();
398        string.digits_en_to_ar_mut();
399        string
400    }
401}
402
403impl DigitsEn2Ar for f32 {
404    fn digits_en_to_ar(&self) -> String {
405        let mut string = self.to_string();
406        string.digits_en_to_ar_mut();
407        string
408    }
409}
410
411impl DigitsEn2Ar for f64 {
412    fn digits_en_to_ar(&self) -> String {
413        let mut string = self.to_string();
414        string.digits_en_to_ar_mut();
415        string
416    }
417}
418
419impl DigitsEn2Fa for str {
420    fn digits_en_to_fa(&self) -> String {
421        en_to_fa(self)
422    }
423}
424
425impl DigitsEn2Fa for String {
426    fn digits_en_to_fa(&self) -> String {
427        en_to_fa(self)
428    }
429}
430
431impl DigitsEn2Fa for Cow<'_, str> {
432    fn digits_en_to_fa(&self) -> String {
433        en_to_fa(self)
434    }
435}
436
437impl DigitsEn2FaMut for String {
438    fn digits_en_to_fa_mut(&mut self) {
439        en_to_fa_mut(self)
440    }
441}
442
443impl DigitsEn2FaMut for Cow<'_, str> {
444    fn digits_en_to_fa_mut(&mut self) {
445        en_to_fa_mut(self.to_mut())
446    }
447}
448
449impl DigitsEn2Fa for u8 {
450    fn digits_en_to_fa(&self) -> String {
451        let mut string = self.to_string();
452        string.digits_en_to_fa_mut();
453        string
454    }
455}
456
457impl DigitsEn2Fa for i8 {
458    fn digits_en_to_fa(&self) -> String {
459        let mut string = self.to_string();
460        string.digits_en_to_fa_mut();
461        string
462    }
463}
464
465impl DigitsEn2Fa for u16 {
466    fn digits_en_to_fa(&self) -> String {
467        let mut string = self.to_string();
468        string.digits_en_to_fa_mut();
469        string
470    }
471}
472
473impl DigitsEn2Fa for i16 {
474    fn digits_en_to_fa(&self) -> String {
475        let mut string = self.to_string();
476        string.digits_en_to_fa_mut();
477        string
478    }
479}
480
481impl DigitsEn2Fa for u32 {
482    fn digits_en_to_fa(&self) -> String {
483        let mut string = self.to_string();
484        string.digits_en_to_fa_mut();
485        string
486    }
487}
488
489impl DigitsEn2Fa for i32 {
490    fn digits_en_to_fa(&self) -> String {
491        let mut string = self.to_string();
492        string.digits_en_to_fa_mut();
493        string
494    }
495}
496
497impl DigitsEn2Fa for u64 {
498    fn digits_en_to_fa(&self) -> String {
499        let mut string = self.to_string();
500        string.digits_en_to_fa_mut();
501        string
502    }
503}
504
505impl DigitsEn2Fa for i64 {
506    fn digits_en_to_fa(&self) -> String {
507        let mut string = self.to_string();
508        string.digits_en_to_fa_mut();
509        string
510    }
511}
512
513impl DigitsEn2Fa for u128 {
514    fn digits_en_to_fa(&self) -> String {
515        let mut string = self.to_string();
516        string.digits_en_to_fa_mut();
517        string
518    }
519}
520
521impl DigitsEn2Fa for i128 {
522    fn digits_en_to_fa(&self) -> String {
523        let mut string = self.to_string();
524        string.digits_en_to_fa_mut();
525        string
526    }
527}
528
529impl DigitsEn2Fa for usize {
530    fn digits_en_to_fa(&self) -> String {
531        let mut string = self.to_string();
532        string.digits_en_to_fa_mut();
533        string
534    }
535}
536
537impl DigitsEn2Fa for isize {
538    fn digits_en_to_fa(&self) -> String {
539        let mut string = self.to_string();
540        string.digits_en_to_fa_mut();
541        string
542    }
543}
544
545impl DigitsEn2Fa for f32 {
546    fn digits_en_to_fa(&self) -> String {
547        let mut string = self.to_string();
548        string.digits_en_to_fa_mut();
549        string
550    }
551}
552
553impl DigitsEn2Fa for f64 {
554    fn digits_en_to_fa(&self) -> String {
555        let mut string = self.to_string();
556        string.digits_en_to_fa_mut();
557        string
558    }
559}
560
561#[inline]
562fn convert(input: impl AsRef<str>, range: Range<u32>, diff: i64) -> String {
563    let mut index = 0;
564    let mut result = String::new();
565    input
566        .as_ref()
567        .match_indices(|code| range.contains(&(code as u32)))
568        .map(|(index, char_str)| unsafe { (index, char_str.chars().next().unwrap_unchecked()) })
569        .for_each(|(char_index, old_char)| unsafe {
570            result += input.as_ref().get_unchecked(index..char_index);
571            let new_char = char::from_u32_unchecked((old_char as i64 + diff) as u32);
572            result.push(new_char);
573            index = char_index + old_char.len_utf8();
574        });
575    result += unsafe { input.as_ref().get_unchecked(index..) };
576    result
577}
578
579#[inline]
580fn convert_mut(input: &mut String, range: Range<u32>, diff: i64) {
581    input
582        .rmatch_indices(|code| range.contains(&(code as u32)))
583        .map(|(index, char_str)| unsafe { (index, char_str.chars().next().unwrap_unchecked()) })
584        .collect::<Vec<_>>()
585        .into_iter()
586        .for_each(|(char_index, old_char)| unsafe {
587            let char = char::from_u32_unchecked((old_char as i64 + diff) as u32);
588            input.remove(char_index);
589            input.insert(char_index, char);
590        });
591}
592
593#[cfg(test)]
594mod tests {
595    use super::*;
596
597    #[test]
598    fn digits_fa_to_en_test() {
599        assert_eq!("123۴۵۶".digits_fa_to_en(), "123456");
600        let mut n = "۸۹123۴۵".to_string();
601        n.digits_fa_to_en_mut();
602        assert_eq!(n, "8912345");
603        let mut n = Cow::Borrowed("۰۱۲۳۴۵۶۷۸۹");
604        n.digits_fa_to_en_mut();
605        assert_eq!(n, Cow::<str>::Owned("0123456789".to_string()));
606    }
607
608    #[test]
609    fn digits_en_to_fa_test() {
610        let mut n = "123۴۵۶".to_string();
611        n.digits_en_to_fa_mut();
612        assert_eq!(n, "۱۲۳۴۵۶");
613        assert_eq!(123i8.digits_en_to_fa(), "۱۲۳");
614        assert_eq!(1234567891usize.digits_en_to_fa(), "۱۲۳۴۵۶۷۸۹۱");
615        assert_eq!(3.14f64.digits_en_to_fa(), "۳.۱۴");
616        assert_eq!("٤٥٦".to_string().digits_en_to_fa(), "٤٥٦");
617        let mut n = Cow::Borrowed("123۴۵۶");
618        n.digits_en_to_fa_mut();
619        assert_eq!(n, Cow::<str>::Owned("۱۲۳۴۵۶".to_string()));
620    }
621
622    #[test]
623    fn digits_en_to_ar_test() {
624        let mut n = "123٤٥٦".to_string();
625        n.digits_en_to_ar_mut();
626        assert_eq!(n, "١٢٣٤٥٦");
627        assert_eq!(123i8.digits_en_to_ar(), "١٢٣");
628        assert_eq!(1234567891usize.digits_en_to_ar(), "١٢٣٤٥٦٧٨٩١");
629        assert_eq!(3.14f64.digits_en_to_ar(), "٣.١٤");
630        assert_eq!("۴۵۶".to_string().digits_en_to_ar(), "۴۵۶");
631        let mut n = Cow::Borrowed("123٤٥٦");
632        n.digits_en_to_ar_mut();
633        assert_eq!(n, Cow::<str>::Owned("١٢٣٤٥٦".to_string()));
634    }
635
636    #[test]
637    fn digits_ar_to_en_test() {
638        assert_eq!("٠١٢٣٤٥٦٧٨٩".digits_ar_to_en(), "0123456789");
639        let mut n = "89١٢٣4٥".to_string();
640        n.digits_ar_to_en_mut();
641        assert_eq!(n, "8912345");
642        let mut n = Cow::Borrowed("0123۴۵۶789");
643        n.digits_ar_to_en_mut();
644        assert_eq!(n, Cow::<str>::Owned("0123۴۵۶789".to_string()));
645    }
646
647    #[test]
648    fn digits_fa_to_ar_test() {
649        assert_eq!("۰۱۲۳۴۵۶۷۸۹".digits_fa_to_ar(), "٠١٢٣٤٥٦٧٨٩");
650        let mut n = "۱۷۸۲۳۴۰۵۶۹".to_string();
651        n.digits_fa_to_ar_mut();
652        assert_eq!(n, "١٧٨٢٣٤٠٥٦٩");
653        assert_eq!("۷۸٤۲۳٤۴".to_string().digits_fa_to_ar(), "٧٨٤٢٣٤٤");
654        let mut n = Cow::Borrowed("٤٤٤444۴۴۴");
655        n.digits_fa_to_ar_mut();
656        assert_eq!(n, Cow::<str>::Owned("٤٤٤444٤٤٤".to_string()));
657    }
658
659    #[test]
660    fn digits_ar_to_fa_test() {
661        assert_eq!("٠١٢٣٤٥٦٧٨٩".digits_ar_to_fa(), "۰۱۲۳۴۵۶۷۸۹");
662        let mut n = "١٧٨٢٣٤٠٥٦٩".to_string();
663        n.digits_ar_to_fa_mut();
664        assert_eq!(n, "۱۷۸۲۳۴۰۵۶۹");
665        assert_eq!("٧٨٤٢٣٤٤".to_string().digits_ar_to_fa(), "۷۸۴۲۳۴۴");
666        let mut n = Cow::Borrowed("٤٤٤444٤٤٤");
667        n.digits_ar_to_fa_mut();
668        assert_eq!(n, Cow::<str>::Owned("۴۴۴444۴۴۴".to_string()));
669    }
670}