Skip to content

Commit c6710b7

Browse files
authored
cxx-qt-lib: QByteArray:from_base64_encoding and QByteArray::to_base64 (#1305)
1 parent 428d190 commit c6710b7

File tree

5 files changed

+171
-9
lines changed

5 files changed

+171
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3838
- Add `QScopedMetaObjectConnectionGuard`, which is `QMetaObjectConnectionGuard` with a scoped lifetime. `QMetaObjectConnectionGuard` is now a type alias for `QScopedMetaObjectConnectionGuard<'static>`.
3939
- Support for setting Qt log message patterns with `q_set_message_pattern` and formatting log messages ith `q_format_log_message`.
4040
- Implement `IntoIterator` for `&QHash`, `&QList`, `&QMap`, `&QSet`, and `&QVector`.
41+
- Add `QByteArray:from_base64_encoding` and `QByteArray::to_base64`.
4142

4243
### Removed
4344

crates/cxx-qt-lib/include/core/qbytearray.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,20 @@ template<>
2020
struct IsRelocatable<QByteArray> : ::std::true_type
2121
{};
2222

23+
template<>
24+
struct IsRelocatable<QByteArray::FromBase64Result> : ::std::true_type
25+
{};
26+
2327
} // namespace rust
2428

2529
namespace rust {
2630
namespace cxxqtlib1 {
2731

32+
using QByteArrayBase64Option = QByteArray::Base64Option;
33+
using QByteArrayBase64Options = QByteArray::Base64Options;
34+
using QByteArrayBase64DecodingStatus = QByteArray::Base64DecodingStatus;
35+
using QByteArrayFromBase64Result = QByteArray::FromBase64Result;
36+
2837
QByteArray
2938
qbytearrayFromSliceU8(::rust::Slice<const ::std::uint8_t> slice);
3039
::rust::Vec<::std::uint8_t>
@@ -41,6 +50,9 @@ void
4150
qbytearrayAppend(QByteArray& byteArray, ::std::uint8_t ch);
4251
void
4352
qbytearrayFill(QByteArray& byteArray, ::std::uint8_t ch, ::rust::isize size);
53+
QByteArray::FromBase64Result
54+
qbytearrayFromBase64Encoding(const QByteArray& base64,
55+
QByteArray::Base64Options options);
4456
void
4557
qbytearrayInsert(QByteArray& byteArray, ::rust::isize pos, ::std::uint8_t ch);
4658
::rust::isize

crates/cxx-qt-lib/src/core/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
// SPDX-License-Identifier: MIT OR Apache-2.0
55

66
mod qbytearray;
7-
pub use qbytearray::QByteArray;
7+
pub use qbytearray::{
8+
QByteArray, QByteArrayBase64Option, QByteArrayBase64Options, QByteArrayFromBase64Error,
9+
};
810

911
mod qcoreapplication;
1012
pub use qcoreapplication::QCoreApplication;

crates/cxx-qt-lib/src/core/qbytearray.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,22 @@ static_assert(!::std::is_trivially_destructible<QByteArray>::value);
3434

3535
static_assert(QTypeInfo<QByteArray>::isRelocatable);
3636

37+
using QByteArrayFromBase64Result = QByteArray::FromBase64Result;
38+
assert_alignment_and_size(QByteArrayFromBase64Result, {
39+
QByteArray decoded;
40+
QByteArray::Base64DecodingStatus decodingStatus;
41+
});
42+
43+
static_assert(
44+
!::std::is_trivially_copy_assignable<QByteArray::FromBase64Result>::value);
45+
static_assert(
46+
!::std::is_trivially_copy_constructible<QByteArray::FromBase64Result>::value);
47+
48+
static_assert(
49+
!::std::is_trivially_destructible<QByteArray::FromBase64Result>::value);
50+
51+
static_assert(QTypeInfo<QByteArray::FromBase64Result>::isRelocatable);
52+
3753
namespace rust {
3854
namespace cxxqtlib1 {
3955

@@ -104,6 +120,13 @@ qbytearrayFill(QByteArray& byteArray, ::std::uint8_t ch, ::rust::isize size)
104120
#endif
105121
}
106122

123+
QByteArray::FromBase64Result
124+
qbytearrayFromBase64Encoding(const QByteArray& base64,
125+
QByteArray::Base64Options options)
126+
{
127+
return QByteArray::fromBase64Encoding(base64, options);
128+
}
129+
107130
void
108131
qbytearrayInsert(QByteArray& byteArray, ::rust::isize pos, ::std::uint8_t ch)
109132
{

crates/cxx-qt-lib/src/core/qbytearray.rs

Lines changed: 132 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,64 @@ use std::fmt;
77
use std::mem::MaybeUninit;
88
use std::str;
99

10+
use crate::{unsafe_impl_qflag, QFlags};
11+
1012
#[cxx::bridge]
1113
mod ffi {
12-
unsafe extern "C++" {
14+
/// This enum contains the options available for encoding and decoding Base64. Base64 is defined by [RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648).
15+
///
16+
/// An empty `QFlags<QByteArrayBase64Option>` will use the regular Base64 alphabet, called simply "base64".
17+
#[namespace = "rust::cxxqtlib1"]
18+
#[repr(u32)]
19+
enum QByteArrayBase64Option {
20+
/// An alternate alphabet, called "base64url", which replaces two characters in the alphabet to be more friendly to URLs.
21+
Base64UrlEncoding = 1,
22+
/// Omits adding the padding equal signs at the end of the encoded data.
23+
OmitTrailingEquals = 2,
24+
}
25+
26+
#[namespace = "rust::cxxqtlib1"]
27+
#[repr(i32)]
28+
enum QByteArrayBase64DecodingStatus {
29+
Ok,
30+
IllegalInputLength,
31+
IllegalCharacter,
32+
IllegalPadding,
33+
}
34+
35+
#[namespace = "rust::cxxqtlib1"]
36+
extern "C++" {
1337
include!("cxx-qt-lib/qbytearray.h");
1438

39+
type QByteArrayBase64DecodingStatus;
40+
type QByteArrayFromBase64Result = super::QByteArrayFromBase64Result;
41+
type QByteArrayBase64Option;
42+
type QByteArrayBase64Options = super::QByteArrayBase64Options;
43+
}
44+
45+
unsafe extern "C++" {
1546
type QByteArray = super::QByteArray;
1647

1748
/// Clears the contents of the byte array and makes it null.
18-
fn clear(self: &mut Self);
49+
fn clear(&mut self);
50+
1951
/// Returns `true` if the byte array has size 0; otherwise returns `false`.
2052
#[rust_name = "is_empty"]
21-
fn isEmpty(self: &Self) -> bool;
53+
fn isEmpty(&self) -> bool;
2254
/// Returns `true` if this byte array is lowercase, that is, if it's identical to its [`to_lower`](Self::to_lower) folding.
2355
#[rust_name = "is_lower"]
24-
fn isLower(self: &Self) -> bool;
56+
fn isLower(&self) -> bool;
2557
/// Returns `true` if this byte array is null; otherwise returns `false`.
2658
#[rust_name = "is_null"]
27-
fn isNull(self: &Self) -> bool;
59+
fn isNull(&self) -> bool;
2860
/// Returns `true` if this byte array is uppercase, that is, if it's identical to its [`to_upper`](Self::to_upper) folding.
2961
#[rust_name = "is_upper"]
30-
fn isUpper(self: &Self) -> bool;
62+
fn isUpper(&self) -> bool;
3163
/// Releases any memory not required to store the array's data.
32-
fn squeeze(self: &mut Self);
64+
fn squeeze(&mut self);
65+
/// Returns a copy of the byte array, encoded using the options `options`.
66+
#[rust_name = "to_base64"]
67+
fn toBase64(&self, options: QByteArrayBase64Options) -> QByteArray;
3368
}
3469

3570
#[namespace = "rust::cxxqtlib1"]
@@ -75,6 +110,12 @@ mod ffi {
75110
#[rust_name = "qbytearray_fill"]
76111
fn qbytearrayFill(bytearray: &mut QByteArray, ch: u8, size: isize);
77112
#[doc(hidden)]
113+
#[rust_name = "qbytearrray_from_base64_encoding"]
114+
fn qbytearrayFromBase64Encoding(
115+
base64: &QByteArray,
116+
options: QByteArrayBase64Options,
117+
) -> QByteArrayFromBase64Result;
118+
#[doc(hidden)]
78119
#[rust_name = "qbytearray_insert"]
79120
fn qbytearrayInsert(bytearray: &mut QByteArray, pos: isize, ch: u8);
80121
#[doc(hidden)]
@@ -107,6 +148,17 @@ mod ffi {
107148
}
108149
}
109150

151+
use ffi::QByteArrayBase64DecodingStatus;
152+
pub use ffi::QByteArrayBase64Option;
153+
154+
/// [`QFlags`] of [`QByteArrayBase64Option`].
155+
pub type QByteArrayBase64Options = QFlags<QByteArrayBase64Option>;
156+
unsafe_impl_qflag!(
157+
QByteArrayBase64Option,
158+
"rust::cxxqtlib1::QByteArrayBase64Options",
159+
u32
160+
);
161+
110162
/// The `QByteArray` class provides an array of bytes.
111163
///
112164
/// Qt Documentation: [QByteArray](https://doc.qt.io/qt/qbytearray.html#details)
@@ -247,6 +299,26 @@ impl QByteArray {
247299
ffi::qbytearray_fill(self, ch, size)
248300
}
249301

302+
/// Decodes the Base64 array `base64`, using the options defined by `options`.
303+
pub fn from_base64_encoding(
304+
base64: &Self,
305+
options: QByteArrayBase64Options,
306+
) -> Result<Self, QByteArrayFromBase64Error> {
307+
let result = ffi::qbytearrray_from_base64_encoding(base64, options);
308+
match result.decoding_status {
309+
QByteArrayBase64DecodingStatus::IllegalInputLength => {
310+
Err(QByteArrayFromBase64Error::IllegalInputLength)
311+
}
312+
QByteArrayBase64DecodingStatus::IllegalCharacter => {
313+
Err(QByteArrayFromBase64Error::IllegalCharacter)
314+
}
315+
QByteArrayBase64DecodingStatus::IllegalPadding => {
316+
Err(QByteArrayFromBase64Error::IllegalPadding)
317+
}
318+
_ => Ok(result.decoded),
319+
}
320+
}
321+
250322
/// Construct a `QByteArray` from a `bytes::Bytes` without a deep copy
251323
///
252324
/// # Safety
@@ -340,6 +412,40 @@ impl QByteArray {
340412
}
341413
}
342414

415+
#[repr(C)]
416+
struct QByteArrayFromBase64Result {
417+
decoded: QByteArray,
418+
decoding_status: QByteArrayBase64DecodingStatus,
419+
}
420+
421+
// Safety:
422+
//
423+
// Static checks on the C++ side to ensure the size is the same.
424+
unsafe impl ExternType for QByteArrayFromBase64Result {
425+
type Id = type_id!("rust::cxxqtlib1::QByteArrayFromBase64Result");
426+
type Kind = cxx::kind::Trivial;
427+
}
428+
429+
#[allow(clippy::enum_variant_names)]
430+
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
431+
pub enum QByteArrayFromBase64Error {
432+
IllegalInputLength = 1,
433+
IllegalCharacter,
434+
IllegalPadding,
435+
}
436+
437+
impl fmt::Display for QByteArrayFromBase64Error {
438+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
439+
f.write_str(match self {
440+
Self::IllegalInputLength => "illegal input length",
441+
Self::IllegalCharacter => "illegal character",
442+
Self::IllegalPadding => "illegal padding",
443+
})
444+
}
445+
}
446+
447+
impl std::error::Error for QByteArrayFromBase64Error {}
448+
343449
#[cfg(feature = "serde")]
344450
impl serde::Serialize for QByteArray {
345451
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
@@ -407,6 +513,24 @@ mod tests {
407513
assert_eq!(crate::serde_impl::roundtrip(&qbytearray), qbytearray)
408514
}
409515

516+
#[test]
517+
fn qbytearray_base64() {
518+
let qbytearray = QByteArray::from("KDAB");
519+
let options = QByteArrayBase64Options::default();
520+
let encoded = qbytearray.to_base64(options);
521+
let decoded = QByteArray::from_base64_encoding(&encoded, options);
522+
assert_eq!(decoded, Ok(qbytearray));
523+
}
524+
525+
#[test]
526+
fn qbytearray_base64_url() {
527+
let qbytearray = QByteArray::from("KDAB");
528+
let options = QByteArrayBase64Option::Base64UrlEncoding.into();
529+
let encoded = qbytearray.to_base64(options);
530+
let decoded = QByteArray::from_base64_encoding(&encoded, options);
531+
assert_eq!(decoded, Ok(qbytearray));
532+
}
533+
410534
#[cfg(feature = "bytes")]
411535
#[test]
412536
fn test_bytes() {
@@ -421,6 +545,6 @@ mod tests {
421545
#[test]
422546
fn test_display_fmt() {
423547
let qbytearray = QByteArray::from("KDAB");
424-
assert_eq!(format!("{:-<8}", qbytearray), "KDAB----")
548+
assert_eq!(format!("{qbytearray:-<8}"), "KDAB----")
425549
}
426550
}

0 commit comments

Comments
 (0)