Skip to content

A Dart library for parsing `String` values into various object types with type safety and ease of use.

License

Notifications You must be signed in to change notification settings

mysCod3r/pars3r

Repository files navigation

Pars3r

Pars3r is a Dart library designed to facilitate the parsing of String values into various object types. It provides a flexible and extensible framework for handling both primitive types and enums, ensuring type safety and ease of use in your Dart applications.

Motivation

Managing diverse data types like String, int, bool, double, enum, object, and even lists of objects from sources like Firebase Remote Config or custom APIs can quickly become complex and error-prone. Manually handling these types often leads to repetitive code and potential bugs.

Pars3r solves this challenge by offering a unified framework for parsing and managing various data types. It ensures simplicity, type safety, and scalability in your Dart applications, making it easier to work with data from multiple sources seamlessly.

Example Usage

  • The complete example can be found in the example directory.

1. Basic Usage with Firebase Remote Config

Pars3r allows you to extend your Firebase Remote Config service with the IParsableService interface. Below is an example implementation:

class FirebaseRemoteConfigService with IParsableService {
  Future<bool> initialize() async {
    await _remoteConfig.fetchAndActivate();
    return _remoteConfig.lastFetchStatus == RemoteConfigFetchStatus.success;
  }

  R read<T extends IParsableModel<T>, R>({
    required String key,
    required T parseModel,
  }) {
    final stringValue = _remoteConfig.getString(key);
    return super.parser<T, R>(parseModel, stringValue);
  }
}

You can then use this service to parse different data types:

final service = FirebaseRemoteConfigService();
await service.initialize();

final version = service.read<String, String>(
  key: 'version', 
  parseModel: ParsablePrimitiveModel<String>(''),
);
print(version); // "1.0.0"

// Below is an example of enum creation.
final appTheme = service.read<ParsableEnumModel, ParsableEnumModel>(
  key: 'app_theme', 
  parseModel: const ParsableEnumModel(AppTheme.system),
);

print(appTheme.value); // AppTheme.dark

2. Managing Multiple Remote Items

For better maintainability, you can define a centralized enum for all your remote configuration keys:

enum RemoteItem<T extends IParsableModel<T>, R> {
  welcomeMessage<ParsablePrimitiveModel<String>, ParsablePrimitiveModel<String>>(),
  appTheme<ParsableEnumModel<AppTheme>, ParsableEnumModel<AppTheme>>(),
  maxRetryCount<ParsablePrimitiveModel<int>, ParsablePrimitiveModel<int>>(),
  isPromoEnabled<ParsablePrimitiveModel<bool>, ParsablePrimitiveModel<bool>>(),
  percentage<ParsablePrimitiveModel<double>, ParsablePrimitiveModel<double>>(),
  forceUpdate<ForceUpdateModel, ForceUpdateModel>(),
  products<ProductModel, List<ProductModel>>(),
  ;

  /// Fetches the remote configuration value associated with this enum item.
  ///
  /// If an error occurs during fetching, it logs the error and returns
  /// a default value based on the type (`empty`).
  R get read {
    try {
      return remoteConfigService.read<T, R>(
        key: name.toSnakeCase(),
        parseModel: _empty,
      );
    } catch (error) {
      debugPrint('RemoteItems.value: $error');
      if (R == List<T>) return [_empty] as R;
      return _empty as R;
    }
  }

  /// Returns a default value for the remote configuration item.
  T get _empty {
    return switch (this) {
      RemoteItem.welcomeMessage => ParsablePrimitiveModel<String>.empty(),
      RemoteItem.appTheme => const ParsableEnumModel(AppTheme.dark),
      RemoteItem.maxRetryCount => ParsablePrimitiveModel<int>.empty(),
      RemoteItem.isPromoEnabled => ParsablePrimitiveModel<bool>.empty(),
      RemoteItem.percentage => ParsablePrimitiveModel<double>.empty(),
      RemoteItem.forceUpdate => const ForceUpdateModel.empty(),
      RemoteItem.products => const ProductModel.empty(),
    } as T;
  }
}

Using the above enum:

final welcomeMessage = RemoteItem.welcomeMessage.read; // "Welcome to Pars3r"
final appTheme = RemoteItem.appTheme.read; // AppTheme.dark
final maxRetryCount = RemoteItem.maxRetryCount.read; // 3
final isPromoEnabled = RemoteItem.isPromoEnabled.read; // true
final percentage = RemoteItem.percentage.read; // 0.5
final forceUpdate = RemoteItem.forceUpdate.read; // ForceUpdateModel(version: '1.0.0', forceUpdate: true)
final products = RemoteItem.products.read; // [ProductModel(name: 'Product 1', price: 100.0)]

Features

Abstract Models

  • IParsableModel: A base interface for models that can parse String values into objects of a specified type.

  • IParsablePrimitiveModel: Abstract mixin for parsing primitive types such as int, bool, and double.

  • IParsableEnumModel: Specialized interface for enums that can be parsed from String values.

  • IParsableObjectModel: Abstract mixin for parsing complex JSON objects.

Concrete Models

  • ParsablePrimitiveModel: A concrete implementation for parsing primitive types.

  • ParsableEnumModel: A concrete implementation for parsing enums.

Parsing Service

  • IParsableService: Provides a parsing mechanism for models, supporting single objects and lists.

Model Definitions

1. Primitive Types

You can use the ready-made models for String, int, double, and bool, or create your own models.

2. Enums

First, the enum class to be parsed should inherit from the IParsableEnum interface. This interface should include a method that returns all values of the enum class. Then, using the IParsableEnumModel class, the enum class can be made parsable.

enum AppTheme with IParsableEnum<AppTheme> {
  system,
  light,
  dark,
  ;
 
  @override
  List<AppTheme> get enumValues => values;
}

3. Complex Objects

class ForceUpdateModel extends IParsableObjectModel<ForceUpdateModel> {
  const ForceUpdateModel({
    required this.version,
    required this.forceUpdate,
  });

  const ForceUpdateModel.empty() : this(version: '', forceUpdate: false);

  final String version;
  final bool forceUpdate;

  @override
  ForceUpdateModel fromJson(Map<String, dynamic> json) {
    return ForceUpdateModel(
      version: json['version'] as String,
      forceUpdate: json['force_update'] as bool,
    );
  }
}

Contributing

Contributions are welcome! Feel free to submit a pull request or open an issue to suggest improvements

Acknowledgments

I would like to express my gratitude to VB10 for their exceptional work on the Vexana package. The structure and functionality provided by Vexana greatly inspired me during the development of this package. Thank you to the Vexana team and community for their contributions.

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

A Dart library for parsing `String` values into various object types with type safety and ease of use.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published