Skip to content

Commit

Permalink
Merge pull request #261 from quartiq/deny
Browse files Browse the repository at this point in the history
deny
  • Loading branch information
jordens authored Nov 13, 2024
2 parents d967031 + b3e84ac commit ac09692
Show file tree
Hide file tree
Showing 8 changed files with 259 additions and 158 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased](https://github.com/quartiq/miniconf/compare/v0.17.1...HEAD) - DATE

### Added

* `deny` proc macro field attribute for fine grained rejection of struct access
(and removed corresponding trait impls).

## [0.17.1](https://github.com/quartiq/miniconf/compare/v0.17.0...v0.17.1) - 2024-11-12

### Added
Expand Down
5 changes: 2 additions & 3 deletions miniconf/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ Fields/variants that form internal nodes (non-leaf) need to implement the respec
Leaf fields/items need to support the respective [`serde`] (and the desired `serde::Serializer`/`serde::Deserializer`
backend) or [`core::any`] trait.

Structs, enums, arrays, and Options can then be cascaded to construct more complex trees.
Structs, enums, arrays, Options, and many other containers can then be cascaded to construct more complex trees.

See also the [`TreeKey`] trait documentation for details.

Expand All @@ -187,8 +187,7 @@ It implements [`Keys`].

## Limitations

* `enum`: The derive macros don't support enums with record (named fields) variants or tuple variants with more than one field. Only unit, newtype and skipped variants are supported. Without the derive macros, these `enums` are still however usable in their atomic `serde` form as leaf nodes. Inline tuple variants are supported.
* The derive macros don't handle `std`/`alloc` smart pointers ( `Box`, `Rc`, `Arc`) in any special way. They however still be handled with accessors (`get`, `get_mut`, `validate`).
* `enum`: The derive macros don't support enums with record (named fields) variants or tuple variants with more than one (non-skip) field. Only unit, newtype and skipped variants are supported. Without the derive macros, any `enum` is still however usable as a `Leaf` node. Note also that netwype variants with a single inline tuple are supported.
* The derive macros only support flattening in non-ambiguous situations (single field structs and single variant enums, both modulo skipped fields/variants and unit variants).

## Features
Expand Down
94 changes: 2 additions & 92 deletions miniconf/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -831,24 +831,6 @@ impl<T: TreeAny> TreeAny for RefCell<T> {
}
}

impl<T: TreeAny> TreeAny for &RefCell<T> {
#[inline]
fn ref_any_by_key<K>(&self, _keys: K) -> Result<&dyn Any, Traversal>
where
K: Keys,
{
Err(Traversal::Access(0, "Can't leak out of RefCell"))
}

#[inline]
fn mut_any_by_key<K>(&mut self, _keys: K) -> Result<&mut dyn Any, Traversal>
where
K: Keys,
{
Err(Traversal::Access(0, "Can't leak out of RefCell"))
}
}

/////////////////////////////////////////////////////////////////////////////////////////

#[cfg(feature = "alloc")]
Expand Down Expand Up @@ -1077,24 +1059,6 @@ mod _alloc {
}
}

impl<T: TreeAny> TreeAny for rc::Weak<T> {
#[inline]
fn ref_any_by_key<K>(&self, _keys: K) -> Result<&dyn Any, Traversal>
where
K: Keys,
{
Err(Traversal::Access(0, "Can't leak out of Weak"))
}

#[inline]
fn mut_any_by_key<K>(&mut self, _keys: K) -> Result<&mut dyn Any, Traversal>
where
K: Keys,
{
Err(Traversal::Access(0, "Can't leak out of Weak"))
}
}

/////////////////////////////////////////////////////////////////////////////////////////

impl<T: TreeKey> TreeKey for Arc<T> {
Expand Down Expand Up @@ -1200,24 +1164,6 @@ mod _alloc {
.deserialize_by_key(keys, de)
}
}

impl<T: TreeAny> TreeAny for sync::Weak<T> {
#[inline]
fn ref_any_by_key<K>(&self, _keys: K) -> Result<&dyn Any, Traversal>
where
K: Keys,
{
Err(Traversal::Access(0, "Can't leak out of Weak"))
}

#[inline]
fn mut_any_by_key<K>(&mut self, _keys: K) -> Result<&mut dyn Any, Traversal>
where
K: Keys,
{
Err(Traversal::Access(0, "Can't leak out of Weak"))
}
}
}

