Skip to content

Commit

Permalink
feat: use TryFrom instead of custom trait for StructValue conversion
Browse files Browse the repository at this point in the history
Signed-off-by: YUE Daian <[email protected]>
  • Loading branch information
sheepduke committed Dec 15, 2023
1 parent a0f9f80 commit b666105
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 20 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ async fn extended_example() {

#### Getting a Struct from a Provider

It is possible to extract a struct from the provider. Internally, this SDK defines a type `StructValue` to store any structure value. The `client.get_struct_value()` functions takes a type parameter `T`. It will try to parse `StructValue` resolved by the provider to `T`, as long as `T` implements trait `FromStructValue`.
It is possible to extract a struct from the provider. Internally, this SDK defines a type `StructValue` to store any structure value. The `client.get_struct_value()` functions takes a type parameter `T`. It will try to parse `StructValue` resolved by the provider to `T`, as long as `T` implements trait `TryFrom<StructValue>`.

You can pass in a type that satisfies this trait bound. When the conversion fails, it returns an `Err` with `EvaluationReason::TypeMismatch`.

Expand Down
34 changes: 16 additions & 18 deletions src/api/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,6 @@ pub struct Client {
global_evaluation_context: GlobalEvaluationContext,
}

/// The trait that converts a [`StructValue`] to a custom type.
/// It is used to return a custom type from `get_struct_value` and `get_string_details`.
pub trait FromStructValue<Out = Self> {
/// Construct type with given `value`.
fn from_struct_value(value: &StructValue) -> anyhow::Result<Out>;
}

impl Client {
/// Create a new [`Client`] instance.
pub fn new(
Expand Down Expand Up @@ -140,7 +133,7 @@ impl Client {
/// If the resolution fails, the `default_value` is returned.
/// The required type should implement [`From<StructValue>`] trait.
#[allow(unused_variables)]
pub async fn get_struct_value<T: FromStructValue>(
pub async fn get_struct_value<T: TryFrom<StructValue>>(
&self,
flag_key: &str,
evaluation_context: Option<&EvaluationContext>,
Expand All @@ -154,7 +147,7 @@ impl Client {
.resolve_struct_value(flag_key, &context)
.await?;

match T::from_struct_value(&result.value) {
match T::try_from(result.value) {
Ok(t) => Ok(t),
Err(error) => Err(EvaluationError {
code: EvaluationErrorCode::TypeMismatch,
Expand Down Expand Up @@ -242,7 +235,7 @@ impl Client {
/// Return the [`EvaluationDetails`] with given `flag_key`, `evaluation_context` and
/// `evaluation_options`.
#[allow(unused_variables)]
pub async fn get_struct_details<T: FromStructValue>(
pub async fn get_struct_details<T: TryFrom<StructValue>>(
&self,
flag_key: &str,
evaluation_context: Option<&EvaluationContext>,
Expand All @@ -256,7 +249,7 @@ impl Client {
.resolve_struct_value(flag_key, &context)
.await?;

match T::from_struct_value(&result.value) {
match T::try_from(result.value) {
Ok(value) => Ok(EvaluationDetails {
flag_key: flag_key.to_string(),
value,
Expand Down Expand Up @@ -322,8 +315,6 @@ mod tests {
Client, EvaluationReason, FlagMetadata, StructValue,
};

use super::FromStructValue;

#[spec(
number = "1.2.2",
text = "The client interface MUST define a metadata member or accessor, containing an immutable name field or accessor of type string, which corresponds to the name value supplied during client creation."
Expand All @@ -339,16 +330,23 @@ mod tests {
name: String,
}

impl FromStructValue for Student {
fn from_struct_value(value: &StructValue) -> anyhow::Result<Self> {
impl TryFrom<StructValue> for Student {
type Error = String;

fn try_from(value: StructValue) -> Result<Self, Self::Error> {
Ok(Student {
id: value.fields.get("id").unwrap().as_i64().unwrap(),
id: value
.fields
.get("id")
.ok_or("id not provided")?
.as_i64()
.ok_or("id is not a valid number")?,
name: value
.fields
.get("name")
.unwrap()
.ok_or("name not provided")?
.as_str()
.unwrap()
.ok_or("name is not a valid string")?
.to_string(),
})
}
Expand Down
2 changes: 1 addition & 1 deletion src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ mod api;
pub use api::OpenFeature;

mod client;
pub use client::{Client, ClientMetadata, FromStructValue};
pub use client::{Client, ClientMetadata};

mod provider_registry;

Expand Down

0 comments on commit b666105

Please sign in to comment.