doc-test fixup
cargo test by default compiles and runs all code snippets found in the documentation... oops... Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
cc84a830c5
commit
c05a8c8d18
|
@ -7,7 +7,7 @@
|
||||||
//! implementing `AsyncRead`, which, however, without async/await cannot be methods due to them
|
//! implementing `AsyncRead`, which, however, without async/await cannot be methods due to them
|
||||||
//! having non-static lifetimes in that case.
|
//! having non-static lifetimes in that case.
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```ignore
|
||||||
//! use std::io;
|
//! use std::io;
|
||||||
//!
|
//!
|
||||||
//! use crate::tools::io::read_exact_allocated;
|
//! use crate::tools::io::read_exact_allocated;
|
||||||
|
@ -23,8 +23,8 @@
|
||||||
//! tokio::io::read_exact(file, unsafe { buffer.grow_uninitialized(1024) })
|
//! tokio::io::read_exact(file, unsafe { buffer.grow_uninitialized(1024) })
|
||||||
//! })
|
//! })
|
||||||
//! .and_then(|(_file, bigger_buffer)| {
|
//! .and_then(|(_file, bigger_buffer)| {
|
||||||
//! use_the(bigger_buffer);
|
//! // use bigger_buffer
|
||||||
//! Ok(bigger_buffer)
|
//! Ok(())
|
||||||
//! });
|
//! });
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
|
@ -55,11 +55,14 @@ pub mod ops;
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```
|
/// ```
|
||||||
|
/// # use futures::future::Future;
|
||||||
|
/// # use proxmox_backup::tools::io::*;
|
||||||
/// tokio::fs::File::open("some.file")
|
/// tokio::fs::File::open("some.file")
|
||||||
/// .and_then(|file| read_exact_allocated(file, 1024))
|
/// .and_then(|file| read_exact_allocated(file, 1024))
|
||||||
/// .and_then(|(_file, data)| {
|
/// .and_then(|(_file, data)| {
|
||||||
/// use_the(data);
|
/// // use data
|
||||||
/// })
|
/// Ok(())
|
||||||
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
pub fn read_exact_allocated<R: AsyncRead>(reader: R, size: usize) -> ReadExactAllocated<R> {
|
pub fn read_exact_allocated<R: AsyncRead>(reader: R, size: usize) -> ReadExactAllocated<R> {
|
||||||
ReadExactAllocated(Some(reader), None, size)
|
ReadExactAllocated(Some(reader), None, size)
|
||||||
|
@ -121,13 +124,16 @@ impl<R: AsyncRead> Future for ReadExactAllocated<R> {
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```
|
/// ```
|
||||||
|
/// # use futures::future::Future;
|
||||||
|
/// # use proxmox_backup::tools::io::*;
|
||||||
/// tokio::fs::File::open("some.file")
|
/// tokio::fs::File::open("some.file")
|
||||||
/// .and_then(|file| append_to_vec(file, Vec::new(), 1024))
|
/// .and_then(|file| append_to_vec(file, Vec::new(), 1024))
|
||||||
/// .and_then(|(_file, data, size)| {
|
/// .and_then(|(_file, data, size)| {
|
||||||
/// assert!(data.len() == size);
|
/// assert!(data.len() == size);
|
||||||
/// println!("Actually got {} bytes of data.", size);
|
/// println!("Actually got {} bytes of data.", size);
|
||||||
/// use_the(data);
|
/// // use the data
|
||||||
/// })
|
/// Ok(())
|
||||||
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
pub fn append_to_vec<R, V>(reader: R, mut vector: V, size: usize) -> AppendToVec<R, V>
|
pub fn append_to_vec<R, V>(reader: R, mut vector: V, size: usize) -> AppendToVec<R, V>
|
||||||
where
|
where
|
||||||
|
@ -188,13 +194,15 @@ where
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```
|
/// ```
|
||||||
|
/// # use futures::future::Future;
|
||||||
|
/// # use proxmox_backup::tools::io::*;
|
||||||
/// tokio::fs::File::open("some.file")
|
/// tokio::fs::File::open("some.file")
|
||||||
/// .and_then(|file| append_exact_to_vec(file, Vec::new(), 1024))
|
/// .and_then(|file| append_exact_to_vec(file, Vec::new(), 1024))
|
||||||
/// .and_then(|(_file, data)| {
|
/// .and_then(|(_file, data)| {
|
||||||
/// assert!(data.len() == size);
|
/// assert!(data.len() == 1024);
|
||||||
/// println!("Actually got {} bytes of data.", size);
|
/// // use data
|
||||||
/// use_the(data);
|
/// Ok(())
|
||||||
/// })
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
pub fn append_exact_to_vec<R, V>(reader: R, mut vector: V, size: usize) -> AppendExactToVec<R, V>
|
pub fn append_exact_to_vec<R, V>(reader: R, mut vector: V, size: usize) -> AppendExactToVec<R, V>
|
||||||
where
|
where
|
||||||
|
|
|
@ -15,22 +15,28 @@ use crate::tools::vec::{self, ops::*};
|
||||||
/// values of a specific endianess (types implementing [`Endian`]).
|
/// values of a specific endianess (types implementing [`Endian`]).
|
||||||
///
|
///
|
||||||
/// Examples:
|
/// Examples:
|
||||||
/// ```
|
/// ```no_run
|
||||||
/// use crate::tools::io::ops::*;
|
/// use proxmox_backup::tools::io::ops::*;
|
||||||
///
|
///
|
||||||
|
/// # fn code() -> std::io::Result<()> {
|
||||||
/// let mut file = std::fs::File::open("some.data")?;
|
/// let mut file = std::fs::File::open("some.data")?;
|
||||||
///
|
///
|
||||||
/// // read some bytes into a newly allocated Vec<u8>:
|
/// // read some bytes into a newly allocated Vec<u8>:
|
||||||
/// let mut data = file.read_exact_allocated(header.data_size as usize)?;
|
/// let mut data = file.read_exact_allocated(64)?;
|
||||||
///
|
///
|
||||||
/// // appending data to a vector:
|
/// // appending data to a vector:
|
||||||
/// let actually_appended = file.append_to_vec(&mut data, length)?; // .read() version
|
/// let actually_appended = file.append_to_vec(&mut data, 64)?; // .read() version
|
||||||
/// file.append_exact_to_vec(&mut data, length)?; // .read_exact() version
|
/// file.append_exact_to_vec(&mut data, 64)?; // .read_exact() version
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Or for reading values of a defined representation and endianess:
|
/// Or for reading values of a defined representation and endianess:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```no_run
|
||||||
|
/// # use endian_trait::Endian;
|
||||||
|
/// # use proxmox_backup::tools::io::ops::*;
|
||||||
|
///
|
||||||
/// #[derive(Endian)]
|
/// #[derive(Endian)]
|
||||||
/// #[repr(C)]
|
/// #[repr(C)]
|
||||||
/// struct Header {
|
/// struct Header {
|
||||||
|
@ -38,15 +44,18 @@ use crate::tools::vec::{self, ops::*};
|
||||||
/// data_size: u16,
|
/// data_size: u16,
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
|
/// # fn code(mut file: std::fs::File) -> std::io::Result<()> {
|
||||||
/// // We have given `Header` a proper binary representation via `#[repr]`, so this is safe:
|
/// // We have given `Header` a proper binary representation via `#[repr]`, so this is safe:
|
||||||
/// let header: Header = unsafe { file.read_le_value()? };
|
/// let header: Header = unsafe { file.read_le_value()? };
|
||||||
/// let mut blob = file.read_exact_allocated(header.data_size as usize)?;
|
/// let mut blob = file.read_exact_allocated(header.data_size as usize)?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
|
/// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
|
||||||
pub trait ReadExtOps {
|
pub trait ReadExtOps {
|
||||||
/// Read data into a newly allocated vector. This is a shortcut for:
|
/// Read data into a newly allocated vector. This is a shortcut for:
|
||||||
/// ```
|
/// ```ignore
|
||||||
/// let mut data = Vec::with_capacity(len);
|
/// let mut data = Vec::with_capacity(len);
|
||||||
/// unsafe {
|
/// unsafe {
|
||||||
/// data.set_len(len);
|
/// data.set_len(len);
|
||||||
|
@ -55,10 +64,12 @@ pub trait ReadExtOps {
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// With this trait, we just use:
|
/// With this trait, we just use:
|
||||||
/// ```
|
/// ```no_run
|
||||||
/// use crate::tools::vec::ops::*;
|
/// use proxmox_backup::tools::io::ops::*;
|
||||||
///
|
/// # fn code(mut reader: std::fs::File, len: usize) -> std::io::Result<()> {
|
||||||
/// let data = reader.read_exact_allocated(len);
|
/// let data = reader.read_exact_allocated(len)?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
fn read_exact_allocated(&mut self, size: usize) -> io::Result<Vec<u8>>;
|
fn read_exact_allocated(&mut self, size: usize) -> io::Result<Vec<u8>>;
|
||||||
|
|
||||||
|
@ -76,8 +87,9 @@ pub trait ReadExtOps {
|
||||||
/// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore
|
/// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore
|
||||||
/// this is considered unsafe.
|
/// this is considered unsafe.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```no_run
|
||||||
/// use crate::tools::vec::ops::*;
|
/// # use endian_trait::Endian;
|
||||||
|
/// use proxmox_backup::tools::io::ops::*;
|
||||||
///
|
///
|
||||||
/// #[derive(Endian)]
|
/// #[derive(Endian)]
|
||||||
/// #[repr(C, packed)]
|
/// #[repr(C, packed)]
|
||||||
|
@ -86,10 +98,13 @@ pub trait ReadExtOps {
|
||||||
/// count: u32,
|
/// count: u32,
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
|
/// # fn code() -> std::io::Result<()> {
|
||||||
/// let mut file = std::fs::File::open("my-raw.dat")?;
|
/// let mut file = std::fs::File::open("my-raw.dat")?;
|
||||||
/// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can
|
/// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can
|
||||||
/// // safely use our helper:
|
/// // safely use our helper:
|
||||||
/// let data: Data = unsafe { file.read_host_value()? };
|
/// let data: Data = unsafe { file.read_host_value()? };
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
|
/// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
|
||||||
|
@ -104,8 +119,9 @@ pub trait ReadExtOps {
|
||||||
/// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore
|
/// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore
|
||||||
/// this is considered unsafe.
|
/// this is considered unsafe.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```no_run
|
||||||
/// use crate::tools::vec::ops::*;
|
/// # use endian_trait::Endian;
|
||||||
|
/// use proxmox_backup::tools::io::ops::*;
|
||||||
///
|
///
|
||||||
/// #[derive(Endian)]
|
/// #[derive(Endian)]
|
||||||
/// #[repr(C, packed)]
|
/// #[repr(C, packed)]
|
||||||
|
@ -114,10 +130,13 @@ pub trait ReadExtOps {
|
||||||
/// count: u32,
|
/// count: u32,
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
|
/// # fn code() -> std::io::Result<()> {
|
||||||
/// let mut file = std::fs::File::open("my-little-endian.dat")?;
|
/// let mut file = std::fs::File::open("my-little-endian.dat")?;
|
||||||
/// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can
|
/// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can
|
||||||
/// // safely use our helper:
|
/// // safely use our helper:
|
||||||
/// let data: Data = unsafe { file.read_le_value()? };
|
/// let data: Data = unsafe { file.read_le_value()? };
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
|
/// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
|
||||||
|
@ -132,8 +151,9 @@ pub trait ReadExtOps {
|
||||||
/// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore
|
/// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore
|
||||||
/// this is considered unsafe.
|
/// this is considered unsafe.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```no_run
|
||||||
/// use crate::tools::vec::ops::*;
|
/// # use endian_trait::Endian;
|
||||||
|
/// use proxmox_backup::tools::io::ops::*;
|
||||||
///
|
///
|
||||||
/// #[derive(Endian)]
|
/// #[derive(Endian)]
|
||||||
/// #[repr(C, packed)]
|
/// #[repr(C, packed)]
|
||||||
|
@ -142,10 +162,13 @@ pub trait ReadExtOps {
|
||||||
/// count: u32,
|
/// count: u32,
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
|
/// # fn code() -> std::io::Result<()> {
|
||||||
/// let mut file = std::fs::File::open("my-big-endian.dat")?;
|
/// let mut file = std::fs::File::open("my-big-endian.dat")?;
|
||||||
/// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can
|
/// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can
|
||||||
/// // safely use our helper:
|
/// // safely use our helper:
|
||||||
/// let data: Data = unsafe { file.read_be_value()? };
|
/// let data: Data = unsafe { file.read_be_value()? };
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
|
/// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! Byte vector helpers.
|
//! Byte vector helpers.
|
||||||
//!
|
//!
|
||||||
//! We have a lot of I/O code such as:
|
//! We have a lot of I/O code such as:
|
||||||
//! ```
|
//! ```ignore
|
||||||
//! let mut buffer = vec![0u8; header_size];
|
//! let mut buffer = vec![0u8; header_size];
|
||||||
//! file.read_exact(&mut buffer)?;
|
//! file.read_exact(&mut buffer)?;
|
||||||
//! ```
|
//! ```
|
||||||
|
@ -16,16 +16,18 @@
|
||||||
//! in the [`tools::io`](crate::tools::io) module.
|
//! in the [`tools::io`](crate::tools::io) module.
|
||||||
//!
|
//!
|
||||||
//! Examples:
|
//! Examples:
|
||||||
//! ```
|
//! ```no_run
|
||||||
//! use crate::tools::vec::{self, ops::*};
|
//! use proxmox_backup::tools::vec::{self, ops::*};
|
||||||
//!
|
//!
|
||||||
|
//! # let size = 64usize;
|
||||||
|
//! # let more = 64usize;
|
||||||
//! let mut buffer = vec::undefined(size); // A zero-initialized buffer with valgrind support
|
//! let mut buffer = vec::undefined(size); // A zero-initialized buffer with valgrind support
|
||||||
//!
|
//!
|
||||||
//! let mut buffer = unsafe { vec::uninitialized(size) }; // an actually uninitialized buffer
|
//! let mut buffer = unsafe { vec::uninitialized(size) }; // an actually uninitialized buffer
|
||||||
//! vec::clear(&mut buffer); // zero out an &mut [u8]
|
//! vec::clear(&mut buffer); // zero out an &mut [u8]
|
||||||
//!
|
//!
|
||||||
//! vec::clear(unsafe {
|
//! vec::clear(unsafe {
|
||||||
//! buffer.grow_unintialized(more); // grow the buffer with uninitialized bytes
|
//! buffer.grow_uninitialized(more) // grow the buffer with uninitialized bytes
|
||||||
//! });
|
//! });
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
@ -34,8 +36,9 @@ pub mod ops;
|
||||||
/// Create an uninitialized byte vector of a specific size.
|
/// Create an uninitialized byte vector of a specific size.
|
||||||
///
|
///
|
||||||
/// This is just a shortcut for:
|
/// This is just a shortcut for:
|
||||||
/// ```
|
/// ```no_run
|
||||||
/// let mut v = Vec::with_capacity(len);
|
/// # let len = 64usize;
|
||||||
|
/// let mut v = Vec::<u8>::with_capacity(len);
|
||||||
/// unsafe {
|
/// unsafe {
|
||||||
/// v.set_len(len);
|
/// v.set_len(len);
|
||||||
/// }
|
/// }
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
//!
|
//!
|
||||||
//! Example:
|
//! Example:
|
||||||
//! ```
|
//! ```
|
||||||
//! use crate::tools::vec::{self, ops::*};
|
//! # use std::io::Read;
|
||||||
|
//! use proxmox_backup::tools::vec::{self, ops::*};
|
||||||
//!
|
//!
|
||||||
//! fn append_1024_to_vec<T: Read>(input: T, buffer: &mut Vec<u8>) -> std::io::Result<()> {
|
//! fn append_1024_to_vec<T: Read>(mut input: T, buffer: &mut Vec<u8>) -> std::io::Result<()> {
|
||||||
//! input.read_exact(unsafe { buffer.grow_uninitialized(1024) })
|
//! input.read_exact(unsafe { buffer.grow_uninitialized(1024) })
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
@ -12,19 +13,22 @@
|
||||||
/// Some additional byte vector operations useful for I/O code.
|
/// Some additional byte vector operations useful for I/O code.
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```
|
/// ```
|
||||||
/// use crate::tools::vec::{self, ops::*};
|
/// # use std::io::Read;
|
||||||
|
/// # use proxmox_backup::tools::io::{self, ops::*};
|
||||||
|
/// use proxmox_backup::tools::vec::{self, ops::*};
|
||||||
///
|
///
|
||||||
/// let mut data = file.read_exact_allocated(1024)?;
|
/// # fn code(mut file: std::fs::File, mut data: Vec<u8>) -> std::io::Result<()> {
|
||||||
/// do_something();
|
|
||||||
/// file.read_exact(unsafe {
|
/// file.read_exact(unsafe {
|
||||||
/// data.grow_uninitialized(1024);
|
/// data.grow_uninitialized(1024)
|
||||||
/// })?;
|
/// })?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Note that this module also provides a safe alternative for the case where
|
/// Note that this module also provides a safe alternative for the case where
|
||||||
/// `grow_uninitialized()` is directly followed by a `read_exact()` call via the [`ReadExtOps`]
|
/// `grow_uninitialized()` is directly followed by a `read_exact()` call via the [`ReadExtOps`]
|
||||||
/// trait:
|
/// trait:
|
||||||
/// ```
|
/// ```ignore
|
||||||
/// file.append_to_vec(&mut data, 1024)?;
|
/// file.append_to_vec(&mut data, 1024)?;
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
@ -35,7 +39,7 @@ pub trait VecU8ExtOps {
|
||||||
/// slice.
|
/// slice.
|
||||||
///
|
///
|
||||||
/// This is a shortcut for:
|
/// This is a shortcut for:
|
||||||
/// ```
|
/// ```ignore
|
||||||
/// vec.reserve(more);
|
/// vec.reserve(more);
|
||||||
/// let total = vec.len() + more;
|
/// let total = vec.len() + more;
|
||||||
/// unsafe {
|
/// unsafe {
|
||||||
|
@ -45,12 +49,17 @@ pub trait VecU8ExtOps {
|
||||||
///
|
///
|
||||||
/// This returns a mutable slice to the newly allocated space, so it can be used inline:
|
/// This returns a mutable slice to the newly allocated space, so it can be used inline:
|
||||||
/// ```
|
/// ```
|
||||||
|
/// # use std::io::Read;
|
||||||
|
/// # use proxmox_backup::tools::vec::ops::*;
|
||||||
|
/// # fn test(mut file: std::fs::File, buffer: &mut Vec<u8>) -> std::io::Result<()> {
|
||||||
/// file.read_exact(unsafe { buffer.grow_uninitialized(1024) })?;
|
/// file.read_exact(unsafe { buffer.grow_uninitialized(1024) })?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Although for the above case it is recommended to use the even shorter version from the
|
/// Although for the above case it is recommended to use the even shorter version from the
|
||||||
/// [`ReadExtOps`] trait:
|
/// [`ReadExtOps`] trait:
|
||||||
/// ```
|
/// ```ignore
|
||||||
/// // use crate::tools::vec::ops::ReadExtOps;
|
/// // use crate::tools::vec::ops::ReadExtOps;
|
||||||
/// file.append_to_vec(&mut buffer, 1024)?;
|
/// file.append_to_vec(&mut buffer, 1024)?;
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -59,7 +68,7 @@ pub trait VecU8ExtOps {
|
||||||
unsafe fn grow_uninitialized(&mut self, more: usize) -> &mut [u8];
|
unsafe fn grow_uninitialized(&mut self, more: usize) -> &mut [u8];
|
||||||
|
|
||||||
/// Resize a vector to a specific size without initializing its data. This is a shortcut for:
|
/// Resize a vector to a specific size without initializing its data. This is a shortcut for:
|
||||||
/// ```
|
/// ```ignore
|
||||||
/// if new_size <= vec.len() {
|
/// if new_size <= vec.len() {
|
||||||
/// vec.truncate(new_size);
|
/// vec.truncate(new_size);
|
||||||
/// } else {
|
/// } else {
|
||||||
|
|
Loading…
Reference in New Issue