mirror of
https://github.com/enso-org/enso.git
synced 2024-12-28 15:53:35 +03:00
Implementation of optimization passes to the FRP engine. ⌛ (https://github.com/enso-org/ide/pull/135)
Original commit: 675039a37d
This commit is contained in:
parent
3c95b1502f
commit
adf6a10fb9
@ -8,6 +8,7 @@ members = [
|
||||
"lib/core/msdf-sys",
|
||||
"lib/data",
|
||||
"lib/eval-tt",
|
||||
"lib/frp",
|
||||
"lib/ide/ast/impl",
|
||||
"lib/ide/ast/macros",
|
||||
"lib/ide/file-manager",
|
||||
|
@ -25,6 +25,6 @@ module.exports = {
|
||||
},
|
||||
performance: {
|
||||
hints: 'error',
|
||||
maxAssetSize: 4.5 * mb,
|
||||
maxAssetSize: 4.7 * mb,
|
||||
},
|
||||
};
|
||||
|
@ -19,6 +19,7 @@ enso-prelude = { version = "0.1.0" , path = "../prelude" }
|
||||
basegl-system-web = { version = "0.1.0" , path = "../system/web" }
|
||||
code-builder = { version = "0.1.0" , path = "../code-builder" }
|
||||
data = { version = "0.1.0" , path = "../data" }
|
||||
enso-frp = { version = "0.1.0" , path = "../frp" }
|
||||
eval-tt = { version = "0.1.0" , path = "../eval-tt" }
|
||||
logger = { version = "0.1.0" , path = "../logger" }
|
||||
optics = { version = "0.1.0" , path = "../optics" }
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
pub mod callback;
|
||||
pub mod event_loop;
|
||||
pub mod frp;
|
||||
pub mod io;
|
||||
|
||||
pub use event_loop::*;
|
||||
|
@ -1,790 +0,0 @@
|
||||
//! This module implements an Functional Reactive Programming system. It is an advanced event
|
||||
//! handling framework which allows describing events and actions by creating declarative event
|
||||
//! flow diagrams.
|
||||
//!
|
||||
//! **WARNING**
|
||||
//! Please note that this file is under heavy development so it may be missing proper docs here and
|
||||
//! there and the API can drastically change day after day.
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
// === Macros ===
|
||||
// ==============
|
||||
|
||||
macro_rules! alias {
|
||||
($( $(#$meta:tt)* $name:ident = {$($tok:tt)*} )*) => {$(
|
||||
$(#$meta)*
|
||||
pub trait $name: $($tok)* {}
|
||||
impl<T:$($tok)*> $name for T {}
|
||||
)*}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===============
|
||||
// === Wrapper ===
|
||||
// ===============
|
||||
|
||||
/// Trait for objects which wrap values.
|
||||
///
|
||||
/// Please note that this implements safe wrappers, so the object - value relation must be
|
||||
/// bijective.
|
||||
pub trait Wrapper {
|
||||
|
||||
/// The wrapped value type.
|
||||
type Content;
|
||||
|
||||
/// Wraps the value and returns the wrapped type.
|
||||
fn wrap(t:Self::Content) -> Self;
|
||||
|
||||
/// Unwraps this type to get the inner value.
|
||||
fn unwrap(&self) -> &Self::Content;
|
||||
}
|
||||
|
||||
/// Accessor for the wrapped value.
|
||||
pub type Unwrap<T> = <T as Wrapper>::Content;
|
||||
|
||||
/// Wraps the value and returns the wrapped type.
|
||||
pub fn wrap<T:Wrapper>(t:T::Content) -> T {
|
||||
T::wrap(t)
|
||||
}
|
||||
|
||||
/// Unwraps this type to get the inner value.
|
||||
pub fn unwrap<T:Wrapper>(t:&T) -> &T::Content {
|
||||
T::unwrap(t)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===============
|
||||
// === Message ===
|
||||
// ===============
|
||||
|
||||
// === Types ===
|
||||
|
||||
alias! {
|
||||
/// Message is a data send between FRP nodes.
|
||||
/// There are two important message implementation – the `BehaviorMessage` and `EventMessage`.
|
||||
Message = { MessageValue + ValueWrapper + KnownNodeStorage }
|
||||
|
||||
/// Abstraction for a value carried by a message.
|
||||
MessageValue = { Clone + Debug + Default + 'static }
|
||||
}
|
||||
|
||||
/// Accessor to a value of a given message. For example, `Value<Behavior<i32>>` resolves to `i32`.
|
||||
pub type Value<T> = Unwrap<T>;
|
||||
|
||||
/// Alias to `Wrapper` with the inner type being `Debug`.
|
||||
pub trait ValueWrapper = Wrapper where Unwrap<Self>:Debug;
|
||||
|
||||
|
||||
// === Definition ===
|
||||
|
||||
/// A newtype containing a value of an event.
|
||||
#[derive(Clone,Copy,Debug,Default)]
|
||||
pub struct EventMessage<T>(T);
|
||||
|
||||
/// A newtype containing a value of a behavior.
|
||||
#[derive(Clone,Copy,Debug,Default)]
|
||||
pub struct BehaviorMessage<T>(T);
|
||||
|
||||
|
||||
// === API ===
|
||||
|
||||
impl<T:Clone> EventMessage<T> {
|
||||
/// Get the unwrapped value of this message.
|
||||
pub fn value(&self) -> T {
|
||||
self.unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Clone> BehaviorMessage<T> {
|
||||
/// Get the unwrapped value of this message.
|
||||
pub fn value(&self) -> T {
|
||||
self.unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Wrappers ===
|
||||
|
||||
impl Wrapper for () {
|
||||
type Content = ();
|
||||
fn wrap (_:()) -> Self {}
|
||||
fn unwrap (&self) -> &() { self }
|
||||
}
|
||||
|
||||
impl<T> Wrapper for EventMessage<T> {
|
||||
type Content = T;
|
||||
fn wrap (t:T) -> Self { EventMessage(t) }
|
||||
fn unwrap (&self) -> &T { &self.0 }
|
||||
}
|
||||
|
||||
impl<T> Wrapper for BehaviorMessage<T> {
|
||||
type Content = T;
|
||||
fn wrap (t:T) -> Self { BehaviorMessage(t) }
|
||||
fn unwrap (&self) -> &T { &self.0 }
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ======================
|
||||
// === Input / Output ===
|
||||
// ======================
|
||||
|
||||
/// Event input associated type. Please note that FRP nodes can have maximum one event input.
|
||||
/// In such a case this trait points to it.
|
||||
pub trait KnownEventInput {
|
||||
/// The event input type.
|
||||
type EventInput : Message;
|
||||
}
|
||||
|
||||
/// Event input accessor.
|
||||
pub type EventInput<T> = <T as KnownEventInput>::EventInput;
|
||||
|
||||
|
||||
/// Each FRP node has a single node, which type is described by this trait.
|
||||
pub trait KnownOutput {
|
||||
/// The output type.
|
||||
type Output : Message;
|
||||
}
|
||||
|
||||
/// Node output accessor.
|
||||
pub type Output<T> = <T as KnownOutput>::Output;
|
||||
|
||||
|
||||
|
||||
// ===================
|
||||
// === NodeStorage ===
|
||||
// ===================
|
||||
|
||||
/// Type level abstraction for node internal storage.
|
||||
pub trait KnownNodeStorage {
|
||||
/// The node storage type.
|
||||
type NodeStorage: CloneRef + Debug;
|
||||
}
|
||||
|
||||
/// Internal node storage type accessor.
|
||||
pub type NodeStorage<T> = <T as KnownNodeStorage>::NodeStorage;
|
||||
|
||||
impl KnownNodeStorage for () {
|
||||
type NodeStorage = ();
|
||||
}
|
||||
|
||||
|
||||
// === EventNodeStorage ===
|
||||
|
||||
/// Event node operations.
|
||||
pub trait EventNodeStorage: KnownOutput + Debug {
|
||||
/// Registers a new event target. Whenever a new event arrives it will be transmitted to all
|
||||
/// registered targets.
|
||||
fn add_event_target(&self, target:AnyEventConsumer<Output<Self>>);
|
||||
}
|
||||
|
||||
impl<Out> KnownNodeStorage for EventMessage<Out> {
|
||||
type NodeStorage = Rc<dyn EventNodeStorage<Output=EventMessage<Out>>>;
|
||||
}
|
||||
|
||||
|
||||
// === BehaviorNodeStorage ===
|
||||
|
||||
/// Behavior node operations.
|
||||
pub trait BehaviorNodeStorage: KnownOutput + Debug {
|
||||
/// Returns the current value of the behavior.
|
||||
fn current_value(&self) -> Value<Output<Self>>;
|
||||
}
|
||||
|
||||
impl<Out> KnownNodeStorage for BehaviorMessage<Out> {
|
||||
type NodeStorage = Rc<dyn BehaviorNodeStorage<Output=BehaviorMessage<Out>>>;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============
|
||||
// === Node ===
|
||||
// ============
|
||||
|
||||
// === Types ===
|
||||
|
||||
/// The type of any FRP node which produces event messages. Having a reference to a node is like
|
||||
/// having a reference to network endpoint which transmits messages of a given type. Thus, it is a
|
||||
/// nice mental simplification to think about it just like about an event (stream).
|
||||
pub type Event<T> = Node<EventMessage<T>>;
|
||||
|
||||
/// The type of any FRP node which can be queried for behavior value. Having a reference to a node
|
||||
/// is like having a reference to network endpoint which transmits messages of a given type. Thus,
|
||||
/// it is a nice mental simplification to think about it just like about a behavior.
|
||||
pub type Behavior <T> = Node<BehaviorMessage<T>>;
|
||||
|
||||
|
||||
// === Definition ===
|
||||
|
||||
/// Node is used as a common types for frp operations. For example, `Event<T>` is just an alias to
|
||||
/// `Node<EventMessage<T>>`.
|
||||
#[derive(Debug)]
|
||||
pub struct Node<Out:KnownNodeStorage> {
|
||||
storage: NodeStorage<Out>,
|
||||
}
|
||||
|
||||
impl<Out:Message> Node<Out> {
|
||||
/// Constructor.
|
||||
pub fn new(storage:NodeStorage<Out>) -> Self {
|
||||
Self {storage}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Instances ===
|
||||
|
||||
impl<Out:Message> KnownOutput for Node<Out> { type Output = Out; }
|
||||
|
||||
impl<Out:KnownNodeStorage> Deref for Node<Out> {
|
||||
type Target = NodeStorage<Out>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.storage
|
||||
}
|
||||
}
|
||||
|
||||
impl<Out:KnownNodeStorage> Clone for Node<Out> {
|
||||
fn clone(&self) -> Self {
|
||||
let storage = self.storage.clone();
|
||||
Self {storage}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Out:KnownNodeStorage> CloneRef for Node<Out> {
|
||||
fn clone_ref(&self) -> Self {
|
||||
let storage = self.storage.clone_ref();
|
||||
Self {storage}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Out:KnownNodeStorage> From<&Node<Out>> for Node<Out> {
|
||||
fn from(t:&Node<Out>) -> Self {
|
||||
t.clone_ref()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Construction ===
|
||||
|
||||
impl<Storage,Out> From<&Storage> for Node<BehaviorMessage<Out>>
|
||||
where Storage : BehaviorNodeStorage<Output=BehaviorMessage<Out>> + Clone + 'static,
|
||||
Out : MessageValue {
|
||||
fn from(storage:&Storage) -> Self {
|
||||
Self::new(Rc::new(storage.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<Storage,Out> From<&Storage> for Node<EventMessage<Out>>
|
||||
where Storage : EventNodeStorage<Output=EventMessage<Out>> + Clone + 'static,
|
||||
Out : MessageValue {
|
||||
fn from(storage:&Storage) -> Self {
|
||||
Self::new(Rc::new(storage.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === AddTarget ===
|
||||
|
||||
/// Abstraction for adding a target to a given node. Nodes which carry behaviors do not need to
|
||||
/// perform any operation here, while event streams want to register the nodes they want to send
|
||||
/// notifications to.
|
||||
pub trait AddTarget<T> {
|
||||
/// Adds a node as a target of the current flow.
|
||||
fn add_target(&self,t:&T);
|
||||
}
|
||||
|
||||
impl<S,T> AddTarget<S> for Node<EventMessage<T>>
|
||||
where for<'t> &'t S : Into<AnyEventConsumer<EventMessage<T>>> {
|
||||
fn add_target(&self,t:&S) {
|
||||
self.add_event_target(t.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S,T> AddTarget<S> for Node<BehaviorMessage<T>> {
|
||||
fn add_target(&self,_:&S) {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===================
|
||||
// === NodeWrapper ===
|
||||
// ===================
|
||||
|
||||
// === NodeWrapper ===
|
||||
|
||||
/// `NodeWrapper` is an outer layer for every FRP node. For example, the `Source<Out>` node is just
|
||||
/// an alias to `NodeWrapper<SourceData<Out>>`, where `SourceData` is it's internal representation.
|
||||
/// This struct bundles each node with information about target edges. Although the edges are used
|
||||
/// only to send events, they are bundled to every node type in order to keep the implementation
|
||||
/// simple.
|
||||
pub type NodeWrapper<Shape> = NodeWrapperTemplate<Shape,Output<Shape>>;
|
||||
|
||||
impl<Shape:KnownOutput> NodeWrapper<Shape> {
|
||||
/// Constructor.
|
||||
pub fn construct(shape:Shape) -> Self {
|
||||
let data = NodeWrapperTemplateData::construct(shape);
|
||||
let rc = Rc::new(RefCell::new(data));
|
||||
Self {rc}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Shape:KnownOutput> NodeWrapper<Shape> {
|
||||
/// Sends an event to all the children.
|
||||
pub fn emit_event(&self, event:&Output<Shape>) {
|
||||
self.rc.borrow().targets.iter().for_each(|target| {
|
||||
target.on_event(event)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Shape:KnownOutput + Debug>
|
||||
EventNodeStorage for NodeWrapper<Shape>
|
||||
where Output<Self>:'static, Output<Shape>:Message {
|
||||
fn add_event_target(&self, target:AnyEventConsumer<Output<Self>>) {
|
||||
self.rc.borrow_mut().targets.push(target);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<Shape:BehaviorNodeStorage + Debug>
|
||||
BehaviorNodeStorage for NodeWrapper<Shape>
|
||||
where Output<Shape>:Message {
|
||||
fn current_value(&self) -> Value<Output<Self>> {
|
||||
self.rc.borrow().shape.current_value()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === NodeWrapperTemplate ===
|
||||
|
||||
/// Internal representation for `NodeWrapper`.
|
||||
#[derive(Debug,Derivative)]
|
||||
#[derivative(Default(bound="Shape:Default"))]
|
||||
#[derivative(Clone(bound=""))]
|
||||
pub struct NodeWrapperTemplate<Shape,Out> {
|
||||
rc: Rc<RefCell<NodeWrapperTemplateData<Shape,Out>>>,
|
||||
}
|
||||
|
||||
impl<Shape,Out:Message> KnownOutput for NodeWrapperTemplate<Shape,Out> {
|
||||
type Output = Out;
|
||||
}
|
||||
|
||||
impl<Shape:KnownEventInput,Out> KnownEventInput for NodeWrapperTemplate<Shape,Out>
|
||||
where Value<EventInput<Shape>> : Debug {
|
||||
type EventInput = EventInput<Shape>;
|
||||
}
|
||||
|
||||
impl<Shape,Out> CloneRef for NodeWrapperTemplate<Shape,Out> {}
|
||||
|
||||
|
||||
|
||||
// === NodeWrapperTemplateData ===
|
||||
|
||||
/// Internal representation for `NodeWrapperTemplate`.
|
||||
#[derive(Debug,Derivative)]
|
||||
#[derivative(Default(bound="Shape:Default"))]
|
||||
pub struct NodeWrapperTemplateData<Shape,Out> {
|
||||
shape : Shape,
|
||||
targets : Vec<AnyEventConsumer<Out>>,
|
||||
}
|
||||
|
||||
impl<Shape,Out> NodeWrapperTemplateData<Shape,Out> {
|
||||
/// Constructor.
|
||||
pub fn construct(shape:Shape) -> Self {
|
||||
let targets = default();
|
||||
Self {shape,targets}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =====================
|
||||
// === EventConsumer ===
|
||||
// =====================
|
||||
|
||||
// === Definition ===
|
||||
|
||||
/// Abstraction for nodes which are able to consume events.
|
||||
pub trait EventConsumer: KnownEventInput + Debug {
|
||||
/// Function called on every new received event.
|
||||
fn on_event(&self, input:&Self::EventInput);
|
||||
}
|
||||
|
||||
|
||||
// === AnyEventConsumer ===
|
||||
|
||||
/// Abstraction for any node which consumes events of a given type.
|
||||
#[derive(Clone,Debug,Shrinkwrap)]
|
||||
pub struct AnyEventConsumer<In> {
|
||||
raw: Rc<dyn EventConsumer<EventInput=In>>,
|
||||
}
|
||||
|
||||
impl<In:Message> AnyEventConsumer<In> {
|
||||
/// Constructor.
|
||||
pub fn new<A:EventConsumer<EventInput=In>+'static>(a:A) -> Self {
|
||||
let raw = Rc::new(a);
|
||||
Self {raw}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T,In> From<&T> for AnyEventConsumer<In>
|
||||
where T : EventConsumer<EventInput=In> + Clone + 'static,
|
||||
In : Message {
|
||||
fn from(t:&T) -> Self {
|
||||
Self::new(t.clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =========================
|
||||
// === Inference Helpers ===
|
||||
// =========================
|
||||
|
||||
/// Message product type-level inference guidance.
|
||||
pub trait Infer<T> {
|
||||
/// Inference results.
|
||||
type Result;
|
||||
}
|
||||
|
||||
/// Accessor for inferred type.
|
||||
pub type Inferred<T,X> = <X as Infer<T>>::Result;
|
||||
|
||||
|
||||
// === Rules ===
|
||||
|
||||
macro_rules! inference_rules {
|
||||
($( $pat:tt => $result:ident )*) => {$(
|
||||
inference_rule! { $pat => $result }
|
||||
)*}
|
||||
|
||||
}
|
||||
|
||||
macro_rules! inference_rule {
|
||||
( $t1:ident => $result:ident ) => {
|
||||
impl<X,T1> Infer <$t1<T1>> for X { type Result = $result<X>; }
|
||||
};
|
||||
|
||||
( ($t1:ident) => $result:ident ) => {
|
||||
impl<X,T1> Infer <$t1<T1>> for X { type Result = $result<X>; }
|
||||
};
|
||||
|
||||
( ($t1:ident, $t2:ident) => $result:ident ) => {
|
||||
impl<X,T1,T2> Infer <($t1<T1>,$t2<T2>)> for X { type Result = $result<X>; }
|
||||
};
|
||||
|
||||
( ($t1:ident, $t2:ident, $t3:ident) => $result:ident ) => {
|
||||
impl<X,T1,T2,T3> Infer <($t1<T1>,$t2<T2>,$t3<T3>)> for X { type Result = $result<X>; }
|
||||
};
|
||||
}
|
||||
|
||||
inference_rules! {
|
||||
EventMessage => EventMessage
|
||||
BehaviorMessage => BehaviorMessage
|
||||
|
||||
(EventMessage , EventMessage ) => EventMessage
|
||||
(BehaviorMessage , EventMessage ) => EventMessage
|
||||
(EventMessage , BehaviorMessage) => EventMessage
|
||||
(BehaviorMessage , BehaviorMessage) => EventMessage
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================================================================================================
|
||||
// === FRP Nodes ===================================================================================
|
||||
// =================================================================================================
|
||||
|
||||
// ==============
|
||||
// === Source ===
|
||||
// ==============
|
||||
|
||||
// === Storage ===
|
||||
|
||||
/// Internal source storage accessor.
|
||||
pub type SourceStorage<T> = <T as KnownSourceStorage>::SourceStorage;
|
||||
|
||||
/// Internal source storage type.
|
||||
pub trait KnownSourceStorage {
|
||||
/// The result type.
|
||||
type SourceStorage : Default;
|
||||
}
|
||||
|
||||
impl<T> KnownSourceStorage for EventMessage <T> {type SourceStorage = ();}
|
||||
impl<T:Default> KnownSourceStorage for BehaviorMessage<T> {type SourceStorage = BehaviorMessage<T>;}
|
||||
|
||||
|
||||
// === Definition ===
|
||||
|
||||
/// Source is a begin point in the FRP network. It is able to emit events or initialize behaviors.
|
||||
type Source<Out> = NodeWrapper<SourceData<Out>>;
|
||||
|
||||
/// Internal definition of the source FRP node.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default (bound="SourceStorage<Out>:Default"))]
|
||||
#[derivative(Debug (bound="SourceStorage<Out>:Debug"))]
|
||||
pub struct SourceData<Out:KnownSourceStorage> {
|
||||
storage: SourceStorage<Out>
|
||||
}
|
||||
|
||||
impl<Out> KnownOutput for SourceData<Out>
|
||||
where Out : KnownSourceStorage + Message {
|
||||
type Output = Out;
|
||||
}
|
||||
|
||||
impl<Out> Source<Out>
|
||||
where Out : KnownSourceStorage + Message {
|
||||
/// Constructor.
|
||||
pub fn new() -> Self {
|
||||
default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Out> BehaviorNodeStorage for SourceData<BehaviorMessage<Out>>
|
||||
where Out : MessageValue {
|
||||
fn current_value(&self) -> Out {
|
||||
self.storage.value()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
// === Lambda ===
|
||||
// ==============
|
||||
|
||||
/// Transforms input data with the provided function. Lambda accepts a single input and outputs
|
||||
/// message of the same type as the input message.
|
||||
pub type Lambda<In,Out> = NodeWrapper<LambdaShape<In,Out>>;
|
||||
|
||||
/// Internal representation of `Lambda`.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Debug)]
|
||||
pub struct LambdaShape<In:Message,Out:Message> {
|
||||
source : Node<In>,
|
||||
#[derivative(Debug="ignore")]
|
||||
func : Rc<dyn Fn(&Value<In>) -> Out>,
|
||||
}
|
||||
|
||||
|
||||
// === Instances ===
|
||||
|
||||
impl<In:Message,Out:Message> KnownEventInput for LambdaShape<In,Out> { type EventInput = In; }
|
||||
impl<In:Message,Out:Message> KnownOutput for LambdaShape<In,Out> { type Output = Out; }
|
||||
|
||||
|
||||
// === Constructor ===
|
||||
|
||||
/// Constructor abstraction. Used only to satisfy Rust type system.
|
||||
pub trait LambdaNew<Source,Func> {
|
||||
/// Constructor.
|
||||
fn new(source:Source,f:Func) -> Self;
|
||||
}
|
||||
|
||||
impl<In,OutVal,Func,Source> LambdaNew<Source,Func> for Lambda<In,Inferred<In,OutVal>>
|
||||
where In : Message,
|
||||
OutVal : Infer<In>,
|
||||
Func : 'static + Fn(&Value<In>) -> OutVal,
|
||||
Source : Into<Node<In>>,
|
||||
Node<In> : AddTarget<Self>,
|
||||
Inferred<In,OutVal> : Message<Content=OutVal> {
|
||||
fn new (source:Source, f:Func) -> Self {
|
||||
let source = source.into();
|
||||
let source_ref = source.clone();
|
||||
let shape = LambdaShape::new(source,f);
|
||||
let this = Self::construct(shape);
|
||||
source_ref.add_target(&this);
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
impl<In:Message,Out:Message> LambdaShape<In,Out> {
|
||||
/// Constructor.
|
||||
pub fn new<Func,Source>(source:Source, f:Func) -> Self
|
||||
where Func : 'static + Fn(&Value<In>) -> Value<Out>,
|
||||
Source : Into<Node<In>> {
|
||||
let source = source.into();
|
||||
let func = Rc::new(move |t:&Value<In>| { wrap(f(t)) });
|
||||
Self {source,func}
|
||||
}
|
||||
}
|
||||
|
||||
impl<In:Message,Out:Message> EventConsumer for Lambda<In,Out> {
|
||||
fn on_event(&self, input:&Self::EventInput) {
|
||||
println!("GOT {:?}",input);
|
||||
let output = (self.rc.borrow().shape.func)(unwrap(input));
|
||||
self.emit_event(&output);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===============
|
||||
// === Lambda2 ===
|
||||
// ===============
|
||||
|
||||
/// Transforms input data with the provided function. `Lambda2` accepts two inputs. If at least one
|
||||
/// of the inputs was event, the output message will be event as well. In case both inputs were
|
||||
/// behavior, a new behavior will be produced.
|
||||
pub type Lambda2<In1,In2,Out> = NodeWrapper<Lambda2Shape<In1,In2,Out>>;
|
||||
|
||||
/// Internal representation for `Lambda2`.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Debug)]
|
||||
pub struct Lambda2Shape<In1:Message,In2:Message,Out:Message> {
|
||||
source1 : Node<In1>,
|
||||
source2 : Node<In2>,
|
||||
#[derivative(Debug="ignore")]
|
||||
func : Rc<dyn Fn(&Value<In1>,&Value<In2>) -> Out>,
|
||||
}
|
||||
|
||||
impl<In1:Message,In2:Message,Out:Message>
|
||||
Lambda2Shape<In1,In2,Out> {
|
||||
/// Constructor.
|
||||
pub fn new
|
||||
< F:'static + Fn(&Value<In1>,&Value<In2>) -> Value<Out>
|
||||
, Source1:Into<Node<In1>>
|
||||
, Source2:Into<Node<In2>>
|
||||
>
|
||||
(source1:Source1, source2:Source2, f:F) -> Self {
|
||||
let source1 = source1.into();
|
||||
let source2 = source2.into();
|
||||
let func = Rc::new(move |a:&Value<In1>,b:&Value<In2>| { wrap(f(a,b)) });
|
||||
Self {source1,source2,func}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Instances ===
|
||||
|
||||
impl<In1,In2,Out> KnownEventInput for Lambda2Shape<EventMessage<In1>,BehaviorMessage<In2>,Out>
|
||||
where In1:MessageValue,In2:MessageValue,Out:Message {
|
||||
type EventInput = EventMessage<In1>;
|
||||
}
|
||||
|
||||
impl<In1,In2,Out> KnownEventInput for Lambda2Shape<BehaviorMessage<In1>,EventMessage<In2>,Out>
|
||||
where In1:MessageValue,In2:MessageValue,Out:Message {
|
||||
type EventInput = EventMessage<In2>;
|
||||
}
|
||||
|
||||
impl<In1,In2,Out> KnownOutput for Lambda2Shape<In1,In2,Out>
|
||||
where In1:Message,In2:Message,Out:Message {
|
||||
type Output = Out;
|
||||
}
|
||||
|
||||
|
||||
// === Construction ===
|
||||
|
||||
/// Constructor abstraction. Used only to satisfy Rust type system.
|
||||
pub trait Lambda2New<Source1,Source2,Function> {
|
||||
/// Constructor.
|
||||
fn new(source:Source1, source2:Source2,f:Function) -> Self;
|
||||
}
|
||||
|
||||
impl<In1,In2,OutVal,Source1,Source2,Function>
|
||||
Lambda2New<Source1,Source2,Function> for Lambda2<In1,In2,Inferred<(In1,In2),OutVal>>
|
||||
where In1 : Message,
|
||||
In2 : Message,
|
||||
OutVal : Infer<(In1,In2)>,
|
||||
Source1 : Into<Node<In1>>,
|
||||
Source2 : Into<Node<In2>>,
|
||||
Function : 'static + Fn(&Value<In1>,&Value<In2>) -> OutVal,
|
||||
Node<In1> : AddTarget<Self>,
|
||||
Node<In2> : AddTarget<Self>,
|
||||
Inferred<(In1,In2),OutVal> : Message<Content=OutVal> {
|
||||
fn new (source1:Source1, source2:Source2, f:Function) -> Self {
|
||||
let source1 = source1.into();
|
||||
let source2 = source2.into();
|
||||
let source1_ref = source1.clone();
|
||||
let source2_ref = source2.clone();
|
||||
let shape = Lambda2Shape::new(source1,source2,f);
|
||||
let this = Self::construct(shape);
|
||||
source1_ref.add_target(&this);
|
||||
source2_ref.add_target(&this);
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
impl<In1,In2,Out> EventConsumer for Lambda2<EventMessage<In1>,BehaviorMessage<In2>,Out>
|
||||
where In1:MessageValue, In2:MessageValue, Out:Message {
|
||||
fn on_event(&self, event:&Self::EventInput) {
|
||||
println!("GOT {:?}",event);
|
||||
let value2 = self.rc.borrow().shape.source2.current_value();
|
||||
let output = (self.rc.borrow().shape.func)(&event.value(),&value2);
|
||||
self.emit_event(&output);
|
||||
}
|
||||
}
|
||||
|
||||
impl<In1,In2,Out> EventConsumer for Lambda2<BehaviorMessage<In1>,EventMessage<In2>,Out>
|
||||
where In1:MessageValue, In2:MessageValue, Out:Message {
|
||||
fn on_event(&self, event:&Self::EventInput) {
|
||||
println!("GOT {:?}",event);
|
||||
let value1 = self.rc.borrow().shape.source1.current_value();
|
||||
let output = (self.rc.borrow().shape.func)(&value1,&event.value());
|
||||
self.emit_event(&output);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================================================================================================
|
||||
// === Examples ====================================================================================
|
||||
// =================================================================================================
|
||||
|
||||
#[allow(missing_docs)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// ================
|
||||
// === Position ===
|
||||
// ================
|
||||
|
||||
#[derive(Clone,Copy,Debug,Default)]
|
||||
pub struct Position {
|
||||
x:i32,
|
||||
y:i32,
|
||||
}
|
||||
|
||||
impl Position {
|
||||
pub fn new(x:i32, y:i32) -> Self {
|
||||
Self {x,y}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============
|
||||
// === Test ===
|
||||
// ============
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub fn test () {
|
||||
println!("\n\n\n--- FRP ---\n");
|
||||
|
||||
|
||||
let mouse_position = Source::<BehaviorMessage<Position>>::new();
|
||||
|
||||
let e1 = Source::<EventMessage<i32>>::new();
|
||||
|
||||
let n1 = Lambda::new(&e1, |i| { i+1 });
|
||||
let nn1: Event<i32> = (&n1).into();
|
||||
let n2 = Lambda::new(&nn1, |i| { i*2 });
|
||||
|
||||
let n3: Lambda<BehaviorMessage<Position>, BehaviorMessage<Position>> =
|
||||
Lambda::new(&mouse_position, |t| { *t });
|
||||
|
||||
|
||||
let n3 = Lambda2::new(&n1,&mouse_position, |e,b| { *e });
|
||||
|
||||
// let n3 = Lambda2::new(&n1,&n2,|i,j| {i * j});
|
||||
|
||||
e1.emit_event(&EventMessage(7));
|
||||
|
||||
}
|
||||
}
|
||||
pub use tests::*;
|
@ -15,7 +15,13 @@ use wasm_bindgen::prelude::*;
|
||||
use crate::display::shape::primitive::def::*;
|
||||
use crate::display::navigation::navigator::Navigator;
|
||||
|
||||
use crate::control::frp;
|
||||
use crate::prelude::*;
|
||||
use enso_frp::*;
|
||||
|
||||
use crate::system::web;
|
||||
use crate::control::io::mouse2;
|
||||
use crate::control::io::mouse2::MouseManager;
|
||||
|
||||
|
||||
|
||||
#[wasm_bindgen]
|
||||
@ -47,18 +53,24 @@ fn init(world: &World) {
|
||||
t.y += screen.height / 2.0;
|
||||
});
|
||||
|
||||
let sprite2 = sprite.clone();
|
||||
|
||||
|
||||
world.add_child(&shape_system);
|
||||
|
||||
let out = frp_test(Box::new(move|x:f32,y:f32| {
|
||||
sprite2.set_position(Vector3::new(x,y,0.0));
|
||||
}));
|
||||
|
||||
|
||||
let mut iter:i32 = 0;
|
||||
let mut time:i32 = 0;
|
||||
world.on_frame(move |_| {
|
||||
let _keep_alive = &navigator;
|
||||
let _keep_alive = &out;
|
||||
on_frame(&mut time,&mut iter,&sprite,&shape_system)
|
||||
}).forget();
|
||||
|
||||
frp::test();
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@ -71,3 +83,52 @@ pub fn on_frame
|
||||
*iter += 1;
|
||||
shape_system.display_object().update();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ================
|
||||
// === FRP Test ===
|
||||
// ================
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub fn frp_test (callback: Box<dyn Fn(f32,f32)>) -> MouseManager {
|
||||
let document = web::document().unwrap();
|
||||
let mouse_manager = MouseManager::new(&document);
|
||||
let mouse = Mouse::new();
|
||||
|
||||
frp! {
|
||||
mouse_down_position = mouse.position.sample (&mouse.down);
|
||||
mouse_position_if_down = mouse.position.gate (&mouse.is_down);
|
||||
final_position_ref = recursive::<Position> ();
|
||||
pos_diff_on_down = mouse_down_position.map2 (&final_position_ref,|m,f|{m-f});
|
||||
final_position = mouse_position_if_down.map2 (&pos_diff_on_down ,|m,f|{m-f});
|
||||
debug = final_position.sample (&mouse.position);
|
||||
}
|
||||
final_position_ref.initialize(&final_position);
|
||||
|
||||
// final_position.event.display_graphviz();
|
||||
|
||||
trace("X" , &debug.event);
|
||||
|
||||
final_position.map("foo",move|p| {callback(p.x as f32,-p.y as f32)});
|
||||
|
||||
let target = mouse.position.event.clone_ref();
|
||||
let handle = mouse_manager.on_move.add(move |event:&mouse2::event::OnMove| {
|
||||
target.emit(Position::new(event.client_x(),event.client_y()));
|
||||
});
|
||||
handle.forget();
|
||||
|
||||
let target = mouse.down.event.clone_ref();
|
||||
let handle = mouse_manager.on_down.add(move |event:&mouse2::event::OnDown| {
|
||||
target.emit(());
|
||||
});
|
||||
handle.forget();
|
||||
|
||||
let target = mouse.up.event.clone_ref();
|
||||
let handle = mouse_manager.on_up.add(move |event:&mouse2::event::OnUp| {
|
||||
target.emit(());
|
||||
});
|
||||
handle.forget();
|
||||
|
||||
mouse_manager
|
||||
}
|
||||
|
@ -54,19 +54,19 @@ pub trait InternalFormat : Default + Into<AnyInternalFormat> + 'static {
|
||||
type Sampler: Sampler;
|
||||
|
||||
/// Checks if the texture format can be rendered as color.
|
||||
type ColorRenderable: Value<Type=bool>;
|
||||
type ColorRenderable: KnownTypeValue<Value=bool>;
|
||||
|
||||
/// Checks it he texture can be filtered.
|
||||
type Filterable: Value<Type=bool>;
|
||||
type Filterable: KnownTypeValue<Value=bool>;
|
||||
|
||||
/// Checks if the texture format can be rendered as color.
|
||||
fn color_renderable() -> bool {
|
||||
<Self::ColorRenderable as Value>::value()
|
||||
<Self::ColorRenderable as KnownTypeValue>::value()
|
||||
}
|
||||
|
||||
/// Checks it he texture can be filtered.
|
||||
fn filterable() -> bool {
|
||||
<Self::Filterable as Value>::value()
|
||||
<Self::Filterable as KnownTypeValue>::value()
|
||||
}
|
||||
}
|
||||
|
||||
|
12
gui/lib/frp/Cargo.toml
Normal file
12
gui/lib/frp/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "enso-frp"
|
||||
version = "0.1.0"
|
||||
authors = ["Enso Team <contact@luna-lang.org>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
|
||||
[dependencies]
|
||||
enso-prelude = { version = "0.1.0" , path = "../prelude" }
|
||||
basegl-system-web = { version = "0.1.0" , path = "../system/web" }
|
||||
percent-encoding = { version = "2.1.0" }
|
10
gui/lib/frp/src/core.rs
Normal file
10
gui/lib/frp/src/core.rs
Normal file
@ -0,0 +1,10 @@
|
||||
//! Root module for core FRP types and abstractions.
|
||||
|
||||
pub mod node;
|
||||
pub mod nodes;
|
||||
|
||||
pub use node::*;
|
||||
pub use nodes::*;
|
||||
|
||||
// Fixes Derivative macro names aliasing.
|
||||
pub use ::core::{fmt,default,clone};
|
11
gui/lib/frp/src/core/node.rs
Normal file
11
gui/lib/frp/src/core/node.rs
Normal file
@ -0,0 +1,11 @@
|
||||
//! Root module for FRP node related abstractions.
|
||||
|
||||
pub mod class;
|
||||
pub mod id;
|
||||
pub mod label;
|
||||
pub mod wrapper;
|
||||
|
||||
pub use class::*;
|
||||
pub use id::*;
|
||||
pub use label::*;
|
||||
pub use wrapper::*;
|
305
gui/lib/frp/src/core/node/class.rs
Normal file
305
gui/lib/frp/src/core/node/class.rs
Normal file
@ -0,0 +1,305 @@
|
||||
//! Defines FRP node related abstractions.
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::debug::*;
|
||||
use crate::node::id::*;
|
||||
use crate::node::label::*;
|
||||
use crate::data::*;
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Types ===
|
||||
// =============
|
||||
|
||||
alias! { no_docs
|
||||
NodeReflection = { HasInputs + HasLabel + KnownOutputType }
|
||||
NodeDebug = { Debug + GraphvizBuilder }
|
||||
AnyNode = { NodeDebug + NodeReflection + HasId + HasDisplayId }
|
||||
AnyNodeWithKnownOutput = { AnyNode + KnownOutput }
|
||||
AnyBehaviorNode = { AnyNodeWithKnownOutput + HasCurrentValue }
|
||||
AnyEventNode = { AnyNodeWithKnownOutput + HasEventTargets + EventEmitter }
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =========================
|
||||
// === NodeAsTraitObject ===
|
||||
// =========================
|
||||
|
||||
/// Type level association between FRP data type and a node's trait object. The associated type
|
||||
/// differs depending on whether it is an event or behavior node, as they provide different APIs.
|
||||
/// For example, behaviors allow lookup for the current value, which does not make sense in case
|
||||
/// of events.
|
||||
pub trait NodeAsTraitObjectForData {
|
||||
/// The trait object representing the node.
|
||||
type NodeAsTraitObject: AnyNodeWithKnownOutput + CloneRef;
|
||||
}
|
||||
|
||||
/// Accessor. See docs of `NodeAsTraitObjectForData` to learn more.
|
||||
pub type NodeAsTraitObject<T> = <T as NodeAsTraitObjectForData>::NodeAsTraitObject;
|
||||
|
||||
|
||||
// === EventDynNode ===
|
||||
|
||||
/// Newtype wrapper for any event node.
|
||||
#[derive(Debug,Derivative,Shrinkwrap)]
|
||||
#[derivative(Clone(bound=""))]
|
||||
pub struct EventDynNode<Out> {
|
||||
rc: Rc<dyn AnyEventNode<Output=EventData<Out>>>,
|
||||
}
|
||||
|
||||
impl<Out:Value> NodeAsTraitObjectForData for EventData<Out> {
|
||||
type NodeAsTraitObject = EventDynNode<Out>;
|
||||
}
|
||||
|
||||
impl<Out> Unwrap for EventDynNode<Out> {}
|
||||
impl<Out> CloneRef for EventDynNode<Out> {}
|
||||
impl<Out> HasContent for EventDynNode<Out> {
|
||||
// TODO: Simplify after fixing https://github.com/rust-lang/rust/issues/68776
|
||||
type Content = <EventDynNode<Out> as Deref>::Target;
|
||||
}
|
||||
|
||||
impl<Out:Value> KnownOutput for EventDynNode<Out> {
|
||||
type Output = EventData<Out>;
|
||||
}
|
||||
|
||||
|
||||
// === AnyBehaviorNode ===
|
||||
|
||||
/// Newtype wrapper for any behavior node.
|
||||
#[derive(Debug,Derivative,Shrinkwrap)]
|
||||
#[derivative(Clone(bound=""))]
|
||||
pub struct BehaviorDynNode<Out> {
|
||||
rc: Rc<dyn AnyBehaviorNode<Output=BehaviorData<Out>>>,
|
||||
}
|
||||
|
||||
impl<Out:Value> NodeAsTraitObjectForData for BehaviorData<Out> {
|
||||
type NodeAsTraitObject = BehaviorDynNode<Out>;
|
||||
}
|
||||
|
||||
impl<Out> Unwrap for BehaviorDynNode<Out> {}
|
||||
impl<Out> CloneRef for BehaviorDynNode<Out> {}
|
||||
impl<Out> HasContent for BehaviorDynNode<Out> {
|
||||
// TODO: Simplify after fixing https://github.com/rust-lang/rust/issues/68776
|
||||
type Content = <BehaviorDynNode<Out> as Deref>::Target;
|
||||
}
|
||||
|
||||
impl<Out:Value> KnownOutput for BehaviorDynNode<Out> {
|
||||
type Output = BehaviorData<Out>;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============
|
||||
// === Node ===
|
||||
// ============
|
||||
|
||||
// === Types ===
|
||||
|
||||
/// Events represents discrete point in time. At a specific time, there can be at most one event.
|
||||
/// Typical examples are mouse clicks, keyboard presses, status changes of the network connection.
|
||||
pub type Event<T> = Node<EventData<T>>;
|
||||
|
||||
/// Behaviours represent a value over time that is always available. For example, mouse coordinates
|
||||
/// vary in time and always have some value.
|
||||
pub type Behavior <T> = Node<BehaviorData<T>>;
|
||||
|
||||
|
||||
// === Definition ===
|
||||
|
||||
/// Node is used as a common types for frp operations. For example, `Event<T>` is just an alias to
|
||||
/// `Node<EventData<T>>`.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Clone(bound=""))]
|
||||
#[derivative(Debug(bound=""))]
|
||||
pub struct Node<Out:NodeAsTraitObjectForData> {
|
||||
storage: NodeAsTraitObject<Out>,
|
||||
}
|
||||
|
||||
impl<Out:Data> Node<Out> {
|
||||
/// Constructor.
|
||||
pub fn new(storage:NodeAsTraitObject<Out>) -> Self {
|
||||
Self {storage}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Type Deps ===
|
||||
|
||||
impl<Out:Data> KnownOutput for Node<Out> { type Output = Out; }
|
||||
impl<Out:Data> HasContent for Node<Out> { type Content = NodeAsTraitObject<Out>; }
|
||||
impl<Out:Data> Unwrap for Node<Out> {
|
||||
fn unwrap(&self) -> &Self::Content {
|
||||
&self.storage
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Instances ===
|
||||
|
||||
impl<Out:Data> Deref for Node<Out> {
|
||||
type Target = NodeAsTraitObject<Out>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.storage
|
||||
}
|
||||
}
|
||||
|
||||
impl<Out:Data> CloneRef for Node<Out> {
|
||||
fn clone_ref(&self) -> Self {
|
||||
let storage = self.storage.clone_ref();
|
||||
Self {storage}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Construction ===
|
||||
|
||||
impl<Out:Data> From<&Node<Out>> for Node<Out> {
|
||||
fn from(t:&Node<Out>) -> Self {
|
||||
t.clone_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Storage,Out:Value>
|
||||
From<&Storage> for Behavior<Out>
|
||||
where Storage : AnyBehaviorNode<Output=BehaviorData<Out>> + Clone + 'static {
|
||||
fn from(storage:&Storage) -> Self {
|
||||
Self::new(BehaviorDynNode{rc:Rc::new(storage.clone())})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Storage,Out:Value>
|
||||
From<&Storage> for Event<Out>
|
||||
where Storage : AnyEventNode<Output=EventData<Out>> + Clone + 'static {
|
||||
fn from(storage:&Storage) -> Self {
|
||||
Self::new(EventDynNode{rc:Rc::new(storage.clone())})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === AddTarget ===
|
||||
|
||||
/// Abstraction for adding a target to a given node. Nodes which carry behaviors do not need to
|
||||
/// perform any operation here, while event streams want to register the nodes they want to send
|
||||
/// notifications to.
|
||||
pub trait AddTarget<T> {
|
||||
/// Adds a node as a target of the current flow.
|
||||
fn add_target(&self,t:&T);
|
||||
}
|
||||
|
||||
impl<S,T:Value> AddTarget<S> for Event<T>
|
||||
where for<'t> &'t S : Into<AnyEventConsumer<EventData<T>>> {
|
||||
fn add_target(&self,t:&S) {
|
||||
self.storage.add_event_target(t.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S,T:Value> AddTarget<S> for Behavior<T> {
|
||||
fn add_target(&self,_:&S) {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =========================
|
||||
// === NodeWithAnyOutput ===
|
||||
// =========================
|
||||
|
||||
/// A type for any possible node type. It hides the result type of a node, which makes it the most
|
||||
/// generic node type out there.
|
||||
#[derive(Debug,Shrinkwrap)]
|
||||
pub struct NodeWithAnyOutput {
|
||||
rc: Rc<dyn AnyNode>,
|
||||
}
|
||||
|
||||
|
||||
// === Instances ===
|
||||
|
||||
impls! { [Out:Data+'static] From <&Node<Out>> for NodeWithAnyOutput { |t| t.clone_ref().into() } }
|
||||
impls! { [Out:Data+'static] From <Node<Out>> for NodeWithAnyOutput { |t| Self {rc:Rc::new(t)} } }
|
||||
|
||||
impl KnownOutputType for NodeWithAnyOutput {
|
||||
fn output_type(&self) -> DataType {
|
||||
self.rc.output_type()
|
||||
}
|
||||
|
||||
fn output_type_value_name(&self) -> String {
|
||||
self.rc.output_type_value_name()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =====================
|
||||
// === EventConsumer ===
|
||||
// =====================
|
||||
|
||||
// === Definition ===
|
||||
|
||||
/// Abstraction for nodes which are able to consume events.
|
||||
pub trait EventConsumer: KnownEventInput + Debug {
|
||||
/// Function called on every new received event.
|
||||
fn on_event(&self, input:&Content<Self::EventInput>);
|
||||
}
|
||||
|
||||
|
||||
// === AnyEventConsumer ===
|
||||
|
||||
/// Abstraction for any node which consumes events of a given type.
|
||||
#[derive(Clone,Debug,Shrinkwrap)]
|
||||
pub struct AnyEventConsumer<In> {
|
||||
raw: Rc<dyn EventConsumer<EventInput=In>>,
|
||||
}
|
||||
|
||||
impl<In:Data> AnyEventConsumer<In> {
|
||||
/// Constructor.
|
||||
pub fn new<A:EventConsumer<EventInput=In>+'static>(a:A) -> Self {
|
||||
let raw = Rc::new(a);
|
||||
Self {raw}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T,In> From<&T> for AnyEventConsumer<In>
|
||||
where T : EventConsumer<EventInput=In> + Clone + 'static,
|
||||
In : Data {
|
||||
fn from(t:&T) -> Self {
|
||||
Self::new(t.clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ====================
|
||||
// === EventEmitter ===
|
||||
// ====================
|
||||
|
||||
// === Definition ===
|
||||
|
||||
/// Abstraction for nodes which are able to emit events.
|
||||
pub trait EventEmitter: KnownOutput {
|
||||
/// Function for emitting new events.
|
||||
fn emit_event(&self, event:&Content<Self::Output>);
|
||||
}
|
||||
|
||||
impl<T> EventEmitter for T
|
||||
where T:Unwrap+KnownOutput, Content<T>:EventEmitter<Output=Output<Self>> {
|
||||
fn emit_event(&self, event:&Content<Self::Output>) {
|
||||
self.unwrap().emit_event(event)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === EventEmitterPoly ===
|
||||
|
||||
/// Polymorphic version of `EventEmitter`. Please note that `EventEmitter` could not be implemented
|
||||
/// this way as we want to use it in a trait object, so all its methods have to be monomorphic.
|
||||
pub trait EventEmitterPoly : KnownOutput where Output<Self>:HasContent {
|
||||
/// Function for emitting new events.
|
||||
fn emit<E:ToRef<Content<Self::Output>>>(&self, event:E);
|
||||
}
|
||||
|
||||
impl<T:EventEmitter> EventEmitterPoly for T {
|
||||
fn emit<E:ToRef<Content<Self::Output>>>(&self, event:E) {
|
||||
self.emit_event(event.to_ref())
|
||||
}
|
||||
}
|
49
gui/lib/frp/src/core/node/id.rs
Normal file
49
gui/lib/frp/src/core/node/id.rs
Normal file
@ -0,0 +1,49 @@
|
||||
//! This module defines FRP node identifiers. They are mainly used for debugging purposes.
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === HasId ===
|
||||
// =============
|
||||
|
||||
/// Each FRP node is assigned with an unique ID. This is currently used mainly for debugging
|
||||
/// purposes.
|
||||
pub trait HasId {
|
||||
/// Id of the entity.
|
||||
fn id(&self) -> usize;
|
||||
}
|
||||
|
||||
impl<T:Unwrap> HasId for T
|
||||
where Content<T> : HasId {
|
||||
default fn id(&self) -> usize {
|
||||
self.unwrap().id()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ====================
|
||||
// === HasDisplayId ===
|
||||
// ====================
|
||||
|
||||
/// Each FRP node can also be assigned with a `display_id`. Unlike `id`, the `display_id` does not
|
||||
/// have to be unique. Nodes with the same `display_id` are displayed as a single node in the graph
|
||||
/// view. Note that `display_id` defaults to `id` if not set explicitly to other value.
|
||||
pub trait HasDisplayId {
|
||||
/// Getter.
|
||||
fn display_id(&self) -> usize;
|
||||
/// Setter.
|
||||
fn set_display_id(&self, id:usize);
|
||||
}
|
||||
|
||||
impl<T> HasDisplayId for T
|
||||
where T:Unwrap, Content<T> : HasDisplayId {
|
||||
default fn display_id(&self) -> usize {
|
||||
self.unwrap().display_id()
|
||||
}
|
||||
|
||||
default fn set_display_id(&self, id:usize) {
|
||||
self.unwrap().set_display_id(id)
|
||||
}
|
||||
}
|
22
gui/lib/frp/src/core/node/label.rs
Normal file
22
gui/lib/frp/src/core/node/label.rs
Normal file
@ -0,0 +1,22 @@
|
||||
//! This module defines FRP node labels. They are mainly used for debugging purposes.
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Label ===
|
||||
// =============
|
||||
|
||||
/// Abstraction for labeled entities. Used mainly for debugging purposes.
|
||||
pub trait HasLabel {
|
||||
/// Label of the entity.
|
||||
fn label(&self) -> &CowString;
|
||||
}
|
||||
|
||||
impl<T:Unwrap> HasLabel for T
|
||||
where Content<T> : HasLabel {
|
||||
default fn label(&self) -> &CowString {
|
||||
self.unwrap().label()
|
||||
}
|
||||
}
|
189
gui/lib/frp/src/core/node/wrapper.rs
Normal file
189
gui/lib/frp/src/core/node/wrapper.rs
Normal file
@ -0,0 +1,189 @@
|
||||
//! This module defines a common template structure used to define FRP nodes.
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::data::*;
|
||||
use crate::debug::*;
|
||||
use crate::node::class::*;
|
||||
use crate::node::id::*;
|
||||
use crate::node::label::*;
|
||||
|
||||
|
||||
|
||||
// ===================
|
||||
// === NodeWrapper ===
|
||||
// ===================
|
||||
|
||||
// === NodeWrapper ===
|
||||
|
||||
/// `NodeWrapper` is an outer layer for every FRP node. For example, the `Source<Out>` node is just
|
||||
/// an alias to `NodeWrapper<SourceShape<Out>>`, where `SourceShape` defines the data kept by the
|
||||
/// node. This struct bundles each node with information about target edges. Although the edges are
|
||||
/// used only to send events, they are bundled to every node type in order to keep the
|
||||
/// implementation simple.
|
||||
pub type NodeWrapper<Shape> = NodeWrapperTemplate<Shape,Output<Shape>>;
|
||||
|
||||
impl<Shape:KnownOutput> NodeWrapper<Shape> {
|
||||
/// Constructor.
|
||||
pub fn construct<Label>(label:Label, shape:Shape) -> Self
|
||||
where Label : Into<CowString> {
|
||||
let data = NodeWrapperTemplateMutable::new();
|
||||
let config = Rc::new(RefCell::new(data));
|
||||
let immutable = Rc::new(NodeWrapperTemplateImmutable::new(label,shape));
|
||||
let this = Self {config,immutable};
|
||||
this.set_display_id(this.id());
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
impl<Shape,Out:Data> NodeWrapperTemplate<Shape,Out> {
|
||||
/// Sends an event to all the children.
|
||||
pub fn emit_event_raw(&self, event:&Content<Out>) {
|
||||
self.config.borrow().targets.iter().for_each(|target| {
|
||||
target.on_event(event)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Shape,T:Value>
|
||||
HasEventTargets for NodeWrapperTemplate<Shape,EventData<T>> {
|
||||
fn add_event_target(&self, target:AnyEventConsumer<EventData<T>>) {
|
||||
self.config.borrow_mut().targets.push(target);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === NodeWrapperTemplate ===
|
||||
|
||||
/// Internal representation for `NodeWrapper`. Please note that we define this structure and the
|
||||
/// `NodeWrapper` alias instead of just single struct in order not to keep bounds on struct
|
||||
/// definition (which is bad and you should never do it).
|
||||
#[derive(Debug,Derivative,Shrinkwrap)]
|
||||
#[derivative(Default(bound="Shape:Default"))]
|
||||
#[derivative(Clone(bound=""))]
|
||||
#[allow(missing_docs)]
|
||||
pub struct NodeWrapperTemplate<Shape,Out> {
|
||||
#[shrinkwrap(main_field)]
|
||||
pub immutable : Rc<NodeWrapperTemplateImmutable<Shape>>,
|
||||
pub config : Rc<RefCell<NodeWrapperTemplateMutable<Out>>>,
|
||||
}
|
||||
|
||||
impl<Shape,Out> CloneRef for NodeWrapperTemplate<Shape,Out> {}
|
||||
|
||||
impl<Shape,Out>
|
||||
HasId for NodeWrapperTemplate<Shape,Out> {
|
||||
fn id(&self) -> usize {
|
||||
Rc::downgrade(&self.config).as_raw() as *const() as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl<Shape,Out>
|
||||
HasDisplayId for NodeWrapperTemplate<Shape,Out> {
|
||||
fn display_id (&self) -> usize { self.config.borrow().display_id }
|
||||
fn set_display_id (&self, id:usize) { self.config.borrow_mut().display_id = id; }
|
||||
}
|
||||
|
||||
impl<Shape,Out:Data>
|
||||
KnownOutput for NodeWrapperTemplate<Shape,Out> {
|
||||
type Output = Out;
|
||||
}
|
||||
|
||||
impl<Shape,Out>
|
||||
KnownEventInput for NodeWrapperTemplate<Shape,Out>
|
||||
where Shape:KnownEventInput, EventInput<Shape>:Data {
|
||||
type EventInput = EventInput<Shape>;
|
||||
}
|
||||
|
||||
impl<Shape,T:Value>
|
||||
EventEmitter for NodeWrapperTemplate<Shape,EventData<T>> {
|
||||
fn emit_event(&self, event:&T) {
|
||||
self.emit_event_raw(event);
|
||||
}
|
||||
}
|
||||
|
||||
impl<Shape:HasInputs,Out>
|
||||
HasInputs for NodeWrapperTemplate<Shape,Out> {
|
||||
fn inputs(&self) -> Vec<NodeWithAnyOutput> {
|
||||
self.immutable.inputs()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Shape,Out>
|
||||
HasLabel for NodeWrapperTemplate<Shape,Out> {
|
||||
fn label(&self) -> &CowString {
|
||||
&self.label
|
||||
}
|
||||
}
|
||||
|
||||
impl<Shape:HasInputs,Out>
|
||||
GraphvizBuilder for NodeWrapperTemplate<Shape,Out> {
|
||||
fn graphviz_build(&self, builder:&mut Graphviz) {
|
||||
let type_name = base_type_name::<Shape>();
|
||||
let label = &self.label;
|
||||
let id = self.id();
|
||||
let display_id = self.display_id();
|
||||
if !builder.contains(id) {
|
||||
builder.add_node(id,display_id,type_name,label);
|
||||
for input in &self.inputs() {
|
||||
let input_display_id = input.display_id();
|
||||
let input_type = input.output_type();
|
||||
let input_type_name = input.output_type_value_name();
|
||||
input.graphviz_build(builder);
|
||||
builder.add_link(input_display_id,display_id,input_type,&input_type_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === NodeWrapperTemplateImmutable ===
|
||||
|
||||
/// Internal representation for `NodeWrapperTemplate`.
|
||||
#[derive(Debug,Default,Shrinkwrap)]
|
||||
pub struct NodeWrapperTemplateImmutable<Shape> {
|
||||
#[shrinkwrap(main_field)]
|
||||
/// The shape of the node.
|
||||
pub shape : Shape,
|
||||
/// The label of the node. Used for debugging purposes.
|
||||
pub label : CowString,
|
||||
}
|
||||
|
||||
impl<Shape> NodeWrapperTemplateImmutable<Shape> {
|
||||
/// Constructor.
|
||||
pub fn new<Label>(label:Label, shape:Shape) -> Self
|
||||
where Label : Into<CowString> {
|
||||
let label = label.into();
|
||||
Self {label,shape}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === NodeWrapperTemplateMutable ===
|
||||
|
||||
/// Internal representation for `NodeWrapperTemplate`.
|
||||
#[derive(Debug,Derivative)]
|
||||
#[derivative(Default(bound=""))]
|
||||
pub struct NodeWrapperTemplateMutable<Out> {
|
||||
/// The display id of the node. Used to group nodes together in the visualization view.
|
||||
pub display_id : usize,
|
||||
/// Event targets of the node.
|
||||
pub targets : Vec<AnyEventConsumer<Out>>,
|
||||
}
|
||||
|
||||
impl<Out> NodeWrapperTemplateMutable<Out> {
|
||||
/// Constructor.
|
||||
pub fn new() -> Self {
|
||||
default()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Utils ===
|
||||
|
||||
fn base_type_name<T>() -> String {
|
||||
let qual_name = type_name::<T>();
|
||||
let base_name = qual_name.split('<').collect::<Vec<_>>()[0];
|
||||
let name = base_name.rsplit("::").collect::<Vec<_>>()[0];
|
||||
let name = name.split("Shape").collect::<Vec<_>>()[0];
|
||||
name.into()
|
||||
}
|
11
gui/lib/frp/src/core/nodes.rs
Normal file
11
gui/lib/frp/src/core/nodes.rs
Normal file
@ -0,0 +1,11 @@
|
||||
//! Root module for FRP node definitions.
|
||||
|
||||
pub mod dynamic;
|
||||
pub mod inference;
|
||||
pub mod lambda;
|
||||
pub mod prim;
|
||||
|
||||
pub use dynamic::*;
|
||||
pub use inference::*;
|
||||
pub use lambda::*;
|
||||
pub use prim::*;
|
319
gui/lib/frp/src/core/nodes/dynamic.rs
Normal file
319
gui/lib/frp/src/core/nodes/dynamic.rs
Normal file
@ -0,0 +1,319 @@
|
||||
//! Root module for FRP Dynamic node types. The Dynamic type is a generalization of Event and
|
||||
//! Behavior and is very easy to work with. You should use this type in most (all?) cases in your
|
||||
//! FRP flows.
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::data::*;
|
||||
use crate::node::*;
|
||||
use crate::nodes::prim::*;
|
||||
use crate::nodes::lambda::*;
|
||||
|
||||
|
||||
|
||||
// ======================
|
||||
// === RefinedDynamic ===
|
||||
// ======================
|
||||
|
||||
/// Similar to `Dynamic` but with a known type of the `event` component. In most cases using
|
||||
/// `Dynamic` is just fine. Sometimes however, you want to use a non-generic utilities of nodes,
|
||||
/// like initializing a recursive one. By using `RefinedDynamic` you do not lose the information
|
||||
/// about the specific shape of the node and you can access all of its methods directly.
|
||||
#[derive(Debug,Derivative)]
|
||||
#[derivative(Clone(bound="Event:Clone"))]
|
||||
pub struct RefinedDynamic<Event>
|
||||
where Event : KnownOutput,
|
||||
Output<Event> : HasContent,
|
||||
Content<Output<Event>> : Value {
|
||||
/// The underlying dynamic.
|
||||
pub dynamic : Dynamic<Content<Output<Event>>>,
|
||||
/// The event with a known type. This is the same event as `dynamic.event`.
|
||||
pub event : Event,
|
||||
}
|
||||
|
||||
impl<Event> Deref for RefinedDynamic<Event>
|
||||
where Event : KnownOutput,
|
||||
Output<Event> : HasContent,
|
||||
Content<Output<Event>> : Value {
|
||||
type Target = Dynamic<Content<Output<Event>>>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.dynamic
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> From<&E> for RefinedDynamic<E>
|
||||
where E : CloneRef + KnownOutput,
|
||||
for <'t> &'t E : Into<Event<Content<Output<E>>>>,
|
||||
Output<E> : HasContent,
|
||||
Content<Output<E>> : Value {
|
||||
fn from(event:&E) -> Self {
|
||||
let event2 = event.into();
|
||||
let event = event.clone_ref();
|
||||
let dynamic = event2.into();
|
||||
Self {event,dynamic}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Out:Value> RefinedDynamic<Recursive<EventData<Out>>> {
|
||||
/// Initialize the recursive `Dynamic` with a value. You need to perform this operation before
|
||||
/// running the FRP graph.
|
||||
pub fn initialize(&self, target:&Dynamic<Out>) {
|
||||
self.event.initialize(&target.event);
|
||||
self.event.set_display_id(target.event.display_id());
|
||||
self.behavior.set_display_id(target.event.display_id());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===============
|
||||
// === Dynamic ===
|
||||
// ===============
|
||||
|
||||
/// The `Dynamic` type is an `Event` with an associated `Behavior`. You can assume that the
|
||||
/// behavior just always holds the last event value.
|
||||
#[derive(Debug,Derivative)]
|
||||
#[derivative(Clone(bound=""))]
|
||||
pub struct Dynamic<Out:Value> {
|
||||
/// The `Event` component.
|
||||
pub event : Event<Out>,
|
||||
/// The `Behavior` component.
|
||||
pub behavior : Behavior<Out>,
|
||||
}
|
||||
|
||||
|
||||
// === Constructors ===
|
||||
|
||||
impl<Out:Value> Dynamic<Out> {
|
||||
/// Constructor.
|
||||
pub fn new<E,B>(event:E, behavior:B) -> Self
|
||||
where E:Into<Event<Out>>, B:Into<Behavior<Out>> {
|
||||
let event = event.into();
|
||||
let behavior = behavior.into();
|
||||
Self {event,behavior}
|
||||
}
|
||||
|
||||
/// Creates a new FRP source node.
|
||||
pub fn source<Label>(label:Label) -> Self
|
||||
where Label : Into<CowString> {
|
||||
let event = Source::<EventData<Out>>::new_named(label);
|
||||
(&event).into()
|
||||
}
|
||||
|
||||
/// Create a new node which will be a placeholder (reference) to another node. Please note that
|
||||
/// this node has to be initialized before the FRP network is run.
|
||||
pub fn recursive<Label>(label:Label) -> RefinedDynamic<Recursive<EventData<Out>>>
|
||||
where Label : Into<CowString> {
|
||||
(&Recursive::<EventData<Out>>::new_named(label)).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Modifiers ===
|
||||
|
||||
impl<Out:Value> Dynamic<Out> {
|
||||
/// Create a new node which drops the incoming event and emits a new event with the constant
|
||||
/// value.
|
||||
pub fn constant<Label,T>(&self, label:Label, value:T) -> Dynamic<T>
|
||||
where Label:Into<CowString>, T:Value {
|
||||
self.map(label,move |_| value.clone())
|
||||
}
|
||||
|
||||
/// Creates a new node which merges two event streams. The output event will be emitted
|
||||
/// whenever one of the streams emit an event.
|
||||
pub fn merge<Label>(&self, label:Label, that:&Dynamic<Out>) -> Self
|
||||
where Label:Into<CowString> {
|
||||
(&Merge::new_named(label,&self.event,&that.event)).into()
|
||||
}
|
||||
|
||||
/// Creates a new node which emits `true`, `false`, `true`, `false`, ... on every incoming
|
||||
/// event.
|
||||
pub fn toggle<Label>(&self, label:Label) -> Dynamic<bool>
|
||||
where Label:Into<CowString> {
|
||||
(&Toggle::new_named(label,&self.event)).into()
|
||||
}
|
||||
|
||||
/// Creates a new node which passes the incoming event only if its second input is `true`.
|
||||
pub fn gate<Label>(&self, label:Label, that:&Dynamic<bool>) -> Self
|
||||
where Label:Into<CowString> {
|
||||
(&Gate::new_named(label,that,self)).into()
|
||||
}
|
||||
|
||||
/// Creates a node which samples this behavior on every incoming argument event. The incoming
|
||||
/// event is dropped and a new event with the behavior value is emitted.
|
||||
pub fn sample<Label,T>(&self, label:Label, that:&Dynamic<T>) -> Self
|
||||
where Label : Into<CowString>,
|
||||
T : Value {
|
||||
(&Sample::new_named(label,&self.behavior,that)).into()
|
||||
}
|
||||
|
||||
/// Creates a node which maps the current value with the provided lambda. This is one of the
|
||||
/// most powerful utilities, however, you should try not to use it too often. The reason is that
|
||||
/// it also makes optimizations impossible, as lambdas are like "black-boxes" for the FRP
|
||||
/// engine.
|
||||
pub fn map<Label,F,R>(&self, label:Label, f:F) -> Dynamic<R>
|
||||
where Label : Into<CowString>,
|
||||
R : Value,
|
||||
F : 'static + Fn(&Out) -> R {
|
||||
(&Lambda::new_named(label,&self.event,f)).into()
|
||||
}
|
||||
|
||||
/// Creates a node which maps the current value with the provided lambda. This is one of the
|
||||
/// most powerful utilities, however, you should try not to use it too often. The reason is that
|
||||
/// it also makes optimizations impossible, as lambdas are like "black-boxes" for the FRP
|
||||
/// engine.
|
||||
pub fn map2<Label,T,F,R>(&self, label:Label, that:&Dynamic<T>, f:F) -> Dynamic<R>
|
||||
where Label : Into<CowString>,
|
||||
T : Value,
|
||||
R : Value,
|
||||
F : 'static + Fn(&Out,&T) -> R {
|
||||
(&Lambda2::new_named(label,&self.event,that,f)).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Debug ===
|
||||
|
||||
impl<Out:Value+Eq> Dynamic<Out> {
|
||||
/// Creates a new node which passes the incoming event trough and panics if it was not equal to
|
||||
/// the given value.
|
||||
pub fn assert_eq<Label>(&self, label:Label) -> RefinedDynamic<AssertEq<EventData<Out>>>
|
||||
where Label:Into<CowString> {
|
||||
(&AssertEq::new_named(label,&self.event)).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// === Instances ===
|
||||
|
||||
impl<Out:Value> CloneRef for Dynamic<Out> {
|
||||
fn clone_ref(&self) -> Self {
|
||||
let event = self.event.clone_ref();
|
||||
let behavior = self.behavior.clone_ref();
|
||||
Self {event,behavior}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Out:Value, T:Into<Event<Out>>> From<T> for Dynamic<Out> {
|
||||
fn from(t:T) -> Self {
|
||||
let event = t.into();
|
||||
let behavior = Hold :: new_named(event.label(),&event);
|
||||
behavior.set_display_id(event.display_id());
|
||||
let event = (&event).into();
|
||||
let behavior = (&behavior).into();
|
||||
Dynamic {event,behavior}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Out:Value> From<&Dynamic<Out>> for Event<Out> {
|
||||
fn from(t:&Dynamic<Out>) -> Self {
|
||||
t.event.clone_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Out:Value> From<&Dynamic<Out>> for Behavior<Out> {
|
||||
fn from(t:&Dynamic<Out>) -> Self {
|
||||
t.behavior.clone_ref()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::frp_def;
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn assert() {
|
||||
frp_def! { source = source::<i32>() }
|
||||
frp_def! { check = source.assert_eq() }
|
||||
check.event.expect(1);
|
||||
source.event.emit(2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn constant() {
|
||||
frp_def! { source = source::<i32>() }
|
||||
frp_def! { constant = source.constant(7) }
|
||||
frp_def! { check = constant.assert_eq() }
|
||||
check.event.expect(7);
|
||||
source.event.emit(0);
|
||||
source.event.emit(7);
|
||||
source.event.emit(1);
|
||||
assert_eq!(check.event.success_count(),3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge() {
|
||||
frp_def! { source1 = source::<i32>() }
|
||||
frp_def! { source2 = source::<i32>() }
|
||||
frp_def! { merge = source1.merge(&source2) }
|
||||
frp_def! { check = merge.assert_eq() }
|
||||
check.event.expect(1);
|
||||
source1.event.emit(1);
|
||||
source2.event.emit(1);
|
||||
check.event.expect(2);
|
||||
source1.event.emit(2);
|
||||
source2.event.emit(2);
|
||||
assert_eq!(check.event.success_count(),4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn toggle() {
|
||||
frp_def! { source = source::<i32>() }
|
||||
frp_def! { toggle = source.toggle() }
|
||||
frp_def! { check = toggle.assert_eq() }
|
||||
check.event.expect(true);
|
||||
source.event.emit(0);
|
||||
check.event.expect(false);
|
||||
source.event.emit(0);
|
||||
check.event.expect(true);
|
||||
source.event.emit(0);
|
||||
assert_eq!(check.event.success_count(),3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gate() {
|
||||
frp_def! { source1 = source::<i32>() }
|
||||
frp_def! { source2 = source::<bool>() }
|
||||
frp_def! { gate = source1.gate(&source2) }
|
||||
frp_def! { check = gate.assert_eq() }
|
||||
source1.event.emit(0);
|
||||
assert_eq!(check.event.success_count(),0);
|
||||
source2.event.emit(true);
|
||||
source1.event.emit(0);
|
||||
assert_eq!(check.event.success_count(),1);
|
||||
source2.event.emit(false);
|
||||
source1.event.emit(0);
|
||||
assert_eq!(check.event.success_count(),1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sample() {
|
||||
frp_def! { source1 = source::<i32>() }
|
||||
frp_def! { source2 = source::<i32>() }
|
||||
frp_def! { sample = source1.sample(&source2) }
|
||||
frp_def! { check = sample.assert_eq() }
|
||||
check.event.expect(1);
|
||||
source1.event.emit(1);
|
||||
assert_eq!(check.event.success_count(),0);
|
||||
source2.event.emit(0);
|
||||
assert_eq!(check.event.success_count(),1);
|
||||
source2.event.emit(0);
|
||||
assert_eq!(check.event.success_count(),2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map() {
|
||||
frp_def! { source = source::<i32>() }
|
||||
frp_def! { map = source.map(|t| {t+1}) }
|
||||
frp_def! { check = map.assert_eq() }
|
||||
check.event.expect(1);
|
||||
source.event.emit(0);
|
||||
check.event.expect(2);
|
||||
source.event.emit(1);
|
||||
assert_eq!(check.event.success_count(),2);
|
||||
}
|
||||
}
|
127
gui/lib/frp/src/core/nodes/inference.rs
Normal file
127
gui/lib/frp/src/core/nodes/inference.rs
Normal file
@ -0,0 +1,127 @@
|
||||
//! This module defines inference helpers allowing for writing more general code in FRP node
|
||||
//! generation macros.
|
||||
|
||||
use crate::data::*;
|
||||
|
||||
|
||||
|
||||
// =========================
|
||||
// === Inference Helpers ===
|
||||
// =========================
|
||||
|
||||
/// Data product type-level inference guidance. For a given input, it infers the product of FRP
|
||||
/// data types. For example, for the input of `(BehaviorData<T1>,BehaviorData<T2>)`, the output
|
||||
/// will be `BehaviorData<X>`. In case there is a single `EventData`, it will become the result.
|
||||
/// For input `(BehaviorData<T1>,EventData<T2>)` it will resolve to `EventData<X>`.
|
||||
pub trait InferProductType<T> {
|
||||
/// Inference results.
|
||||
type ProductType;
|
||||
}
|
||||
|
||||
/// Accessor for inferred type.
|
||||
pub type ProductType<T,X> = <X as InferProductType<T>>::ProductType;
|
||||
|
||||
|
||||
// === Rules ===
|
||||
|
||||
/// Defines product type-level inference rules. To learn more, see the expanded version of the
|
||||
/// usage below.
|
||||
macro_rules! inference_rules {
|
||||
($( $pat:tt => $result:ident )*) => {$(
|
||||
inference_rule! { $pat => $result }
|
||||
)*}
|
||||
}
|
||||
|
||||
/// Internal utility for the `inference_rules` macro.
|
||||
macro_rules! inference_rule {
|
||||
( $t1:ident => $result:ident ) => {
|
||||
impl<X,T1> InferProductType <$t1<T1>> for X {
|
||||
type ProductType = $result<X>;
|
||||
}
|
||||
};
|
||||
|
||||
( ($t1:ident) => $result:ident ) => {
|
||||
impl<X,T1> InferProductType <$t1<T1>> for X {
|
||||
type ProductType = $result<X>;
|
||||
}
|
||||
};
|
||||
|
||||
( ($t1:ident, $t2:ident) => $result:ident ) => {
|
||||
impl<X,T1,T2> InferProductType <($t1<T1>,$t2<T2>)> for X {
|
||||
type ProductType = $result<X>;
|
||||
}
|
||||
};
|
||||
|
||||
( ($t1:ident, $t2:ident, $t3:ident) => $result:ident ) => {
|
||||
impl<X,T1,T2,T3> InferProductType <($t1<T1>,$t2<T2>,$t3<T3>)> for X {
|
||||
type ProductType = $result<X>;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
inference_rules! {
|
||||
EventData => EventData
|
||||
BehaviorData => BehaviorData
|
||||
|
||||
(EventData , EventData ) => EventData
|
||||
(BehaviorData , EventData ) => EventData
|
||||
(EventData , BehaviorData) => EventData
|
||||
(BehaviorData , BehaviorData) => EventData
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =========================
|
||||
// === ContainsEventData ===
|
||||
// =========================
|
||||
|
||||
// === Definition ===
|
||||
|
||||
/// From the given set of FRP data selects the first occurrence of `EventData` if any. For example,
|
||||
/// `SelectEventData<(BehaviorData<T1>,EventData<T2>,EventData<T3>)>` resolves to `EventData<T2>`.
|
||||
pub type SelectEventData<T> = <T as ContainsEventData>::Result;
|
||||
|
||||
/// From the given set of FRP data selects the first occurrence of `EventData` if any.
|
||||
pub trait ContainsEventData {
|
||||
/// The selected `EventData` type.
|
||||
type Result : Data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// === Instances ===
|
||||
|
||||
/// Defines instances for `ContainsEventData`. See the expanded version of the usages below to
|
||||
/// learn more.
|
||||
macro_rules! define_contains_event_data_impls {
|
||||
($( $pat:tt => $result:ident<$result_type:ident> )*) => {$(
|
||||
define_contains_event_data_impls_item! { $pat => $result<$result_type> }
|
||||
)*}
|
||||
}
|
||||
|
||||
/// Internal utility for the `define_contains_event_data_impls` macro.
|
||||
macro_rules! define_contains_event_data_impls_item {
|
||||
($( ($($pat:tt<$pat_type:ident>),*) => $result:ident<$result_type:ident> )*) => {$(
|
||||
#[allow(unused_parens)]
|
||||
impl<$($pat_type),*> ContainsEventData for ($($pat<$pat_type>),*)
|
||||
where $result<$result_type> : Data {
|
||||
type Result = $result<$result_type>;
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
define_contains_event_data_impls! {
|
||||
( EventData <T1> ) => EventData<T1>
|
||||
|
||||
( EventData <T1> , EventData <T2> ) => EventData <T1>
|
||||
( EventData <T1> , BehaviorData <T2> ) => EventData <T1>
|
||||
( BehaviorData <T1> , EventData <T2> ) => EventData <T2>
|
||||
|
||||
( EventData <T1> , EventData <T2> , EventData <T3> ) => EventData <T1>
|
||||
( BehaviorData <T1> , EventData <T2> , EventData <T3> ) => EventData <T2>
|
||||
( EventData <T1> , BehaviorData <T2> , EventData <T3> ) => EventData <T1>
|
||||
( EventData <T1> , EventData <T2> , BehaviorData <T3> ) => EventData <T1>
|
||||
( BehaviorData <T1> , BehaviorData <T2> , EventData <T3> ) => EventData <T3>
|
||||
( BehaviorData <T1> , EventData <T2> , BehaviorData <T3> ) => EventData <T2>
|
||||
( EventData <T1> , BehaviorData <T2> , BehaviorData <T3> ) => EventData <T1>
|
||||
}
|
208
gui/lib/frp/src/core/nodes/lambda.rs
Normal file
208
gui/lib/frp/src/core/nodes/lambda.rs
Normal file
@ -0,0 +1,208 @@
|
||||
//! This module defines a set of complex FRP building blocks. They allow you to run arbitrary Rust
|
||||
//! code on the input data. You should rather not need to often use these.
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::data::*;
|
||||
use crate::node::*;
|
||||
use crate::nodes::inference::*;
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
// === Macros ===
|
||||
// ==============
|
||||
|
||||
/// Similar to `deinfe_node` but specialized for lambda definitions.
|
||||
///
|
||||
/// Generates a ot of boilerplate for a new node definition. It generates the node struct, the
|
||||
/// constructor, input / output relations, etc. In order to learn more, see the expanded version
|
||||
/// of this macro used below.
|
||||
macro_rules! define_lambda_node {
|
||||
(
|
||||
$(#$meta:tt)*
|
||||
pub struct $name:ident $shape_name:ident [$($poly_input:ident)*]
|
||||
{ $( $field:ident : $field_type:ty ),* }
|
||||
) => {
|
||||
$(#$meta)*
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type $name<$($poly_input,)* Out> = NodeWrapper<$shape_name<$($poly_input,)* Out>>;
|
||||
|
||||
$(#$meta)*
|
||||
#[derive(Debug)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct $shape_name<$($poly_input:Data,)* Out:Data> {
|
||||
$( $poly_input : Node<$poly_input> ),* ,
|
||||
$( $field : $field_type ),*
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
impl<$($poly_input:Data,)* Out:Data>
|
||||
KnownOutput for $shape_name<$($poly_input,)* Out> {
|
||||
type Output = Out;
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types,unused_parens)]
|
||||
impl<$($poly_input:Data,)* Out:Data>
|
||||
KnownEventInput for $shape_name<$($poly_input,)* Out>
|
||||
where ($($poly_input),*) : ContainsEventData,
|
||||
SelectEventData<($($poly_input),*)> : Data {
|
||||
type EventInput = SelectEventData<($($poly_input),*)>;
|
||||
}
|
||||
|
||||
paste::item! {
|
||||
/// A constructor trait. Used only in order to make Rust type checker happy.
|
||||
#[allow(non_camel_case_types)]
|
||||
pub trait [<$name New>]<$($poly_input,)* Func> {
|
||||
/// Constructor.
|
||||
fn new_named<Label:Into<CowString>>
|
||||
(label:Label, $($poly_input:$poly_input,)* f:Func) -> Self;
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types,unused_parens)]
|
||||
impl<$($poly_input,)* OutVal, $([<T $poly_input>],)* Function>
|
||||
[<$name New>]<$([<T $poly_input>],)* Function>
|
||||
for $name<$($poly_input,)* ProductType<($($poly_input),*),OutVal>>
|
||||
where $($poly_input : Data,)*
|
||||
$([<T $poly_input>] : Into<Node<$poly_input>>,)*
|
||||
$(Node<$poly_input> : AddTarget<Self>,)*
|
||||
OutVal : InferProductType<($($poly_input),*)>,
|
||||
Function : 'static + Fn($(&Content<$poly_input>),*) -> OutVal,
|
||||
ProductType<($($poly_input),*),OutVal> : Data<Content=OutVal> {
|
||||
fn new_named<Label>
|
||||
(label:Label, $($poly_input:[<T $poly_input>],)* func:Function) -> Self
|
||||
where Label : Into<CowString> {
|
||||
$(let $poly_input = $poly_input.into();)*
|
||||
let func = func.into();
|
||||
let shape = $shape_name{$($poly_input,)* func};
|
||||
let this = Self::construct(label,shape);
|
||||
{
|
||||
$(this.$poly_input.add_target(&this);)*
|
||||
}
|
||||
this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
impl<$($poly_input:Data,)* Out:Data> HasInputs for $shape_name<$($poly_input,)* Out> {
|
||||
fn inputs(&self) -> Vec<NodeWithAnyOutput> {
|
||||
vec![$((&self.$poly_input).into()),*]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ==============
|
||||
// === Lambda ===
|
||||
// ==============
|
||||
|
||||
define_lambda_node! {
|
||||
/// Transforms input data with the provided function. Lambda accepts a single input and outputs
|
||||
/// message of the same type as the input message.
|
||||
pub struct Lambda LambdaShape [source] {
|
||||
func : Lambda1Func<source,Out>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === LambdaFunc ===
|
||||
|
||||
/// Newtype wrapper for function stored in the `Lambda` node.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Debug)]
|
||||
pub struct Lambda1Func<In1:Data,Out:Data> {
|
||||
#[derivative(Debug="ignore")]
|
||||
raw : Rc<dyn Fn(&Content<In1>) -> Out>
|
||||
}
|
||||
|
||||
impl<In1,Out,Func> From<Func> for Lambda1Func<In1,Out>
|
||||
where In1 : Data,
|
||||
Out : Data,
|
||||
Func : 'static + Fn(&Content<In1>) -> Content<Out> {
|
||||
fn from(func:Func) -> Self {
|
||||
let raw = Rc::new(move |a:&Content<In1>| { wrap(func(a)) });
|
||||
Self {raw}
|
||||
}
|
||||
}
|
||||
|
||||
impl<In:Value,Out:Data> EventConsumer for Lambda<EventData<In>,Out> {
|
||||
fn on_event(&self, input:&Content<Self::EventInput>) {
|
||||
let output = (self.func.raw)(input);
|
||||
self.emit_event_raw(unwrap(&output));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===============
|
||||
// === Lambda2 ===
|
||||
// ===============
|
||||
|
||||
define_lambda_node! {
|
||||
/// Transforms input data with the provided function. `Lambda2` accepts two inputs. If at least
|
||||
/// one of the inputs was event, the output message will be event as well. In case both inputs
|
||||
/// were behavior, a new behavior will be produced.
|
||||
pub struct Lambda2 Lambda2Shape [source1 source2] {
|
||||
func : Lambda2Func<source1,source2,Out>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === LambdaFunc ===
|
||||
|
||||
/// Newtype wrapper for function stored in the `Lambda2` node.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Debug)]
|
||||
pub struct Lambda2Func<In1:Data,In2:Data,Out:Data> {
|
||||
#[derivative(Debug="ignore")]
|
||||
raw : Rc<dyn Fn(&Content<In1>,&Content<In2>) -> Out>
|
||||
}
|
||||
|
||||
impl<In1,In2,Out,Func> From<Func> for Lambda2Func<In1,In2,Out>
|
||||
where In1:Data, In2:Data, Out:Data,
|
||||
Func : 'static + Fn(&Content<In1>,&Content<In2>) -> Content<Out> {
|
||||
fn from(func:Func) -> Self {
|
||||
let raw = Rc::new(move |a:&Content<In1>,b:&Content<In2>| { wrap(func(a,b)) });
|
||||
Self {raw}
|
||||
}
|
||||
}
|
||||
|
||||
impl<In1,In2,Out> EventConsumer for Lambda2<EventData<In1>,BehaviorData<In2>,Out>
|
||||
where In1:Value, In2:Value, Out:Data {
|
||||
fn on_event(&self, event:&Content<Self::EventInput>) {
|
||||
let value2 = self.source2.current_value();
|
||||
let output = (self.func.raw)(event,&value2);
|
||||
self.emit_event_raw(unwrap(&output));
|
||||
}
|
||||
}
|
||||
|
||||
impl<In1,In2,Out> EventConsumer for Lambda2<BehaviorData<In1>,EventData<In2>,Out>
|
||||
where In1:Value, In2:Value, Out:Data {
|
||||
fn on_event(&self, event:&Content<Self::EventInput>) {
|
||||
let value1 = self.source1.current_value();
|
||||
let output = (self.func.raw)(&value1,event);
|
||||
self.emit_event_raw(unwrap(&output));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Utils ===
|
||||
// =============
|
||||
|
||||
/// A debug trace utility. Prints every incoming event to the console.
|
||||
pub fn trace<T,Label,Source>(label:Label, source:Source) -> Lambda<T,T>
|
||||
where T : Data,
|
||||
Label : Str,
|
||||
Source : Into<Node<T>>,
|
||||
Content<T> : Value + InferProductType<T,ProductType=T>,
|
||||
Node<T> : AddTarget<Lambda<T,T>> {
|
||||
let label = label.into();
|
||||
Lambda::new_named("trace",source, move |t| {
|
||||
println!("TRACE [{}]: {:?}", label, t);
|
||||
t.clone()
|
||||
})
|
||||
}
|
383
gui/lib/frp/src/core/nodes/prim.rs
Normal file
383
gui/lib/frp/src/core/nodes/prim.rs
Normal file
@ -0,0 +1,383 @@
|
||||
//! This module defines a set of primitive FRP building blocks.
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::data::*;
|
||||
use crate::node::*;
|
||||
use crate::nodes::inference::*;
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
// === Macros ===
|
||||
// ==============
|
||||
|
||||
/// Generates a ot of boilerplate for a new node definition. It generates the node struct, the
|
||||
/// constructor, input / output relations, etc. In order to learn more, see the expanded version
|
||||
/// of this macro used below.
|
||||
macro_rules! define_node {
|
||||
(
|
||||
$(#$meta:tt)*
|
||||
$name:ident $shape_name:ident [$($poly_input:ident)*] $(-> [$($out:tt)*])?
|
||||
{ $( $field:ident : $field_type:ty ),* $(,)? }
|
||||
) => {
|
||||
/// The main type definition.
|
||||
$(#$meta)*
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type $name<$($poly_input,)*> = NodeWrapper<$shape_name<$($poly_input,)*>>;
|
||||
|
||||
/// Shape definition.
|
||||
$(#$meta)*
|
||||
#[derive(Debug)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct $shape_name<$($poly_input:Data,)*> {
|
||||
$( $poly_input : Node<$poly_input>,)*
|
||||
$( $field : $field_type ),*
|
||||
}
|
||||
|
||||
define_node_output! { $shape_name [$($poly_input)*] $(-> [$($out)*])? }
|
||||
|
||||
#[allow(non_camel_case_types,unused_parens)]
|
||||
impl<$($poly_input:Data,)*>
|
||||
KnownEventInput for $shape_name<$($poly_input,)*>
|
||||
where ($($poly_input),*) : ContainsEventData,
|
||||
SelectEventData<($($poly_input),*)> : Data {
|
||||
type EventInput = SelectEventData<($($poly_input),*)>;
|
||||
}
|
||||
|
||||
|
||||
paste::item! {
|
||||
#[allow(non_camel_case_types)]
|
||||
impl<$($poly_input:Data,)*> $name<$($poly_input,)*>
|
||||
where $shape_name<$($poly_input),*> : KnownOutput,
|
||||
$(Node<$poly_input> : AddTarget<Self>,)*
|
||||
$(Content<$poly_input> : Value,)*
|
||||
{
|
||||
/// Constructor.
|
||||
pub fn new_named<Label,$([<T $poly_input>],)*>
|
||||
(label:Label, $($poly_input:[<T $poly_input>],)*) -> Self
|
||||
where Label : Into<CowString>,
|
||||
$([<T $poly_input>] : Into<Node<$poly_input>>),*
|
||||
{
|
||||
$(let $poly_input = $poly_input.into();)*
|
||||
$(let $field = default();)*
|
||||
let shape = $shape_name { $($poly_input,)* $($field,)* };
|
||||
let this = Self::construct(label,shape);
|
||||
{
|
||||
$(this.$poly_input.add_target(&this);)*
|
||||
}
|
||||
this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
impl<$($poly_input:Data),*> HasInputs for $shape_name<$($poly_input),*> {
|
||||
fn inputs(&self) -> Vec<NodeWithAnyOutput> {
|
||||
vec![$((&self.$poly_input).into()),*]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal utility for the `define_node` macro.
|
||||
macro_rules! define_node_output {
|
||||
( $shape_name:ident [$($poly_input:ident)*] -> [$($out:tt)*] ) => {
|
||||
#[allow(non_camel_case_types)]
|
||||
impl<$($poly_input:Data,)*>
|
||||
KnownOutput for $shape_name<$($poly_input,)*>
|
||||
where $($out)* : Data {
|
||||
type Output = $($out)*;
|
||||
}
|
||||
};
|
||||
|
||||
( $($t:tt)* ) => {};
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
// === Source ===
|
||||
// ==============
|
||||
|
||||
// === Storage ===
|
||||
|
||||
/// Internal source storage accessor.
|
||||
pub type SourceStorage<T> = <T as KnownSourceStorage>::SourceStorage;
|
||||
|
||||
/// Internal source storage type.
|
||||
pub trait KnownSourceStorage {
|
||||
/// The result type.
|
||||
type SourceStorage : Default;
|
||||
}
|
||||
|
||||
impl<T> KnownSourceStorage for EventData <T> {type SourceStorage = ();}
|
||||
impl<T:Default> KnownSourceStorage for BehaviorData<T> {type SourceStorage = BehaviorData<T>;}
|
||||
|
||||
|
||||
// === Definition ===
|
||||
|
||||
/// Source is a begin point in the FRP network. It is able to emit events or initialize behaviors.
|
||||
pub type Source<Out> = NodeWrapper<SourceShape<Out>>;
|
||||
|
||||
/// Internal definition of the source FRP node.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default (bound="SourceStorage<Out>:Default"))]
|
||||
#[derivative(Debug (bound="SourceStorage<Out>:Debug"))]
|
||||
pub struct SourceShape<Out:KnownSourceStorage> {
|
||||
storage: SourceStorage<Out>
|
||||
}
|
||||
|
||||
impl<Out> KnownOutput for SourceShape<Out>
|
||||
where Out : KnownSourceStorage + Data {
|
||||
type Output = Out;
|
||||
}
|
||||
|
||||
impl<Out> Source<Out>
|
||||
where Out : KnownSourceStorage + Data {
|
||||
/// Constructor.
|
||||
pub fn new_named<Label:Into<CowString>>(label:Label) -> Self {
|
||||
Self::construct(label,default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Out> HasCurrentValue for Source<BehaviorData<Out>>
|
||||
where Out : Value {
|
||||
fn current_value(&self) -> Out {
|
||||
self.storage.value()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Out:KnownSourceStorage> HasInputs for SourceShape<Out> {
|
||||
fn inputs(&self) -> Vec<NodeWithAnyOutput> {
|
||||
default()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Merge ===
|
||||
// =============
|
||||
|
||||
define_node! {
|
||||
Merge MergeShape [source1 source2] -> [source1] {}
|
||||
}
|
||||
|
||||
impl<T1:Data,T2:Data> EventConsumer for Merge<T1,T2>
|
||||
where MergeShape<T1,T2> : KnownEventInput<EventInput=Output<Self>> {
|
||||
fn on_event(&self, event:&Content<Self::EventInput>) {
|
||||
self.emit_event_raw(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
// === Toggle ===
|
||||
// ==============
|
||||
|
||||
define_node! {
|
||||
Toggle ToggleShape [source] -> [EventData<bool>] {
|
||||
status : Cell<bool>
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Value> EventConsumer for Toggle<EventData<T>> {
|
||||
fn on_event(&self, _:&Content<Self::EventInput>) {
|
||||
let val = !self.status.get();
|
||||
self.status.set(val);
|
||||
self.emit_event_raw(&val);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ================
|
||||
// === AssertEq ===
|
||||
// ================
|
||||
|
||||
define_node! {
|
||||
AssertEq AssertEqShape [source] -> [source] {
|
||||
expected_value : RefCell<Content<source>>,
|
||||
success_count : Cell<usize>
|
||||
}
|
||||
}
|
||||
|
||||
impl<Out:Data> AssertEq<Out> {
|
||||
/// Asserts that the next event value will be equal to this value. In other case, it will
|
||||
/// panic after next even occurs and it will behave just as `asser_eq`.
|
||||
pub fn expect(&self, value:Content<Out>) {
|
||||
*self.expected_value.borrow_mut() = value;
|
||||
}
|
||||
|
||||
/// Provides the count of successful events which went trough this node.
|
||||
pub fn success_count(&self) -> usize {
|
||||
self.success_count.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Value> EventConsumer for AssertEq<EventData<T>>
|
||||
where for<'t> &'t T : Eq {
|
||||
fn on_event(&self, event:&Content<Self::EventInput>) {
|
||||
assert_eq!(event,&self.expected_value.borrow());
|
||||
let success_count = self.success_count.get();
|
||||
self.success_count.set(success_count + 1);
|
||||
self.emit_event_raw(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============
|
||||
// === Hold ===
|
||||
// ============
|
||||
|
||||
define_node! {
|
||||
Hold HoldShape [source] -> [BehaviorData<Content<source>>] {
|
||||
last_val : RefCell<Content<source>>
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Value> EventConsumer for Hold<EventData<T>> {
|
||||
fn on_event(&self, event:&Content<Self::EventInput>) {
|
||||
*self.last_val.borrow_mut() = event.clone();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> HasCurrentValue for Hold<EventData<T>>
|
||||
where T : Value {
|
||||
fn current_value(&self) -> T {
|
||||
self.last_val.borrow().clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
// === Sample ===
|
||||
// ==============
|
||||
|
||||
define_node! {
|
||||
Sample SampleShape [source1 source2] {}
|
||||
}
|
||||
|
||||
impl<In1,In2> KnownOutput for SampleShape<EventData<In1>,BehaviorData<In2>>
|
||||
where In1:Value, In2:Value {
|
||||
type Output = EventData<In2>;
|
||||
}
|
||||
|
||||
impl<In1,In2> KnownOutput for SampleShape<BehaviorData<In1>,EventData<In2>>
|
||||
where In1:Value, In2:Value {
|
||||
type Output = EventData<In1>;
|
||||
}
|
||||
|
||||
impl<In1,In2> EventConsumer for Sample<BehaviorData<In1>,EventData<In2>>
|
||||
where In1:Value, In2:Value {
|
||||
fn on_event(&self, _:&Content<Self::EventInput>) {
|
||||
let value = self.source1.current_value();
|
||||
self.emit_event_raw(&value);
|
||||
}
|
||||
}
|
||||
|
||||
impl<In1,In2> EventConsumer for Sample<EventData<In1>,BehaviorData<In2>>
|
||||
where In1:Value, In2:Value {
|
||||
fn on_event(&self, _:&Content<Self::EventInput>) {
|
||||
let value = self.source2.current_value();
|
||||
self.emit_event_raw(&value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============
|
||||
// === Gate ===
|
||||
// ============
|
||||
|
||||
define_node! {
|
||||
Gate GateShape [source1 source2] {}
|
||||
}
|
||||
|
||||
impl<In1,In2> KnownOutput for GateShape<EventData<In1>,BehaviorData<In2>>
|
||||
where In1:Value, In2:Value {
|
||||
type Output = EventData<In1>;
|
||||
}
|
||||
|
||||
impl<In1,In2> KnownOutput for GateShape<BehaviorData<In1>,EventData<In2>>
|
||||
where In1:Value, In2:Value {
|
||||
type Output = EventData<In2>;
|
||||
}
|
||||
|
||||
impl<In:Value> EventConsumer for Gate<BehaviorData<bool>,EventData<In>> {
|
||||
fn on_event(&self, event:&Content<Self::EventInput>) {
|
||||
let check = self.source1.current_value();
|
||||
if check { self.emit_event_raw(event); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === Recursive ===
|
||||
// =================
|
||||
|
||||
/// A very special FRP node. It allows the definition of recursive FRP flows. Please note that it
|
||||
/// has to be initialized with a target node before it can be evaluated. See the examples to learn
|
||||
/// more.
|
||||
pub type Recursive<T> = NodeWrapper<RecursiveShape<T>>;
|
||||
|
||||
/// Shape definition.
|
||||
#[derive(Debug)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct RecursiveShape<T:Data> {
|
||||
source : RefCell<Option<Node<T>>>,
|
||||
}
|
||||
|
||||
impl<T:Data> KnownOutput for RecursiveShape<T> {
|
||||
type Output = T;
|
||||
}
|
||||
|
||||
impl<T:Data> KnownEventInput for RecursiveShape<T> {
|
||||
type EventInput = T;
|
||||
}
|
||||
|
||||
|
||||
// === Constructor ===
|
||||
|
||||
impl<T:Data> Recursive<T> {
|
||||
/// Constructor.
|
||||
pub fn new_named<Label>(label:Label) -> Self
|
||||
where Label : Into<CowString> {
|
||||
let source = default();
|
||||
Self::construct(label,RecursiveShape{source})
|
||||
}
|
||||
|
||||
/// Initializes this node with a target node. Note that this node could not be evaluated before
|
||||
/// the initialization.
|
||||
pub fn initialize<S>(&self, t:S)
|
||||
where S : Into<Node<T>>,
|
||||
Node<T> : AddTarget<Self> {
|
||||
let node = t.into();
|
||||
node.add_target(self);
|
||||
self.set_display_id(node.display_id());
|
||||
*self.source.borrow_mut() = Some(node);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Data> EventConsumer for Recursive<T> {
|
||||
fn on_event(&self, event:&Content<T>) {
|
||||
self.emit_event_raw(event);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Value> HasCurrentValue for Recursive<BehaviorData<T>> {
|
||||
fn current_value(&self) -> T {
|
||||
self.source.borrow().as_ref().unwrap().current_value()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Data> HasInputs for RecursiveShape<T> {
|
||||
fn inputs(&self) -> Vec<NodeWithAnyOutput> {
|
||||
vec![self.source.borrow().as_ref().unwrap().clone_ref().into()]
|
||||
}
|
||||
}
|
177
gui/lib/frp/src/data.rs
Normal file
177
gui/lib/frp/src/data.rs
Normal file
@ -0,0 +1,177 @@
|
||||
//! This module defines abstraction for FRP data types.
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::NodeAsTraitObjectForData;
|
||||
use crate::AnyEventConsumer;
|
||||
use crate::NodeWithAnyOutput;
|
||||
|
||||
|
||||
// =============
|
||||
// === Value ===
|
||||
// =============
|
||||
|
||||
alias! {
|
||||
/// Abstraction for a value carried by the data sent between FRP nodes.
|
||||
Value = { Clone + Debug + Default + 'static }
|
||||
}
|
||||
|
||||
/// Trait for every FRP data which contains valid FRP value.
|
||||
pub trait KnownValue : HasContent {
|
||||
/// The raw value of the data.
|
||||
fn value(&self) -> Content<Self>;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============
|
||||
// === Data ===
|
||||
// ============
|
||||
|
||||
// === Types ===
|
||||
|
||||
alias! {
|
||||
/// Data is information sent between FRP nodes. There are two possible data types:
|
||||
/// `BehaviorData` and `EventData`.
|
||||
Data = { Value + DebugWrapper + NodeAsTraitObjectForData + PhantomInto<DataType> }
|
||||
}
|
||||
|
||||
|
||||
/// A newtype containing a value of an event.
|
||||
#[derive(Clone,Copy,Debug,Default)]
|
||||
pub struct EventData<T>(pub T);
|
||||
|
||||
/// A newtype containing a value of a behavior.
|
||||
#[derive(Clone,Copy,Debug,Default)]
|
||||
pub struct BehaviorData<T>(pub T);
|
||||
|
||||
/// Alias to `Wrapper` with the inner type being `Debug`.
|
||||
pub trait DebugWrapper = Wrapper where Content<Self> : Default + Debug;
|
||||
|
||||
|
||||
// === DataType ===
|
||||
|
||||
/// A value-level information about the data type.
|
||||
#[derive(Clone,Debug,Copy)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum DataType {Event,Behavior}
|
||||
|
||||
impls!{[T] PhantomFrom<EventData<T>> for DataType { Self::Event }}
|
||||
impls!{[T] PhantomFrom<BehaviorData<T>> for DataType { Self::Behavior }}
|
||||
|
||||
|
||||
// === Instances ===
|
||||
|
||||
impl<T:Clone> KnownValue for EventData<T> {
|
||||
fn value(&self) -> T {
|
||||
self.unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Clone> KnownValue for BehaviorData<T> {
|
||||
fn value(&self) -> T {
|
||||
self.unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Wrappers ===
|
||||
|
||||
impl<T> HasContent for EventData<T> { type Content = T; }
|
||||
impl<T> Wrap for EventData<T> { fn wrap (t:T) -> Self { EventData(t) } }
|
||||
impl<T> Unwrap for EventData<T> { fn unwrap (&self) -> &T { &self.0 } }
|
||||
|
||||
impl<T> HasContent for BehaviorData<T> { type Content = T; }
|
||||
impl<T> Wrap for BehaviorData<T> { fn wrap (t:T) -> Self { BehaviorData(t) } }
|
||||
impl<T> Unwrap for BehaviorData<T> { fn unwrap (&self) -> &T { &self.0 } }
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Input ===
|
||||
// =============
|
||||
|
||||
/// Event input associated type. Please note that FRP nodes can have maximum one event input.
|
||||
/// In such a case this trait points to it.
|
||||
pub trait KnownEventInput {
|
||||
/// The event input type.
|
||||
type EventInput : Data;
|
||||
}
|
||||
|
||||
/// Event input accessor.
|
||||
pub type EventInput<T> = <T as KnownEventInput>::EventInput;
|
||||
|
||||
/// Provides a list of all inputs to a node.
|
||||
pub trait HasInputs {
|
||||
/// Accessor.
|
||||
fn inputs(&self) -> Vec<NodeWithAnyOutput>;
|
||||
}
|
||||
|
||||
impl<T> HasInputs for T
|
||||
where T:Unwrap, Content<T>:HasInputs {
|
||||
fn inputs(&self) -> Vec<NodeWithAnyOutput> {
|
||||
self.unwrap().inputs()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
// === Output ===
|
||||
// ==============
|
||||
|
||||
// === Definition ===
|
||||
|
||||
/// Each FRP node has a single node, which type is described by this trait.
|
||||
pub trait KnownOutput {
|
||||
/// The output type.
|
||||
type Output : Data;
|
||||
}
|
||||
|
||||
/// Node output accessor.
|
||||
pub type Output<T> = <T as KnownOutput>::Output;
|
||||
|
||||
impl<T:?Sized+KnownOutput> KnownOutput for Rc<T>
|
||||
where Output<T> : Data {
|
||||
type Output = Output<T>;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// === Traits ===
|
||||
|
||||
/// Trait for nodes which can register an event target.
|
||||
pub trait HasEventTargets : KnownOutput {
|
||||
/// Registers a new event target.
|
||||
fn add_event_target(&self, target:AnyEventConsumer<Output<Self>>);
|
||||
}
|
||||
|
||||
/// Trait for nodes which remember the current value.
|
||||
pub trait HasCurrentValue : KnownOutput {
|
||||
/// Gets the current value of the node.
|
||||
fn current_value(&self) -> Content<Output<Self>>;
|
||||
}
|
||||
|
||||
|
||||
// === KnownOutputType ===
|
||||
|
||||
/// Value-level information about the node output type. Used mainly for debugging purposes.
|
||||
#[allow(missing_docs)]
|
||||
pub trait KnownOutputType {
|
||||
fn output_type (&self) -> DataType;
|
||||
fn output_type_value_name (&self) -> String;
|
||||
}
|
||||
|
||||
impl<T:KnownOutput> KnownOutputType for T
|
||||
where Output<Self> : Data {
|
||||
fn output_type(&self) -> DataType {
|
||||
PhantomData::<Output<Self>>.into()
|
||||
}
|
||||
|
||||
fn output_type_value_name(&self) -> String {
|
||||
let qual = type_name::<Output<Self>>();
|
||||
let param = qual.split('<').skip(1).collect::<String>();
|
||||
let param = ¶m[0..param.len()-1];
|
||||
let param = param.rsplit("::").collect::<Vec<_>>()[0];
|
||||
param.into()
|
||||
}
|
||||
}
|
5
gui/lib/frp/src/debug.rs
Normal file
5
gui/lib/frp/src/debug.rs
Normal file
@ -0,0 +1,5 @@
|
||||
//! Root module for debugging utilities.
|
||||
|
||||
pub mod graphviz;
|
||||
|
||||
pub use graphviz::*;
|
184
gui/lib/frp/src/debug/graphviz.rs
Normal file
184
gui/lib/frp/src/debug/graphviz.rs
Normal file
@ -0,0 +1,184 @@
|
||||
//! This module defines FRP Graphviz bindings. It allows visualizing the FRP network as Graphviz
|
||||
//! diagram.
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::DataType;
|
||||
|
||||
|
||||
|
||||
// ================
|
||||
// === Graphviz ===
|
||||
// ================
|
||||
|
||||
/// Visualization data for a nodes.
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct VizNode {
|
||||
display_id : usize,
|
||||
variant : String,
|
||||
label : String,
|
||||
}
|
||||
|
||||
impl VizNode {
|
||||
/// Constructor
|
||||
pub fn new(display_id:usize, variant:String, label:String) -> Self {
|
||||
VizNode {display_id,variant,label}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Visualization data for a link between nodes.
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct VizLink {
|
||||
source_display_id : usize,
|
||||
target_display_id : usize,
|
||||
message_type : DataType,
|
||||
data_type : String,
|
||||
}
|
||||
|
||||
impl VizLink {
|
||||
/// Constructor.
|
||||
pub fn new
|
||||
(source_display_id:usize, target_display_id:usize, message_type:DataType, data_type:String)
|
||||
-> Self {
|
||||
Self {source_display_id,target_display_id,message_type,data_type}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Graphviz FRP system visualizer.
|
||||
#[derive(Debug,Default)]
|
||||
pub struct Graphviz {
|
||||
nodes : HashMap<usize,VizNode>,
|
||||
labels : HashMap<usize,String>,
|
||||
links : Vec<VizLink>,
|
||||
}
|
||||
|
||||
impl Graphviz {
|
||||
/// Defines a new node.
|
||||
pub fn add_node<Tp:Str,Label:Str>
|
||||
(&mut self, id:usize, display_id:usize, tp:Tp, label:Label) {
|
||||
let tp = tp.into();
|
||||
let label = label.into();
|
||||
self.nodes.insert(id,VizNode::new(display_id,tp,label.clone()));
|
||||
self.labels.insert(id,label);
|
||||
}
|
||||
|
||||
/// Defines a new link between nodes.
|
||||
pub fn add_link<S:Str>
|
||||
(&mut self, source:usize, target:usize, message_type:DataType, data_type:S) {
|
||||
let link = VizLink::new(source,target,message_type,data_type.into());
|
||||
self.links.push(link);
|
||||
}
|
||||
|
||||
/// Checks if a node with the given id is already registered in the node map.
|
||||
pub fn contains(&mut self, id:usize) -> bool {
|
||||
self.nodes.contains_key(&id)
|
||||
}
|
||||
|
||||
/// Takes a set of nodes and outputs a map from `display_id` to a particular node. In case the
|
||||
/// `display_id` points to several nodes, the node types `Hold` and `Recursive` has weaker
|
||||
/// preference.
|
||||
fn create_node_map(&self) -> HashMap<usize,VizNode> {
|
||||
let mut node_map : HashMap<usize,VizNode> = default();
|
||||
for node in self.nodes.values() {
|
||||
let entry = node_map.entry(node.display_id);
|
||||
let merged_entry = entry.and_modify(|node2|{
|
||||
let variant = &node2.variant;
|
||||
if variant == "Hold" || variant == "Recursive" {
|
||||
*node2 = node.clone();
|
||||
}
|
||||
});
|
||||
merged_entry.or_insert_with(|| node.clone());
|
||||
}
|
||||
node_map
|
||||
}
|
||||
|
||||
/// Outputs a Graphviz Dot code.
|
||||
pub fn to_code(&self) -> String {
|
||||
let mut code = String::default();
|
||||
let node_map = self.create_node_map();
|
||||
|
||||
for node in node_map.values() {
|
||||
let color = match node.variant.as_str() {
|
||||
"Toggle" => "534666",
|
||||
"Gate" => "e69d45",
|
||||
"Hold" => "308695",
|
||||
"Lambda" => "d45769",
|
||||
"Lambda2" => "d45769",
|
||||
_ => "455054",
|
||||
};
|
||||
let fill = iformat!("[fillcolor=\"#{color}\"]");
|
||||
let spacing = "<br/><FONT POINT-SIZE=\"5\"> </FONT><br/>";
|
||||
let variant = iformat!("<FONT POINT-SIZE=\"9\">{node.variant}</FONT>");
|
||||
let label = iformat!("[label=< {node.label} {spacing} {variant} >]");
|
||||
let line = iformat!("\n{node.display_id} {fill} {label}");
|
||||
code.push_str(&line);
|
||||
}
|
||||
|
||||
for link in &self.links {
|
||||
let source = &link.source_display_id;
|
||||
let target = &link.target_display_id;
|
||||
let data_type = &link.data_type;
|
||||
let not_loop = source != target;
|
||||
if not_loop {
|
||||
let style = match link.message_type {
|
||||
DataType::Behavior => "[style=\"dashed\"]",
|
||||
_ => ""
|
||||
};
|
||||
let label = if data_type == "()" { "" } else { &data_type };
|
||||
let label = iformat!("[label=\" {label}\"]");
|
||||
let line = iformat!("\n{source} -> {target} {style} {label}");
|
||||
code.push_str(&line);
|
||||
}
|
||||
}
|
||||
|
||||
let fonts = "[fontname=\"Helvetica Neue\" fontsize=11]";
|
||||
let node_shape = "[shape=box penwidth=0 margin=0.12 style=\"rounded,filled\"]";
|
||||
let node_style = "[fontcolor=white fillcolor=\"#5397dc\"]";
|
||||
let edge_style = "[arrowsize=.7 fontcolor=\"#555555\"]";
|
||||
let graph_cfg = iformat!("rankdir=TD; graph {fonts};");
|
||||
let nodes_cfg = iformat!("node {fonts} {node_shape} {node_style};");
|
||||
let edges_cfg = iformat!("edge {fonts} {edge_style};");
|
||||
iformat!("digraph G {{ \n{graph_cfg} \n{nodes_cfg} \n{edges_cfg} \n{code} \n}}")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Graphviz> for String {
|
||||
fn from(cfg:Graphviz) -> String {
|
||||
cfg.to_code()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =======================
|
||||
// === GraphvizBuilder ===
|
||||
// =======================
|
||||
|
||||
/// Trait for every node which can be visualized.
|
||||
pub trait GraphvizBuilder {
|
||||
/// Adds the current object to the builder.
|
||||
fn graphviz_build(&self, builder:&mut Graphviz);
|
||||
|
||||
/// Converts the current object to Graphviz Dot syntax.
|
||||
fn to_graphviz(&self) -> String {
|
||||
let mut builder = Graphviz::default();
|
||||
self.graphviz_build(&mut builder);
|
||||
builder.into()
|
||||
}
|
||||
|
||||
/// Converts the current object to Graphviz and displays it in a new tab in a web browser.
|
||||
fn display_graphviz(&self) {
|
||||
let code = self.to_graphviz();
|
||||
let url = percent_encoding::utf8_percent_encode(&code,percent_encoding::NON_ALPHANUMERIC);
|
||||
let url = format!("https://dreampuf.github.io/GraphvizOnline/#{}",url);
|
||||
crate::web::window().open_with_url_and_target(&url,"_blank").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> GraphvizBuilder for T
|
||||
where T:Unwrap, Content<T>:GraphvizBuilder {
|
||||
default fn graphviz_build(&self, builder:&mut Graphviz) {
|
||||
self.unwrap().graphviz_build(builder)
|
||||
}
|
||||
}
|
5
gui/lib/frp/src/io.rs
Normal file
5
gui/lib/frp/src/io.rs
Normal file
@ -0,0 +1,5 @@
|
||||
//! Root module for Input / Output FRP bindings
|
||||
|
||||
pub mod mouse;
|
||||
|
||||
pub use mouse::*;
|
74
gui/lib/frp/src/io/mouse.rs
Normal file
74
gui/lib/frp/src/io/mouse.rs
Normal file
@ -0,0 +1,74 @@
|
||||
//! Mouse FRP bindings.
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::nodes::*;
|
||||
use crate::frp_def;
|
||||
|
||||
|
||||
|
||||
// ================
|
||||
// === Position ===
|
||||
// ================
|
||||
|
||||
/// A 2-dimensional position. Used for storing the mouse position on the screen.
|
||||
#[derive(Clone,Copy,Debug,Default,PartialEq,Eq)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Position {
|
||||
pub x:i32,
|
||||
pub y:i32,
|
||||
}
|
||||
|
||||
impl Position {
|
||||
/// Constructor.
|
||||
pub fn new(x:i32, y:i32) -> Self {
|
||||
Self {x,y}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Sub<&Position> for &Position {
|
||||
type Output = Position;
|
||||
fn sub(self, rhs: &Position) -> Self::Output {
|
||||
let x = self.x - rhs.x;
|
||||
let y = self.y - rhs.y;
|
||||
Position {x,y}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Mouse ===
|
||||
// =============
|
||||
|
||||
/// Mouse FRP bindings.
|
||||
#[derive(Debug)]
|
||||
pub struct Mouse {
|
||||
/// The mouse up event.
|
||||
pub up : Dynamic<()>,
|
||||
/// The mouse down event.
|
||||
pub down : Dynamic<()>,
|
||||
/// Mouse button press status.
|
||||
pub is_down : Dynamic<bool>,
|
||||
/// Current mouse position.
|
||||
pub position : Dynamic<Position>,
|
||||
}
|
||||
|
||||
impl Default for Mouse {
|
||||
fn default() -> Self {
|
||||
frp_def! { mouse.up = source() }
|
||||
frp_def! { mouse.down = source() }
|
||||
frp_def! { mouse.position = source() }
|
||||
frp_def! { mouse.down_bool = down.constant(true) }
|
||||
frp_def! { mouse.up_bool = up.constant(false) }
|
||||
frp_def! { mouse.is_down = down_bool.merge(&up_bool) }
|
||||
Self {up,down,is_down,position}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mouse {
|
||||
/// Constructor.
|
||||
pub fn new() -> Self {
|
||||
default()
|
||||
}
|
||||
}
|
78
gui/lib/frp/src/lib.rs
Normal file
78
gui/lib/frp/src/lib.rs
Normal file
@ -0,0 +1,78 @@
|
||||
//! This module implements an Functional Reactive Programming system. It is an advanced event
|
||||
//! handling framework which allows describing events and actions by creating declarative event
|
||||
//! flow diagrams.
|
||||
|
||||
#![warn(missing_copy_implementations)]
|
||||
#![warn(missing_debug_implementations)]
|
||||
#![warn(missing_docs)]
|
||||
#![warn(trivial_casts)]
|
||||
#![warn(trivial_numeric_casts)]
|
||||
#![warn(unsafe_code)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![warn(unused_qualifications)]
|
||||
|
||||
#![feature(specialization)]
|
||||
#![feature(trait_alias)]
|
||||
#![feature(weak_into_raw)]
|
||||
#![feature(associated_type_defaults)]
|
||||
|
||||
pub mod data;
|
||||
pub mod debug;
|
||||
pub mod io;
|
||||
pub mod macros;
|
||||
pub mod core;
|
||||
|
||||
pub use data::*;
|
||||
pub use debug::*;
|
||||
pub use io::*;
|
||||
pub use macros::*;
|
||||
pub use crate::core::*;
|
||||
|
||||
use enso_prelude as prelude;
|
||||
use basegl_system_web as web;
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Tests ===
|
||||
// =============
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn drag_and_drop() {
|
||||
let mouse = Mouse::new();
|
||||
|
||||
frp_def! { mouse_down_position = mouse.position.sample (&mouse.down) }
|
||||
frp_def! { mouse_position_if_down = mouse.position.gate (&mouse.is_down) }
|
||||
|
||||
let final_position_ref_event = Recursive::<EventData<Position>>::new_named("final_position_ref");
|
||||
let final_position_ref = Dynamic::from(&final_position_ref_event);
|
||||
|
||||
frp_def! { pos_diff_on_down = mouse_down_position.map2 (&final_position_ref,|m,f|{m-f}) }
|
||||
frp_def! { final_position = mouse_position_if_down.map2 (&pos_diff_on_down ,|m,f|{m-f}) }
|
||||
|
||||
final_position_ref_event.initialize(&final_position);
|
||||
|
||||
final_position_ref.event.set_display_id(final_position.event.display_id());
|
||||
final_position_ref.behavior.set_display_id(final_position.event.display_id());
|
||||
|
||||
assert_eq!(final_position.behavior.current_value(),Position::new(0,0));
|
||||
mouse.position.event.emit(Position::new(3,4));
|
||||
assert_eq!(final_position.behavior.current_value(),Position::new(0,0));
|
||||
mouse.down.event.emit(());
|
||||
assert_eq!(final_position.behavior.current_value(),Position::new(0,0));
|
||||
mouse.position.event.emit(Position::new(4,6));
|
||||
assert_eq!(final_position.behavior.current_value(),Position::new(1,2));
|
||||
mouse.position.event.emit(Position::new(4,7));
|
||||
assert_eq!(final_position.behavior.current_value(),Position::new(1,3));
|
||||
mouse.up.event.emit(());
|
||||
mouse.position.event.emit(Position::new(4,0));
|
||||
assert_eq!(final_position.behavior.current_value(),Position::new(1,3));
|
||||
mouse.down.event.emit(());
|
||||
mouse.position.event.emit(Position::new(0,0));
|
||||
assert_eq!(final_position.behavior.current_value(),Position::new(-3,3));
|
||||
}
|
||||
}
|
69
gui/lib/frp/src/macros.rs
Normal file
69
gui/lib/frp/src/macros.rs
Normal file
@ -0,0 +1,69 @@
|
||||
//! This module defines common macros for FRP netwrok definition.
|
||||
|
||||
|
||||
|
||||
/// Utility for easy definition of the FRP network. In order to keep the network easy to debug and
|
||||
/// reason about, each node constructor consumes a label. Providing labels manually is time
|
||||
/// consuming and error prone. This utility infers the name from the assignment shape and provides
|
||||
/// it automatically to the FRP node constructor.
|
||||
#[macro_export]
|
||||
macro_rules! frp {
|
||||
($($ts:tt)*) => { split_on_terminator! { [[frp_lines]] [] [] $($ts)* } };
|
||||
}
|
||||
|
||||
/// Utility for easy definition of the FRP network definition. Read docs of `frp` macro to learn
|
||||
/// more.
|
||||
#[macro_export]
|
||||
macro_rules! frp_def {
|
||||
($var:ident = $fn:ident $(::<$ty:ty>)? ($($args:tt)*)) => {
|
||||
let $var = Dynamic $(::<$ty>)? :: $fn
|
||||
( stringify!{$var}, $($args)* );
|
||||
};
|
||||
|
||||
($scope:ident . $var:ident = $fn:ident $(::<$ty:ty>)? ($($args:tt)*)) => {
|
||||
let $var = Dynamic $(::<$ty>)? :: $fn
|
||||
( concat! {stringify!{$scope},".",stringify!{$var}}, $($args)* );
|
||||
};
|
||||
|
||||
($var:ident = $fn:ident $(.$fn2:ident)* $(::<$ty:ty>)? ($($args:tt)*)) => {
|
||||
let $var = $fn $(.$fn2)* $(::<$ty>)?
|
||||
( concat! {stringify!{$var}}, $($args)* );
|
||||
};
|
||||
|
||||
($scope:ident . $var:ident = $fn1:ident . $fn2:ident $(.$fn3:ident)* $(::<$ty:ty>)? ($($args:tt)*)) => {
|
||||
let $var = $fn1 . $fn2 $(.$fn3)* $(::<$ty>)?
|
||||
( concat! {stringify!{$scope},".",stringify!{$var}}, $($args)* );
|
||||
};
|
||||
}
|
||||
|
||||
/// Internal helper for the `frp` macro.
|
||||
#[macro_export]
|
||||
macro_rules! frp_lines {
|
||||
([ $([$($line:tt)*])* ]) => {
|
||||
$( frp_def! { $($line)* } )*
|
||||
};
|
||||
}
|
||||
|
||||
/// Splits the token stream on terminators.
|
||||
#[macro_export]
|
||||
macro_rules! split_on_terminator {
|
||||
([[$($f:tt)*] $args:tt] $out:tt []) => {
|
||||
$($f)*! { $args $out }
|
||||
};
|
||||
|
||||
([[$($f:tt)*]] $out:tt []) => {
|
||||
$($f)*! { $out }
|
||||
};
|
||||
|
||||
($f:tt [$($out:tt)*] $current:tt ; $($ts:tt)*) => {
|
||||
split_on_terminator! { $f [$($out)* $current] [] $($ts)* }
|
||||
};
|
||||
|
||||
($f:tt $out:tt [$($current:tt)*] $t:tt $($ts:tt)*) => {
|
||||
split_on_terminator! { $f $out [$($current)* $t] $($ts)* }
|
||||
};
|
||||
|
||||
([[$($f:tt)*] $($args:tt)?] [$($out:tt)*] $current:tt) => {
|
||||
split_on_terminator! { [[$($f)*] $($args)?] [$($out)* $current] [] }
|
||||
};
|
||||
}
|
53
gui/lib/prelude/src/cow_string.rs
Normal file
53
gui/lib/prelude/src/cow_string.rs
Normal file
@ -0,0 +1,53 @@
|
||||
//! This module defines copy-on-write String implementation.
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::impls;
|
||||
use std::ops::Deref;
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === CowString ===
|
||||
// =================
|
||||
|
||||
// === Definition ===
|
||||
|
||||
/// A copy-on-write String implementation. It is a newtype wrapper for `Cow<'static,str>` and
|
||||
/// provides many useful impls for efficient workflow. Use it whenever you want to store a string
|
||||
/// but you are not sure if the string will be allocated or not. This way you can store a static
|
||||
/// slice as long as you can and switch to allocated String on demand.
|
||||
#[derive(Clone,Debug,Default)]
|
||||
pub struct CowString(Cow<'static,str>);
|
||||
|
||||
|
||||
// === Conversions From CowString ===
|
||||
|
||||
impls!{ From <&CowString> for String { |t| t.clone().into() } }
|
||||
impls!{ From <CowString> for String { |t| t.0.into() } }
|
||||
|
||||
|
||||
// === Conversions To CowString ===
|
||||
|
||||
impls!{ From <Cow<'static,str>> for CowString { |t| Self(t) } }
|
||||
impls!{ From <&Cow<'static,str>> for CowString { |t| Self(t.clone()) } }
|
||||
impls!{ From <&'static str> for CowString { |t| Self(t.into()) } }
|
||||
impls!{ From <String> for CowString { |t| Self(t.into()) } }
|
||||
impls!{ From <&String> for CowString { |t| t.to_string().into() } }
|
||||
impls!{ From <&CowString> for CowString { |t| t.clone() } }
|
||||
|
||||
|
||||
// === Instances ===
|
||||
|
||||
impl Deref for CowString {
|
||||
type Target = str;
|
||||
fn deref(&self) -> &str {
|
||||
self.0.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for CowString {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.deref()
|
||||
}
|
||||
}
|
@ -9,6 +9,16 @@
|
||||
#![feature(specialization)]
|
||||
#![feature(trait_alias)]
|
||||
|
||||
pub mod cow_string;
|
||||
pub mod macros;
|
||||
pub mod std_reexports;
|
||||
pub mod wrapper;
|
||||
|
||||
pub use cow_string::*;
|
||||
pub use macros::*;
|
||||
pub use std_reexports::*;
|
||||
pub use wrapper::*;
|
||||
|
||||
pub use boolinator::Boolinator;
|
||||
pub use core::any::type_name;
|
||||
pub use core::fmt::Debug;
|
||||
@ -23,31 +33,7 @@ pub use num::Num;
|
||||
pub use paste;
|
||||
pub use shrinkwraprs::Shrinkwrap;
|
||||
pub use smallvec::SmallVec;
|
||||
pub use std::any::Any;
|
||||
pub use std::cell::Ref;
|
||||
pub use std::cell::RefCell;
|
||||
pub use std::cell::RefMut;
|
||||
pub use std::collections::BTreeMap;
|
||||
pub use std::collections::HashMap;
|
||||
pub use std::collections::HashSet;
|
||||
pub use std::convert::identity;
|
||||
pub use std::convert::TryFrom;
|
||||
pub use std::convert::TryInto;
|
||||
pub use std::fmt::Display;
|
||||
pub use std::fmt;
|
||||
pub use std::hash::Hash;
|
||||
pub use std::iter::FromIterator;
|
||||
pub use std::iter;
|
||||
pub use std::marker::PhantomData;
|
||||
pub use std::ops::Add;
|
||||
pub use std::ops::Deref;
|
||||
pub use std::ops::DerefMut;
|
||||
pub use std::ops::Index;
|
||||
pub use std::ops::IndexMut;
|
||||
pub use std::rc::Rc;
|
||||
pub use std::rc::Weak;
|
||||
pub use std::slice::SliceIndex;
|
||||
pub use std::slice;
|
||||
|
||||
|
||||
use nalgebra::Matrix;
|
||||
use nalgebra::DimName;
|
||||
@ -118,6 +104,19 @@ impl<T> OptionOps for Option<T> {
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === ToRef ===
|
||||
// =============
|
||||
|
||||
/// Similar to `AsRef` but more specific and automatically implemented for every type. Allows for
|
||||
/// conversion `&T` to `&T` (identity) and `T` to `&T` for any type `T`. In contrast, `AsRef`
|
||||
/// requires explicit impls, so for example you cannot do `let t:&() = ().as_ref()`
|
||||
pub trait ToRef<T> where T:?Sized { fn to_ref(&self) -> &T; }
|
||||
impl<T> ToRef<T> for T where T:?Sized { fn to_ref(&self) -> &T { self } }
|
||||
impl<T> ToRef<T> for &T where T:?Sized { fn to_ref(&self) -> &T { self } }
|
||||
|
||||
|
||||
|
||||
// ================
|
||||
// === CloneRef ===
|
||||
// ================
|
||||
@ -430,15 +429,17 @@ impl<T:Deref> WithContent for T
|
||||
// =============
|
||||
|
||||
/// Defines relation between types and values, like between `True` and `true`.
|
||||
pub trait Value {
|
||||
pub trait KnownTypeValue {
|
||||
|
||||
/// The value-level counterpart of this type-value.
|
||||
type Type;
|
||||
type Value;
|
||||
|
||||
/// The value of this type-value.
|
||||
fn value() -> Self::Type;
|
||||
fn value() -> Self::Value;
|
||||
}
|
||||
|
||||
pub type TypeValue<T> = <T as KnownTypeValue>::Value;
|
||||
|
||||
|
||||
|
||||
// =======================
|
||||
@ -453,16 +454,16 @@ pub struct True {}
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
pub struct False {}
|
||||
|
||||
impl Value for True {
|
||||
type Type = bool;
|
||||
fn value() -> Self::Type {
|
||||
impl KnownTypeValue for True {
|
||||
type Value = bool;
|
||||
fn value() -> Self::Value {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Value for False {
|
||||
type Type = bool;
|
||||
fn value() -> Self::Type {
|
||||
impl KnownTypeValue for False {
|
||||
type Value = bool;
|
||||
fn value() -> Self::Value {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
61
gui/lib/prelude/src/macros.rs
Normal file
61
gui/lib/prelude/src/macros.rs
Normal file
@ -0,0 +1,61 @@
|
||||
//! This macro defines set of common macros which are useful across different projects.
|
||||
|
||||
|
||||
/// Allows for nicer definition of impls, similar to what Haskell or Scala does. Reduces the needed
|
||||
/// boilerplate. For example, the following usage:
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// struct A { name:String };
|
||||
/// impls! { From<A> for String { |t| t.name.clone() } }
|
||||
/// ```
|
||||
///
|
||||
/// compiles to:
|
||||
/// ```
|
||||
/// struct A { name:String };
|
||||
/// impl From<A> for String {
|
||||
/// fn from(t:A) -> Self {
|
||||
/// t.name.clone()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This macro is meant to support many standard traits (like From) and should grow in the future.
|
||||
#[macro_export]
|
||||
macro_rules! impls {
|
||||
($([$($impl_params:tt)*])? From<$ty:ty> for $target:ty {
|
||||
|$arg:tt| $($result:tt)*
|
||||
} ) => {
|
||||
#[allow(clippy::redundant_closure_call)]
|
||||
impl <$($($impl_params)*)?> From <$ty> for $target {
|
||||
fn from (arg:$ty) -> Self {
|
||||
(|$arg:$ty| $($result)*)(arg)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
($([$($impl_params:tt)*])? PhantomFrom<$ty:ty> for $target:ty {
|
||||
$($result:tt)*
|
||||
} ) => {
|
||||
impl <$($($impl_params)*)?> From <PhantomData<$ty>> for $target {
|
||||
fn from (_:PhantomData<$ty>) -> Self {
|
||||
$($result)*
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! alias {
|
||||
($( $(#$meta:tt)* $name:ident = {$($tok:tt)*} )*) => {$(
|
||||
$(#$meta)*
|
||||
pub trait $name: $($tok)* {}
|
||||
impl<T:$($tok)*> $name for T {}
|
||||
)*};
|
||||
|
||||
(no_docs $( $(#$meta:tt)* $name:ident = {$($tok:tt)*} )*) => {$(
|
||||
$(#$meta)*
|
||||
#[allow(missing_docs)]
|
||||
pub trait $name: $($tok)* {}
|
||||
impl<T:$($tok)*> $name for T {}
|
||||
)*};
|
||||
}
|
29
gui/lib/prelude/src/std_reexports.rs
Normal file
29
gui/lib/prelude/src/std_reexports.rs
Normal file
@ -0,0 +1,29 @@
|
||||
//! This module reexports several commonly used types defined in the standard library.
|
||||
|
||||
pub use std::any::Any;
|
||||
pub use std::borrow::Cow;
|
||||
pub use std::cell::Cell;
|
||||
pub use std::cell::Ref;
|
||||
pub use std::cell::RefCell;
|
||||
pub use std::cell::RefMut;
|
||||
pub use std::collections::BTreeMap;
|
||||
pub use std::collections::HashMap;
|
||||
pub use std::collections::HashSet;
|
||||
pub use std::convert::identity;
|
||||
pub use std::convert::TryFrom;
|
||||
pub use std::convert::TryInto;
|
||||
pub use std::fmt::Display;
|
||||
pub use std::fmt;
|
||||
pub use std::hash::Hash;
|
||||
pub use std::iter::FromIterator;
|
||||
pub use std::iter;
|
||||
pub use std::marker::PhantomData;
|
||||
pub use std::ops::Add;
|
||||
pub use std::ops::Deref;
|
||||
pub use std::ops::DerefMut;
|
||||
pub use std::ops::Index;
|
||||
pub use std::ops::IndexMut;
|
||||
pub use std::rc::Rc;
|
||||
pub use std::rc::Weak;
|
||||
pub use std::slice::SliceIndex;
|
||||
pub use std::slice;
|
113
gui/lib/prelude/src/wrapper.rs
Normal file
113
gui/lib/prelude/src/wrapper.rs
Normal file
@ -0,0 +1,113 @@
|
||||
//! This type defines Wrap / Unwrap utilities. Unwrap is like `Deref` but does not implement
|
||||
//! `impl<'a, T> Unwrap for &'a T` in order to make it less error prone. `Wrap` is like `pure` in
|
||||
//! applicative functors – if lifts a value to the specific type.
|
||||
|
||||
use crate::std_reexports::*;
|
||||
|
||||
// ===============
|
||||
// === Wrapper ===
|
||||
// ===============
|
||||
|
||||
/// Trait for any type which wraps other type. See docs of `Wrapper` to learn more.
|
||||
pub trait HasContent {
|
||||
type Content : ?Sized;
|
||||
}
|
||||
|
||||
/// Accessor for the wrapped value.
|
||||
pub type Content<T> = <T as HasContent>::Content;
|
||||
|
||||
/// Trait which enables `Sized` super-bound on the `Content` type.
|
||||
pub trait SizedContent = HasContent where Content<Self> : Sized;
|
||||
|
||||
/// Trait for objects which wrap values. Please note that this implements safe wrappers, so the
|
||||
/// object - value relation must be bijective.
|
||||
pub trait Wrapper = Wrap + Unwrap;
|
||||
|
||||
/// Wrapping utility for values.
|
||||
pub trait Wrap : HasContent + SizedContent {
|
||||
/// Wraps the value and returns the wrapped type.
|
||||
fn wrap(t:Self::Content) -> Self;
|
||||
}
|
||||
|
||||
/// Unwrapping utility for wrapped types.
|
||||
///
|
||||
/// Please note that this trait is very similar to the Deref trait. However, there is a very
|
||||
/// important difference. Unlike `Deref`, there is no `impl<'a, T> Unwrap for &'a T` defined. The
|
||||
/// existence of such impl is very error prone when writing complex impls. The `Deref` docs warn
|
||||
/// about it explicitly: "[...] Because of this, Deref should only be implemented for smart pointers
|
||||
/// to avoid confusion.". As an example, consider the following code which contains infinite loop:
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// pub trait HasId {
|
||||
/// fn id(&self) -> usize;
|
||||
/// }
|
||||
///
|
||||
/// // Notice the lack of bound `<T as Deref>::Target : HasId`
|
||||
/// impl<T:Deref> HasId for T {
|
||||
/// fn id(&self) -> usize {
|
||||
/// self.deref().id()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// And the correct version:
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// pub trait HasId {
|
||||
/// fn id(&self) -> usize;
|
||||
/// }
|
||||
///
|
||||
/// // Notice the lack of bound `<T as Deref>::Target : HasId`
|
||||
/// impl<T:Deref> HasId for T where <T as Deref>::Target : HasId {
|
||||
/// fn id(&self) -> usize {
|
||||
/// self.deref().id()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Both versions compile fine, but the former loops for ever.
|
||||
pub trait Unwrap : HasContent {
|
||||
/// Unwraps this type to get the inner value.
|
||||
fn unwrap(&self) -> &Self::Content;
|
||||
}
|
||||
|
||||
|
||||
// === Utils ===
|
||||
|
||||
/// Wraps the value and returns the wrapped type.
|
||||
pub fn wrap<T:Wrap>(t:T::Content) -> T {
|
||||
T::wrap(t)
|
||||
}
|
||||
|
||||
/// Unwraps this type to get the inner value.
|
||||
pub fn unwrap<T:Unwrap>(t:&T) -> &T::Content {
|
||||
T::unwrap(t)
|
||||
}
|
||||
|
||||
|
||||
// === Default Impls ===
|
||||
|
||||
// FIXME: https://github.com/rust-lang/rust/issues/68776
|
||||
//default impl<T:Deref> HasContent for T {
|
||||
// type Content = <Self as Deref>::Target;
|
||||
//}
|
||||
|
||||
default impl<T> Unwrap for T
|
||||
where T:Deref<Target=Content<T>> {
|
||||
fn unwrap (&self) -> &Self::Content {
|
||||
self.deref()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Impls ===
|
||||
|
||||
impl<T:?Sized> HasContent for Rc<T> { type Content = T; }
|
||||
impl<T> Wrap for Rc<T> { fn wrap(t:T) -> Self { Rc::new(t) } }
|
||||
impl<T:?Sized> Unwrap for Rc<T> {}
|
||||
|
||||
impl HasContent for String { type Content = char; }
|
||||
impl Wrap for String { fn wrap(t:char) -> Self { t.to_string() } }
|
||||
|
||||
impl<T> HasContent for Vec<T> { type Content = T; }
|
||||
impl<T> Wrap for Vec<T> { fn wrap(t:T) -> Self { vec![t] } }
|
Loading…
Reference in New Issue
Block a user