diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 62c2083..db48f90 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -119,6 +119,18 @@ jobs: with: command: update args: --package windows_x86_64_msvc:0.48.5 --precise 0.48.5 + - name: Restrict libc version + if: matrix.restrict_deps_versions + uses: actions-rs/cargo@v1 + with: + command: update + args: --package libc --precise 0.2.164 + - name: Restrict num-traits version + if: matrix.restrict_deps_versions + uses: actions-rs/cargo@v1 + with: + command: update + args: --package num-traits --precise 0.2.18 - name: Check formatting if: matrix.lint uses: actions-rs/cargo@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 438dcf5..439e875 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.15.0, 0.53.0 +* Don't stop deserialization of `Any` due to `REG_NONE` (pullrequest [#67](https://github.com/gentoo90/winreg-rs/pull/67), fixes [#66](https://github.com/gentoo90/winreg-rs/issues/66)) +* Implement (de)serialization of `Option` ([#56](https://github.com/gentoo90/winreg-rs/issues/56)) +* Add `RegKey` methods for creating/opening subkeys with custom options ([#65](https://github.com/gentoo90/winreg-rs/pull/65)) + ## 0.52.0 * Breaking change: `.commit()` and `.rollback()` now consume the transaction ([#62](https://github.com/gentoo90/winreg-rs/issues/62)) * Add `RegKey::rename_subkey()` method ([#58](https://github.com/gentoo90/winreg-rs/issues/58)) diff --git a/Cargo.toml b/Cargo.toml index 512f2fe..654eba1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "winreg" edition = "2018" -version = "0.52.0" +version = "0.53.0" authors = ["Igor Shaula "] license = "MIT" description = "Rust bindings to MS Windows Registry API" @@ -13,7 +13,7 @@ categories = ["api-bindings", "os::windows-apis"] [dependencies] cfg-if = "1.0" -windows-sys = {version = "0.48.0", features = [ +windows-sys = {version = "0.48", features = [ "Win32_Foundation", "Win32_System_Time", "Win32_System_Registry", diff --git a/README.md b/README.md index 56fccbe..6eac643 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Current features: * `u64` <=> `REG_QWORD` * Iteration through key names and through values * Transactions -* Transacted serialization of rust types into/from registry (only primitives, structures and maps for now) +* Transacted serialization of rust types into/from registry (only primitives, `Option`s, structures and maps for now) ## Usage @@ -36,7 +36,7 @@ Current features: ```toml # Cargo.toml [dependencies] -winreg = "0.52" +winreg = "0.53" ``` ```rust @@ -138,7 +138,7 @@ fn main() -> io::Result<()> { ```toml # Cargo.toml [dependencies] -winreg = { version = "0.52", features = ["transactions"] } +winreg = { version = "0.53", features = ["transactions"] } ``` ```rust @@ -179,7 +179,7 @@ fn main() -> io::Result<()> { ```toml # Cargo.toml [dependencies] -winreg = { version = "0.52", features = ["serialization-serde"] } +winreg = { version = "0.53", features = ["serialization-serde"] } serde = "1" serde_derive = "1" ``` @@ -204,7 +204,7 @@ struct Size { #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Rectangle { - coords: Coords, + coords: Option, size: Size, } @@ -219,6 +219,7 @@ struct Test { t_struct: Rectangle, t_map: HashMap, t_string: String, + t_optional_string: Option, #[serde(rename = "")] // empty name becomes the (Default) value in the registry t_char: char, t_i8: i8, @@ -248,11 +249,12 @@ fn main() -> Result<(), Box> { t_u64: 123_456_789_101_112, t_usize: 1_234_567_891, t_struct: Rectangle { - coords: Coords { x: 55, y: 77 }, + coords: Some(Coords { x: 55, y: 77 }), size: Size { w: 500, h: 300 }, }, t_map: map, t_string: "test 123!".to_owned(), + t_optional_string: Some("test 456!".to_owned()), t_char: 'a', t_i8: -123, t_i16: -2049, diff --git a/examples/map_key_serialization.rs b/examples/map_key_serialization.rs index 729c662..44c9bc4 100644 --- a/examples/map_key_serialization.rs +++ b/examples/map_key_serialization.rs @@ -22,7 +22,7 @@ struct Size { #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Rectangle { - coords: Coords, + coords: Option, size: Size, } @@ -33,14 +33,14 @@ fn main() -> Result<(), Box> { v1.insert( "first".to_owned(), Rectangle { - coords: Coords { x: 55, y: 77 }, + coords: Some(Coords { x: 55, y: 77 }), size: Size { w: 500, h: 300 }, }, ); v1.insert( "second".to_owned(), Rectangle { - coords: Coords { x: 11, y: 22 }, + coords: None, size: Size { w: 1000, h: 600 }, }, ); diff --git a/examples/serialization.rs b/examples/serialization.rs index c6df51e..3c31a4c 100644 --- a/examples/serialization.rs +++ b/examples/serialization.rs @@ -22,7 +22,7 @@ struct Size { #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Rectangle { - coords: Coords, + coords: Option, size: Size, } @@ -37,6 +37,7 @@ struct Test { t_struct: Rectangle, t_map: HashMap, t_string: String, + t_optional_string: Option, #[serde(with = "serde_bytes")] t_bytes: Vec, #[serde(rename = "")] // empty name becomes the (Default) value in the registry @@ -68,11 +69,12 @@ fn main() -> Result<(), Box> { t_u64: 123_456_789_101_112, t_usize: 1_234_567_891, t_struct: Rectangle { - coords: Coords { x: 55, y: 77 }, + coords: Some(Coords { x: 55, y: 77 }), size: Size { w: 500, h: 300 }, }, t_map: map, t_string: "test 123!".to_owned(), + t_optional_string: Some("test 456!".to_owned()), t_bytes: vec![0xDE, 0xAD, 0xBE, 0xEF], t_char: 'a', t_i8: -123, diff --git a/examples/transacted_serialization.rs b/examples/transacted_serialization.rs index e586aae..8d7e95e 100644 --- a/examples/transacted_serialization.rs +++ b/examples/transacted_serialization.rs @@ -23,7 +23,7 @@ struct Size { #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Rectangle { - coords: Coords, + coords: Option, size: Size, } @@ -70,7 +70,7 @@ fn main() -> Result<(), Box> { t_u64: 123_456_789_101_112, t_usize: 1_234_567_891, t_struct: Rectangle { - coords: Coords { x: 55, y: 77 }, + coords: Some(Coords { x: 55, y: 77 }), size: Size { w: 500, h: 300 }, }, t_map: map, diff --git a/src/common.rs b/src/common.rs index 6e8651d..a38d4ac 100644 --- a/src/common.rs +++ b/src/common.rs @@ -15,10 +15,7 @@ macro_rules! werr { } pub(crate) fn to_utf16>(s: P) -> Vec { - s.as_ref() - .encode_wide() - .chain(Some(0).into_iter()) - .collect() + s.as_ref().encode_wide().chain(Some(0)).collect() } pub(crate) fn v16_to_v8(v: &[u16]) -> Vec { diff --git a/src/decoder/serialization_serde.rs b/src/decoder/serialization_serde.rs index fe6af36..f8672ec 100644 --- a/src/decoder/serialization_serde.rs +++ b/src/decoder/serialization_serde.rs @@ -4,7 +4,7 @@ // may not be copied, modified, or distributed // except according to those terms. use super::{DecodeResult, Decoder, DecoderCursor, DecoderError, DECODER_SAM}; -use crate::types::FromRegValue; +use crate::{types::FromRegValue, RegValue}; use serde::de::*; use std::fmt; @@ -14,7 +14,7 @@ impl Error for DecoderError { } } -impl<'de, 'a> Deserializer<'de> for &'a mut Decoder { +impl<'de> Deserializer<'de> for &mut Decoder { type Error = DecoderError; fn deserialize_any(self, visitor: V) -> DecodeResult where @@ -36,7 +36,11 @@ impl<'de, 'a> Deserializer<'de> for &'a mut Decoder { REG_DWORD => visitor.visit_u32(u32::from_reg_value(&v)?), REG_QWORD => visitor.visit_u64(u64::from_reg_value(&v)?), REG_BINARY => visitor.visit_byte_buf(v.bytes), - _ => no_impl!("value type deserialization not implemented"), + REG_NONE => visitor.visit_none(), + _ => no_impl!(format!( + "value type deserialization not implemented {:?}", + v.vtype + )), } } _ => no_impl!("deserialize_any"), @@ -175,9 +179,21 @@ impl<'de, 'a> Deserializer<'de> for &'a mut Decoder { let v = { use super::DecoderCursor::*; match self.cursor { - FieldVal(_, ref name) => { - self.key.get_raw_value(name).map_err(DecoderError::IoError) - } + Start => return visitor.visit_some(&mut *self), + FieldVal(index, ref name) => self + .key + .get_raw_value(name) + .map_err(DecoderError::IoError) + .and_then(|v| match v { + RegValue { + vtype: crate::enums::RegType::REG_NONE, + .. + } => { + self.cursor = DecoderCursor::Field(index + 1); + Err(DecoderError::DeserializerError("Found REG_NONE".to_owned())) + } + val => Ok(val), + }), _ => Err(DecoderError::DeserializerError("Nothing found".to_owned())), } }; diff --git a/src/encoder/mod.rs b/src/encoder/mod.rs index d1ca5c9..18fe3a3 100644 --- a/src/encoder/mod.rs +++ b/src/encoder/mod.rs @@ -100,7 +100,7 @@ impl Encoder<&Transaction> { key: &RegKey, tr: &'a Transaction, ) -> EncodeResult> { - key.open_subkey_transacted_with_flags("", &tr, ENCODER_SAM) + key.open_subkey_transacted_with_flags("", tr, ENCODER_SAM) .map(|k| Encoder::new_transacted(k, tr)) .map_err(EncoderError::IoError) } diff --git a/src/encoder/serialization_serde.rs b/src/encoder/serialization_serde.rs index 787db06..3dde243 100644 --- a/src/encoder/serialization_serde.rs +++ b/src/encoder/serialization_serde.rs @@ -106,11 +106,14 @@ impl<'a, Tr: AsRef> Serializer for &'a mut Encoder { } fn serialize_none(self) -> EncodeResult { - no_impl!("serialize_none") + match mem::replace(&mut self.state, Start) { + NextKey(..) => Ok(()), + Start => Err(EncoderError::NoFieldName), + } } - fn serialize_some(self, _value: &T) -> EncodeResult { - no_impl!("serialize_some") + fn serialize_some(self, value: &T) -> EncodeResult { + value.serialize(self) } fn serialize_unit(self) -> EncodeResult { @@ -455,9 +458,9 @@ impl serde::Serializer for MapKeySerializer { Err(EncoderError::KeyMustBeAString) } - fn collect_str(self, value: &T) -> EncodeResult + fn collect_str(self, value: &T) -> EncodeResult where - T: fmt::Display, + T: fmt::Display + ?Sized, { Ok(value.to_string()) } @@ -468,7 +471,7 @@ pub struct StructMapEncoder<'a, Tr: AsRef> { is_root: bool, } -impl<'a, Tr: AsRef> SerializeStruct for StructMapEncoder<'a, Tr> { +impl> SerializeStruct for StructMapEncoder<'_, Tr> { type Ok = (); type Error = EncoderError; @@ -489,7 +492,7 @@ impl<'a, Tr: AsRef> SerializeStruct for StructMapEncoder<'a, Tr> { } } -impl<'a, Tr: AsRef> SerializeMap for StructMapEncoder<'a, Tr> { +impl> SerializeMap for StructMapEncoder<'_, Tr> { type Ok = (); type Error = EncoderError; diff --git a/src/enums.rs b/src/enums.rs index 97b76c2..64a9118 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -10,7 +10,9 @@ pub use windows_sys::Win32::System::Registry::{ HKEY_DYN_DATA, HKEY_LOCAL_MACHINE, HKEY_PERFORMANCE_DATA, HKEY_PERFORMANCE_NLSTEXT, HKEY_PERFORMANCE_TEXT, HKEY_USERS, KEY_ALL_ACCESS, KEY_CREATE_LINK, KEY_CREATE_SUB_KEY, KEY_ENUMERATE_SUB_KEYS, KEY_EXECUTE, KEY_NOTIFY, KEY_QUERY_VALUE, KEY_READ, KEY_SET_VALUE, - KEY_WOW64_32KEY, KEY_WOW64_64KEY, KEY_WOW64_RES, KEY_WRITE, REG_PROCESS_APPKEY, + KEY_WOW64_32KEY, KEY_WOW64_64KEY, KEY_WOW64_RES, KEY_WRITE, REG_OPTION_BACKUP_RESTORE, + REG_OPTION_CREATE_LINK, REG_OPTION_DONT_VIRTUALIZE, REG_OPTION_NON_VOLATILE, + REG_OPTION_OPEN_LINK, REG_OPTION_RESERVED, REG_OPTION_VOLATILE, REG_PROCESS_APPKEY, }; macro_rules! winapi_enum{ diff --git a/src/lib.rs b/src/lib.rs index 99d6fad..5c19804 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ //!```toml,ignore //!# Cargo.toml //![dependencies] -//!winreg = "0.52" +//!winreg = "0.53" //!``` //! //!```no_run diff --git a/src/reg_key.rs b/src/reg_key.rs index 56f3883..7f5dae0 100644 --- a/src/reg_key.rs +++ b/src/reg_key.rs @@ -165,11 +165,35 @@ impl RegKey { &self, path: P, perms: Registry::REG_SAM_FLAGS, + ) -> io::Result { + self.open_subkey_with_options_flags(path, 0, perms) + } + + /// Open subkey with desired permissions and options. + /// Will open another handle to itself if `path` is an empty string. + /// + /// # Examples + /// + /// ```no_run + /// # use std::error::Error; + /// # use winreg::RegKey; + /// # use winreg::enums::*; + /// # fn main() -> Result<(), Box> { + /// let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); + /// hklm.open_subkey_with_options_flags("SOFTWARE\\LinkKey", REG_OPTION_OPEN_LINK, KEY_READ)?; + /// # Ok(()) + /// # } + /// ``` + pub fn open_subkey_with_options_flags>( + &self, + path: P, + options: Registry::REG_OPEN_CREATE_OPTIONS, + perms: Registry::REG_SAM_FLAGS, ) -> io::Result { let c_path = to_utf16(path); let mut new_hkey: HKEY = 0; match unsafe { - Registry::RegOpenKeyExW(self.hkey, c_path.as_ptr(), 0, perms, &mut new_hkey) + Registry::RegOpenKeyExW(self.hkey, c_path.as_ptr(), options, perms, &mut new_hkey) } { 0 => Ok(RegKey { hkey: new_hkey }), err => werr!(err), @@ -193,6 +217,18 @@ impl RegKey { path: P, t: &Transaction, perms: Registry::REG_SAM_FLAGS, + ) -> io::Result { + self.open_subkey_transacted_with_options_flags(path, t, 0, perms) + } + + /// Part of `transactions` feature. + #[cfg(feature = "transactions")] + pub fn open_subkey_transacted_with_options_flags>( + &self, + path: P, + t: &Transaction, + options: Registry::REG_OPEN_CREATE_OPTIONS, + perms: Registry::REG_SAM_FLAGS, ) -> io::Result { let c_path = to_utf16(path); let mut new_hkey: HKEY = 0; @@ -200,7 +236,7 @@ impl RegKey { Registry::RegOpenKeyTransactedW( self.hkey, c_path.as_ptr(), - 0, + options, perms, &mut new_hkey, t.handle, @@ -245,6 +281,15 @@ impl RegKey { &self, path: P, perms: Registry::REG_SAM_FLAGS, + ) -> io::Result<(RegKey, RegDisposition)> { + self.create_subkey_with_options_flags(path, REG_OPTION_NON_VOLATILE, perms) + } + + pub fn create_subkey_with_options_flags>( + &self, + path: P, + options: Registry::REG_OPEN_CREATE_OPTIONS, + perms: Registry::REG_SAM_FLAGS, ) -> io::Result<(RegKey, RegDisposition)> { let c_path = to_utf16(path); let mut new_hkey: HKEY = 0; @@ -255,7 +300,7 @@ impl RegKey { c_path.as_ptr(), 0, ptr::null_mut(), - Registry::REG_OPTION_NON_VOLATILE, + options, perms, ptr::null_mut(), &mut new_hkey, @@ -287,6 +332,18 @@ impl RegKey { path: P, t: &Transaction, perms: Registry::REG_SAM_FLAGS, + ) -> io::Result<(RegKey, RegDisposition)> { + self.create_subkey_transacted_with_options_flags(path, t, REG_OPTION_NON_VOLATILE, perms) + } + + /// Part of `transactions` feature. + #[cfg(feature = "transactions")] + pub fn create_subkey_transacted_with_options_flags>( + &self, + path: P, + t: &Transaction, + options: Registry::REG_OPEN_CREATE_OPTIONS, + perms: Registry::REG_SAM_FLAGS, ) -> io::Result<(RegKey, RegDisposition)> { let c_path = to_utf16(path); let mut new_hkey: HKEY = 0; @@ -297,7 +354,7 @@ impl RegKey { c_path.as_ptr(), 0, ptr::null_mut(), - Registry::REG_OPTION_NON_VOLATILE, + options, perms, ptr::null_mut(), &mut new_hkey, @@ -608,7 +665,7 @@ impl RegKey { match unsafe { Registry::RegQueryValueExW( self.hkey, - c_name.as_ptr() as *const u16, + c_name.as_ptr(), ptr::null_mut(), &mut buf_type, buf.as_mut_ptr() as *mut u8, @@ -960,7 +1017,7 @@ pub struct EnumKeys<'key> { index: u32, } -impl<'key> Iterator for EnumKeys<'key> { +impl Iterator for EnumKeys<'_> { type Item = io::Result; fn next(&mut self) -> Option> { @@ -985,7 +1042,7 @@ pub struct EnumValues<'key> { index: u32, } -impl<'key> Iterator for EnumValues<'key> { +impl Iterator for EnumValues<'_> { type Item = io::Result<(String, RegValue)>; fn next(&mut self) -> Option> { diff --git a/src/transaction.rs b/src/transaction.rs index d97335b..58366c4 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -37,7 +37,6 @@ //! } //!} //!``` -#![cfg(feature = "transactions")] use std::io; use std::ptr; use windows_sys::Win32::Foundation; diff --git a/tests/serialization.rs b/tests/serialization.rs index 07e3bf6..a9d35c2 100644 --- a/tests/serialization.rs +++ b/tests/serialization.rs @@ -23,7 +23,7 @@ struct Size { #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Rectangle { - coords: Coords, + coords: Option, size: Size, } @@ -35,9 +35,14 @@ struct AllFields { t_u32: u32, t_u64: u64, t_usize: usize, - t_struct: Rectangle, + t_rect_no_coords: Rectangle, + t_rect_with_coords: Rectangle, t_string: String, t_map: HashMap>, + t_optional_u32_none: Option, + t_optional_u32_some: Option, + t_optional_map_none: Option>, + t_optional_map_some: Option>, t_i8: i8, t_i16: i16, t_i32: i32, @@ -63,7 +68,7 @@ impl AllFields { k2.insert("val3".to_owned(), 1024); let mut map = HashMap::new(); - map.insert("key1".to_owned(), k1); + map.insert("key1".to_owned(), k1.clone()); map.insert("key2".to_owned(), k2); AllFields { @@ -73,11 +78,19 @@ impl AllFields { t_u32: 123_456_789, t_u64: 123_456_789_101_112, t_usize: 1_234_567_891, - t_struct: Rectangle { - coords: Coords { x: 55, y: 77 }, + t_rect_no_coords: Rectangle { + coords: None, + size: Size { w: 320, h: 240 }, + }, + t_rect_with_coords: Rectangle { + coords: Some(Coords { x: 55, y: 77 }), size: Size { w: 500, h: 300 }, }, t_map: map, + t_optional_u32_none: None, + t_optional_u32_some: Some(1234), + t_optional_map_none: None, + t_optional_map_some: Some(k1), t_string: "Test123 \n$%^&|+-*/\\()".to_owned(), t_i8: -123, t_i16: -2049, @@ -95,7 +108,7 @@ impl AllFields { #[derive(Debug, PartialEq, Serialize, Deserialize)] struct SomeFields { t_usize: usize, - t_struct: Rectangle, + t_rect_no_coords: Rectangle, t_string: String, t_u32: Option, t_none: Option, @@ -105,7 +118,7 @@ impl PartialEq for SomeFields { fn eq(&self, other: &AllFields) -> bool { *self.t_string == other.t_string && self.t_usize == other.t_usize - && self.t_struct == other.t_struct + && self.t_rect_no_coords == other.t_rect_no_coords && self.t_u32 == Some(other.t_u32) && self.t_none.is_none() }