@@ -257,6 +257,11 @@ class EntityResolver extends Builder {
257257 }
258258 }
259259
260+ // Verify there is at most 1 unique property with REPLACE strategy.
261+ ensureSingleUniqueReplace (entity);
262+ // If sync enabled, verify all unique properties use REPLACE strategy.
263+ ifSyncEnsureAllUniqueAreReplace (entity);
264+
260265 entity.properties.forEach ((p) => log.info (' $p ' ));
261266
262267 return entity;
@@ -301,9 +306,9 @@ class EntityResolver extends Builder {
301306 FieldElement f, int ? fieldType, Element elementBare, ModelProperty prop) {
302307 IndexType ? indexType;
303308
304- final hasIndexAnnotation = _indexChecker.hasAnnotationOfExact (f);
305- final hasUniqueAnnotation = _uniqueChecker.hasAnnotationOfExact (f);
306- if (! hasIndexAnnotation && ! hasUniqueAnnotation ) return null ;
309+ final indexAnnotation = _indexChecker.firstAnnotationOfExact (f);
310+ final uniqueAnnotation = _uniqueChecker.firstAnnotationOfExact (f);
311+ if (indexAnnotation == null && uniqueAnnotation == null ) return null ;
307312
308313 // Throw if property type does not support any index.
309314 if (fieldType == OBXPropertyType .Float ||
@@ -319,8 +324,6 @@ class EntityResolver extends Builder {
319324 }
320325
321326 // If available use index type from annotation.
322- final indexAnnotation =
323- hasIndexAnnotation ? _indexChecker.firstAnnotationOfExact (f) : null ;
324327 if (indexAnnotation != null && ! indexAnnotation.isNull) {
325328 final enumValItem = enumValueItem (indexAnnotation.getField ('type' )! );
326329 if (enumValItem != null ) indexType = IndexType .values[enumValItem];
@@ -343,8 +346,15 @@ class EntityResolver extends Builder {
343346 "entity ${elementBare .name }: a hash index is not supported for type '${f .type }' of field '${f .name }'" );
344347 }
345348
346- if (hasUniqueAnnotation ) {
349+ if (uniqueAnnotation != null && ! uniqueAnnotation.isNull ) {
347350 prop.flags | = OBXPropertyFlags .UNIQUE ;
351+ // Determine unique conflict resolution.
352+ final onConflictVal =
353+ enumValueItem (uniqueAnnotation.getField ('onConflict' )! );
354+ if (onConflictVal != null &&
355+ ConflictStrategy .values[onConflictVal] == ConflictStrategy .replace) {
356+ prop.flags | = OBXPropertyFlags .UNIQUE_ON_CONFLICT_REPLACE ;
357+ }
348358 }
349359
350360 switch (indexType) {
@@ -363,6 +373,27 @@ class EntityResolver extends Builder {
363373 }
364374 }
365375
376+ void ensureSingleUniqueReplace (ModelEntity entity) {
377+ final uniqueReplaceProps = entity.properties
378+ .where ((p) => p.hasFlag (OBXPropertyFlags .UNIQUE_ON_CONFLICT_REPLACE ));
379+ if (uniqueReplaceProps.length > 1 ) {
380+ throw InvalidGenerationSourceError (
381+ "ConflictStrategy.replace can only be used on a single property, but found multiple in '${entity .name }':\n ${uniqueReplaceProps .join ('\n ' )}" );
382+ }
383+ }
384+
385+ void ifSyncEnsureAllUniqueAreReplace (ModelEntity entity) {
386+ if (! entity.hasFlag (OBXEntityFlags .SYNC_ENABLED )) return ;
387+ final uniqueButNotReplaceProps = entity.properties.where ((p) {
388+ return p.hasFlag (OBXPropertyFlags .UNIQUE ) &&
389+ ! p.hasFlag (OBXPropertyFlags .UNIQUE_ON_CONFLICT_REPLACE );
390+ });
391+ if (uniqueButNotReplaceProps.isNotEmpty) {
392+ throw InvalidGenerationSourceError (
393+ "Synced entities must use @Unique(onConflict: ConflictStrategy.replace) on all unique properties, but found others in '${entity .name }':\n ${uniqueButNotReplaceProps .join ('\n ' )}" );
394+ }
395+ }
396+
366397 int ? enumValueItem (DartObject typeField) {
367398 if (! typeField.isNull) {
368399 final enumValues = (typeField.type as InterfaceType )
0 commit comments