From 654777a75c4e71c2ec4dbe7be135bbce8cd15669 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Mon, 2 May 2022 11:24:13 +0100 Subject: [PATCH] utils: Tidy up and move DigestRead and DigestWrite Now this file is more navigable. Signed-off-by: Ian Jackson --- src/bundles.rs | 2 +- src/digestrw.rs | 93 ++++++ src/lib.rs | 1 + src/prelude.rs | 1 + src/utils.rs | 853 +++++++++++++++++++++++------------------------- 5 files changed, 498 insertions(+), 452 deletions(-) create mode 100644 src/digestrw.rs diff --git a/src/bundles.rs b/src/bundles.rs index d59d1276..be2cdd5c 100644 --- a/src/bundles.rs +++ b/src/bundles.rs @@ -7,7 +7,7 @@ use crate::prelude::*; //---------- public types ---------- pub use crate::prelude::Sha512_256 as Digester; -pub type DigestWrite = crate::utils::DigestWrite; +pub type DigestWrite = digestrw::DigestWrite; #[derive(Copy,Clone,Hash,Eq,PartialEq,Serialize,Deserialize)] pub struct Hash(pub [u8; 32]); diff --git a/src/digestrw.rs b/src/digestrw.rs new file mode 100644 index 00000000..eb8a14a4 --- /dev/null +++ b/src/digestrw.rs @@ -0,0 +1,93 @@ +// Copyright 2020-2021 Ian Jackson and contributors to Otter +// SPDX-License-Identifier: AGPL-3.0-or-later +// There is NO WARRANTY. + +use crate::imports::*; +use crate::prelude::*; + +#[derive(Debug,Copy,Clone)] +pub struct DigestRead { + d: D, + r: R, +} + +impl DigestRead { + pub fn new(r: R) -> Self { DigestRead { r, d: D::new() } } + pub fn into_inner(self) -> (D, R) { (self.d, self.r) } + pub fn finish(self) -> digest::Output { + self.d.finalize() + } +} + +impl Read for DigestRead { + #[throws(io::Error)] + fn read(&mut self, buf: &mut [u8]) -> usize { + let count = self.r.read(buf)?; + self.d.update(&buf[0..count]); + count + } +} + +#[test] +#[cfg(not(miri))] +fn test_digest_read() { + let ibuffer = b"abc"; + let exp = Sha512_256::digest(&ibuffer[..]); + let inner = &ibuffer[..]; + let mut dr = DigestRead::::new(inner); + let mut obuffer = [0;4]; + assert_eq!( dr.read(&mut obuffer).unwrap(), 3 ); + assert_eq!( &obuffer, b"abc\0" ); + let got = dr.finish(); + assert_eq!( got, exp ); +} + +#[derive(Debug,Copy,Clone)] +pub struct DigestWrite { + d: D, + w: W, +} + +impl DigestWrite { + pub fn new(w: W) -> Self { DigestWrite { w, d: D::new() } } + pub fn into_inner(self) -> (D, W) { (self.d, self.w) } + pub fn finish(self) -> (digest::Output, W) { + (self.d.finalize(), self.w) + } +} +impl DigestWrite { + pub fn sink() -> Self { DigestWrite::new(io::sink()) } + + #[throws(io::Error)] + pub fn of(r: &mut R) -> digest::Output where R: Read { + let mut dw = DigestWrite::::sink(); + io::copy(r, &mut dw)?; + dw.finish().0 + } +} + +impl Write for DigestWrite { + #[throws(io::Error)] + fn write(&mut self, buf: &[u8]) -> usize { + let count = self.w.write(buf)?; + self.d.update(&buf[0..count]); + count + } + #[throws(io::Error)] + fn flush(&mut self) { self.w.flush()? } +} + +#[test] +#[cfg(not(miri))] +fn test_digest_write() { + let ibuffer = b"xyz"; + let exp = Sha512_256::digest(&ibuffer[..]); + let mut obuffer = [0;4]; + let inner = &mut obuffer[..]; + let mut dw = bundles::DigestWrite::new(inner); + assert_eq!( dw.write(&ibuffer[..]).unwrap(), 3); + let (got, recov) = dw.finish(); + assert_eq!( recov, b"\0" ); + assert_eq!( got, exp ); + assert_eq!( &obuffer, b"xyz\0" ); +} diff --git a/src/lib.rs b/src/lib.rs index 38ec79cf..af864b19 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,7 @@ pub mod config; pub mod currency; pub mod deck; pub mod dice; +pub mod digestrw; pub mod debugmutex; pub mod debugreader; pub mod error; diff --git a/src/prelude.rs b/src/prelude.rs index a669c99c..661c03a0 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -154,6 +154,7 @@ pub use crate::commands::{ProgressUpdateMode}; pub use crate::config::*; pub use crate::debugmutex::DebugIdentify; pub use crate::debugreader::DebugReader; +pub use crate::digestrw::{self, *}; pub use crate::error::*; pub use crate::fake_rng::*; pub use crate::fake_time::*; diff --git a/src/utils.rs b/src/utils.rs index b9e1b151..f10e8418 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -5,22 +5,44 @@ use crate::imports::*; use crate::prelude::*; -#[macro_export] -macro_rules! ensure_eq { - ($v1:expr, $v2:expr) => { - ({ - let v1 = &$v1; - let v2 = &$v2; - if v1 != v2 { - Err(anyhow!("ensure_eq failed: {} != {}: {:?} != {:?}", - stringify!($v1), stringify!($v2), - v1, v2)) - } else { - Ok(()) - } - }?) +/* +put trait OptionExt { + type Output; + fn get_or_try_insert_with< + E: Error, + F: FnOnce() -> Result, + >(&mut self, f: F) -> Result<&mut Output, E>; +} + +impl OptionExt for Option { + type Output = T; + fn get_or_try_insert_with + (&mut self, f: F) -> Result<&mut Output, E> + where E: Error, F: FnOnce() -> Result, + { + if self.is_none() { + *self = Some(f()?); + } + Ok(self.as_mut().unwrap()) } } +*/ + +//========== miscellany ========== +// (roughly in order of implementation length) + +pub fn is_default(t: &T) -> bool { t == &T::DEFAULT } + +// TODO: this is not used anywhere! +#[derive(Error,Clone,Copy,Debug,Eq,PartialEq,Serialize,Deserialize)] +#[error("error parsing Z coordinate")] +pub struct FooParseError; + +#[ext(pub, name=SeekExt)] +impl T { + #[throws(io::Error)] + fn rewind(&mut self) { self.seek(io::SeekFrom::Start(0))? } +} #[ext(pub, name=OrdExt)] impl T { @@ -39,6 +61,148 @@ impl str { } } +#[derive(Debug,Clone)] +pub struct JsonString(pub T); +impl Serialize for JsonString where T:Serialize { + #[throws(S::Error)] + fn serialize(&self, s: S) -> S::Ok where S:Serializer { + let json = serde_json::to_string(&self.0) + .map_err(|e| ::custom(e))?; + Serialize::serialize(&json, s)? + } +} + +#[throws(Either)] +pub fn io_copy_interactive(read: &mut BufReader, write: &mut W) +where R: Read, W: Write { + loop { + let buf = read.fill_buf().map_err(Either::Left)?; + if buf.len() == 0 { break } + + let did = (||{ + let did = write.write(buf)?; + if did == 0 { throw!(ErrorKind::WriteZero) } + Ok::<_,io::Error>(did) + })().map_err(Either::Right)?; + + read.consume(did); + write.flush().map_err(Either::Right)?; + } +} + +/// Allows the use of serde for a compat struct +/// +/// Ideally we would have +/// ```rust ignore +/// #[derive(Deserialize)] +/// #[serde(try_from=Compat)] +/// struct Main { /* new definition */ } +/// +/// #[derive(Deserialize)] +/// #[serde(untagged)] +/// enum Compat { V1(Main), V2(Old) } +/// +/// #[derive(Deserialize)] +/// struct Old { /* old version we still want to read */ } +/// +/// impl TryFrom for Main { /* ... */ } +/// ``` +/// +/// But the impl for `Compat` ends up honouring the `try_from` on `Main` +/// so is recursive. We solve that abusing serde's remote feature. +/// +/// For an example, see `IOccultIlk`. +/// +/// The name of the main structure must be passed twice, once as an +/// identifier and once as a literal, because `stringify!` doesn't work +/// in the serde attribute. +#[macro_export] +macro_rules! serde_with_compat { { + [ $( #[ $($attrs:meta)* ] )* ] [ $vis:vis ] [ $($intro:tt)* ] + $main:ident=$main_s:literal $new:ident $compat_s:literal + [ $($body:tt)* ] +} => { + $(#[ $($attrs)* ])* + #[serde(try_from=$compat_s)] + $vis $($intro)* $main $($body)* + + #[allow(non_camel_case_types)] + $(#[ $($attrs)* ])* + #[serde(remote=$main_s)] + $($intro)* $new $($body)* +} } + +//---------- Timespec (for serde) ---------- + +pub mod timespec_serde { + use super::*; + + #[derive(Serialize, Deserialize)] + struct Timespec(i64, u32); + + #[throws(S::Error)] + pub fn serialize(v: &TimeSpec, s: S) -> S::Ok { + let v = Timespec(v.tv_sec(), v.tv_nsec().try_into().unwrap()); + Serialize::serialize(&v, s)? + } + #[throws(D::Error)] + pub fn deserialize<'de, D:Deserializer<'de>>(d: D) -> TimeSpec { + let Timespec(sec, nsec) = Deserialize::deserialize(d)?; + libc::timespec { tv_sec: sec, tv_nsec: nsec.into() }.into() + } +} + +//---------- emptytype ---------- + +// TODO: replace with Void + +pub trait EmptyType { fn diverge(self) -> T; } + +impl EmptyType for Infallible { + fn diverge(self) -> T { match self { } } +} + +//---------- IpAddress ---------- + +pub trait IpAddress: Debug { + fn with_port(&self, port: u16) -> SocketAddr; +} + +impl IpAddress for A where A: Into + Debug + Clone { + fn with_port(&self, port: u16) -> SocketAddr { + match (self.clone().into(), port) + .to_socket_addrs() + .map(|i| i.at_most_one()) { + Ok(Ok(Some(addr))) => addr, + x => panic!("{:?},{} gave {:?}", self, port, x), + } + } +} + +//---------- get_or_extend_with ---------- + + +#[ext(pub)] +impl Vec { + fn get_or_extend_with(&mut self, i: usize, f: F) -> &mut T + where F: FnMut() -> T { + if self.get(i).is_none() { + self.resize_with(i+1, f); + } + &mut self[i] + } +} + +#[ext(pub)] +impl IndexVec where I: index_vec::Idx { + fn get_or_extend_with(&mut self, i: I, f: F) -> &mut T + where F: FnMut() -> T { + self.raw.get_or_extend_with(i.index(), f) + } +} + +//========== OldNew ========== + #[derive(Copy,Clone,Debug,From,Into)] #[derive(Hash,Eq,PartialEq,Serialize,Deserialize)] #[serde(transparent)] @@ -86,49 +250,7 @@ impl Index for OldNew { fn index(&self, i: OldNewIndex) -> &T { &self.0[i as usize] } } -/* -put trait OptionExt { - type Output; - fn get_or_try_insert_with< - E: Error, - F: FnOnce() -> Result, - >(&mut self, f: F) -> Result<&mut Output, E>; -} - -impl OptionExt for Option { - type Output = T; - fn get_or_try_insert_with - (&mut self, f: F) -> Result<&mut Output, E> - where E: Error, F: FnOnce() -> Result, - { - if self.is_none() { - *self = Some(f()?); - } - Ok(self.as_mut().unwrap()) - } -} -*/ - -// https://github.com/rust-lang/rust/issues/32255 :-( - -#[ext(pub, name=LocalFileExt, supertraits=Sized)] -impl fs::File { - #[throws(io::Error)] - fn close(self) { - let r = unsafe { - let fd = self.into_raw_fd(); - libc::close(fd) - }; - if r == 0 { - () - } else if r == -1 { - throw!(io::Error::last_os_error()) - } else { - panic!("close(2) returned {}", r) - } - } -} - +//========== Thunk ========== // todo #[derive(Clone)] pub struct Thunk U> ( @@ -177,27 +299,7 @@ impl Result> // todo: DerefMut -#[derive(Error,Clone,Copy,Debug,Eq,PartialEq,Serialize,Deserialize)] -#[error("error parsing Z coordinate")] -pub struct FooParseError; - -pub mod timespec_serde { - use super::*; - - #[derive(Serialize, Deserialize)] - struct Timespec(i64, u32); - - #[throws(S::Error)] - pub fn serialize(v: &TimeSpec, s: S) -> S::Ok { - let v = Timespec(v.tv_sec(), v.tv_nsec().try_into().unwrap()); - Serialize::serialize(&v, s)? - } - #[throws(D::Error)] - pub fn deserialize<'de, D:Deserializer<'de>>(d: D) -> TimeSpec { - let Timespec(sec, nsec) = Deserialize::deserialize(d)?; - libc::timespec { tv_sec: sec, tv_nsec: nsec.into() }.into() - } -} +//========== toml_merge ==================== pub fn toml_merge<'u, S: 'u + AsRef, @@ -238,216 +340,7 @@ pub fn toml_merge<'u, } } -#[derive(Debug,Clone)] -pub struct JsonString(pub T); -impl Serialize for JsonString where T:Serialize { - #[throws(S::Error)] - fn serialize(&self, s: S) -> S::Ok where S:Serializer { - let json = serde_json::to_string(&self.0) - .map_err(|e| ::custom(e))?; - Serialize::serialize(&json, s)? - } -} - -#[macro_export] -macro_rules! deref_to_field { - {$({ $($gen:tt)* })? $outer:ty, $inner:ty, $($field:tt)*} => { - impl $(< $($gen)* >)? Deref for $outer { - type Target = $inner; - fn deref(&self) -> &$inner { &self.$($field)* } - } - } -} -#[macro_export] -macro_rules! deref_to_field_mut { - {$({ $($gen:tt)* })? $outer:ty, $inner:ty, $($field:tt)*} => { - deref_to_field!{ $({ $($gen)* })? $outer, $inner, $($field)*} - impl $(< $($gen)* >)? DerefMut for $outer { - fn deref_mut(&mut self) -> &mut $inner { &mut self.$($field)* } - } - } -} - -pub trait EmptyType { fn diverge(self) -> T; } - -impl EmptyType for Infallible { - fn diverge(self) -> T { match self { } } -} - -#[macro_export] // <- otherwise bogus warning `unused_macros` -macro_rules! matches_doesnot_yn2bool { - (=) => (true); - (!) => (false); -} - -#[macro_export] -macro_rules! matches_doesnot { - ($v:expr, - $( - $yn:tt $p:pat - ),* $(,)? - ) => { - match $v { - $( - $p => $crate::matches_doesnot_yn2bool!($yn), - )* - } - } -} - -#[test] -fn matches_doesnot_test() { - assert!( - matches_doesnot!( - Some(42), - = Some(_), - ! None - ) - ); - assert!( - matches_doesnot!( - Some(42), - ! None, - ! Some(3), - = Some(_), - ) - ); - assert!( - matches_doesnot!( - Some(1), - = Some(1) | Some(2), - ! Some(_) | None - ) - ); - assert!( - ! matches_doesnot!( - Some(1), - ! Some(1) | Some(2), - = Some(_) | None - ) - ); -} - -#[macro_export] -macro_rules! trace_dbg { - ($msg:expr $(,$val:expr)*) => { - if log_enabled!(log::Level::Trace) { - #[allow(unused_mut)] - let mut buf = format!("{}", &$msg); - $( write!(&mut buf, " {}={:?}", stringify!($val), &$val).unwrap(); )* - trace!("{}", buf); - } - } - -} - -#[macro_export] -macro_rules! want_failed_internal { - { $variant:ident($binding:pat) = $input:expr, $x:expr, $($d:expr),* } => { - InternalLogicError::new({ - #[allow(unused_mut)] - let mut s = format!("wanted {}({}) = {}, but got {:?}", - stringify!($variant), stringify!($binding), - stringify!($input), $x); - $( - write!(&mut s, " {}={:?}", stringify!($d), &$d).unwrap(); - )* - s - }).tolerate() - } -} - -#[macro_export] -macro_rules! want { - { $variant:ident = $input:expr, - ? $($d:expr),* - } => ( - match $input { - $variant(y) => Some(y), - x => { - want_failed_internal!{ $variant(_)=$input, x, $($d),* } - None - }, - } - ); - { $variant:ident = $input:expr } => { - want!( $variant = $input, - ? ) - }; -} - -#[macro_export] -macro_rules! wants { - { $($d:tt)* } => { want!(Some = $($d)*) } -} -#[macro_export] -macro_rules! wantok { - { $($d:tt)* } => { want!(Ok = $($d)*) } -} - -#[macro_export] -macro_rules! want_let { - { $($variant:ident)::+($binding:pat) = $input:expr; - else ? $($d:expr),*; $($otherwise:tt)* - } => { - let $binding = match $input { - $($variant(y))::+ => y, - x => { - want_failed_internal!{ - $($variant)::+($binding)=$input, x, $($d),* - } - $($otherwise)* - }, - }; - }; - { $($variant:ident)::+($binding:pat) = $input:expr; - else $($otherwise:tt)* - } => { - want_let!{ $($variant($binding))::+ = $input; else ?; $($otherwise)* } - }; -} - -/// Allows the use of serde for a compat struct -/// -/// Ideally we would have -/// ```rust ignore -/// #[derive(Deserialize)] -/// #[serde(try_from=Compat)] -/// struct Main { /* new definition */ } -/// -/// #[derive(Deserialize)] -/// #[serde(untagged)] -/// enum Compat { V1(Main), V2(Old) } -/// -/// #[derive(Deserialize)] -/// struct Old { /* old version we still want to read */ } -/// -/// impl TryFrom for Main { /* ... */ } -/// ``` -/// -/// But the impl for `Compat` ends up honouring the `try_from` on `Main` -/// so is recursive. We solve that abusing serde's remote feature. -/// -/// For an example, see `IOccultIlk`. -/// -/// The name of the main structure must be passed twice, once as an -/// identifier and once as a literal, because `stringify!` doesn't work -/// in the serde attribute. -#[macro_export] -macro_rules! serde_with_compat { { - [ $( #[ $($attrs:meta)* ] )* ] [ $vis:vis ] [ $($intro:tt)* ] - $main:ident=$main_s:literal $new:ident $compat_s:literal - [ $($body:tt)* ] -} => { - $(#[ $($attrs)* ])* - #[serde(try_from=$compat_s)] - $vis $($intro)* $main $($body)* - - #[allow(non_camel_case_types)] - $(#[ $($attrs)* ])* - #[serde(remote=$main_s)] - $($intro)* $new $($body)* -} } +//========== .insert() and .remove() on various Entry ========== macro_rules! entry_define_insert_remove { { $name:ident, $name_mod:ident, $entry:path, $into_key:ident } => @@ -484,146 +377,35 @@ entry_define_insert_remove!{ key } -#[derive(Debug,Copy,Clone,Eq,PartialEq,Ord,PartialOrd)] -#[derive(From,Into)] -#[derive(Serialize, Deserialize)] -#[serde(into="Duration", try_from="Duration")] -pub struct FutureInstant(pub Instant); - -impl Into for FutureInstant { - fn into(self) -> Duration { - let now = config().global_clock.now(); - Instant::from(self).checked_duration_since(now).unwrap_or_default() - } -} - -#[derive(Error,Debug)] -#[error("Duration (eg during load) implies out-of-range FutureInstant")] -pub struct FutureInstantOutOfRange; - -impl TryFrom for FutureInstant { - type Error = FutureInstantOutOfRange; - #[throws(FutureInstantOutOfRange)] - fn try_from(duration: Duration) -> FutureInstant { - let now = config().global_clock.now(); - now.checked_add(duration).ok_or(FutureInstantOutOfRange)?.into() - } -} - - -#[derive(Debug,Copy,Clone)] -pub struct DigestRead { - d: D, - r: R, -} - -impl DigestRead { - pub fn new(r: R) -> Self { DigestRead { r, d: D::new() } } - pub fn into_inner(self) -> (D, R) { (self.d, self.r) } - pub fn finish(self) -> digest::Output { - self.d.finalize() - } -} - -impl Read for DigestRead { - #[throws(io::Error)] - fn read(&mut self, buf: &mut [u8]) -> usize { - let count = self.r.read(buf)?; - self.d.update(&buf[0..count]); - count - } -} - -#[test] -#[cfg(not(miri))] -fn test_digest_read() { - let ibuffer = b"abc"; - let exp = Sha512_256::digest(&ibuffer[..]); - let inner = &ibuffer[..]; - let mut dr = DigestRead::::new(inner); - let mut obuffer = [0;4]; - assert_eq!( dr.read(&mut obuffer).unwrap(), 3 ); - assert_eq!( &obuffer, b"abc\0" ); - let got = dr.finish(); - assert_eq!( got, exp ); -} - -#[derive(Debug,Copy,Clone)] -pub struct DigestWrite { - d: D, - w: W, -} - -impl DigestWrite { - pub fn new(w: W) -> Self { DigestWrite { w, d: D::new() } } - pub fn into_inner(self) -> (D, W) { (self.d, self.w) } - pub fn finish(self) -> (digest::Output, W) { - (self.d.finalize(), self.w) - } -} -impl DigestWrite { - pub fn sink() -> Self { DigestWrite::new(io::sink()) } - - #[throws(io::Error)] - pub fn of(r: &mut R) -> digest::Output where R: Read { - let mut dw = DigestWrite::::sink(); - io::copy(r, &mut dw)?; - dw.finish().0 - } -} - -impl Write for DigestWrite { - #[throws(io::Error)] - fn write(&mut self, buf: &[u8]) -> usize { - let count = self.w.write(buf)?; - self.d.update(&buf[0..count]); - count - } - #[throws(io::Error)] - fn flush(&mut self) { self.w.flush()? } -} - -#[test] -#[cfg(not(miri))] -fn test_digest_write() { - let ibuffer = b"xyz"; - let exp = Sha512_256::digest(&ibuffer[..]); - let mut obuffer = [0;4]; - let inner = &mut obuffer[..]; - let mut dw = bundles::DigestWrite::new(inner); - assert_eq!( dw.write(&ibuffer[..]).unwrap(), 3); - let (got, recov) = dw.finish(); - assert_eq!( recov, b"\0" ); - assert_eq!( got, exp ); - assert_eq!( &obuffer, b"xyz\0" ); -} - -#[ext(pub, name=SeekExt)] -impl T { - #[throws(io::Error)] - fn rewind(&mut self) { self.seek(io::SeekFrom::Start(0))? } -} - -#[ext(pub)] -impl Vec { - fn get_or_extend_with(&mut self, i: usize, f: F) -> &mut T - where F: FnMut() -> T { - if self.get(i).is_none() { - self.resize_with(i+1, f); - } - &mut self[i] +//========== FutureInstant ========== + +#[derive(Debug,Copy,Clone,Eq,PartialEq,Ord,PartialOrd)] +#[derive(From,Into)] +#[derive(Serialize, Deserialize)] +#[serde(into="Duration", try_from="Duration")] +pub struct FutureInstant(pub Instant); + +impl Into for FutureInstant { + fn into(self) -> Duration { + let now = config().global_clock.now(); + Instant::from(self).checked_duration_since(now).unwrap_or_default() } } -#[ext(pub)] -impl IndexVec where I: index_vec::Idx { - fn get_or_extend_with(&mut self, i: I, f: F) -> &mut T - where F: FnMut() -> T { - self.raw.get_or_extend_with(i.index(), f) +#[derive(Error,Debug)] +#[error("Duration (eg during load) implies out-of-range FutureInstant")] +pub struct FutureInstantOutOfRange; + +impl TryFrom for FutureInstant { + type Error = FutureInstantOutOfRange; + #[throws(FutureInstantOutOfRange)] + fn try_from(duration: Duration) -> FutureInstant { + let now = config().global_clock.now(); + now.checked_add(duration).ok_or(FutureInstantOutOfRange)?.into() } -} +} -pub fn is_default(t: &T) -> bool { t == &T::DEFAULT } +//========== Error handling ========== #[derive(Debug)] pub struct AnyhowDisplay<'a>(pub &'a anyhow::Error); @@ -681,24 +463,31 @@ impl anyhow::Error { } } -#[throws(Either)] -pub fn io_copy_interactive(read: &mut BufReader, write: &mut W) -where R: Read, W: Write { - loop { - let buf = read.fill_buf().map_err(Either::Left)?; - if buf.len() == 0 { break } +//========== IO - File::close ========== - let did = (||{ - let did = write.write(buf)?; - if did == 0 { throw!(ErrorKind::WriteZero) } - Ok::<_,io::Error>(did) - })().map_err(Either::Right)?; - - read.consume(did); - write.flush().map_err(Either::Right)?; +// https://github.com/rust-lang/rust/issues/32255 :-( + +#[ext(pub, name=LocalFileExt, supertraits=Sized)] +impl fs::File { + #[throws(io::Error)] + fn close(self) { + let r = unsafe { + let fd = self.into_raw_fd(); + libc::close(fd) + }; + if r == 0 { + () + } else if r == -1 { + throw!(io::Error::last_os_error()) + } else { + panic!("close(2) returned {}", r) + } } } + +//========== IO - SigPipeWriter and RawStdout/CookedStdout ========== + pub struct SigPipeWriter(pub W); impl SigPipeWriter { @@ -762,20 +551,7 @@ impl Drop for CookedStdout { fn drop(&mut self) { self.must_flush() } } -pub trait IpAddress: Debug { - fn with_port(&self, port: u16) -> SocketAddr; -} - -impl IpAddress for A where A: Into + Debug + Clone { - fn with_port(&self, port: u16) -> SocketAddr { - match (self.clone().into(), port) - .to_socket_addrs() - .map(|i| i.at_most_one()) { - Ok(Ok(Some(addr))) => addr, - x => panic!("{:?},{} gave {:?}", self, port, x), - } - } -} +//========== hex ========== #[throws(fmt::Error)] pub fn fmt_hex(f: &mut Formatter, buf: &[u8]) { @@ -832,3 +608,178 @@ fn test_parse_hex(){ assert_eq!( parse_fixed_hex("1" ), None::<[_;1]> ); assert_eq!( parse_fixed_hex("xy" ), None::<[_;1]> ); } + +//========== matches_doesnot ========== + +#[macro_export] // <- otherwise bogus warning `unused_macros` +macro_rules! matches_doesnot_yn2bool { + (=) => (true); + (!) => (false); +} + +#[macro_export] +macro_rules! matches_doesnot { + ($v:expr, + $( + $yn:tt $p:pat + ),* $(,)? + ) => { + match $v { + $( + $p => $crate::matches_doesnot_yn2bool!($yn), + )* + } + } +} + +#[test] +fn matches_doesnot_test() { + assert!( + matches_doesnot!( + Some(42), + = Some(_), + ! None + ) + ); + assert!( + matches_doesnot!( + Some(42), + ! None, + ! Some(3), + = Some(_), + ) + ); + assert!( + matches_doesnot!( + Some(1), + = Some(1) | Some(2), + ! Some(_) | None + ) + ); + assert!( + ! matches_doesnot!( + Some(1), + ! Some(1) | Some(2), + = Some(_) | None + ) + ); +} + +//========== want* macros ========== + +#[macro_export] +macro_rules! want_failed_internal { + { $variant:ident($binding:pat) = $input:expr, $x:expr, $($d:expr),* } => { + InternalLogicError::new({ + #[allow(unused_mut)] + let mut s = format!("wanted {}({}) = {}, but got {:?}", + stringify!($variant), stringify!($binding), + stringify!($input), $x); + $( + write!(&mut s, " {}={:?}", stringify!($d), &$d).unwrap(); + )* + s + }).tolerate() + } +} + +#[macro_export] +macro_rules! want { + { $variant:ident = $input:expr, + ? $($d:expr),* + } => ( + match $input { + $variant(y) => Some(y), + x => { + want_failed_internal!{ $variant(_)=$input, x, $($d),* } + None + }, + } + ); + { $variant:ident = $input:expr } => { + want!( $variant = $input, + ? ) + }; +} + +#[macro_export] +macro_rules! wants { + { $($d:tt)* } => { want!(Some = $($d)*) } +} +#[macro_export] +macro_rules! wantok { + { $($d:tt)* } => { want!(Ok = $($d)*) } +} + +#[macro_export] +macro_rules! want_let { + { $($variant:ident)::+($binding:pat) = $input:expr; + else ? $($d:expr),*; $($otherwise:tt)* + } => { + let $binding = match $input { + $($variant(y))::+ => y, + x => { + want_failed_internal!{ + $($variant)::+($binding)=$input, x, $($d),* + } + $($otherwise)* + }, + }; + }; + { $($variant:ident)::+($binding:pat) = $input:expr; + else $($otherwise:tt)* + } => { + want_let!{ $($variant($binding))::+ = $input; else ?; $($otherwise)* } + }; +} + +//========== miscellaneous macros ========== + +#[macro_export] +macro_rules! trace_dbg { + ($msg:expr $(,$val:expr)*) => { + if log_enabled!(log::Level::Trace) { + #[allow(unused_mut)] + let mut buf = format!("{}", &$msg); + $( write!(&mut buf, " {}={:?}", stringify!($val), &$val).unwrap(); )* + trace!("{}", buf); + } + } + +} + +#[macro_export] +macro_rules! ensure_eq { + ($v1:expr, $v2:expr) => { + ({ + let v1 = &$v1; + let v2 = &$v2; + if v1 != v2 { + Err(anyhow!("ensure_eq failed: {} != {}: {:?} != {:?}", + stringify!($v1), stringify!($v2), + v1, v2)) + } else { + Ok(()) + } + }?) + } +} + +#[macro_export] +macro_rules! deref_to_field { + {$({ $($gen:tt)* })? $outer:ty, $inner:ty, $($field:tt)*} => { + impl $(< $($gen)* >)? Deref for $outer { + type Target = $inner; + fn deref(&self) -> &$inner { &self.$($field)* } + } + } +} +#[macro_export] +macro_rules! deref_to_field_mut { + {$({ $($gen:tt)* })? $outer:ty, $inner:ty, $($field:tt)*} => { + deref_to_field!{ $({ $($gen)* })? $outer, $inner, $($field)*} + impl $(< $($gen)* >)? DerefMut for $outer { + fn deref_mut(&mut self) -> &mut $inner { &mut self.$($field)* } + } + } +} -- 2.30.2