/////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -1303,24 +1249,6 @@ mod _std {
}
}

impl<T: TreeAny> TreeAny for &Mutex<T> {
#[inline]
fn ref_any_by_key<K>(&self, _keys: K) -> Result<&dyn Any, Traversal>
where
K: Keys,
{
Err(Traversal::Access(0, "Can't leak out of Mutex"))
}

#[inline]
fn mut_any_by_key<K>(&mut self, _keys: K) -> Result<&mut dyn Any, Traversal>
where
K: Keys,
{
Err(Traversal::Access(0, "Can't leak out of Mutex"))
}
}

/////////////////////////////////////////////////////////////////////////////////////////

impl<T: TreeKey> TreeKey for RwLock<T> {
Expand All @@ -1347,7 +1275,7 @@ mod _std {
S: Serializer,
{
self.read()
.or(Err(Traversal::Access(0, "Locked")))?
.or(Err(Traversal::Access(0, "Poisoned")))?
.serialize_by_key(keys, ser)
}
}
Expand All @@ -1360,7 +1288,7 @@ mod _std {
D: Deserializer<'de>,
{
self.write()
.or(Err(Traversal::Access(0, "Locked")))?
.or(Err(Traversal::Access(0, "Poisoned")))?
.deserialize_by_key(keys, de)
}
}
Expand Down Expand Up @@ -1397,22 +1325,4 @@ mod _std {
.mut_any_by_key(keys)
}
}

impl<T: TreeAny> TreeAny for &RwLock<T> {
#[inline]
fn ref_any_by_key<K>(&self, _keys: K) -> Result<&dyn Any, Traversal>
where
K: Keys,
{
Err(Traversal::Access(0, "Can't leak out of RwLock"))
}

#[inline]
fn mut_any_by_key<K>(&mut self, _keys: K) -> Result<&mut dyn Any, Traversal>
where
K: Keys,
{
Err(Traversal::Access(0, "Can't leak out of RwLock"))
}
}
}
103 changes: 101 additions & 2 deletions miniconf/src/leaf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ impl<T> TreeAny for StrLeaf<T> {
K: Keys,
{
keys.finalize()?;
Err(Traversal::Access(1, "No Any access for StrLeaf"))
Err(Traversal::Access(0, "No Any access for StrLeaf"))
}

#[inline]
Expand All @@ -246,6 +246,105 @@ impl<T> TreeAny for StrLeaf<T> {
K: Keys,
{
keys.finalize()?;
Err(Traversal::Access(1, "No Any access for StrLeaf"))
Err(Traversal::Access(0, "No Any access for StrLeaf"))
}
}

/// Deny any value access
#[derive(
Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize,
)]
#[serde(transparent)]
#[repr(transparent)]
pub struct Deny<T: ?Sized>(pub T);

impl<T: ?Sized> Deref for Deny<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}

impl<T: ?Sized> DerefMut for Deny<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl<T> Deny<T> {
/// Extract just the inner
#[inline]
pub fn into_inner(self) -> T {
self.0
}
}

impl<T> From<T> for Deny<T> {
#[inline]
fn from(value: T) -> Self {
Self(value)
}
}

