From 35b9731aab9e7c5cec8c98dd7aff74f9e1d78657 Mon Sep 17 00:00:00 2001 From: Christopher Serr Date: Fri, 1 Dec 2023 15:01:20 +0100 Subject: [PATCH] Make Value API more ergonomic (#80) By using a trait, we can make it more ergonomic to pass other types to functions that expect a setting value. --- asr-derive/src/lib.rs | 16 +++++++++++++ src/runtime/settings/list.rs | 12 +++++----- src/runtime/settings/map.rs | 12 ++++++---- src/runtime/settings/mod.rs | 2 +- src/runtime/settings/value.rs | 44 ++++++++++++++++++++++++++++++++++- 5 files changed, 73 insertions(+), 13 deletions(-) diff --git a/asr-derive/src/lib.rs b/asr-derive/src/lib.rs index d9eadd7..f395d16 100644 --- a/asr-derive/src/lib.rs +++ b/asr-derive/src/lib.rs @@ -65,6 +65,22 @@ use syn::{spanned::Spanned, Data, DeriveInput, Expr, ExprLit, Lit, Meta}; /// _title: Title, /// # } /// ``` +/// +/// # Tracking changes +/// +/// You can track changes to a setting by wrapping the widget type in a `Pair`. +/// It acts like the widget by itself, but also keeps track of the previous +/// value when you call `update` on the struct. +/// +/// ```no_run +/// use asr::watcher::Pair; +/// +/// #[derive(Gui)] +/// struct Settings { +/// /// Use Game Time +/// use_game_time: Pair, +/// } +/// ``` #[proc_macro_derive(Gui, attributes(default, heading_level))] pub fn settings_macro(input: TokenStream) -> TokenStream { let ast: DeriveInput = syn::parse(input).unwrap(); diff --git a/src/runtime/settings/list.rs b/src/runtime/settings/list.rs index 4174d61..8256cf8 100644 --- a/src/runtime/settings/list.rs +++ b/src/runtime/settings/list.rs @@ -1,8 +1,8 @@ -use core::fmt; +use core::{borrow::Borrow, fmt}; use crate::{runtime::sys, Error}; -use super::Value; +use super::{AsValue, Value}; /// A list of [`Value`]s that can itself be a [`Value`] and thus be stored in a /// [`Map`](super::Map). @@ -71,10 +71,10 @@ impl List { /// Pushes a copy of the value to the end of the list. #[inline] - pub fn push(&self, value: &Value) { + pub fn push(&self, value: impl AsValue) { // SAFETY: The settings list handle is valid and the value handle is // valid. - unsafe { sys::settings_list_push(self.0, value.0) } + unsafe { sys::settings_list_push(self.0, value.as_value().borrow().0) } } /// Inserts a copy of the value at the given index, pushing all values at @@ -82,11 +82,11 @@ impl List { /// is out of bounds. You may specify an index that is equal to the length /// of the list to append the value to the end of the list. #[inline] - pub fn insert(&self, index: u64, value: &Value) -> Result<(), Error> { + pub fn insert(&self, index: u64, value: impl AsValue) -> Result<(), Error> { // SAFETY: The settings list handle is valid and the value handle is // valid. unsafe { - if sys::settings_list_insert(self.0, index, value.0) { + if sys::settings_list_insert(self.0, index, value.as_value().borrow().0) { Ok(()) } else { Err(Error {}) diff --git a/src/runtime/settings/map.rs b/src/runtime/settings/map.rs index 0164a46..8885521 100644 --- a/src/runtime/settings/map.rs +++ b/src/runtime/settings/map.rs @@ -1,10 +1,10 @@ -use core::fmt; +use core::{borrow::Borrow, fmt}; use arrayvec::ArrayString; use crate::{runtime::sys, Error}; -use super::Value; +use super::{AsValue, Value}; /// A map consisting of settings that are configured. Every setting has a string /// based key and a [`Value`]. There is a global settings map that represents @@ -90,13 +90,15 @@ impl Map { } /// Inserts a copy of the setting value into the settings map based on the - /// key. If the key already exists, it will be overwritten. + /// key. If the key already exists, the existing value will be overwritten. #[inline] - pub fn insert(&self, key: &str, value: &Value) { + pub fn insert(&self, key: &str, value: impl AsValue) { // SAFETY: The settings map handle is valid. We provide a valid pointer // and length to the key which is guaranteed to be valid UTF-8. The // setting value handle is also valid. - unsafe { sys::settings_map_insert(self.0, key.as_ptr(), key.len(), value.0) } + unsafe { + sys::settings_map_insert(self.0, key.as_ptr(), key.len(), value.as_value().borrow().0) + } } /// Gets a copy of the setting value from the settings map based on the key. diff --git a/src/runtime/settings/mod.rs b/src/runtime/settings/mod.rs index 1bb8d0f..c61c808 100644 --- a/src/runtime/settings/mod.rs +++ b/src/runtime/settings/mod.rs @@ -48,7 +48,7 @@ //! ```no_run //! # use asr::settings; //! let mut map = settings::Map::load(); -//! map.insert("key", &true.into()); +//! map.insert("key", true); //! map.store(); //! ``` //! diff --git a/src/runtime/settings/value.rs b/src/runtime/settings/value.rs index bf38db4..9077016 100644 --- a/src/runtime/settings/value.rs +++ b/src/runtime/settings/value.rs @@ -1,4 +1,4 @@ -use core::{fmt, mem::MaybeUninit}; +use core::{borrow::Borrow, fmt, mem::MaybeUninit}; use arrayvec::ArrayString; @@ -259,6 +259,34 @@ impl Drop for Value { } } +/// A trait for types that can be converted into a [`Value`] or allow accessing +/// it as a reference. +pub trait AsValue { + /// The type of the value. It needs to be able to be borrowed as a + /// [`Value`]. + type Output: Borrow; + /// Converts the value into a type that can be borrowed as a [`Value`]. + #[allow(clippy::wrong_self_convention)] + fn as_value(self) -> Self::Output; +} + +impl<'a> AsValue for &'a Value { + type Output = Self; + fn as_value(self) -> Self::Output { + self + } +} + +impl AsValue for T +where + Value: From, +{ + type Output = Value; + fn as_value(self) -> Self::Output { + self.into() + } +} + impl From<&Map> for Value { #[inline] fn from(value: &Map) -> Self { @@ -277,6 +305,20 @@ impl From<&List> for Value { } } +impl From for Value { + #[inline] + fn from(value: Map) -> Self { + (&value).into() + } +} + +impl From for Value { + #[inline] + fn from(value: List) -> Self { + (&value).into() + } +} + impl From for Value { #[inline] fn from(value: bool) -> Self {