generate method

Future<void> generate()

Implementation

Future<void> generate() async {
  // Consider adding a specific flag in SupabaseGenConfig if needed,
  // e.g., config.generateUserPreferencesManager
  // For now, generating if this method is called.

  final managersDir = p.join(config.outputDirectory, 'managers');
  final fileName = 'user_preferences_manager.g.dart';
  final filePath = p.join(managersDir, fileName);

  final buffer = StringBuffer();

  buffer.writeln("""
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: constant_identifier_names

import 'dart:convert';
import 'package:sqlite_async/sqlite_async.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../database.dart'; // Assuming database.dart is at the root of outputDirectory

/// Defines the valid types for user preferences.
enum UserPreferenceValueType {
text,
integer,
number,
boolean,
datetime,
stringList,
integerArray,
numberArray,
jsonObject,
jsonArray,
}

/// Utility to convert UserPreferenceValueType enum to its string representation for storage.
String userPreferenceValueTypeToString(UserPreferenceValueType type) {
switch (type) {
  case UserPreferenceValueType.text:
    return 'text';
  case UserPreferenceValueType.integer:
    return 'integer';
  case UserPreferenceValueType.number:
    return 'number';
  case UserPreferenceValueType.boolean:
    return 'boolean';
  case UserPreferenceValueType.datetime:
    return 'datetime';
  case UserPreferenceValueType.stringList:
    return 'stringList';
  case UserPreferenceValueType.integerArray:
    return 'integerArray';
  case UserPreferenceValueType.numberArray:
    return 'numberArray';
  case UserPreferenceValueType.jsonObject:
    return 'jsonObject';
  case UserPreferenceValueType.jsonArray:
    return 'jsonArray';
}
}

/// Utility to convert a string representation from storage back to UserPreferenceValueType enum.
UserPreferenceValueType? userPreferenceValueTypeFromString(String? typeString) {
if (typeString == null) return null;
switch (typeString) {
  case 'text':
    return UserPreferenceValueType.text;
  case 'integer':
    return UserPreferenceValueType.integer;
  case 'number':
    return UserPreferenceValueType.number;
  case 'boolean':
    return UserPreferenceValueType.boolean;
  case 'datetime':
    return UserPreferenceValueType.datetime;
  case 'stringList':
    return UserPreferenceValueType.stringList;
  case 'integerArray':
    return UserPreferenceValueType.integerArray;
  case 'numberArray':
    return UserPreferenceValueType.numberArray;
  case 'jsonObject':
    return UserPreferenceValueType.jsonObject;
  case 'jsonArray':
    return UserPreferenceValueType.jsonArray;
  default:
    // Optionally, log or throw an error for unknown types
    print('Warning: Unknown UserPreferenceValueType string: \$typeString');
    return null;
}
}


/// Manages storing, retrieving, and streaming user preferences from the SQLite database.
class UserPreferencesManager {
final SqliteDatabase db;
static const String _tableName = 'user_preferences';

UserPreferencesManager(this.db);

/// Sets or updates a user preference.
/// [key]: The unique key for the preference.
/// [value]: The value to store. It will be JSON encoded.
/// [valueType]: The type of the value, used as a hint for deserialization.
Future<void> setPreference<T>(
  String key,
  T value, {
  required UserPreferenceValueType valueType,
}) async {
  final String jsonValue = jsonEncode(value);
  final String valueTypeString = userPreferenceValueTypeToString(valueType);
  await db.execute(
    '''INSERT INTO \$_tableName (preference_key, preference_value, value_type)
       VALUES (?, ?, ?)
       ON CONFLICT(preference_key) DO UPDATE SET
         preference_value = excluded.preference_value,
         value_type = excluded.value_type;''',
    [key, jsonValue, valueTypeString],
  );
}

/// Retrieves a user preference and deserializes its JSON value.
/// Returns `null` if the key is not found or if deserialization fails.
/// [key]: The key of the preference to retrieve.
/// [fromJson]: A function to convert the decoded JSON (dynamic) to type [T].
Future<T?> getPreference<T>(
  String key, {
  required T Function(dynamic jsonData) fromJson,
}) async {
  final row = await db.get(
    'SELECT preference_value FROM \$_tableName WHERE preference_key = ?',
    [key],
  );
  // If row is null, the key doesn't exist.
  if (row == null) return null;
  final rawValue = row['preference_value'] as String?;
  if (rawValue == null) {
    try {
      // Allow fromJson to handle null if it's designed to (e.g. for nullable types)
      return fromJson(null);
    } catch (_) {
      // If fromJson(null) throws, return null or handle as appropriate
      return null;
    }
  }
  try {
    final jsonData = jsonDecode(rawValue);
    return fromJson(jsonData);
  } catch (e, s) {
    print('Error decoding preference for key "\$key": \$e\$s');
    return null;
  }
}

/// Watches for changes to a specific user preference and streams its deserialized JSON value.
/// Emits `null` if the key is not found, deleted, or if deserialization fails.
/// [key]: The key of the preference to watch.
/// [fromJson]: A function to convert the decoded JSON (dynamic) to type [T].
Stream<T?> watchPreference<T>(
  String key, {
  required T Function(dynamic jsonData) fromJson,
}) {
  return db
      .watch(
        'SELECT preference_value FROM \$_tableName WHERE preference_key = ?',
        parameters: [key],
      )
      .map((result) {
        if (result.isEmpty) return null; // No rows found
        final row = result.first;
        final rawValue = row['preference_value'] as String?;
        if (rawValue == null) {
          try {
            return fromJson(null);
          } catch (_) {
            return null;
          }
        }
        try {
          final jsonData = jsonDecode(rawValue);
          return fromJson(jsonData);
        } catch (e, s) {
          print('Error decoding preference for key "\$key" in stream: \$e\$s');
          return null;
        }
      });
}

/// Retrieves the raw preference data (key, JSON value, value_type) as a map.
/// Returns `null` if the key is not found.
Future<Map<String, dynamic>?> getRawPreference(String key) async {
  final row = await db.get(
    'SELECT preference_key, preference_value, value_type FROM \$_tableName WHERE preference_key = ?',
    [key],
  );
  if (row == null) return null;
  return {
    'preference_key': row['preference_key'],
    'preference_value': row['preference_value'],
    'value_type': row['value_type'],
  };
}

/// Watches for changes to a specific user preference and streams its raw data as a map.
/// Emits `null` if the key is not found or deleted.
Stream<Map<String, dynamic>?> watchRawPreference(String key) {
  return db
      .watch(
        'SELECT preference_key, preference_value, value_type FROM \$_tableName WHERE preference_key = ?',
        parameters: [key],
      )
      .map((result) {
        if (result.isEmpty) return null; // No rows found
        final row = result.first;
        return {
          'preference_key': row['preference_key'],
          'preference_value': row['preference_value'],
          'value_type': row['value_type'],
        };
      });
}

/// Deletes a user preference by its key.
Future<void> deletePreference(String key) async {
  await db.execute('DELETE FROM \$_tableName WHERE preference_key = ?', [key]);
}

/// Ensures that a set of default preferences are set if they don't already exist.
/// [defaultSettings]: A map where keys are preference keys and values are records
/// containing the default `value` and `valueType`.
Future<void> ensureDefaultPreferences(
  Map<String, ({Object value, UserPreferenceValueType valueType})> defaultSettings,
) async {
  for (final entry in defaultSettings.entries) {
    final key = entry.key;
    final defaultValue = entry.value.value;
    final valueType = entry.value.valueType;

    final existing = await getRawPreference(key);
    if (existing == null) {
      // Key does not exist, set the default
      await setPreference<dynamic>(key, defaultValue, valueType: valueType);
      print('Set default preference for key "\$key"');
    }
  }
}
}

""");

  // --- Riverpod Provider ---
  if (config.useRiverpod) {
    buffer.writeln(
      'final userPreferencesManagerProvider = Provider<UserPreferencesManager>((ref) {',
    );
    buffer.writeln('  final database = ref.watch(databaseProvider);');
    buffer.writeln('  return UserPreferencesManager(database.db);');
    buffer.writeln('});');
  }

  try {
    final file = File(filePath);
    await file.parent.create(recursive: true);
    await file.writeAsString(buffer.toString());
    _logger.info('Generated User Preferences Manager: $filePath');
  } catch (e) {
    _logger.severe(
      'Error writing User Preferences Manager file $filePath: $e',
    );
  }
}