impl<T: ?Sized> TreeKey for Deny<T> {
#[inline]
fn traverse_all<W: Walk>() -> Result<W, W::Error> {
Ok(W::leaf())
}

#[inline]
fn traverse_by_key<K, F, E>(mut keys: K, _func: F) -> Result<usize, Error<E>>
where
K: Keys,
F: FnMut(usize, Option<&'static str>, NonZero<usize>) -> Result<(), E>,
{
keys.finalize()?;
Ok(0)
}
}

impl<T: ?Sized> TreeSerialize for Deny<T> {
#[inline]
fn serialize_by_key<K, S>(&self, mut keys: K, _ser: S) -> Result<usize, Error<S::Error>>
where
K: Keys,
S: Serializer,
{
keys.finalize()?;
Err(Traversal::Access(0, "Denied").into())
}
}

impl<'de, T: ?Sized> TreeDeserialize<'de> for Deny<T> {
#[inline]
fn deserialize_by_key<K, D>(&mut self, mut keys: K, _de: D) -> Result<usize, Error<D::Error>>
where
K: Keys,
D: Deserializer<'de>,
{
keys.finalize()?;
Err(Traversal::Access(0, "Denied").into())
}
}

impl<T> TreeAny for Deny<T> {
#[inline]
fn ref_any_by_key<K>(&self, mut keys: K) -> Result<&dyn Any, Traversal>
where
K: Keys,
{
keys.finalize()?;
Err(Traversal::Access(0, "Denied"))
}

#[inline]
fn mut_any_by_key<K>(&mut self, mut keys: K) -> Result<&mut dyn Any, Traversal>
where
K: Keys,
{
keys.finalize()?;
Err(Traversal::Access(0, "Denied"))
}
}
18 changes: 7 additions & 11 deletions miniconf/src/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ pub trait TreeKey {
/// bar: [Leaf<u16>; 5],
/// };
///
/// let idx = [1usize, 1];
/// let idx = [1, 1];
///
/// let (path, node) = S::transcode::<Path<String, '/'>, _>(idx).unwrap();
/// assert_eq!(path.as_str(), "/bar/1");
Expand Down Expand Up @@ -377,14 +377,12 @@ pub trait TreeAny {

/// Serialize a leaf node by its keys.
///
/// See also [`crate::json`] or `crate::postcard` for convenient functions using these traits.
/// See also [`crate::json`] or `crate::postcard` for convenient wrappers using this trait.
///
/// # Derive macro
///
/// [`macro@crate::TreeSerialize`] derives `TreeSerialize` for structs with named fields and tuple structs
/// and for enums with newtype and unit variants.
///
/// The field attributes are described in the [`TreeKey`] trait.
/// See [`macro@crate::TreeSerialize`].
/// The derive macro attributes are described in the [`TreeKey`] trait.
pub trait TreeSerialize {
/// Serialize a node by keys.
///
Expand Down Expand Up @@ -423,14 +421,12 @@ pub trait TreeSerialize {

/// Deserialize a leaf node by its keys.
///
/// See also [`crate::json`] for a convenient helper functions using this trait.
/// See also [`crate::json`] or `crate::postcard` for convenient wrappers using this trait.
///
/// # Derive macro
///
/// [`macro@crate::TreeDeserialize`] derives `TreeSerialize` for structs with named fields and tuple structs
/// and for enums with newtype and unit variants.
///
/// The field attributes are described in the [`TreeKey`] trait.
/// See [`macro@crate::TreeDeserialize`].
/// The derive macro attributes are described in the [`TreeKey`] trait.
pub trait TreeDeserialize<'de> {
/// Deserialize a leaf node by its keys.
///
Expand Down
26 changes: 25 additions & 1 deletion miniconf/tests/structs.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use miniconf::{
json, Deserialize, Leaf, Metadata, Serialize, Tree, TreeDeserialize, TreeKey, TreeSerialize,
json, Deserialize, IntoKeys, Leaf, Metadata, Serialize, Traversal, Tree, TreeAny,
TreeDeserialize, TreeKey, TreeSerialize,
};

mod common;
Expand Down Expand Up @@ -74,3 +75,26 @@ fn tuple_struct() {

assert_eq!(paths::<Settings, 1>(), ["/0", "/1"]);
}

#[test]
fn deny_access() {
use core::cell::RefCell;
#[derive(Tree)]
struct S<'a> {
#[tree(deny(deserialize = "no de", mut_any = "no any"))]
field: Leaf<i32>,
#[tree(deny(ref_any = "no any", mut_any = "no any"))]
cell: &'a RefCell<Leaf<i32>>,
}
let cell = RefCell::new(2.into());
let mut s = S {
field: 1.into(),
cell: &cell,
};
common::set_get(&mut s, "/cell", b"3");
s.ref_any_by_key([0].into_keys()).unwrap();
assert!(matches!(
s.mut_any_by_key([0].into_keys()),
Err(Traversal::Access(1, "no any"))
));
}
Loading

0 comments on commit ac09692

Please sign in to comment.