<commit_before>package tink.state;

import tink.state.Observable;

using tink.CoreApi;

@:forward(set)
abstract State<T>(StateObject<T>) to Observable<T> from StateObject<T> {

  public var value(get, never):T;
    @:to function get_value() return observe().value;

  public inline function new(value, ?isEqual, ?guard)
    this = new SimpleState(value, isEqual, guard);

  public inline function observe():Observable<T>
    return this;

  public function transform<R>(rules:{ function read(v:T):R; function write(v:R):T; }):State<R>
    return new CompoundState(observe().map(rules.read), function (value) this.set(rules.write(value)));

  public inline function bind(?options:BindingOptions<T>, cb:Callback<T>):CallbackLink
    return observe().bind(options, cb);

  @:impl static public function toggle(s:StateObject<Bool>) {
    s.set(!s.poll().value);
  }

  @:to public function toCallback():Callback<T>
    return this.set;

  static public function compound<T>(source:Observable<T>, update:T->Void, ?comparator:T->T->Bool)
    return new CompoundState(source, update, comparator);

}

private interface StateObject<T> extends ObservableObject<T> {
  function set(value:T):Void;
}

private class CompoundState<T> implements StateObject<T> {

  var data:ObservableObject<T>;
  var update:T->Void;
  var comparator:Null<T->T->Bool>;

  public function new(data, set, ?comparator) {
    this.data = data;
    this.update = set;
    this.comparator = comparator;
  }

  public function isValid()
    return data.isValid();

  public function poll()
    return data.poll();

  public function set(value)
    update(value);

  public function getComparator()<commit_msg>Make compound states work correctly when based on auto observables.<commit_after>package tink.state;

import tink.state.Observable;

using tink.CoreApi;

@:forward(set)
abstract State<T>(StateObject<T>) to Observable<T> from StateObject<T> {

  public var value(get, never):T;
    @:to function get_value() return observe().value;

  public inline function new(value, ?isEqual, ?guard)
    this = new SimpleState(value, isEqual, guard);

  public inline function observe():Observable<T>
    return this;

  public function transform<R>(rules:{ function read(v:T):R; function write(v:R):T; }):State<R>
    return new CompoundState(observe().map(rules.read), function (value) this.set(rules.write(value)));

  public inline function bind(?options:BindingOptions<T>, cb:Callback<T>):CallbackLink
    return observe().bind(options, cb);

  @:impl static public function toggle(s:StateObject<Bool>) {
    s.set(!s.poll().value);
  }

  @:to public function toCallback():Callback<T>
    return this.set;

  static public function compound<T>(source:Observable<T>, update:T->Void, ?comparator:T->T->Bool)
    return new CompoundState(source, update, comparator);

}

private interface StateObject<T> extends ObservableObject<T> {
  function set(value:T):Void;
}

private class CompoundState<T> implements StateObject<T> {

  var data:ObservableObject<T>;
  var update:T->Void;
  var comparator:Null<T->T->Bool>;

  public function new(data, set, ?comparator) {
    this.data = data;
    this.update = set;
    this.comparator = comparator;
  }

  public function isValid()
    return data.isValid();

  public function poll()
    return (data:Observable<T>).measure();

  public function set(value)
    update(value);

  public function getComparator()
{'fraction_non_alphanumeric': 0.13637693883523558, 'fraction_numerical': 0.0, 'mean_word_length': 4.965095986038395, 'pattern_counts': {'":': 0, '<': 42, '<?xml version=': 0, '>': 54, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '886941', 'n_tokens_mistral': 1078, 'n_tokens_neox': 1049, 'n_words': 268}