1package com.peachpasswords.autofill2345678910111213data class PasswordPolicy(14 val minLength: Int? = null,15 val maxLength: Int? = null,16) {17 1819202122 fun generationLength(default: Int = DEFAULT_LENGTH, hardMax: Int = HARD_MAX): Int {23 val target = (maxLength ?: default).coerceAtMost(hardMax)24 val raised = minLength?.let { target.coerceAtLeast(it) } ?: target25 val capped = maxLength?.let { raised.coerceAtMost(it) } ?: raised26 return capped.coerceAtLeast(1)27 }2829 30 fun constraintNote(length: Int): String = when {31 maxLength != null && length >= maxLength -> "This site allows max $maxLength characters"32 minLength != null && length <= minLength -> "This site requires at least $minLength characters"33 else -> ""34 }3536 companion object {37 const val DEFAULT_LENGTH = 2038 const val HARD_MAX = 1283940 414243444546 fun fromHtmlAttributes(attrs: Map<String, String>): PasswordPolicy {47 var min = attrs["minlength"]?.toIntOrNull()?.takeIf { it > 0 }48 var max = attrs["maxlength"]?.toIntOrNull()?.takeIf { it > 0 }49 val pattern = attrs["pattern"]50 if (pattern != null && (min == null || max == null)) {51 val range = Regex("\\{(\\d+),(\\d+)\\}").find(pattern)52 val minOnly = Regex("\\{(\\d+),\\}").find(pattern)53 val exact = Regex("\\{(\\d+)\\}").find(pattern)54 when {55 range != null -> {56 if (min == null) min = range.groupValues[1].toIntOrNull()57 if (max == null) max = range.groupValues[2].toIntOrNull()58 }59 minOnly != null -> if (min == null) min = minOnly.groupValues[1].toIntOrNull()60 exact != null -> {61 val n = exact.groupValues[1].toIntOrNull()62 if (min == null) min = n63 if (max == null) max = n64 }65 }66 }67 return PasswordPolicy(minLength = min, maxLength = max)68 }69 }70}