Skip to content

Commit

Permalink
Adds some documentation and examples (#1697)
Browse files Browse the repository at this point in the history
Co-authored-by: Gabe Jackson <17556281+gj@users.noreply.github.com>
  • Loading branch information
xfbs and gj authored May 4, 2023
1 parent 2b6ff3c commit d43ed90
Show file tree
Hide file tree
Showing 4 changed files with 255 additions and 44 deletions.
128 changes: 121 additions & 7 deletions languages/rust/oso/src/host/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,20 @@ fn iterator_not_supported() -> IteratorMethod {
Arc::new(into_iter)
}

/// Class that can be registered with [`Oso`](crate::Oso).
///
/// A class represents an entity, such as an *actor* or a *resource*. It is typically backed by
/// a Rust struct and can carry some internal state, as well as have methods that can be made
/// accessible from within the policy file.
///
/// While the instance of the class itself is stored as a [`PolarValue::Instance`], the [`Class`]
/// struct contains metadata, such as the constructor, attributes, instance methods, comparison
/// functions as well as the name.
#[derive(Clone)]
pub struct Class {
/// The class name. Defaults to the `std::any::type_name`
/// The class name. Defaults to [`std::any::type_name`].
pub name: String,
/// Type ID of the class.
pub type_id: TypeId,
/// A wrapped method that constructs an instance of `T` from `PolarValue`s
constructor: Option<Constructor>,
Expand All @@ -67,15 +77,20 @@ pub struct Class {

into_iter: IteratorMethod,

// Hooks to be called on the class once it's been registered with host.
/// Hooks to be called on the class once it's been registered with host.
pub register_hooks: RegisterHooks,
}

impl Class {
/// Builder instance to build class.
///
/// Use this when you want to hook your own class into [`Oso`](crate::Oso).
/// See [`ClassBuilder`] for usage examples.
pub fn builder<T: 'static>() -> ClassBuilder<T> {
ClassBuilder::new()
}

/// Initialize new class instance.
pub fn init(&self, fields: Vec<PolarValue>) -> crate::Result<Instance> {
if let Some(constructor) = &self.constructor {
constructor.invoke(fields)
Expand All @@ -88,7 +103,7 @@ impl Class {

/// Call class method `attr` on `self` with arguments from `args`.
///
/// Returns: The result as a `PolarValue`
/// Returns the result as a `PolarValue`.
pub fn call(&self, attr: &str, args: Vec<PolarValue>) -> crate::Result<PolarValue> {
let attr =
self.class_methods
Expand Down Expand Up @@ -122,6 +137,16 @@ impl Class {
}
}

/// Builder for new Oso [`Class`].
///
/// This helps you create a `Class` instance which holds metadata for your custom type. Using the
/// builder, you can add attribute getters, class methods, instance methods, constants, iterator
/// methods, override the class name, set the constructor or equality check.
///
/// You can create a new instance of [`ClassBuilder`] using
/// [`PolarClass::get_polar_class_builder()`](crate::PolarClass::get_polar_class_builder), using
/// [`Class::builder()`] or using one of the [`ClassBuilder::with_default()`] or
/// [`ClassBuilder::with_constructor()`] methods.
#[derive(Clone)]
pub struct ClassBuilder<T> {
class: Class,
Expand Down Expand Up @@ -153,7 +178,23 @@ where
}
}

/// Create a new class builder for a type that implements Default and use that as the constructor.
/// Create a new class builder for a type that implements [`Default`] and use that as the
/// constructor.
///
/// This is equivalent to setting the constructor to [`Default::default()`].
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use oso::ClassBuilder;
///
/// #[derive(Default)]
/// struct MyClass;
///
/// let class = ClassBuilder::<MyClass>::with_default().build();
/// ```
pub fn with_default() -> Self
where
T: std::default::Default,
Expand All @@ -163,6 +204,19 @@ where
}

/// Create a new class builder with a given constructor.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use oso::ClassBuilder;
///
/// struct MyClass(u16);
///
/// let class = ClassBuilder::<MyClass>::with_constructor(|| MyClass(42)).build();
///
/// ```
pub fn with_constructor<F, Args>(f: F) -> Self
where
F: Function<Args, Result = T>,
Expand All @@ -175,6 +229,12 @@ where
}

/// Set the constructor function to use for polar `new` statements.
///
/// # Examples
///
/// ```
/// use oso::ClassBuilder;
/// ```
pub fn set_constructor<F, Args>(mut self, f: F) -> Self
where
F: Function<Args, Result = T>,
Expand All @@ -186,6 +246,21 @@ where
}

/// Set an equality function to be used for polar `==` statements.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use oso::ClassBuilder;
///
/// #[derive(Default)]
/// struct MyClass;
///
/// let class = ClassBuilder::<MyClass>::with_default()
/// .set_equality_check(|left, right| true)
/// .build();
/// ```
pub fn set_equality_check<F>(mut self, f: F) -> Self
where
F: Fn(&T, &T) -> bool + Send + Sync + 'static,
Expand Down Expand Up @@ -230,16 +305,55 @@ where
self.set_into_iter(|t| t.clone().into_iter())
}

