Skip to main content

singe_cutensor/
types.rs

1use std::fmt::{self, Display, Formatter};
2
3use singe_core::{impl_enum_conversion, impl_enum_display};
4
5use num_enum::{IntoPrimitive, TryFromPrimitive};
6use singe_cutensor_sys as sys;
7
8/// Algorithm used to perform a tensor operation.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive, IntoPrimitive)]
10#[repr(i32)]
11#[non_exhaustive]
12pub enum Algorithm {
13    /// More time-consuming than CUTENSOR_DEFAULT, but typically provides a more accurate kernel selection.
14    DefaultPatient = sys::cutensorAlgo_t::CUTENSOR_ALGO_DEFAULT_PATIENT as _,
15    /// Choose the GETT algorithm (only applicable to contractions).
16    Gett = sys::cutensorAlgo_t::CUTENSOR_ALGO_GETT as _,
17    /// Transpose (A or B) + GETT (only applicable to contractions).
18    Tgett = sys::cutensorAlgo_t::CUTENSOR_ALGO_TGETT as _,
19    /// Transpose-Transpose-GEMM-Transpose (requires additional memory) (only applicable to contractions).
20    Ttgt = sys::cutensorAlgo_t::CUTENSOR_ALGO_TTGT as _,
21    /// A performance model chooses the appropriate algorithm and kernel.
22    Default = sys::cutensorAlgo_t::CUTENSOR_ALGO_DEFAULT as _,
23}
24
25impl_enum_conversion!(i32, sys::cutensorAlgo_t, Algorithm);
26
27impl_enum_display!(Algorithm, {
28    Algorithm::DefaultPatient => "CUTENSOR_ALGO_DEFAULT_PATIENT",
29    Algorithm::Gett => "CUTENSOR_ALGO_GETT",
30    Algorithm::Tgett => "CUTENSOR_ALGO_TGETT",
31    Algorithm::Ttgt => "CUTENSOR_ALGO_TTGT",
32    Algorithm::Default => "CUTENSOR_ALGO_DEFAULT",
33});
34
35/// Encodes cuTENSOR's legacy compute type.
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive, IntoPrimitive)]
37#[repr(u32)]
38#[non_exhaustive]
39pub enum ComputeType {
40    F16 = sys::cutensorComputeType_t::CUTENSOR_COMPUTE_16F as _,
41    Bf16 = sys::cutensorComputeType_t::CUTENSOR_COMPUTE_16BF as _,
42    Tf32 = sys::cutensorComputeType_t::CUTENSOR_COMPUTE_TF32 as _,
43    Tf32x3 = sys::cutensorComputeType_t::CUTENSOR_COMPUTE_3XTF32 as _,
44    F32 = sys::cutensorComputeType_t::CUTENSOR_COMPUTE_32F as _,
45    F64 = sys::cutensorComputeType_t::CUTENSOR_COMPUTE_64F as _,
46    U8 = sys::cutensorComputeType_t::CUTENSOR_COMPUTE_8U as _,
47    I8 = sys::cutensorComputeType_t::CUTENSOR_COMPUTE_8I as _,
48    U32 = sys::cutensorComputeType_t::CUTENSOR_COMPUTE_32U as _,
49    I32 = sys::cutensorComputeType_t::CUTENSOR_COMPUTE_32I as _,
50}
51
52impl_enum_conversion!(sys::cutensorComputeType_t, ComputeType);
53
54impl_enum_display!(ComputeType, {
55    ComputeType::F16 => "CUTENSOR_COMPUTE_16F",
56    ComputeType::Bf16 => "CUTENSOR_COMPUTE_16BF",
57    ComputeType::Tf32 => "CUTENSOR_COMPUTE_TF32",
58    ComputeType::Tf32x3 => "CUTENSOR_COMPUTE_3XTF32",
59    ComputeType::F32 => "CUTENSOR_COMPUTE_32F",
60    ComputeType::F64 => "CUTENSOR_COMPUTE_64F",
61    ComputeType::U8 => "CUTENSOR_COMPUTE_8U",
62    ComputeType::I8 => "CUTENSOR_COMPUTE_8I",
63    ComputeType::U32 => "CUTENSOR_COMPUTE_32U",
64    ComputeType::I32 => "CUTENSOR_COMPUTE_32I",
65});
66
67/// Unary and binary element-wise operations supported by cuTENSOR.
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive, IntoPrimitive)]
69#[repr(u32)]
70#[non_exhaustive]
71pub enum Operator {
72    /// Identity operator; elements are not changed.
73    Identity = sys::cutensorOperator_t::CUTENSOR_OP_IDENTITY as _,
74    /// Square root.
75    Sqrt = sys::cutensorOperator_t::CUTENSOR_OP_SQRT as _,
76    /// Rectified linear unit.
77    Relu = sys::cutensorOperator_t::CUTENSOR_OP_RELU as _,
78    /// Complex conjugate.
79    Conj = sys::cutensorOperator_t::CUTENSOR_OP_CONJ as _,
80    /// Reciprocal.
81    Rcp = sys::cutensorOperator_t::CUTENSOR_OP_RCP as _,
82    /// y=1/(1+exp(-x)).
83    Sigmoid = sys::cutensorOperator_t::CUTENSOR_OP_SIGMOID as _,
84    /// y=tanh(x).
85    Tanh = sys::cutensorOperator_t::CUTENSOR_OP_TANH as _,
86    /// Exponentiation.
87    Exp = sys::cutensorOperator_t::CUTENSOR_OP_EXP as _,
88    /// Log (base e).
89    Log = sys::cutensorOperator_t::CUTENSOR_OP_LOG as _,
90    /// Absolute value.
91    Abs = sys::cutensorOperator_t::CUTENSOR_OP_ABS as _,
92    /// Negation.
93    Neg = sys::cutensorOperator_t::CUTENSOR_OP_NEG as _,
94    /// Sine.
95    Sin = sys::cutensorOperator_t::CUTENSOR_OP_SIN as _,
96    /// Cosine.
97    Cos = sys::cutensorOperator_t::CUTENSOR_OP_COS as _,
98    /// Tangent.
99    Tan = sys::cutensorOperator_t::CUTENSOR_OP_TAN as _,
100    /// Hyperbolic sine.
101    Sinh = sys::cutensorOperator_t::CUTENSOR_OP_SINH as _,
102    /// Hyperbolic cosine.
103    Cosh = sys::cutensorOperator_t::CUTENSOR_OP_COSH as _,
104    /// Inverse sine.
105    Asin = sys::cutensorOperator_t::CUTENSOR_OP_ASIN as _,
106    /// Inverse cosine.
107    Acos = sys::cutensorOperator_t::CUTENSOR_OP_ACOS as _,
108    /// Inverse tangent.
109    Atan = sys::cutensorOperator_t::CUTENSOR_OP_ATAN as _,
110    /// Inverse hyperbolic sine.
111    Asinh = sys::cutensorOperator_t::CUTENSOR_OP_ASINH as _,
112    /// Inverse hyperbolic cosine.
113    Acosh = sys::cutensorOperator_t::CUTENSOR_OP_ACOSH as _,
114    /// Inverse hyperbolic tangent.
115    Atanh = sys::cutensorOperator_t::CUTENSOR_OP_ATANH as _,
116    /// Ceiling.
117    Ceil = sys::cutensorOperator_t::CUTENSOR_OP_CEIL as _,
118    /// Floor.
119    Floor = sys::cutensorOperator_t::CUTENSOR_OP_FLOOR as _,
120    /// Mish y=x\*tanh(softplus(x)).
121    Mish = sys::cutensorOperator_t::CUTENSOR_OP_MISH as _,
122    /// Swish y=x\*sigmoid(x).
123    Swish = sys::cutensorOperator_t::CUTENSOR_OP_SWISH as _,
124    /// Softplus y=log(exp(x)+1).
125    SoftPlus = sys::cutensorOperator_t::CUTENSOR_OP_SOFT_PLUS as _,
126    /// Softsign y=x/(abs(x)+1).
127    SoftSign = sys::cutensorOperator_t::CUTENSOR_OP_SOFT_SIGN as _,
128    /// Addition of two elements.
129    Add = sys::cutensorOperator_t::CUTENSOR_OP_ADD as _,
130    /// Multiplication of two elements.
131    Mul = sys::cutensorOperator_t::CUTENSOR_OP_MUL as _,
132    /// Maximum of two elements.
133    Max = sys::cutensorOperator_t::CUTENSOR_OP_MAX as _,
134    /// Minimum of two elements.
135    Min = sys::cutensorOperator_t::CUTENSOR_OP_MIN as _,
136    /// Reserved for internal use only.
137    Unknown = sys::cutensorOperator_t::CUTENSOR_OP_UNKNOWN as _,
138}
139
140impl_enum_conversion!(sys::cutensorOperator_t, Operator);
141
142impl_enum_display!(Operator, {
143    Operator::Identity => "CUTENSOR_OP_IDENTITY",
144    Operator::Sqrt => "CUTENSOR_OP_SQRT",
145    Operator::Relu => "CUTENSOR_OP_RELU",
146    Operator::Conj => "CUTENSOR_OP_CONJ",
147    Operator::Rcp => "CUTENSOR_OP_RCP",
148    Operator::Sigmoid => "CUTENSOR_OP_SIGMOID",
149    Operator::Tanh => "CUTENSOR_OP_TANH",
150    Operator::Exp => "CUTENSOR_OP_EXP",
151    Operator::Log => "CUTENSOR_OP_LOG",
152    Operator::Abs => "CUTENSOR_OP_ABS",
153    Operator::Neg => "CUTENSOR_OP_NEG",
154    Operator::Sin => "CUTENSOR_OP_SIN",
155    Operator::Cos => "CUTENSOR_OP_COS",
156    Operator::Tan => "CUTENSOR_OP_TAN",
157    Operator::Sinh => "CUTENSOR_OP_SINH",
158    Operator::Cosh => "CUTENSOR_OP_COSH",
159    Operator::Asin => "CUTENSOR_OP_ASIN",
160    Operator::Acos => "CUTENSOR_OP_ACOS",
161    Operator::Atan => "CUTENSOR_OP_ATAN",
162    Operator::Asinh => "CUTENSOR_OP_ASINH",
163    Operator::Acosh => "CUTENSOR_OP_ACOSH",
164    Operator::Atanh => "CUTENSOR_OP_ATANH",
165    Operator::Ceil => "CUTENSOR_OP_CEIL",
166    Operator::Floor => "CUTENSOR_OP_FLOOR",
167    Operator::Mish => "CUTENSOR_OP_MISH",
168    Operator::Swish => "CUTENSOR_OP_SWISH",
169    Operator::SoftPlus => "CUTENSOR_OP_SOFT_PLUS",
170    Operator::SoftSign => "CUTENSOR_OP_SOFT_SIGN",
171    Operator::Add => "CUTENSOR_OP_ADD",
172    Operator::Mul => "CUTENSOR_OP_MUL",
173    Operator::Max => "CUTENSOR_OP_MAX",
174    Operator::Min => "CUTENSOR_OP_MIN",
175    Operator::Unknown => "CUTENSOR_OP_UNKNOWN",
176});
177
178/// Controls the workspace amount suggested by [`Plan::estimate_workspace_size`](crate::plan::Plan::estimate_workspace_size).
179#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive, IntoPrimitive)]
180#[repr(u32)]
181#[non_exhaustive]
182pub enum WorkspacePreference {
183    /// Least memory requirement; at least one algorithm is available.
184    Min = sys::cutensorWorksizePreference_t::CUTENSOR_WORKSPACE_MIN as _,
185    /// Aims to attain high performance while also reducing the workspace requirement.
186    Default = sys::cutensorWorksizePreference_t::CUTENSOR_WORKSPACE_DEFAULT as _,
187    /// Highest memory requirement; all algorithms are available.
188    Max = sys::cutensorWorksizePreference_t::CUTENSOR_WORKSPACE_MAX as _,
189}
190
191impl_enum_conversion!(sys::cutensorWorksizePreference_t, WorkspacePreference);
192
193impl_enum_display!(WorkspacePreference, {
194    WorkspacePreference::Min => "CUTENSOR_WORKSPACE_MIN",
195    WorkspacePreference::Default => "CUTENSOR_WORKSPACE_DEFAULT",
196    WorkspacePreference::Max => "CUTENSOR_WORKSPACE_MAX",
197});
198
199/// Controls what is considered a cache hit.
200#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive, IntoPrimitive)]
201#[repr(u32)]
202#[non_exhaustive]
203pub enum CacheMode {
204    /// Plans are not cached.
205    None = sys::cutensorCacheMode_t::CUTENSOR_CACHE_MODE_NONE as _,
206    /// All descriptor parameters must match the cached plan.
207    Pedantic = sys::cutensorCacheMode_t::CUTENSOR_CACHE_MODE_PEDANTIC as _,
208}
209
210impl_enum_conversion!(sys::cutensorCacheMode_t, CacheMode);
211
212impl_enum_display!(CacheMode, {
213    CacheMode::None => "CUTENSOR_CACHE_MODE_NONE",
214    CacheMode::Pedantic => "CUTENSOR_CACHE_MODE_PEDANTIC",
215});
216
217/// Attributes of a cuTENSOR operation descriptor that can be read or modified.
218#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive, IntoPrimitive)]
219#[repr(u32)]
220#[non_exhaustive]
221pub enum OperationDescriptorAttribute {
222    /// `i32` tag that distinguishes otherwise identical problems in the software-managed plan cache.
223    /// (default value: 0).
224    Tag = sys::cutensorOperationDescriptorAttribute_t::CUTENSOR_OPERATION_DESCRIPTOR_TAG as _,
225    /// [`DataType`](singe_cuda::data_type::DataType): data type of the scaling factors.
226    ScalarType =
227        sys::cutensorOperationDescriptorAttribute_t::CUTENSOR_OPERATION_DESCRIPTOR_SCALAR_TYPE as _,
228    /// `f32`: number of floating-point operations necessary to perform this operation, assuming all scalars are nonzero unless otherwise specified.
229    Flops = sys::cutensorOperationDescriptorAttribute_t::CUTENSOR_OPERATION_DESCRIPTOR_FLOPS as _,
230    /// `f32`: minimal number of bytes transferred from or to global memory, assuming all scalars are nonzero unless otherwise specified.
231    MovedBytes =
232        sys::cutensorOperationDescriptorAttribute_t::CUTENSOR_OPERATION_DESCRIPTOR_MOVED_BYTES as _,
233    /// `u32` array of length `desc_out.num_modes`; entry `i` is the number of padded values to the left of dimension `i`.
234    PaddingLeft =
235        sys::cutensorOperationDescriptorAttribute_t::CUTENSOR_OPERATION_DESCRIPTOR_PADDING_LEFT
236            as _,
237    /// `u32` array of length `desc_out.num_modes`; entry `i` is the number of padded values to the right of dimension `i`.
238    PaddingRight =
239        sys::cutensorOperationDescriptorAttribute_t::CUTENSOR_OPERATION_DESCRIPTOR_PADDING_RIGHT
240            as _,
241    /// Host-side pointer to an element of the same type as the output tensor: constant padding value.
242    PaddingValue =
243        sys::cutensorOperationDescriptorAttribute_t::CUTENSOR_OPERATION_DESCRIPTOR_PADDING_VALUE
244            as _,
245}
246
247impl_enum_conversion!(
248    sys::cutensorOperationDescriptorAttribute_t,
249    OperationDescriptorAttribute
250);
251
252impl_enum_display!(OperationDescriptorAttribute, {
253    OperationDescriptorAttribute::Tag => "CUTENSOR_OPERATION_DESCRIPTOR_TAG",
254    OperationDescriptorAttribute::ScalarType => "CUTENSOR_OPERATION_DESCRIPTOR_SCALAR_TYPE",
255    OperationDescriptorAttribute::Flops => "CUTENSOR_OPERATION_DESCRIPTOR_FLOPS",
256    OperationDescriptorAttribute::MovedBytes => "CUTENSOR_OPERATION_DESCRIPTOR_MOVED_BYTES",
257    OperationDescriptorAttribute::PaddingLeft => "CUTENSOR_OPERATION_DESCRIPTOR_PADDING_LEFT",
258    OperationDescriptorAttribute::PaddingRight => "CUTENSOR_OPERATION_DESCRIPTOR_PADDING_RIGHT",
259    OperationDescriptorAttribute::PaddingValue => "CUTENSOR_OPERATION_DESCRIPTOR_PADDING_VALUE",
260});
261
262/// Attributes of a [`PlanPreference`](crate::plan::PlanPreference) that can be read or modified.
263#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive, IntoPrimitive)]
264#[repr(u32)]
265#[non_exhaustive]
266pub enum PlanPreferenceAttribute {
267    /// [`AutotuneMode`]: determines whether recurrent executions of the plan autotune.
268    AutotuneMode =
269        sys::cutensorPlanPreferenceAttribute_t::CUTENSOR_PLAN_PREFERENCE_AUTOTUNE_MODE as _,
270    /// [`CacheMode`]: determines whether the algorithm or kernel for this plan is cached and controls what is considered a cache hit.
271    CacheMode = sys::cutensorPlanPreferenceAttribute_t::CUTENSOR_PLAN_PREFERENCE_CACHE_MODE as _,
272    /// `i32`: only applicable when [`PlanPreferenceAttribute::CacheMode`] is set to [`AutotuneMode::Incremental`].
273    IncrementalCount =
274        sys::cutensorPlanPreferenceAttribute_t::CUTENSOR_PLAN_PREFERENCE_INCREMENTAL_COUNT as _,
275    /// [`Algorithm`]: fixes the algorithm.
276    Algorithm = sys::cutensorPlanPreferenceAttribute_t::CUTENSOR_PLAN_PREFERENCE_ALGO as _,
277    /// `i32`: fixes a kernel subvariant of an algorithm; for example, `kernel_rank == 1` with [`Algorithm::Tgett`] selects the second-best GETT kernel variant according to cuTENSOR's performance model, while `kernel_rank == 2` selects the third-best.
278    KernelRank = sys::cutensorPlanPreferenceAttribute_t::CUTENSOR_PLAN_PREFERENCE_KERNEL_RANK as _,
279    /// [`JitMode`]: determines whether just-in-time compilation is enabled (default: [`JitMode::None`]).
280    JitMode = sys::cutensorPlanPreferenceAttribute_t::CUTENSOR_PLAN_PREFERENCE_JIT as _,
281    /// `i32`: plan for a specific GPU architecture instead of the architecture associated with the context.
282    /// The value encodes the SM version as `10 * SM.major + SM.minor`.
283    /// Currently only SM versions 80, 90 and 100 are supported.
284    GpuArch = sys::cutensorPlanPreferenceAttribute_t::CUTENSOR_PLAN_PREFERENCE_GPU_ARCH as _,
285}
286
287impl_enum_conversion!(
288    sys::cutensorPlanPreferenceAttribute_t,
289    PlanPreferenceAttribute
290);
291
292impl_enum_display!(PlanPreferenceAttribute, {
293    PlanPreferenceAttribute::AutotuneMode => "CUTENSOR_PLAN_PREFERENCE_AUTOTUNE_MODE",
294    PlanPreferenceAttribute::CacheMode => "CUTENSOR_PLAN_PREFERENCE_CACHE_MODE",
295    PlanPreferenceAttribute::IncrementalCount => "CUTENSOR_PLAN_PREFERENCE_INCREMENTAL_COUNT",
296    PlanPreferenceAttribute::Algorithm => "CUTENSOR_PLAN_PREFERENCE_ALGO",
297    PlanPreferenceAttribute::KernelRank => "CUTENSOR_PLAN_PREFERENCE_KERNEL_RANK",
298    PlanPreferenceAttribute::JitMode => "CUTENSOR_PLAN_PREFERENCE_JIT",
299    PlanPreferenceAttribute::GpuArch => "CUTENSOR_PLAN_PREFERENCE_GPU_ARCH",
300});
301
302/// Controls cuTENSOR autotuning behavior.
303#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive, IntoPrimitive)]
304#[repr(u32)]
305#[non_exhaustive]
306pub enum AutotuneMode {
307    /// Indicates no autotuning (default); the cache reduces plan-creation overhead.
308    /// On a cache hit, the cached plan is reused; otherwise the plan cache is ignored.
309    None = sys::cutensorAutotuneMode_t::CUTENSOR_AUTOTUNE_MODE_NONE as _,
310    /// Indicates incremental autotuning.
311    /// Each corresponding [`sys::cutensorCreatePlan`] invocation creates a plan based on a different algorithm or kernel.
312    /// [`PlanPreferenceAttribute::IncrementalCount`] defines the maximum number of kernels tested.
313    /// May choose different algorithms across plan creations, so it does not
314    /// guarantee bitwise-identical results.
315    Incremental = sys::cutensorAutotuneMode_t::CUTENSOR_AUTOTUNE_MODE_INCREMENTAL as _,
316}
317
318impl_enum_conversion!(sys::cutensorAutotuneMode_t, AutotuneMode);
319
320impl_enum_display!(AutotuneMode, {
321    AutotuneMode::None => "CUTENSOR_AUTOTUNE_MODE_NONE",
322    AutotuneMode::Incremental => "CUTENSOR_AUTOTUNE_MODE_INCREMENTAL",
323});
324
325/// Controls cuTENSOR just-in-time compilation behavior.
326#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive, IntoPrimitive)]
327#[repr(u32)]
328#[non_exhaustive]
329pub enum JitMode {
330    /// Indicates that no kernel is just-in-time compiled.
331    None = sys::cutensorJitMode_t::CUTENSOR_JIT_MODE_NONE as _,
332    /// Indicates that the corresponding plan tries to compile a dedicated kernel for the given operation.
333    /// Only supported for GPUs with compute capability >= 8.0 (Ampere or newer).
334    Default = sys::cutensorJitMode_t::CUTENSOR_JIT_MODE_DEFAULT as _,
335}
336
337impl_enum_conversion!(sys::cutensorJitMode_t, JitMode);
338
339impl_enum_display!(JitMode, {
340    JitMode::None => "CUTENSOR_JIT_MODE_NONE",
341    JitMode::Default => "CUTENSOR_JIT_MODE_DEFAULT",
342});
343
344/// Attributes of a cuTENSOR plan that can be retrieved via [`sys::cutensorPlanGetAttribute`].
345#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive, IntoPrimitive)]
346#[repr(u32)]
347#[non_exhaustive]
348pub enum PlanAttribute {
349    /// Exact required workspace in bytes needed to execute the plan.
350    RequiredWorkspace = sys::cutensorPlanAttribute_t::CUTENSOR_PLAN_REQUIRED_WORKSPACE as _,
351}
352
353impl_enum_conversion!(sys::cutensorPlanAttribute_t, PlanAttribute);
354
355impl_enum_display!(PlanAttribute, {
356    PlanAttribute::RequiredWorkspace => "CUTENSOR_PLAN_REQUIRED_WORKSPACE",
357});
358
359#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
360#[repr(i32)]
361#[non_exhaustive]
362pub enum LoggerLevel {
363    Off = 0,
364    Error = 1,
365    PerformanceTrace = 2,
366    PerformanceHints = 3,
367    HeuristicsTrace = 4,
368    ApiTrace = 5,
369}
370
371impl LoggerLevel {
372    pub const fn as_raw(self) -> i32 {
373        self as i32
374    }
375}
376
377bitflags::bitflags! {
378    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
379    pub struct LoggerMask: i32 {
380        const OFF = 0;
381        const ERROR = 1;
382        const PERFORMANCE_TRACE = 2;
383        const PERFORMANCE_HINTS = 4;
384        const HEURISTICS_TRACE = 8;
385        const API_TRACE = 16;
386    }
387}
388
389#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
390#[repr(transparent)]
391pub struct Mode(i32);
392
393impl Mode {
394    pub const fn new(raw: i32) -> Self {
395        Self(raw)
396    }
397
398    pub const fn from_char(mode: char) -> Self {
399        Self(mode as i32)
400    }
401
402    pub const fn as_raw(self) -> i32 {
403        self.0
404    }
405}
406
407impl From<char> for Mode {
408    fn from(value: char) -> Self {
409        Self::from_char(value)
410    }
411}
412
413impl From<i32> for Mode {
414    fn from(value: i32) -> Self {
415        Self::new(value)
416    }
417}
418
419impl From<Mode> for i32 {
420    fn from(value: Mode) -> Self {
421        value.as_raw()
422    }
423}
424
425impl Display for Mode {
426    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
427        if let Some(ch) = char::from_u32(self.0 as u32)
428            && !ch.is_control()
429        {
430            return write!(f, "{ch}");
431        }
432
433        write!(f, "{}", self.0)
434    }
435}