mirror of
https://github.com/rosenpass/rosenpass.git
synced 2026-03-01 15:03:21 -08:00
Merge branch 'rosenpass:main' into main
This commit is contained in:
8
.github/workflows/qc.yaml
vendored
8
.github/workflows/qc.yaml
vendored
@@ -25,6 +25,14 @@ jobs:
|
|||||||
- name: Run ShellCheck
|
- name: Run ShellCheck
|
||||||
uses: ludeeus/action-shellcheck@master
|
uses: ludeeus/action-shellcheck@master
|
||||||
|
|
||||||
|
rustfmt:
|
||||||
|
name: Rust Format
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Run Rust Formatting Script
|
||||||
|
run: bash format_rust_code.sh --mode check
|
||||||
|
|
||||||
cargo-audit:
|
cargo-audit:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
115
format_rust_code.sh
Executable file
115
format_rust_code.sh
Executable file
@@ -0,0 +1,115 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Parse command line options
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--mode)
|
||||||
|
mode="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown option: $1"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check if mode is specified
|
||||||
|
if [ -z "$mode" ]; then
|
||||||
|
echo "Please specify the mode using --mode option. Valid modes are 'check' and 'fix'."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find all Markdown files in the current directory and its subdirectories
|
||||||
|
mapfile -t md_files < <(find . -type f -name "*.md")
|
||||||
|
|
||||||
|
count=0
|
||||||
|
# Iterate through each Markdown file
|
||||||
|
for file in "${md_files[@]}"; do
|
||||||
|
# Use awk to extract Rust code blocks enclosed within triple backticks
|
||||||
|
rust_code_blocks=$(awk '/```rust/{flag=1; next}/```/{flag=0} flag' "$file")
|
||||||
|
|
||||||
|
# Count the number of Rust code blocks
|
||||||
|
num_fences=$(awk '/```rust/{f=1} f{if(/```/){f=0; count++}} END{print count}' "$file")
|
||||||
|
|
||||||
|
if [ -n "$rust_code_blocks" ]; then
|
||||||
|
echo "Processing Rust code in $file"
|
||||||
|
# Iterate through each Rust code block
|
||||||
|
for ((i=1; i <= num_fences ; i++)); do
|
||||||
|
# Extract individual Rust code block using awk
|
||||||
|
current_rust_block=$(awk -v i="$i" '/```rust/{f=1; if (++count == i) next} f&&/```/{f=0;next} f' "$file")
|
||||||
|
# Variable to check if we have added the main function
|
||||||
|
add_main=0
|
||||||
|
# Check if the Rust code block is already inside a function
|
||||||
|
if ! echo "$current_rust_block" | grep -q "fn main()"; then
|
||||||
|
# If not, wrap it in a main function
|
||||||
|
current_rust_block=$'fn main() {\n'"$current_rust_block"$'\n}'
|
||||||
|
add_main=1
|
||||||
|
fi
|
||||||
|
if [ "$mode" == "check" ]; then
|
||||||
|
# Apply changes to the Rust code block
|
||||||
|
formatted_rust_code=$(echo "$current_rust_block" | rustfmt)
|
||||||
|
# Use rustfmt to format the Rust code block, remove first and last lines, and remove the first 4 spaces if added main function
|
||||||
|
if [ "$add_main" == 1 ]; then
|
||||||
|
formatted_rust_code=$(echo "$formatted_rust_code" | sed '1d;$d' | sed 's/^ //')
|
||||||
|
current_rust_block=$(echo "$current_rust_block" | sed '1d;')
|
||||||
|
current_rust_block=$(echo "$current_rust_block" | sed '$d')
|
||||||
|
fi
|
||||||
|
if [ "$formatted_rust_code" == "$current_rust_block" ]; then
|
||||||
|
echo "No changes needed in Rust code block $i in $file"
|
||||||
|
else
|
||||||
|
echo -e "\nChanges needed in Rust code block $i in $file:\n"
|
||||||
|
echo "$formatted_rust_code"
|
||||||
|
count=+1
|
||||||
|
fi
|
||||||
|
|
||||||
|
elif [ "$mode" == "fix" ]; then
|
||||||
|
# Replace current_rust_block with formatted_rust_code in the file
|
||||||
|
formatted_rust_code=$(echo "$current_rust_block" | rustfmt)
|
||||||
|
# Use rustfmt to format the Rust code block, remove first and last lines, and remove the first 4 spaces if added main function
|
||||||
|
if [ "$add_main" == 1 ]; then
|
||||||
|
formatted_rust_code=$(echo "$formatted_rust_code" | sed '1d;$d' | sed 's/^ //')
|
||||||
|
current_rust_block=$(echo "$current_rust_block" | sed '1d;')
|
||||||
|
current_rust_block=$(echo "$current_rust_block" | sed '$d')
|
||||||
|
fi
|
||||||
|
# Check if the formatted code is the same as the current Rust code block
|
||||||
|
if [ "$formatted_rust_code" == "$current_rust_block" ]; then
|
||||||
|
echo "No changes needed in Rust code block $i in $file"
|
||||||
|
else
|
||||||
|
echo "Formatting Rust code block $i in $file"
|
||||||
|
# Replace current_rust_block with formatted_rust_code in the file
|
||||||
|
# Use awk to find the line number of the pattern
|
||||||
|
|
||||||
|
start_line=$(grep -n "^\`\`\`rust" "$file" | sed -n "${i}p" | cut -d: -f1)
|
||||||
|
end_line=$(grep -n "^\`\`\`" "$file" | awk -F: -v start_line="$start_line" '$1 > start_line {print $1; exit;}')
|
||||||
|
|
||||||
|
if [ -n "$start_line" ] && [ -n "$end_line" ]; then
|
||||||
|
# Print lines before the Rust code block
|
||||||
|
head -n "$((start_line - 1))" "$file"
|
||||||
|
|
||||||
|
# Print the formatted Rust code block
|
||||||
|
echo "\`\`\`rust"
|
||||||
|
echo "$formatted_rust_code"
|
||||||
|
echo "\`\`\`"
|
||||||
|
|
||||||
|
# Print lines after the Rust code block
|
||||||
|
tail -n +"$((end_line + 1))" "$file"
|
||||||
|
else
|
||||||
|
# Rust code block not found or end line not found
|
||||||
|
cat "$file"
|
||||||
|
fi > tmpfile && mv tmpfile "$file"
|
||||||
|
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Unknown mode: $mode. Valid modes are 'check' and 'fix'."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# CI failure if changes are needed
|
||||||
|
if [ $count -gt 0 ]; then
|
||||||
|
echo "CI failed: Changes needed in Rust code blocks."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -87,6 +87,15 @@ pub enum Cli {
|
|||||||
force: bool,
|
force: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Deprecated - use gen-keys instead
|
||||||
|
#[allow(rustdoc::broken_intra_doc_links)]
|
||||||
|
#[allow(rustdoc::invalid_html_tags)]
|
||||||
|
Keygen {
|
||||||
|
// NOTE yes, the legacy keygen argument initially really accepted "privet-key", not "secret-key"!
|
||||||
|
/// public-key <PATH> private-key <PATH>
|
||||||
|
args: Vec<String>,
|
||||||
|
},
|
||||||
|
|
||||||
/// Validate a configuration
|
/// Validate a configuration
|
||||||
Validate { config_files: Vec<PathBuf> },
|
Validate { config_files: Vec<PathBuf> },
|
||||||
|
|
||||||
@@ -119,6 +128,40 @@ impl Cli {
|
|||||||
config::Rosenpass::example_config().store(config_file)?;
|
config::Rosenpass::example_config().store(config_file)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated - use gen-keys instead
|
||||||
|
Keygen { args } => {
|
||||||
|
log::warn!("The 'keygen' command is deprecated. Please use the 'gen-keys' command instead.");
|
||||||
|
|
||||||
|
let mut public_key: Option<PathBuf> = None;
|
||||||
|
let mut secret_key: Option<PathBuf> = None;
|
||||||
|
|
||||||
|
// Manual arg parsing, since clap wants to prefix flags with "--"
|
||||||
|
let mut args = args.into_iter();
|
||||||
|
loop {
|
||||||
|
match (args.next().as_ref().map(String::as_str), args.next()) {
|
||||||
|
(Some("private-key"), Some(opt)) | (Some("secret-key"), Some(opt)) => {
|
||||||
|
secret_key = Some(opt.into());
|
||||||
|
}
|
||||||
|
(Some("public-key"), Some(opt)) => {
|
||||||
|
public_key = Some(opt.into());
|
||||||
|
}
|
||||||
|
(Some(flag), _) => {
|
||||||
|
bail!("Unknown option `{}`", flag);
|
||||||
|
}
|
||||||
|
(_, _) => break,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if secret_key.is_none() {
|
||||||
|
bail!("private-key is required");
|
||||||
|
}
|
||||||
|
if public_key.is_none() {
|
||||||
|
bail!("public-key is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_and_save_keypair(secret_key.unwrap(), public_key.unwrap())?;
|
||||||
|
}
|
||||||
|
|
||||||
GenKeys {
|
GenKeys {
|
||||||
config_file,
|
config_file,
|
||||||
public_key,
|
public_key,
|
||||||
@@ -160,12 +203,7 @@ impl Cli {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generate the keys and store them in files
|
// generate the keys and store them in files
|
||||||
let mut ssk = crate::protocol::SSk::random();
|
generate_and_save_keypair(skf, pkf)?;
|
||||||
let mut spk = crate::protocol::SPk::random();
|
|
||||||
StaticKem::keygen(ssk.secret_mut(), spk.secret_mut())?;
|
|
||||||
|
|
||||||
ssk.store_secret(skf)?;
|
|
||||||
spk.store_secret(pkf)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ExchangeConfig { config_file } => {
|
ExchangeConfig { config_file } => {
|
||||||
@@ -246,3 +284,12 @@ impl Cli {
|
|||||||
srv.event_loop()
|
srv.event_loop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// generate secret and public keys, store in files according to the paths passed as arguments
|
||||||
|
fn generate_and_save_keypair(secret_key: PathBuf, public_key: PathBuf) -> anyhow::Result<()> {
|
||||||
|
let mut ssk = crate::protocol::SSk::random();
|
||||||
|
let mut spk = crate::protocol::SPk::random();
|
||||||
|
StaticKem::keygen(ssk.secret_mut(), spk.secret_mut())?;
|
||||||
|
ssk.store_secret(secret_key)?;
|
||||||
|
spk.store_secret(public_key)
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ use std::process::exit;
|
|||||||
|
|
||||||
/// Catches errors, prints them through the logger, then exits
|
/// Catches errors, prints them through the logger, then exits
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
env_logger::init();
|
// default to displaying warning and error log messages only
|
||||||
|
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init();
|
||||||
|
|
||||||
let res = attempt!({
|
let res = attempt!({
|
||||||
rosenpass_sodium::init()?;
|
rosenpass_sodium::init()?;
|
||||||
|
|||||||
109
to/README.md
109
to/README.md
@@ -12,15 +12,17 @@ The crate provides chained functions to simplify allocating the destination para
|
|||||||
For now this crate is experimental; patch releases are guaranteed not to contain any breaking changes, but minor releases may.
|
For now this crate is experimental; patch releases are guaranteed not to contain any breaking changes, but minor releases may.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use std::ops::BitXorAssign;
|
|
||||||
use rosenpass_to::{To, to, with_destination};
|
|
||||||
use rosenpass_to::ops::copy_array;
|
use rosenpass_to::ops::copy_array;
|
||||||
|
use rosenpass_to::{to, with_destination, To};
|
||||||
|
use std::ops::BitXorAssign;
|
||||||
|
|
||||||
// Destination functions return some value that implements the To trait.
|
// Destination functions return some value that implements the To trait.
|
||||||
// Unfortunately dealing with lifetimes is a bit more finicky than it would#
|
// Unfortunately dealing with lifetimes is a bit more finicky than it would#
|
||||||
// be without destination parameters
|
// be without destination parameters
|
||||||
fn xor_slice<'a, T>(src: &'a[T]) -> impl To<[T], ()> + 'a
|
fn xor_slice<'a, T>(src: &'a [T]) -> impl To<[T], ()> + 'a
|
||||||
where T: BitXorAssign + Clone {
|
where
|
||||||
|
T: BitXorAssign + Clone,
|
||||||
|
{
|
||||||
// Custom implementations of the to trait can be created, but the easiest
|
// Custom implementations of the to trait can be created, but the easiest
|
||||||
with_destination(move |dst: &mut [T]| {
|
with_destination(move |dst: &mut [T]| {
|
||||||
assert!(src.len() == dst.len());
|
assert!(src.len() == dst.len());
|
||||||
@@ -65,7 +67,7 @@ assert_eq!(&dst[..], &flip01[..]);
|
|||||||
// The builtin function copy_array supports to_value() since its
|
// The builtin function copy_array supports to_value() since its
|
||||||
// destination parameter is a fixed size array, which can be allocated
|
// destination parameter is a fixed size array, which can be allocated
|
||||||
// using default()
|
// using default()
|
||||||
let dst : [u8; 4] = copy_array(flip01).to_value();
|
let dst: [u8; 4] = copy_array(flip01).to_value();
|
||||||
assert_eq!(&dst, flip01);
|
assert_eq!(&dst, flip01);
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -84,7 +86,9 @@ Functions declared like this are more cumbersome to use when the destination par
|
|||||||
use std::ops::BitXorAssign;
|
use std::ops::BitXorAssign;
|
||||||
|
|
||||||
fn xor_slice<T>(dst: &mut [T], src: &[T])
|
fn xor_slice<T>(dst: &mut [T], src: &[T])
|
||||||
where T: BitXorAssign + Clone {
|
where
|
||||||
|
T: BitXorAssign + Clone,
|
||||||
|
{
|
||||||
assert!(src.len() == dst.len());
|
assert!(src.len() == dst.len());
|
||||||
for (d, s) in dst.iter_mut().zip(src.iter()) {
|
for (d, s) in dst.iter_mut().zip(src.iter()) {
|
||||||
*d ^= s.clone();
|
*d ^= s.clone();
|
||||||
@@ -114,8 +118,8 @@ assert_eq!(&dst[..], &flip01[..]);
|
|||||||
There are a couple of ways to use a function with destination:
|
There are a couple of ways to use a function with destination:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rosenpass_to::{to, To};
|
|
||||||
use rosenpass_to::ops::{copy_array, copy_slice_least};
|
use rosenpass_to::ops::{copy_array, copy_slice_least};
|
||||||
|
use rosenpass_to::{to, To};
|
||||||
|
|
||||||
let mut dst = b" ".to_vec();
|
let mut dst = b" ".to_vec();
|
||||||
|
|
||||||
@@ -129,7 +133,8 @@ copy_slice_least(b"This is fin").to(&mut dst[..]);
|
|||||||
assert_eq!(&dst[..], b"This is fin");
|
assert_eq!(&dst[..], b"This is fin");
|
||||||
|
|
||||||
// You can allocate the destination variable on the fly using `.to_this(...)`
|
// You can allocate the destination variable on the fly using `.to_this(...)`
|
||||||
let tmp = copy_slice_least(b"This is new---").to_this(|| b"This will be overwritten".to_owned());
|
let tmp =
|
||||||
|
copy_slice_least(b"This is new---").to_this(|| b"This will be overwritten".to_owned());
|
||||||
assert_eq!(&tmp[..], b"This is new---verwritten");
|
assert_eq!(&tmp[..], b"This is new---verwritten");
|
||||||
|
|
||||||
// You can allocate the destination variable on the fly `.collect(..)` if it implements default
|
// You can allocate the destination variable on the fly `.collect(..)` if it implements default
|
||||||
@@ -147,8 +152,11 @@ assert_eq!(&tmp[..], b"Fixed");
|
|||||||
The to crate provides basic functions with destination for copying data between slices and arrays.
|
The to crate provides basic functions with destination for copying data between slices and arrays.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
use rosenpass_to::ops::{
|
||||||
|
copy_array, copy_slice, copy_slice_least, copy_slice_least_src, try_copy_slice,
|
||||||
|
try_copy_slice_least_src,
|
||||||
|
};
|
||||||
use rosenpass_to::{to, To};
|
use rosenpass_to::{to, To};
|
||||||
use rosenpass_to::ops::{copy_array, copy_slice, copy_slice_least, copy_slice_least_src, try_copy_slice, try_copy_slice_least_src};
|
|
||||||
|
|
||||||
let mut dst = b" ".to_vec();
|
let mut dst = b" ".to_vec();
|
||||||
|
|
||||||
@@ -161,18 +169,33 @@ to(&mut dst[4..], copy_slice_least_src(b"!!!"));
|
|||||||
assert_eq!(&dst[..], b"Hell!!!orld");
|
assert_eq!(&dst[..], b"Hell!!!orld");
|
||||||
|
|
||||||
// Copy a slice, copying as many bytes as possible
|
// Copy a slice, copying as many bytes as possible
|
||||||
to(&mut dst[6..], copy_slice_least(b"xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"));
|
to(
|
||||||
|
&mut dst[6..],
|
||||||
|
copy_slice_least(b"xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"),
|
||||||
|
);
|
||||||
assert_eq!(&dst[..], b"Hell!!xxxxx");
|
assert_eq!(&dst[..], b"Hell!!xxxxx");
|
||||||
|
|
||||||
// Copy a slice, will return None and abort if the sizes do not much
|
// Copy a slice, will return None and abort if the sizes do not much
|
||||||
assert_eq!(Some(()), to(&mut dst[..], try_copy_slice(b"Hello World")));
|
assert_eq!(Some(()), to(&mut dst[..], try_copy_slice(b"Hello World")));
|
||||||
assert_eq!(None, to(&mut dst[..], try_copy_slice(b"---")));
|
assert_eq!(None, to(&mut dst[..], try_copy_slice(b"---")));
|
||||||
assert_eq!(None, to(&mut dst[..], try_copy_slice(b"---------------------")));
|
assert_eq!(
|
||||||
|
None,
|
||||||
|
to(&mut dst[..], try_copy_slice(b"---------------------"))
|
||||||
|
);
|
||||||
assert_eq!(&dst[..], b"Hello World");
|
assert_eq!(&dst[..], b"Hello World");
|
||||||
|
|
||||||
// Copy a slice, will return None and abort if source is longer than destination
|
// Copy a slice, will return None and abort if source is longer than destination
|
||||||
assert_eq!(Some(()), to(&mut dst[4..], try_copy_slice_least_src(b"!!!")));
|
assert_eq!(
|
||||||
assert_eq!(None, to(&mut dst[4..], try_copy_slice_least_src(b"-------------------------")));
|
Some(()),
|
||||||
|
to(&mut dst[4..], try_copy_slice_least_src(b"!!!"))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
None,
|
||||||
|
to(
|
||||||
|
&mut dst[4..],
|
||||||
|
try_copy_slice_least_src(b"-------------------------")
|
||||||
|
)
|
||||||
|
);
|
||||||
assert_eq!(&dst[..], b"Hell!!!orld");
|
assert_eq!(&dst[..], b"Hell!!!orld");
|
||||||
|
|
||||||
// Copy fixed size arrays all at once
|
// Copy fixed size arrays all at once
|
||||||
@@ -186,12 +209,14 @@ assert_eq!(&dst, b"Hello");
|
|||||||
The easiest way to declare a function with destination is to use the with_destination function.
|
The easiest way to declare a function with destination is to use the with_destination function.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rosenpass_to::{To, to, with_destination};
|
|
||||||
use rosenpass_to::ops::copy_array;
|
use rosenpass_to::ops::copy_array;
|
||||||
|
use rosenpass_to::{to, with_destination, To};
|
||||||
|
|
||||||
/// Copy the given slice to the start of a vector, reusing its memory if possible
|
/// Copy the given slice to the start of a vector, reusing its memory if possible
|
||||||
fn copy_to_vec<'a, T>(src: &'a [T]) -> impl To<Vec<T>, ()> + 'a
|
fn copy_to_vec<'a, T>(src: &'a [T]) -> impl To<Vec<T>, ()> + 'a
|
||||||
where T: Clone {
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
with_destination(move |dst: &mut Vec<T>| {
|
with_destination(move |dst: &mut Vec<T>| {
|
||||||
dst.clear();
|
dst.clear();
|
||||||
dst.extend_from_slice(src);
|
dst.extend_from_slice(src);
|
||||||
@@ -217,7 +242,9 @@ The same pattern can be implemented without `to`, at the cost of being slightly
|
|||||||
```rust
|
```rust
|
||||||
/// Copy the given slice to the start of a vector, reusing its memory if possible
|
/// Copy the given slice to the start of a vector, reusing its memory if possible
|
||||||
fn copy_to_vec<T>(dst: &mut Vec<T>, src: &[T])
|
fn copy_to_vec<T>(dst: &mut Vec<T>, src: &[T])
|
||||||
where T: Clone {
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
dst.clear();
|
dst.clear();
|
||||||
dst.extend_from_slice(src);
|
dst.extend_from_slice(src);
|
||||||
}
|
}
|
||||||
@@ -240,11 +267,11 @@ Alternative functions are returned, that return a `to::Beside` value, containing
|
|||||||
destination variable and the return value.
|
destination variable and the return value.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use std::cmp::{min, max};
|
use rosenpass_to::{to, with_destination, Beside, To};
|
||||||
use rosenpass_to::{To, to, with_destination, Beside};
|
use std::cmp::{max, min};
|
||||||
|
|
||||||
/// Copy an array of floats and calculate the average
|
/// Copy an array of floats and calculate the average
|
||||||
pub fn copy_and_average<'a>(src: &'a[f64]) -> impl To<[f64], f64> + 'a {
|
pub fn copy_and_average<'a>(src: &'a [f64]) -> impl To<[f64], f64> + 'a {
|
||||||
with_destination(move |dst: &mut [f64]| {
|
with_destination(move |dst: &mut [f64]| {
|
||||||
assert!(src.len() == dst.len());
|
assert!(src.len() == dst.len());
|
||||||
let mut sum = 0f64;
|
let mut sum = 0f64;
|
||||||
@@ -300,8 +327,8 @@ assert_eq!(tmp, Beside([42f64; 3], 42f64));
|
|||||||
When Beside values contain a `()`, `Option<()>`, or `Result<(), Error>` return value, they expose a special method called `.condense()`; this method consumes the Beside value and condenses destination and return value into one value.
|
When Beside values contain a `()`, `Option<()>`, or `Result<(), Error>` return value, they expose a special method called `.condense()`; this method consumes the Beside value and condenses destination and return value into one value.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
use rosenpass_to::Beside;
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
use rosenpass_to::{Beside};
|
|
||||||
|
|
||||||
assert_eq!((), Beside((), ()).condense());
|
assert_eq!((), Beside((), ()).condense());
|
||||||
|
|
||||||
@@ -318,8 +345,8 @@ assert_eq!(Err(()), Beside(42, err_unit).condense());
|
|||||||
When condense is implemented for a type, `.to_this(|| ...)`, `.to_value()`, and `.collect::<...>()` on the `To` trait can be used even with a return value:
|
When condense is implemented for a type, `.to_this(|| ...)`, `.to_value()`, and `.collect::<...>()` on the `To` trait can be used even with a return value:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
use rosenpass_to::ops::try_copy_slice;
|
||||||
use rosenpass_to::To;
|
use rosenpass_to::To;
|
||||||
use rosenpass_to::ops::try_copy_slice;;
|
|
||||||
|
|
||||||
let tmp = try_copy_slice(b"Hello World").collect::<[u8; 11]>();
|
let tmp = try_copy_slice(b"Hello World").collect::<[u8; 11]>();
|
||||||
assert_eq!(tmp, Some(*b"Hello World"));
|
assert_eq!(tmp, Some(*b"Hello World"));
|
||||||
@@ -337,8 +364,8 @@ assert_eq!(tmp, None);
|
|||||||
The same naturally also works for Results, but the example is a bit harder to motivate:
|
The same naturally also works for Results, but the example is a bit harder to motivate:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
use rosenpass_to::{to, with_destination, To};
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
use rosenpass_to::{to, To, with_destination};
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Default)]
|
#[derive(PartialEq, Eq, Debug, Default)]
|
||||||
struct InvalidFloat;
|
struct InvalidFloat;
|
||||||
@@ -380,8 +407,8 @@ Condensation is implemented through a trait called CondenseBeside ([local](Conde
|
|||||||
If you can not implement this trait because its for an external type (see [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type)), this crate welcomes contributions of new Condensation rules.
|
If you can not implement this trait because its for an external type (see [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type)), this crate welcomes contributions of new Condensation rules.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rosenpass_to::{To, with_destination, Beside, CondenseBeside};
|
|
||||||
use rosenpass_to::ops::copy_slice;
|
use rosenpass_to::ops::copy_slice;
|
||||||
|
use rosenpass_to::{with_destination, Beside, CondenseBeside, To};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Default)]
|
#[derive(PartialEq, Eq, Debug, Default)]
|
||||||
struct MyTuple<Left, Right>(Left, Right);
|
struct MyTuple<Left, Right>(Left, Right);
|
||||||
@@ -396,7 +423,10 @@ impl<Val, Right> CondenseBeside<Val> for MyTuple<(), Right> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn copy_slice_and_return_something<'a, T, U>(src: &'a [T], something: U) -> impl To<[T], U> + 'a
|
fn copy_slice_and_return_something<'a, T, U>(src: &'a [T], something: U) -> impl To<[T], U> + 'a
|
||||||
where T: Copy, U: 'a {
|
where
|
||||||
|
T: Copy,
|
||||||
|
U: 'a,
|
||||||
|
{
|
||||||
with_destination(move |dst: &mut [T]| {
|
with_destination(move |dst: &mut [T]| {
|
||||||
copy_slice(src).to(dst);
|
copy_slice(src).to(dst);
|
||||||
something
|
something
|
||||||
@@ -417,7 +447,7 @@ Using `with_destination(...)` is convenient, but since it uses closures it resul
|
|||||||
Implementing the ToTrait manual is the right choice for library use cases.
|
Implementing the ToTrait manual is the right choice for library use cases.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rosenpass_to::{to, To, with_destination};
|
use rosenpass_to::{to, with_destination, To};
|
||||||
|
|
||||||
struct TryCopySliceSource<'a, T: Copy> {
|
struct TryCopySliceSource<'a, T: Copy> {
|
||||||
src: &'a [T],
|
src: &'a [T],
|
||||||
@@ -425,17 +455,20 @@ struct TryCopySliceSource<'a, T: Copy> {
|
|||||||
|
|
||||||
impl<'a, T: Copy> To<[T], Option<()>> for TryCopySliceSource<'a, T> {
|
impl<'a, T: Copy> To<[T], Option<()>> for TryCopySliceSource<'a, T> {
|
||||||
fn to(self, dst: &mut [T]) -> Option<()> {
|
fn to(self, dst: &mut [T]) -> Option<()> {
|
||||||
(self.src.len() == dst.len())
|
(self.src.len() == dst.len()).then(|| dst.copy_from_slice(self.src))
|
||||||
.then(|| dst.copy_from_slice(self.src))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_copy_slice<'a, T>(src: &'a [T]) -> TryCopySliceSource<'a, T>
|
fn try_copy_slice<'a, T>(src: &'a [T]) -> TryCopySliceSource<'a, T>
|
||||||
where T: Copy {
|
where
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
TryCopySliceSource { src }
|
TryCopySliceSource { src }
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut dst = try_copy_slice(b"Hello World").collect::<[u8; 11]>().unwrap();
|
let mut dst = try_copy_slice(b"Hello World")
|
||||||
|
.collect::<[u8; 11]>()
|
||||||
|
.unwrap();
|
||||||
assert_eq!(&dst[..], b"Hello World");
|
assert_eq!(&dst[..], b"Hello World");
|
||||||
assert_eq!(None, to(&mut dst[..], try_copy_slice(b"---")));
|
assert_eq!(None, to(&mut dst[..], try_copy_slice(b"---")));
|
||||||
```
|
```
|
||||||
@@ -445,8 +478,8 @@ assert_eq!(None, to(&mut dst[..], try_copy_slice(b"---")));
|
|||||||
Destinations can also be used with methods. This example demonstrates using destinations in an extension trait for everything that implements `Borrow<[T]>` for any `T` and a concrete `To` trait implementation.
|
Destinations can also be used with methods. This example demonstrates using destinations in an extension trait for everything that implements `Borrow<[T]>` for any `T` and a concrete `To` trait implementation.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
use rosenpass_to::{to, with_destination, To};
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use rosenpass_to::{to, To, with_destination};
|
|
||||||
|
|
||||||
struct TryCopySliceSource<'a, T: Copy> {
|
struct TryCopySliceSource<'a, T: Copy> {
|
||||||
src: &'a [T],
|
src: &'a [T],
|
||||||
@@ -454,24 +487,24 @@ struct TryCopySliceSource<'a, T: Copy> {
|
|||||||
|
|
||||||
impl<'a, T: Copy> To<[T], Option<()>> for TryCopySliceSource<'a, T> {
|
impl<'a, T: Copy> To<[T], Option<()>> for TryCopySliceSource<'a, T> {
|
||||||
fn to(self, dst: &mut [T]) -> Option<()> {
|
fn to(self, dst: &mut [T]) -> Option<()> {
|
||||||
(self.src.len() == dst.len())
|
(self.src.len() == dst.len()).then(|| dst.copy_from_slice(self.src))
|
||||||
.then(|| dst.copy_from_slice(self.src))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait TryCopySliceExt<'a, T: Copy> {
|
trait TryCopySliceExt<'a, T: Copy> {
|
||||||
fn try_copy_slice(&'a self) -> TryCopySliceSource<'a, T>;
|
fn try_copy_slice(&'a self) -> TryCopySliceSource<'a, T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: 'a + Copy, Ref: 'a + Borrow<[T]>> TryCopySliceExt<'a, T> for Ref {
|
impl<'a, T: 'a + Copy, Ref: 'a + Borrow<[T]>> TryCopySliceExt<'a, T> for Ref {
|
||||||
fn try_copy_slice(&'a self) -> TryCopySliceSource<'a, T> {
|
fn try_copy_slice(&'a self) -> TryCopySliceSource<'a, T> {
|
||||||
TryCopySliceSource {
|
TryCopySliceSource { src: self.borrow() }
|
||||||
src: self.borrow()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut dst = b"Hello World".try_copy_slice().collect::<[u8; 11]>().unwrap();
|
let mut dst = b"Hello World"
|
||||||
|
.try_copy_slice()
|
||||||
|
.collect::<[u8; 11]>()
|
||||||
|
.unwrap();
|
||||||
assert_eq!(&dst[..], b"Hello World");
|
assert_eq!(&dst[..], b"Hello World");
|
||||||
assert_eq!(None, to(&mut dst[..], b"---".try_copy_slice()));
|
assert_eq!(None, to(&mut dst[..], b"---".try_copy_slice()));
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user