1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use std::fmt; |
4 | |
5 | use glib::translate::{FromGlib, GlibNoneError, IntoGlib, OptionIntoGlib, TryFromGlib}; |
6 | |
7 | use super::{ |
8 | Buffers, Bytes, ClockTime, CompatibleFormattedValue, Default, Format, FormattedValue, |
9 | FormattedValueError, FormattedValueFullRange, FormattedValueIntrinsic, |
10 | FormattedValueNoneBuilder, Percent, Signed, SignedIntrinsic, Undefined, UnsignedIntoSigned, |
11 | }; |
12 | use crate::utils::Displayable; |
13 | |
14 | #[derive (PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)] |
15 | pub struct Other(u64); |
16 | impl Other { |
17 | pub const MAX: Self = Self(u64::MAX - 1); |
18 | } |
19 | |
20 | impl Other { |
21 | // rustdoc-stripper-ignore-next |
22 | /// Builds a new `Other` value with the provided quantity. |
23 | /// |
24 | /// # Panics |
25 | /// |
26 | /// Panics if the provided quantity equals `u64::MAX`, |
27 | /// which is reserved for `None` in C. |
28 | #[track_caller ] |
29 | #[inline ] |
30 | pub const fn from_u64(quantity: u64) -> Self { |
31 | if quantity == u64::MAX { |
32 | panic!("`Other` value out of range" ); |
33 | } |
34 | |
35 | Other(quantity) |
36 | } |
37 | |
38 | // rustdoc-stripper-ignore-next |
39 | /// Builds a new `Other` value with the provided quantity. |
40 | /// |
41 | /// # Panics |
42 | /// |
43 | /// Panics if the provided quantity equals `u64::MAX`, |
44 | /// which is reserved for `None` in C. |
45 | #[track_caller ] |
46 | #[inline ] |
47 | pub fn from_usize(quantity: usize) -> Self { |
48 | // FIXME can't use `try_into` in `const` (rustc 1.64.0) |
49 | Other::from_u64(quantity.try_into().unwrap()) |
50 | } |
51 | } |
52 | |
53 | impl_common_ops_for_newtype_uint!(Other, u64); |
54 | impl_signed_div_mul!(Other, u64); |
55 | impl_signed_int_into_signed!(Other, u64); |
56 | option_glib_newtype_from_to!(Other, u64::MAX); |
57 | glib_newtype_display!(Other, DisplayableOptionOther); |
58 | |
59 | impl TryFrom<u64> for Other { |
60 | type Error = GlibNoneError; |
61 | #[inline ] |
62 | fn try_from(val: u64) -> Result<Self, GlibNoneError> { |
63 | skip_assert_initialized!(); |
64 | unsafe { Self::try_from_glib(val) } |
65 | } |
66 | } |
67 | |
68 | impl TryFromGlib<i64> for Other { |
69 | type Error = GlibNoneError; |
70 | #[inline ] |
71 | unsafe fn try_from_glib(val: i64) -> Result<Self, GlibNoneError> { |
72 | skip_assert_initialized!(); |
73 | Self::try_from_glib(val as u64) |
74 | } |
75 | } |
76 | |
77 | impl TryFrom<Other> for usize { |
78 | type Error = std::num::TryFromIntError; |
79 | |
80 | fn try_from(value: Other) -> Result<Self, Self::Error> { |
81 | value.0.try_into() |
82 | } |
83 | } |
84 | |
85 | // FIXME `functions in traits cannot be const` (rustc 1.64.0) |
86 | // rustdoc-stripper-ignore-next |
87 | /// `Other` formatted value constructor trait. |
88 | pub trait OtherFormatConstructor { |
89 | // rustdoc-stripper-ignore-next |
90 | /// Builds an `Other` formatted value from `self`. |
91 | fn other_format(self) -> Other; |
92 | } |
93 | |
94 | impl OtherFormatConstructor for u64 { |
95 | #[track_caller ] |
96 | #[inline ] |
97 | fn other_format(self) -> Other { |
98 | Other::from_u64(self) |
99 | } |
100 | } |
101 | |
102 | #[derive (PartialEq, Eq, Hash, Clone, Copy, Debug)] |
103 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))] |
104 | pub enum GenericFormattedValue { |
105 | Undefined(Undefined), |
106 | Default(Option<Default>), |
107 | Bytes(Option<Bytes>), |
108 | Time(Option<ClockTime>), |
109 | Buffers(Option<Buffers>), |
110 | Percent(Option<Percent>), |
111 | Other(Format, Option<Other>), |
112 | } |
113 | |
114 | impl fmt::Display for GenericFormattedValue { |
115 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
116 | match self { |
117 | Self::Undefined(val: &Undefined) => val.fmt(f), |
118 | Self::Default(val: &Option) => val.display().fmt(f), |
119 | Self::Bytes(val: &Option) => val.display().fmt(f), |
120 | Self::Time(val: &Option) => val.display().fmt(f), |
121 | Self::Buffers(val: &Option) => val.display().fmt(f), |
122 | Self::Percent(val: &Option) => val.display().fmt(f), |
123 | Self::Other(format: &Format, val: &Option) => { |
124 | val.display().fmt(f)?; |
125 | fmt::Write::write_char(self:f, c:' ' )?; |
126 | fmt::Display::fmt(&format, f) |
127 | } |
128 | } |
129 | } |
130 | } |
131 | |
132 | impl Displayable for GenericFormattedValue { |
133 | type DisplayImpl = Self; |
134 | fn display(self) -> Self { |
135 | self |
136 | } |
137 | } |
138 | |
139 | impl GenericFormattedValue { |
140 | #[inline ] |
141 | pub fn new(format: Format, value: i64) -> Self { |
142 | skip_assert_initialized!(); |
143 | match format { |
144 | Format::Undefined => Self::Undefined(value.into()), |
145 | Format::Default => Self::Default(unsafe { FromGlib::from_glib(value) }), |
146 | Format::Bytes => Self::Bytes(unsafe { FromGlib::from_glib(value) }), |
147 | Format::Time => Self::Time(unsafe { FromGlib::from_glib(value) }), |
148 | Format::Buffers => Self::Buffers(unsafe { FromGlib::from_glib(value) }), |
149 | Format::Percent => Self::Percent(unsafe { FromGlib::from_glib(value) }), |
150 | Format::__Unknown(_) => Self::Other(format, unsafe { FromGlib::from_glib(value) }), |
151 | } |
152 | } |
153 | |
154 | #[doc (alias = "get_format" )] |
155 | #[inline ] |
156 | pub fn format(&self) -> Format { |
157 | match *self { |
158 | Self::Undefined(_) => Format::Undefined, |
159 | Self::Default(_) => Format::Default, |
160 | Self::Bytes(_) => Format::Bytes, |
161 | Self::Time(_) => Format::Time, |
162 | Self::Buffers(_) => Format::Buffers, |
163 | Self::Percent(_) => Format::Percent, |
164 | Self::Other(f, _) => f, |
165 | } |
166 | } |
167 | |
168 | #[doc (alias = "get_value" )] |
169 | #[inline ] |
170 | pub fn value(&self) -> i64 { |
171 | unsafe { |
172 | match *self { |
173 | Self::Undefined(v) => *v, |
174 | Self::Default(v) => v.into_raw_value(), |
175 | Self::Bytes(v) => v.into_raw_value(), |
176 | Self::Time(v) => v.into_raw_value(), |
177 | Self::Buffers(v) => v.into_raw_value(), |
178 | Self::Percent(v) => v.into_raw_value(), |
179 | Self::Other(_, v) => v.into_glib() as i64, |
180 | } |
181 | } |
182 | } |
183 | } |
184 | |
185 | impl FormattedValue for GenericFormattedValue { |
186 | // The intrinsic value for `GenericFormattedValue` is also |
187 | // `GenericFormattedValue`. We can't dissociate the `Option` |
188 | // from the variants' inner type since they are not all `Option`s. |
189 | type FullRange = GenericFormattedValue; |
190 | |
191 | #[inline ] |
192 | fn default_format() -> Format { |
193 | Format::Undefined |
194 | } |
195 | |
196 | #[inline ] |
197 | fn format(&self) -> Format { |
198 | self.format() |
199 | } |
200 | |
201 | #[inline ] |
202 | fn is_some(&self) -> bool { |
203 | match self { |
204 | Self::Undefined(_) => true, |
205 | Self::Default(v) => v.is_some(), |
206 | Self::Bytes(v) => v.is_some(), |
207 | Self::Time(v) => v.is_some(), |
208 | Self::Buffers(v) => v.is_some(), |
209 | Self::Percent(v) => v.is_some(), |
210 | Self::Other(_, v) => v.is_some(), |
211 | } |
212 | } |
213 | |
214 | #[inline ] |
215 | unsafe fn into_raw_value(self) -> i64 { |
216 | self.value() |
217 | } |
218 | } |
219 | |
220 | impl FormattedValueFullRange for GenericFormattedValue { |
221 | #[inline ] |
222 | unsafe fn from_raw(format: Format, value: i64) -> Self { |
223 | GenericFormattedValue::new(format, value) |
224 | } |
225 | } |
226 | |
227 | impl FormattedValueIntrinsic for GenericFormattedValue {} |
228 | impl SignedIntrinsic for GenericFormattedValue {} |
229 | |
230 | impl FormattedValueNoneBuilder for GenericFormattedValue { |
231 | #[track_caller ] |
232 | fn none() -> Self { |
233 | panic!(concat!( |
234 | "`GenericFormattedValue` can't build `None` without knowing" , |
235 | "the target format. Use `GenericFormattedValue::none_for_format`" , |
236 | )); |
237 | } |
238 | |
239 | #[track_caller ] |
240 | #[inline ] |
241 | fn none_for_format(format: Format) -> Self { |
242 | skip_assert_initialized!(); |
243 | match format { |
244 | Format::Undefined => panic!("`None` can't be represented by `Undefined`" ), |
245 | Format::Default => Self::Default(None), |
246 | Format::Bytes => Self::Bytes(None), |
247 | Format::Time => Self::Time(None), |
248 | Format::Buffers => Self::Buffers(None), |
249 | Format::Percent => Self::Percent(None), |
250 | unknown: Format => Self::Other(unknown, Other::NONE), |
251 | } |
252 | } |
253 | } |
254 | |
255 | impl UnsignedIntoSigned for GenericFormattedValue { |
256 | type Signed = GenericSignedFormattedValue; |
257 | |
258 | #[track_caller ] |
259 | #[inline ] |
260 | fn into_positive(self) -> Self::Signed { |
261 | use Signed::Positive; |
262 | match self { |
263 | Self::Undefined(_) => { |
264 | unimplemented!("`GenericFormattedValue::Undefined` is already signed" ) |
265 | } |
266 | Self::Default(val) => Self::Signed::Default(val.map(Positive)), |
267 | Self::Bytes(val) => Self::Signed::Bytes(val.map(Positive)), |
268 | Self::Time(val) => Self::Signed::Time(val.map(Positive)), |
269 | Self::Buffers(val) => Self::Signed::Buffers(val.map(Positive)), |
270 | Self::Percent(val) => Self::Signed::Percent(val.map(Positive)), |
271 | Self::Other(format, val) => Self::Signed::Other(format, val.map(Positive)), |
272 | } |
273 | } |
274 | |
275 | #[track_caller ] |
276 | #[inline ] |
277 | fn into_negative(self) -> Self::Signed { |
278 | use Signed::Negative; |
279 | match self { |
280 | Self::Undefined(_) => { |
281 | unimplemented!("`GenericFormattedValue::Undefined` is already signed" ) |
282 | } |
283 | Self::Default(val) => Self::Signed::Default(val.map(Negative)), |
284 | Self::Bytes(val) => Self::Signed::Bytes(val.map(Negative)), |
285 | Self::Time(val) => Self::Signed::Time(val.map(Negative)), |
286 | Self::Buffers(val) => Self::Signed::Buffers(val.map(Negative)), |
287 | Self::Percent(val) => Self::Signed::Percent(val.map(Negative)), |
288 | Self::Other(format, val) => Self::Signed::Other(format, val.map(Negative)), |
289 | } |
290 | } |
291 | } |
292 | |
293 | impl CompatibleFormattedValue<GenericFormattedValue> for GenericFormattedValue { |
294 | type Original = Self; |
295 | #[inline ] |
296 | fn try_into_checked(self, other: GenericFormattedValue) -> Result<Self, FormattedValueError> { |
297 | skip_assert_initialized!(); |
298 | if self.format() == other.format() { |
299 | Ok(self) |
300 | } else { |
301 | Err(FormattedValueError(self.format())) |
302 | } |
303 | } |
304 | |
305 | #[inline ] |
306 | fn try_into_checked_explicit( |
307 | self, |
308 | format: Format, |
309 | ) -> Result<Self::Original, FormattedValueError> { |
310 | skip_assert_initialized!(); |
311 | if self.format() == format { |
312 | Ok(self) |
313 | } else { |
314 | Err(FormattedValueError(self.format())) |
315 | } |
316 | } |
317 | } |
318 | |
319 | #[derive (Clone, Copy, Debug, PartialEq, Eq, Hash)] |
320 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))] |
321 | pub enum GenericSignedFormattedValue { |
322 | Default(Option<Signed<Default>>), |
323 | Bytes(Option<Signed<Bytes>>), |
324 | Time(Option<Signed<ClockTime>>), |
325 | Buffers(Option<Signed<Buffers>>), |
326 | Percent(Option<Signed<Percent>>), |
327 | Other(Format, Option<Signed<Other>>), |
328 | } |
329 | |
330 | impl GenericSignedFormattedValue { |
331 | #[doc (alias = "get_format" )] |
332 | #[inline ] |
333 | pub fn format(&self) -> Format { |
334 | match *self { |
335 | Self::Default(_) => Format::Default, |
336 | Self::Bytes(_) => Format::Bytes, |
337 | Self::Time(_) => Format::Time, |
338 | Self::Buffers(_) => Format::Buffers, |
339 | Self::Percent(_) => Format::Percent, |
340 | Self::Other(format, _) => format, |
341 | } |
342 | } |
343 | |
344 | #[inline ] |
345 | pub fn abs(self) -> GenericFormattedValue { |
346 | use GenericFormattedValue as Unsigned; |
347 | match self { |
348 | Self::Default(opt_signed) => Unsigned::Default(opt_signed.map(Signed::abs)), |
349 | Self::Bytes(opt_signed) => Unsigned::Bytes(opt_signed.map(Signed::abs)), |
350 | Self::Time(opt_signed) => Unsigned::Time(opt_signed.map(Signed::abs)), |
351 | Self::Buffers(opt_signed) => Unsigned::Buffers(opt_signed.map(Signed::abs)), |
352 | Self::Percent(opt_signed) => Unsigned::Percent(opt_signed.map(Signed::abs)), |
353 | Self::Other(format, opt_signed) => Unsigned::Other(format, opt_signed.map(Signed::abs)), |
354 | } |
355 | } |
356 | |
357 | #[inline ] |
358 | pub fn is_some(&self) -> bool { |
359 | match self { |
360 | Self::Default(v) => v.is_some(), |
361 | Self::Bytes(v) => v.is_some(), |
362 | Self::Time(v) => v.is_some(), |
363 | Self::Buffers(v) => v.is_some(), |
364 | Self::Percent(v) => v.is_some(), |
365 | Self::Other(_, v) => v.is_some(), |
366 | } |
367 | } |
368 | |
369 | #[inline ] |
370 | pub fn is_none(&self) -> bool { |
371 | !self.is_some() |
372 | } |
373 | |
374 | #[track_caller ] |
375 | #[inline ] |
376 | pub fn none_for_format(format: Format) -> Self { |
377 | skip_assert_initialized!(); |
378 | match format { |
379 | Format::Default => Self::Default(None), |
380 | Format::Bytes => Self::Bytes(None), |
381 | Format::Time => Self::Time(None), |
382 | Format::Buffers => Self::Buffers(None), |
383 | Format::Percent => Self::Percent(None), |
384 | Format::Undefined => { |
385 | panic!("`Undefined` is already signed, use `GenericFormattedValue`" ) |
386 | } |
387 | other => Self::Other(other, None), |
388 | } |
389 | } |
390 | } |
391 | |
392 | macro_rules! impl_gsfv_fn_opt_ret( |
393 | ($fn:ident(self) -> Option<$ret_ty:ty>) => { |
394 | #[inline] |
395 | pub fn $fn(self) -> Option<$ret_ty> { |
396 | match self { |
397 | Self::Default(opt_signed) => opt_signed.map(|signed| signed.$fn()), |
398 | Self::Bytes(opt_signed) => opt_signed.map(|signed| signed.$fn()), |
399 | Self::Time(opt_signed) => opt_signed.map(|signed| signed.$fn()), |
400 | Self::Buffers(opt_signed) => opt_signed.map(|signed| signed.$fn()), |
401 | Self::Percent(opt_signed) => opt_signed.map(|signed| signed.$fn()), |
402 | Self::Other(_, opt_signed) => opt_signed.map(|signed| signed.$fn()), |
403 | } |
404 | } |
405 | }; |
406 | ); |
407 | |
408 | impl GenericSignedFormattedValue { |
409 | impl_gsfv_fn_opt_ret!(is_positive(self) -> Option<bool>); |
410 | impl_gsfv_fn_opt_ret!(is_negative(self) -> Option<bool>); |
411 | impl_gsfv_fn_opt_ret!(signum(self) -> Option<i32>); |
412 | } |
413 | |
414 | impl std::ops::Neg for GenericSignedFormattedValue { |
415 | type Output = Self; |
416 | |
417 | #[inline ] |
418 | fn neg(self) -> Self { |
419 | use std::ops::Neg; |
420 | match self { |
421 | Self::Default(opt_signed: Option>) => Self::Default(opt_signed.map(Neg::neg)), |
422 | Self::Bytes(opt_signed: Option>) => Self::Bytes(opt_signed.map(Neg::neg)), |
423 | Self::Time(opt_signed: Option>) => Self::Time(opt_signed.map(Neg::neg)), |
424 | Self::Buffers(opt_signed: Option>) => Self::Buffers(opt_signed.map(Neg::neg)), |
425 | Self::Percent(opt_signed: Option>) => Self::Percent(opt_signed.map(Neg::neg)), |
426 | Self::Other(format: Format, opt_signed: Option>) => Self::Other(format, opt_signed.map(Neg::neg)), |
427 | } |
428 | } |
429 | } |
430 | |
431 | impl fmt::Display for GenericSignedFormattedValue { |
432 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
433 | match self { |
434 | Self::Default(opt_signed: &Option>) => opt_signed.display().fmt(f), |
435 | Self::Bytes(opt_signed: &Option>) => opt_signed.display().fmt(f), |
436 | Self::Time(opt_signed: &Option>) => opt_signed.display().fmt(f), |
437 | Self::Buffers(opt_signed: &Option>) => opt_signed.display().fmt(f), |
438 | Self::Percent(opt_signed: &Option>) => opt_signed.display().fmt(f), |
439 | Self::Other(format: &Format, opt_signed: &Option>) => { |
440 | opt_signed.display().fmt(f)?; |
441 | fmt::Write::write_char(self:f, c:' ' )?; |
442 | fmt::Display::fmt(&format, f) |
443 | } |
444 | } |
445 | } |
446 | } |
447 | |
448 | impl Displayable for GenericSignedFormattedValue { |
449 | type DisplayImpl = Self; |
450 | |
451 | fn display(self) -> Self::DisplayImpl { |
452 | self |
453 | } |
454 | } |
455 | |
456 | #[cfg (test)] |
457 | mod tests { |
458 | use super::*; |
459 | |
460 | #[test ] |
461 | #[allow (clippy::eq_op, clippy::op_ref)] |
462 | fn other() { |
463 | // Check a few ops on `Other`, better coverage for |
464 | // the macro ops impl ensured as part of the `clock_time` module. |
465 | |
466 | use opt_ops::prelude::*; |
467 | |
468 | let other_none: Option<Other> = Other::try_from(u64::MAX).ok(); |
469 | assert!(other_none.is_none()); |
470 | |
471 | let other_10 = Other::from_u64(10); |
472 | let other_20 = Other::from_usize(20); |
473 | let other_30 = 30.other_format(); |
474 | |
475 | assert_eq!(other_10 + other_20, other_30); |
476 | assert_eq!(other_30 - other_20, other_10); |
477 | |
478 | assert!(other_10 < Other::MAX); |
479 | |
480 | assert_eq!(Some(other_10).opt_add(other_20), Some(other_30)); |
481 | } |
482 | |
483 | #[test ] |
484 | #[allow (clippy::eq_op, clippy::op_ref)] |
485 | fn generic_other() { |
486 | let gen_other_42: GenericFormattedValue = |
487 | GenericFormattedValue::new(Format::__Unknown(128), 42); |
488 | assert_eq!( |
489 | gen_other_42, |
490 | GenericFormattedValue::Other(Format::__Unknown(128), Other::try_from(42).ok()) |
491 | ); |
492 | assert_eq!(gen_other_42.format(), Format::__Unknown(128)); |
493 | assert_eq!(gen_other_42.value(), 42); |
494 | assert!(gen_other_42.is_some()); |
495 | |
496 | let other_none: Option<Other> = Other::NONE; |
497 | assert!(other_none.is_none()); |
498 | |
499 | let gen_other_none: GenericFormattedValue = |
500 | GenericFormattedValue::none_for_format(Format::__Unknown(128)); |
501 | assert!(gen_other_none.is_none()); |
502 | assert_eq!( |
503 | gen_other_none, |
504 | GenericFormattedValue::Other(Format::__Unknown(128), None) |
505 | ); |
506 | } |
507 | |
508 | #[test ] |
509 | #[allow (clippy::eq_op, clippy::op_ref)] |
510 | fn generic_signed_other() { |
511 | let gen_other_42: GenericFormattedValue = |
512 | GenericFormattedValue::new(Format::__Unknown(128), 42); |
513 | |
514 | let p_gen_other_42 = gen_other_42.into_positive(); |
515 | assert_eq!( |
516 | p_gen_other_42, |
517 | GenericSignedFormattedValue::Other( |
518 | Format::__Unknown(128), |
519 | Some(Signed::Positive(42.other_format())), |
520 | ), |
521 | ); |
522 | |
523 | let n_gen_other_42 = gen_other_42.into_negative(); |
524 | assert_eq!( |
525 | n_gen_other_42, |
526 | GenericSignedFormattedValue::Other( |
527 | Format::__Unknown(128), |
528 | Some(Signed::Negative(42.other_format())), |
529 | ), |
530 | ); |
531 | } |
532 | } |
533 | |