#![allow(dead_code)]
use crate::data::BindingValue;
use crate::reactive::{Disposable, IDisposable, IObservable, IObserver, ObservableError};
use crate::BoxedValue;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
mod attached_property_tests;
mod binding_default_value_converter_tests;
mod binding_expression_tests_attached_property;
mod binding_expression_tests_data_validation;
mod binding_expression_tests_ferro_property;
mod binding_expression_tests_get_value;
mod binding_expression_tests_mode;
mod binding_expression_tests_negation;
mod binding_expression_tests_observable;
mod binding_expression_tests_property;
mod binding_expression_tests_set_value;
mod binding_expression_tests_update_source_trigger;
mod binding_expression_observer_indexer_tests;
mod binding_expression_tests_indexer;
mod binding_expression_tests_task;
mod binding_model_tests;
mod binding_null_conditional_tests;
mod binding_operations_tests;
mod binding_plugin_tests;
mod binding_setter_tests;
mod reference_semantics_tests;
mod binding_test_support;
mod binding_typed_expression_tests;
mod class_registration_tests;
mod compiled_binding_tests_create;
mod direct_property_tests;
#[cfg(any(target_os = "linux", target_os = "macos"))]
mod ferro_object_tests_add_owner;
mod ferro_object_tests_attached;
mod ferro_object_tests_binding;
mod ferro_object_tests_binding_two_way;
mod ferro_object_tests_coercion;
mod ferro_object_tests_data_validation;
mod ferro_object_tests_direct;
mod ferro_object_tests_get_observable;
mod ferro_object_tests_get_value;
mod ferro_object_tests_inheritance;
mod ferro_object_tests_metadata;
mod ferro_object_tests_multi_binding;
mod ferro_object_tests_notification_order;
mod ferro_object_tests_on_property_changed;
mod ferro_object_tests_reentrancy;
mod ferro_object_tests_set_current_value;
mod ferro_object_tests_set_value;
mod ferro_object_tests_validation;
mod ferro_property_registry_tests;
mod ferro_property_tests;
mod property_registration_tests;
mod property_store;
mod styled_property_tests;
mod virtual_dispatch_tests;
macro_rules! test_class {
($name:ident : $base:ident) => {
#[repr(C)]
pub struct $name {
base: $base,
}
$crate::ferro_class!($name: $base);
$crate::ferro_impl_classes!($name: $crate::FerroObjectImpl);
impl $name {
pub fn construct() -> Self {
Self { base: $base::construct() }
}
pub fn new() -> $crate::Ref<Self> {
$crate::instantiate(Self::construct())
}
}
};
}
pub(crate) use test_class;
macro_rules! static_type {
($name:ident) => {
pub struct $name;
impl $crate::StaticType for $name {
const TYPE: &'static $crate::TypeInfo = {
static TYPE: $crate::TypeInfo = $crate::TypeInfo::new(stringify!($name), None);
&TYPE
};
}
};
}
pub(crate) use static_type;
macro_rules! once_per_thread {
($body:block) => {{
$crate::ferro_thread_local! {
static DONE: ::std::cell::Cell<bool> = const { ::std::cell::Cell::new(false) };
}
if !DONE.replace(true) $body
}};
}
pub(crate) use once_per_thread;
#[track_caller]
pub(crate) fn assert_panics(f: impl FnOnce()) {
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f));
assert!(result.is_err(), "expected a panic");
}
pub(crate) fn s(value: &str) -> String {
value.to_string()
}
pub(crate) fn bv(value: &str) -> BindingValue<String> {
BindingValue::new(value.to_string())
}
pub(crate) fn boxed<T: PartialEq + 'static>(value: T) -> BoxedValue {
Rc::new(value)
}
pub(crate) fn error(message: &str) -> crate::data::BindingError {
crate::data::BindingError::message(message)
}
struct SubjectInner<T> {
current: RefCell<Option<T>>,
track_current: bool,
remove_on_completed: bool,
observers: RefCell<Vec<(u64, Rc<dyn IObserver<T>>)>>,
next_id: Cell<u64>,
completed: Cell<bool>,
}
pub(crate) struct Subject<T> {
inner: Rc<SubjectInner<T>>,
}
impl<T> Clone for Subject<T> {
fn clone(&self) -> Self {
Self { inner: self.inner.clone() }
}
}
impl<T: Clone + 'static> Subject<T> {
fn create(current: Option<T>, track_current: bool, remove_on_completed: bool) -> Self {
Self {
inner: Rc::new(SubjectInner {
current: RefCell::new(current),
track_current,
remove_on_completed,
observers: RefCell::new(Vec::new()),
next_id: Cell::new(0),
completed: Cell::new(false),
}),
}
}
pub fn new() -> Self {
Self::create(None, false, true)
}
pub fn behavior(initial: T) -> Self {
Self::create(Some(initial), true, true)
}
pub fn test(initial: T) -> Self {
Self::create(Some(initial), false, false)
}
pub fn subscriber_count(&self) -> usize {
self.inner.observers.borrow().len()
}
pub fn observable(&self) -> Rc<dyn IObservable<T>> {
Rc::new(self.clone())
}
fn snapshot(&self) -> Vec<Rc<dyn IObserver<T>>> {
self.inner.observers.borrow().iter().map(|(_, o)| o.clone()).collect()
}
pub fn on_next(&self, value: T) {
if self.inner.track_current {
*self.inner.current.borrow_mut() = Some(value.clone());
}
for observer in self.snapshot() {
observer.on_next(value.clone());
}
}
pub fn on_completed(&self) {
self.inner.completed.set(true);
let observers = self.snapshot();
if self.inner.remove_on_completed {
self.inner.observers.borrow_mut().clear();
}
for observer in observers {
observer.on_completed();
}
}
pub fn on_error(&self, error: ObservableError) {
self.inner.completed.set(true);
let observers = self.snapshot();
if self.inner.remove_on_completed {
self.inner.observers.borrow_mut().clear();
}
for observer in observers {
observer.on_error(error.clone());
}
}
}
impl<T: Clone + 'static> IObservable<T> for Subject<T> {
fn subscribe(&self, observer: Rc<dyn IObserver<T>>) -> Rc<dyn IDisposable> {
if self.inner.completed.get() && self.inner.remove_on_completed {
observer.on_completed();
return Disposable::empty();
}
let id = self.inner.next_id.get();
self.inner.next_id.set(id + 1);
self.inner.observers.borrow_mut().push((id, observer.clone()));
let current = self.inner.current.borrow().clone();
if let Some(current) = current {
observer.on_next(current);
}
let inner = Rc::downgrade(&self.inner);
Disposable::create(move || {
if let Some(inner) = inner.upgrade() {
inner.observers.borrow_mut().retain(|(i, _)| *i != id);
}
})
}
}
pub(crate) struct Recorder<T> {
values: Rc<RefCell<Vec<T>>>,
}
impl<T> Clone for Recorder<T> {
fn clone(&self) -> Self {
Self { values: self.values.clone() }
}
}
impl<T: Clone> Recorder<T> {
pub fn new() -> Self {
Self { values: Rc::new(RefCell::new(Vec::new())) }
}
pub fn push(&self, value: T) {
self.values.borrow_mut().push(value);
}
pub fn clear(&self) {
self.values.borrow_mut().clear();
}
pub fn len(&self) -> usize {
self.values.borrow().len()
}
pub fn get(&self) -> Vec<T> {
self.values.borrow().clone()
}
}
#[derive(Clone, Default)]
pub(crate) struct Counter(Rc<Cell<i32>>);
impl Counter {
pub fn new() -> Self {
Self::default()
}
pub fn increment(&self) {
self.0.set(self.0.get() + 1);
}
pub fn get(&self) -> i32 {
self.0.get()
}
}
#[derive(Clone, Default)]
pub(crate) struct Flag(Rc<Cell<bool>>);
impl Flag {
pub fn new() -> Self {
Self::default()
}
pub fn set(&self, value: bool) {
self.0.set(value);
}
pub fn get(&self) -> bool {
self.0.get()
}
}
pub(crate) fn is_sender<T: crate::ObjectType>(
e: &crate::FerroPropertyChangedEventArgs<'_>,
target: &crate::WeakRef<T>,
) -> bool {
match target.upgrade() {
Some(target) => e.sender().to_ref().ptr_eq(&target),
None => false,
}
}
pub(crate) fn old_string(e: &crate::FerroPropertyChangedEventArgs<'_>) -> Option<String> {
e.get_old_value::<String>()
}
pub(crate) fn new_string(e: &crate::FerroPropertyChangedEventArgs<'_>) -> String {
e.get_new_value::<String>()
}