optics 0.0.1 copy "optics: ^0.0.1" to clipboard
optics: ^0.0.1 copied to clipboard

Lenses and prisms for Dart objects.

example/optics_example.dart

import 'package:meta/meta.dart';
import 'package:optics/optics.dart';

void main() {
  Person person = Person(
    name: 'Joe',
    address: Address(streetName: '123 Capital of Texas Hwy'),
  );

  assert(person.address.streetName == '123 Capital of Texas Hwy');

  /// Joe moved!
  person = person.addressOptic.compound(AddressOptics.streetName).set(
        '456 Mesa Dr',
      );

  assert(person.address.streetName == '456 Mesa Dr');

  /// Joe got a job!
  person = BoundPrism(source: person, prism: PersonOptics.job).set(
    Job(address: Address(streetName: '789 E 6th St'), title: 'Sales bro'),
  );

  assert(person.job?.title == 'Sales bro');

  final personJobTitle = PersonOptics.job.compound(JobOptics.title.asPrism());

  /// Joe got a promotion!
  person = personJobTitle.set(person, 'Executive sales bro');

  assert(person.job?.title == 'Executive sales bro');

  print('''Howdy, ${person.name}!
You're ${personJobTitle.get(person)?.indefiniteArticle()} ${personJobTitle.get(person)}.
Nice place you've got @ ${person.addressOptic.compound(AddressOptics.streetName)()}.''');
}

@immutable
class Person {
  const Person({
    required this.name,
    required this.address,
    this.job,
  });

  final String name;

  final Address address;

  final Job? job;

  Person copyWith({
    String? name,
    Address? address,
    Job? job,
  }) =>
      Person(
        name: name ?? this.name,
        address: address ?? this.address,
        job: job ?? this.job,
      );

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is Person &&
          name == other.name &&
          address == other.address &&
          job == other.job;

  @override
  int get hashCode => Object.hash(name, address, job);
}

extension PersonOptics on Person {
  static final Lens<Person, Address> address = Lens(
    getter: (subject) => subject.address,
    setter: (subject, value) => subject.copyWith(address: value),
  );

  static final Prism<Person, Job?> job = Prism(
    getter: (subject) => subject.job,
    setter: (subject, value) => subject.copyWith(job: value),
  );

  BoundLens<Person, Address> get addressOptic =>
      BoundLens(source: this, lens: address);

  BoundPrism<Person, Job?> get jobOptic => BoundPrism(source: this, prism: job);
}

@immutable
class Address {
  const Address({required this.streetName});

  final String streetName;

  Address copyWith({String? streetName}) => Address(
        streetName: streetName ?? this.streetName,
      );

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is Address && streetName == other.streetName;

  @override
  int get hashCode => streetName.hashCode;
}

extension AddressOptics on Address {
  static final Lens<Address, String> streetName = Lens(
    getter: (subject) {
      return subject.streetName;
    },
    setter: (subject, value) {
      return subject.copyWith(streetName: value);
    },
  );

  BoundLens<Address, String> get streetNameOptic =>
      BoundLens(source: this, lens: streetName);
}

@immutable
class Job {
  Job({
    required this.address,
    required this.title,
  });

  final String title;

  final Address address;

  Job copyWith({String? title, Address? address}) => Job(
        title: title ?? this.title,
        address: address ?? this.address,
      );

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is Job && title == other.title && address == other.address;

  @override
  int get hashCode => Object.hash(title, address);
}

extension JobOptics on Job {
  static final Lens<Job, String> title = Lens(
    getter: (subject) {
      return subject.title;
    },
    setter: (subject, value) {
      return subject.copyWith(title: value);
    },
  );

  static final Lens<Job, Address> address = Lens(
    getter: (subject) {
      return subject.address;
    },
    setter: (subject, value) {
      return subject.copyWith(address: value);
    },
  );

  BoundLens<Job, String> get titleOptic => BoundLens(source: this, lens: title);

  BoundLens<Job, Address> get addressOptic =>
      BoundLens(source: this, lens: address);
}

extension StringOptics on String {
  BoundLens<String, String> get indefiniteArticle => BoundLens(
        source: this,
        lens: Lens(
          getter: (source) {
            if (source.isEmpty) {
              return source;
            }

            final vowels = ['a', 'e', 'i', 'o', 'u'];

            for (final vowel in vowels) {
              if (source.toLowerCase().startsWith(vowel)) {
                return 'an';
              }
            }

            return 'a';
          },
          setter: (source, value) => value,
        ),
      );
}

class Nullable {
  const Nullable();
  static const instance = Nullable();
}
1
likes
0
points
5
downloads

Publisher

unverified uploader

Weekly Downloads

Lenses and prisms for Dart objects.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

meta

More

Packages that depend on optics