entity_mapper 0.5.0 copy "entity_mapper: ^0.5.0" to clipboard
entity_mapper: ^0.5.0 copied to clipboard

Lightweight code generator for Clean Architecture focused projects. Automatically creates type-safe Entity ↔ Model mapping methods with dart_mappable-style patterns.

example/main.dart

// Comprehensive runnable example for the entity_mapper package.
//
// Run from the package root:
//
//   dart run build_runner build
//   dart run example/main.dart
//
// Each section below demonstrates one capability of the generator and prints
// the result so you can see entity ↔ model conversion in action.

import 'package:entity_mapper/entity_mapper.dart';

part 'main.entity_mapper.dart';

// ────────────────────────────────────────────────────────────────────────
// Domain entities — pure Dart, no annotations, no codegen.
// ────────────────────────────────────────────────────────────────────────

class User {
  const User({required this.id, required this.name, required this.age});

  final String id;
  final String name;
  final int age;
}

class Address {
  const Address({required this.street, required this.city});

  final String street;
  final String city;
}

class UserWithAddress {
  const UserWithAddress({
    required this.id,
    required this.name,
    required this.address,
  });

  final String id;
  final String name;
  final Address address;
}

class UserWithAddresses {
  const UserWithAddresses({required this.id, required this.addresses});

  final String id;
  final List<Address> addresses;
}

class UserMaybeAddress {
  const UserMaybeAddress({required this.id, required this.name, this.address});

  final String id;
  final String name;
  final Address? address;
}

class UserMaybeAddresses {
  const UserMaybeAddresses({required this.id, this.addresses});

  final String id;
  final List<Address>? addresses;
}

class NullablePerson {
  const NullablePerson({this.id, this.name, this.age});

  final String? id;
  final String? name;
  final int? age;
}

/// Entity with only two fields. A model targeting this can declare extra
/// fields — entity_mapper passes `null` for required-nullable extras and lets
/// optional extras use their defaults.
class PartialUser {
  const PartialUser({required this.id, required this.name});

  final String id;
  final String name;
}

// ────────────────────────────────────────────────────────────────────────
// Data models — annotated with @MapToEntity. The companion
// `main.entity_mapper.dart` file is generated by build_runner.
// ────────────────────────────────────────────────────────────────────────

@MapToEntity(User)
class UserModel with UserEntityMappable {
  const UserModel({required this.id, required this.name, required this.age});

  final String id;
  final String name;
  final int age;
}

@MapToEntity(Address)
class AddressModel with AddressEntityMappable {
  const AddressModel({required this.street, required this.city});

  final String street;
  final String city;
}

@MapToEntity(UserWithAddress)
class UserWithAddressModel with UserWithAddressEntityMappable {
  const UserWithAddressModel({
    required this.id,
    required this.name,
    required this.address,
  });

  final String id;
  final String name;
  final AddressModel address; // non-list nested model
}

@MapToEntity(UserWithAddresses)
class UserWithAddressesModel with UserWithAddressesEntityMappable {
  const UserWithAddressesModel({required this.id, required this.addresses});

  final String id;
  final List<AddressModel> addresses; // list of nested models
}

@MapToEntity(UserMaybeAddress)
class UserMaybeAddressModel with UserMaybeAddressEntityMappable {
  const UserMaybeAddressModel({
    required this.id,
    required this.name,
    this.address,
  });

  final String id;
  final String name;
  final AddressModel? address; // nullable nested model
}

@MapToEntity(UserMaybeAddresses)
class UserMaybeAddressesModel with UserMaybeAddressesEntityMappable {
  const UserMaybeAddressesModel({required this.id, this.addresses});

  final String id;
  final List<AddressModel>? addresses; // nullable list of nested models
}

@MapToEntity(NullablePerson)
class NullablePersonModel with NullablePersonEntityMappable {
  const NullablePersonModel({this.id, this.name, this.age});

  final String? id;
  final String? name;
  final int? age;
}

/// Model with a required-nullable field that has no counterpart on the
/// `PartialUser` entity. The generator passes `null` for `extra` when
/// constructing the model from the entity. (A non-nullable required field
/// in the same position would fail at codegen.)
@MapToEntity(PartialUser)
class PartialUserExtraModel with PartialUserExtraEntityMappable {
  const PartialUserExtraModel({
    required this.id,
    required this.name,
    required this.extra,
  });

  final String id;
  final String name;
  final String? extra;
}

// ────────────────────────────────────────────────────────────────────────
// Demonstration
// ────────────────────────────────────────────────────────────────────────

