Skip to main content

rune/modules/
char.rs

1//! The character module for Rune.
2
3use core::char::ParseCharError;
4
5use crate::alloc;
6use crate::alloc::fmt::TryWrite;
7use crate::runtime::{Formatter, Value, VmError, VmErrorKind};
8use crate::{ContextError, Module};
9
10use crate as rune;
11
12/// The character module for Rune.
13#[rune::module(::std::char)]
14pub fn module() -> Result<Module, ContextError> {
15    let mut module = Module::from_meta(self::module__meta)?;
16
17    module.ty::<ParseCharError>()?;
18    module.function_meta(parse_char_error_display_fmt)?;
19    module.function_meta(parse_char_error_debug_fmt)?;
20    module.function_meta(from_i64)?;
21    module.function_meta(to_i64)?;
22    module.function_meta(is_alphabetic)?;
23    module.function_meta(is_alphanumeric)?;
24    module.function_meta(is_control)?;
25    module.function_meta(is_lowercase)?;
26    module.function_meta(is_numeric)?;
27    module.function_meta(is_uppercase)?;
28    module.function_meta(is_whitespace)?;
29    module.function_meta(to_digit)?;
30    Ok(module)
31}
32
33/// Write why a character could not be parsed.
34///
35/// # Examples
36///
37/// ```rune
38/// let text = if let Err(error) = "ab".parse::<char>() {
39///     format!("{error}")
40/// } else {
41///     ""
42/// };
43///
44/// assert_eq!(text, "too many characters in string");
45/// ```
46#[rune::function(instance, protocol = DISPLAY_FMT)]
47fn parse_char_error_display_fmt(error: &ParseCharError, f: &mut Formatter) -> alloc::Result<()> {
48    write!(f, "{error}")
49}
50
51/// Write a debug representation of why a character could not be parsed.
52///
53/// # Examples
54///
55/// ```rune
56/// let text = if let Err(error) = "ab".parse::<char>() {
57///     format!("{error:?}")
58/// } else {
59///     ""
60/// };
61///
62/// assert!(text.starts_with("ParseCharError"));
63/// ```
64#[rune::function(instance, protocol = DEBUG_FMT)]
65fn parse_char_error_debug_fmt(error: &ParseCharError, f: &mut Formatter) -> alloc::Result<()> {
66    write!(f, "{error:?}")
67}
68
69/// Try to convert a number into a character.
70///
71/// # Examples
72///
73/// ```rune
74/// let c = char::from_i64(80);
75/// assert!(c.is_some());
76/// ```
77#[rune::function]
78fn from_i64(value: i64) -> Result<Option<Value>, VmError> {
79    if value < 0 {
80        Err(VmError::new(VmErrorKind::Underflow))
81    } else if value > u32::MAX as i64 {
82        Err(VmError::new(VmErrorKind::Overflow))
83    } else {
84        let Some(c) = char::from_u32(value as u32) else {
85            return Ok(None);
86        };
87
88        Ok(Some(Value::from(c)))
89    }
90}
91
92/// Convert a character into an integer.
93///
94/// # Examples
95///
96/// ```rune
97/// let c = char::from_i64(80)?;
98/// assert_eq!(c.to_i64(), 80);
99/// ```
100#[rune::function(instance)]
101fn to_i64(value: char) -> i64 {
102    value as i64
103}
104
105/// Returns `true` if this `char` has the `Alphabetic` property.
106///
107/// `Alphabetic` is described in Chapter 4 (Character Properties) of the [Unicode Standard] and
108/// specified in the [Unicode Character Database][ucd] [`DerivedCoreProperties.txt`].
109///
110/// [Unicode Standard]: https://www.unicode.org/versions/latest/
111/// [ucd]: https://www.unicode.org/reports/tr44/
112/// [`DerivedCoreProperties.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt
113///
114/// # Examples
115///
116/// ```rune
117/// assert!('a'.is_alphabetic());
118/// assert!('京'.is_alphabetic());
119///
120/// let c = '💝';
121/// // love is many things, but it is not alphabetic
122/// assert!(!c.is_alphabetic());
123/// ```
124#[rune::function(instance)]
125#[inline]
126fn is_alphabetic(c: char) -> bool {
127    char::is_alphabetic(c)
128}
129
130/// Returns `true` if this `char` satisfies either [`is_alphabetic()`] or [`is_numeric()`].
131///
132/// [`is_alphabetic()`]: #method.is_alphabetic
133/// [`is_numeric()`]: #method.is_numeric
134///
135/// # Examples
136///
137/// Basic usage:
138///
139/// ```rune
140/// assert!('٣'.is_alphanumeric());
141/// assert!('7'.is_alphanumeric());
142/// assert!('৬'.is_alphanumeric());
143/// assert!('¾'.is_alphanumeric());
144/// assert!('①'.is_alphanumeric());
145/// assert!('K'.is_alphanumeric());
146/// assert!('و'.is_alphanumeric());
147/// assert!('藏'.is_alphanumeric());
148/// ```
149#[rune::function(instance)]
150#[inline]
151fn is_alphanumeric(c: char) -> bool {
152    char::is_alphanumeric(c)
153}
154
155/// Returns `true` if this `char` has the general category for control codes.
156///
157/// Control codes (code points with the general category of `Cc`) are described
158/// in Chapter 4 (Character Properties) of the [Unicode Standard] and specified
159/// in the [Unicode Character Database][ucd] [`UnicodeData.txt`].
160///
161/// [Unicode Standard]: https://www.unicode.org/versions/latest/
162/// [ucd]: https://www.unicode.org/reports/tr44/
163/// [`UnicodeData.txt`]:
164///     https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt
165///
166/// # Examples
167///
168/// Basic usage:
169///
170/// ```rune
171/// // U+009C, STRING TERMINATOR
172/// assert!('\u{009c}'.is_control());
173/// assert!(!'q'.is_control());
174/// ```
175#[rune::function(instance)]
176#[inline]
177fn is_control(c: char) -> bool {
178    char::is_control(c)
179}
180
181/// Returns `true` if this `char` has the `Lowercase` property.
182///
183/// `Lowercase` is described in Chapter 4 (Character Properties) of the [Unicode
184/// Standard] and specified in the [Unicode Character Database][ucd]
185/// [`DerivedCoreProperties.txt`].
186///
187/// [Unicode Standard]: https://www.unicode.org/versions/latest/
188/// [ucd]: https://www.unicode.org/reports/tr44/
189/// [`DerivedCoreProperties.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt
190///
191/// # Examples
192///
193/// Basic usage:
194///
195/// ```rune
196/// assert!('a'.is_lowercase());
197/// assert!('δ'.is_lowercase());
198/// assert!(!'A'.is_lowercase());
199/// assert!(!'Δ'.is_lowercase());
200///
201/// // The various Chinese scripts and punctuation do not have case, and so:
202/// assert!(!'中'.is_lowercase());
203/// assert!(!' '.is_lowercase());
204/// ```
205#[rune::function(instance)]
206#[inline]
207fn is_lowercase(c: char) -> bool {
208    char::is_lowercase(c)
209}
210
211/// Returns `true` if this `char` has one of the general categories for numbers.
212///
213/// The general categories for numbers (`Nd` for decimal digits, `Nl` for
214/// letter-like numeric characters, and `No` for other numeric characters) are
215/// specified in the [Unicode Character Database][ucd] [`UnicodeData.txt`].
216///
217/// This method doesn't cover everything that could be considered a number, e.g.
218/// ideographic numbers like '三'. If you want everything including characters
219/// with overlapping purposes then you might want to use a unicode or
220/// language-processing library that exposes the appropriate character
221/// properties instead of looking at the unicode categories.
222///
223/// If you want to parse ASCII decimal digits (0-9) or ASCII base-N, use
224/// `is_ascii_digit` or `is_digit` instead.
225///
226/// [Unicode Standard]: https://www.unicode.org/versions/latest/
227/// [ucd]: https://www.unicode.org/reports/tr44/
228/// [`UnicodeData.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt
229///
230/// # Examples
231///
232/// Basic usage:
233///
234/// ```rune
235/// assert!('٣'.is_numeric());
236/// assert!('7'.is_numeric());
237/// assert!('৬'.is_numeric());
238/// assert!('¾'.is_numeric());
239/// assert!('①'.is_numeric());
240/// assert!(!'K'.is_numeric());
241/// assert!(!'و'.is_numeric());
242/// assert!(!'藏'.is_numeric());
243/// assert!(!'三'.is_numeric());
244/// ```
245#[rune::function(instance)]
246#[inline]
247fn is_numeric(c: char) -> bool {
248    char::is_numeric(c)
249}
250
251/// Returns `true` if this `char` has the `Uppercase` property.
252///
253/// `Uppercase` is described in Chapter 4 (Character Properties) of the [Unicode
254/// Standard] and specified in the [Unicode Character Database][ucd]
255/// [`DerivedCoreProperties.txt`].
256///
257/// [Unicode Standard]: https://www.unicode.org/versions/latest/
258/// [ucd]: https://www.unicode.org/reports/tr44/
259/// [`DerivedCoreProperties.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt
260///
261/// # Examples
262///
263/// Basic usage:
264///
265/// ```rune
266/// assert!(!'a'.is_uppercase());
267/// assert!(!'δ'.is_uppercase());
268/// assert!('A'.is_uppercase());
269/// assert!('Δ'.is_uppercase());
270///
271/// // The various Chinese scripts and punctuation do not have case, and so:
272/// assert!(!'中'.is_uppercase());
273/// assert!(!' '.is_uppercase());
274/// ```
275#[rune::function(instance)]
276#[inline]
277fn is_uppercase(c: char) -> bool {
278    char::is_uppercase(c)
279}
280
281/// Returns `true` if this `char` has the `White_Space` property.
282///
283/// `White_Space` is specified in the [Unicode Character Database][ucd]
284/// [`PropList.txt`].
285///
286/// [ucd]: https://www.unicode.org/reports/tr44/
287/// [`PropList.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt
288///
289/// # Examples
290///
291/// Basic usage:
292///
293/// ```rune
294/// assert!(' '.is_whitespace());
295///
296/// // line break
297/// assert!('\n'.is_whitespace());
298///
299/// // a non-breaking space
300/// assert!('\u{A0}'.is_whitespace());
301///
302/// assert!(!'越'.is_whitespace());
303/// ```
304#[rune::function(instance)]
305#[inline]
306fn is_whitespace(c: char) -> bool {
307    char::is_whitespace(c)
308}
309
310/// Converts a `char` to a digit in the given radix.
311///
312/// A 'radix' here is sometimes also called a 'base'. A radix of two
313/// indicates a binary number, a radix of ten, decimal, and a radix of
314/// sixteen, hexadecimal, to give some common values. Arbitrary
315/// radices are supported.
316///
317/// 'Digit' is defined to be only the following characters:
318///
319/// * `0-9`
320/// * `a-z`
321/// * `A-Z`
322///
323/// # Errors
324///
325/// Returns `None` if the `char` does not refer to a digit in the given radix.
326///
327/// # Panics
328///
329/// Panics if given a radix larger than 36.
330///
331/// # Examples
332///
333/// Basic usage:
334///
335/// ```rune
336/// assert_eq!('1'.to_digit(10), Some(1));
337/// assert_eq!('f'.to_digit(16), Some(15));
338/// ```
339///
340/// Passing a non-digit results in failure:
341///
342/// ```rune
343/// assert_eq!('f'.to_digit(10), None);
344/// assert_eq!('z'.to_digit(16), None);
345/// ```
346///
347/// Passing a large radix, causing a panic:
348///
349/// ```rune,should_panic
350/// // this panics
351/// let _ = '1'.to_digit(37);
352/// ```
353#[rune::function(instance)]
354#[inline]
355fn to_digit(c: char, radix: u32) -> Result<Option<u32>, VmError> {
356    if radix > 36 {
357        return Err(VmError::panic("to_digit: radix is too high (maximum 36)"));
358    }
359
360    Ok(char::to_digit(c, radix))
361}