/// Use PartialEq::eq as the equality check for polar `==` statements.
/// Use [`PartialEq`] as the equality check for Polar `==` statements.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use oso::ClassBuilder;
///
/// #[derive(Default, PartialEq)]
/// struct MyClass(u64);
///
/// let class = ClassBuilder::<MyClass>::with_default()
/// .with_equality_check()
/// .build();
/// ```
pub fn with_equality_check(self) -> Self
where
T: PartialEq<T>,
{
self.set_equality_check(|a, b| PartialEq::eq(a, b))
}

/// Add an attribute getter for statements like `foo.bar`
/// `class.add_attribute_getter("bar", |instance| instance.bar)
/// Add an attribute getter.
///
/// An attribute getter allows you to write statements like `foo.bar`, where `foo` is a class
/// instance and `bar` is an attribute.
///
/// Typically, if you use the [`PolarClass`] derive macro, you can use `#[polar(attribute)]` to
/// generate this automatically.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use oso::ClassBuilder;
///
/// #[derive(Default)]
/// struct MyClass {
/// name: String,
/// age: u32,
/// };
///
/// let class = ClassBuilder::<MyClass>::with_default()
/// .add_attribute_getter("name", |instance| instance.name.clone())
/// .add_attribute_getter("age", |instance| instance.age)
/// .build();
/// ```
pub fn add_attribute_getter<F, R>(mut self, name: &'static str, f: F) -> Self
where
F: Fn(&T) -> R + Send + Sync + 'static,
Expand Down
85 changes: 71 additions & 14 deletions languages/rust/oso/src/host/to_polar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,48 @@ use crate::PolarValue;

/// Convert Rust types to Polar types.
///
/// This trait is automatically implemented for any
/// type that implements the `PolarClass` marker trait,
/// which should be preferred.
/// This trait is automatically implemented for any type that implements the [`PolarClass`] marker
/// trait, which should be preferred. You can use the [`PolarClass`](oso_derive::PolarClass) derive
/// macro to derive that trait.
///
/// This is also implemented automatically using the
/// `#[derive(PolarClass)]` macro.
/// For non-primitive types, the instance will be stored on the provided `Host`.
///
/// For non-primitive types, the instance will be stored
/// on the provided `Host`.
/// # Trait bounds
///
/// ## Trait bounds
/// The default implementation for [`PolarClass`] requires types to be [`Send`] and [`Sync`], since
/// it is possible to store a [`ToPolar`] value on an [`Oso`](crate::Oso) instance which can be
/// shared between threads.
///
/// The default implementation for `PolarClass`
/// requires types to be `Send + Sync`, since it
/// is possible to store a `ToPolar` value on an `Oso` instance
/// which can be shared between threads.
/// [`ToPolar`] implementors must also be concrete, sized types without any borrows.
///
/// `ToPolar` implementors must also be concrete, sized types without
/// any borrows.
/// # Examples
///
/// Convert a primitive type into a polar value:
///
/// ```rust
/// use oso::{PolarValue, ToPolar};
///
/// let string = "This is a string";
/// let value = string.to_polar();
///
/// assert_eq!(value, PolarValue::String(string.into()));
/// ```
///
/// Convert a custom type into a polar value:
///
/// ```
/// use oso::{PolarValue, PolarClass, ToPolar};
///
/// #[derive(PolarClass)]
/// struct MyClass;
///
/// let class = MyClass;
/// let value = class.to_polar();
///
/// assert!(matches!(value, PolarValue::Instance(_)));
/// ```
pub trait ToPolar {
/// Convert this value into a Polar value.
fn to_polar(self) -> PolarValue;
}

Expand Down Expand Up @@ -81,7 +103,42 @@ mod private {
pub trait Sealed {}
}

/// Convert tuples to Polar types.
///
/// This is a helper trait to convert Rust tuples (of types which implement [`ToPolar`]) into a
/// [`Vec<PolarValue>`].
///
/// # Examples
///
/// Empty tuple:
///
/// ```
/// use oso::ToPolarList;
///
/// assert_eq!(().to_polar_list(), vec![]);
/// ```
///
/// Mixed tuples:
///
/// ```rust
/// use oso::{PolarValue, PolarClass, ToPolarList};
///
/// #[derive(PolarClass)]
/// struct MyClass;
///
/// let class = MyClass;
/// let string = "Hello, World!";
/// let number = 42;
///
/// let list = (class, string, number).to_polar_list();
///
/// assert_eq!(list.len(), 3);
/// assert!(matches!(list[0], PolarValue::Instance(_)));
/// assert_eq!(list[1], PolarValue::String(string.to_string()));
/// assert_eq!(list[2], PolarValue::Integer(number));
/// ```
pub trait ToPolarList: private::Sealed {
/// Convert these values into an array of Polar values.
fn to_polar_list(self) -> Vec<PolarValue>
where
Self: Sized;
Expand Down
Loading

2 comments on commit d43ed90

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rust Benchmark

Benchmark suite Current: d43ed90 Previous: 2b6ff3c Ratio
rust_get_attribute 56452 ns/iter (± 7773) 42799 ns/iter (± 1195) 1.32
n_plus_one/100 2545185 ns/iter (± 108763) 2233006 ns/iter (± 12928) 1.14
n_plus_one/500 12398343 ns/iter (± 570100) 10827113 ns/iter (± 347573) 1.15
n_plus_one/1000 24196617 ns/iter (± 1515819) 21473327 ns/iter (± 365087) 1.13
unify_once 1047 ns/iter (± 306) 871 ns/iter (± 281) 1.20
unify_twice 2751 ns/iter (± 210) 2573 ns/iter (± 55) 1.07
many_rules 66164 ns/iter (± 3561) 60312 ns/iter (± 1032) 1.10
fib/5 611973 ns/iter (± 22169) 531546 ns/iter (± 9581) 1.15
prime/3 20226 ns/iter (± 2014) 17596 ns/iter (± 474) 1.15
prime/23 19968 ns/iter (± 2122) 17586 ns/iter (± 510) 1.14
prime/43 20224 ns/iter (± 2486) 17588 ns/iter (± 479) 1.15
prime/83 20429 ns/iter (± 2126) 17621 ns/iter (± 457) 1.16
prime/255 18339 ns/iter (± 1341) 15774 ns/iter (± 486) 1.16
indexed/100 6559 ns/iter (± 1392) 5286 ns/iter (± 527) 1.24
indexed/500 7836 ns/iter (± 3479) 6002 ns/iter (± 1422) 1.31
indexed/1000 10447 ns/iter (± 2071) 7276 ns/iter (± 251) 1.44
indexed/10000 22325 ns/iter (± 5098) 15375 ns/iter (± 2455) 1.45
not 6630 ns/iter (± 772) 5776 ns/iter (± 76) 1.15
double_not 13967 ns/iter (± 794) 11966 ns/iter (± 459) 1.17
De_Morgan_not 8800 ns/iter (± 373) 7850 ns/iter (± 108) 1.12
load_policy 1054241 ns/iter (± 81544) 942494 ns/iter (± 2827) 1.12
partial_and/1 36945 ns/iter (± 3682) 30876 ns/iter (± 1128) 1.20
partial_and/5 131455 ns/iter (± 15383) 104732 ns/iter (± 2955) 1.26
partial_and/10 240575 ns/iter (± 58941) 200151 ns/iter (± 8764) 1.20
partial_and/20 497525 ns/iter (± 23622) 422584 ns/iter (± 13041) 1.18
partial_and/40 1093992 ns/iter (± 82265) 914584 ns/iter (± 16112) 1.20
partial_and/80 2481239 ns/iter (± 166244) 2084689 ns/iter (± 9713) 1.19
partial_and/100 3213273 ns/iter (± 207880) 2761225 ns/iter (± 11016) 1.16
partial_rule_depth/1 120928 ns/iter (± 13954) 95358 ns/iter (± 3314) 1.27
partial_rule_depth/5 390782 ns/iter (± 26338) 323104 ns/iter (± 7479) 1.21
partial_rule_depth/10 894062 ns/iter (± 53593) 713586 ns/iter (± 10936) 1.25
partial_rule_depth/20 2547297 ns/iter (± 230047) 2041455 ns/iter (± 22542) 1.25
partial_rule_depth/40 9954455 ns/iter (± 640327) 7279647 ns/iter (± 46667) 1.37
partial_rule_depth/80 64740108 ns/iter (± 3354331) 36757365 ns/iter (± 322586) 1.76
partial_rule_depth/100 118806810 ns/iter (± 3533319) 68437300 ns/iter (± 480564) 1.74

This comment was automatically generated by workflow using github-action-benchmark.

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'Rust Benchmark'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.50.

Benchmark suite Current: d43ed90 Previous: 2b6ff3c Ratio
partial_rule_depth/80 64740108 ns/iter (± 3354331) 36757365 ns/iter (± 322586) 1.76
partial_rule_depth/100 118806810 ns/iter (± 3533319) 68437300 ns/iter (± 480564) 1.74

This comment was automatically generated by workflow using github-action-benchmark.

CC: @osohq/eng

Please sign in to comment.