void main() {
  // 1. Primitives — entity → model → entity round-trip.
  const user = User(id: 'u1', name: 'Alice', age: 30);
  final userModel = UserEntityMapper.toModel(user);
  print('1. Primitives');
  print('   entity:  id=${user.id} name=${user.name} age=${user.age}');
  print(
    '   model:   id=${userModel.id} name=${userModel.name} age=${userModel.age}',
  );

  // 2. Non-list nested model.
  const userWithAddr = UserWithAddress(
    id: 'u2',
    name: 'Bob',
    address: Address(street: 'Main', city: 'Metropolis'),
  );
  final uwaModel = UserWithAddressEntityMapper.toModel(userWithAddr);
  print('\n2. Nested model (non-list)');
  print(
    '   model.address is an AddressModel: ${uwaModel.address.runtimeType}',
  );
  print(
    '   address: street=${uwaModel.address.street} city=${uwaModel.address.city}',
  );

  // 3. List of nested models.
  const userWithList = UserWithAddresses(
    id: 'u3',
    addresses: [
      Address(street: 'Main', city: 'A'),
      Address(street: 'Oak', city: 'B'),
    ],
  );
  final uwlModel = UserWithAddressesEntityMapper.toModel(userWithList);
  print('\n3. List of nested models');
  print(
    '   model has ${uwlModel.addresses.length} AddressModels: '
    '${uwlModel.addresses.map((a) => "${a.street}/${a.city}").join(", ")}',
  );

  // 4. Nullable nested model — both populated and null.
  const filledNested = UserMaybeAddress(
    id: 'u4',
    name: 'Carol',
    address: Address(street: 'Pine', city: 'Gotham'),
  );
  const emptyNested = UserMaybeAddress(id: 'u5', name: 'Dave');
  final filledModel = UserMaybeAddressEntityMapper.toModel(filledNested);
  final emptyModel = UserMaybeAddressEntityMapper.toModel(emptyNested);
  print('\n4. Nullable nested model');
  print('   populated → model.address.street = ${filledModel.address?.street}');
  print('   null      → model.address is null: ${emptyModel.address == null}');

  // 5. Nullable list of nested models.
  const filledList = UserMaybeAddresses(
    id: 'u6',
    addresses: [Address(street: 'Elm', city: 'X')],
  );
  const noList = UserMaybeAddresses(id: 'u7');
  final filledListModel = UserMaybeAddressesEntityMapper.toModel(filledList);
  final noListModel = UserMaybeAddressesEntityMapper.toModel(noList);
  print('\n5. Nullable list of nested models');
  print(
    '   populated → ${filledListModel.addresses?.length} item(s) of type '
    '${filledListModel.addresses?.first.runtimeType}',
  );
  print('   null      → addresses is null: ${noListModel.addresses == null}');

  // 6. Fully-nullable primitives.
  const allNull = NullablePerson();
  const someNull = NullablePerson(id: 'p1', age: 42);
  final allNullModel = NullablePersonEntityMapper.toModel(allNull);
  final someNullModel = NullablePersonEntityMapper.toModel(someNull);
  print('\n6. Nullable primitives');
  print(
    '   all-null  → id=${allNullModel.id} name=${allNullModel.name} '
    'age=${allNullModel.age}',
  );
  print(
    '   partial   → id=${someNullModel.id} name=${someNullModel.name} '
    'age=${someNullModel.age}',
  );

  // 7. Required-nullable model field with no entity counterpart.
  //    `PartialUser` has only id+name; `PartialUserExtraModel` declares an
  //    extra `required` nullable field. The generator inserts `null` so the
  //    required keyword on the model is satisfied.
  const partial = PartialUser(id: 'u8', name: 'Eve');
  final partialModel = PartialUserExtraEntityMapper.toModel(partial);
  print('\n7. Required-nullable model field absent from entity');
  print('   model.extra = ${partialModel.extra} (filled by the generator)');

  // 8. The `toEntity()` mixin method matches the static `toEntity` call.
  const m = UserModel(id: 'u9', name: 'Frank', age: 50);
  final viaMixin = m.toEntity();
  final viaStatic = UserEntityMapper.toEntity(m);
  print('\n8. Mixin vs static toEntity');
  print('   viaMixin.id == viaStatic.id: ${viaMixin.id == viaStatic.id}');
  print(
    '   mappers are singletons: '
    '${identical(UserEntityMapper.ensureInitialized(), UserEntityMapper.ensureInitialized())}',
  );
}
1
likes
160
points
82
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

Lightweight code generator for Clean Architecture focused projects. Automatically creates type-safe Entity ↔ Model mapping methods with dart_mappable-style patterns.

Repository (GitHub)
View/report issues
Contributing

Topics

#clean-architecture #entity-mapping #code-generation #domain-driven-design #dart

License

MIT (license)

Dependencies

analyzer, build, source_gen

More

Packages that depend on entity_mapper