From 805cd6ed403773f2bc66353635db0dd9644d11f8 Mon Sep 17 00:00:00 2001 From: Kirthisai Date: Fri, 10 Apr 2026 20:23:28 +0530 Subject: [PATCH] docs: improve API reference coverage for pub.dev Add /// Dart doc comments to all public classes, methods, and fields. Every public method now documents its parameters, return value, and includes dart code block examples. Closes #28 --- lib/safe_text.dart | 22 ++++++++++ lib/src/aho_corasick.dart | 79 ++++++++++++++++++++++++++++++++++-- lib/src/models/language.dart | 45 ++++++++++++++++++++ 3 files changed, 143 insertions(+), 3 deletions(-) diff --git a/lib/safe_text.dart b/lib/safe_text.dart index ddb1dd8..349bd25 100644 --- a/lib/safe_text.dart +++ b/lib/safe_text.dart @@ -8,9 +8,25 @@ import 'src/models/mask_strategy.dart'; import 'src/safe_text_filter.dart'; import 'src/phone_number_checker.dart'; +/// Legacy facade that delegates to [SafeTextFilter] and [PhoneNumberChecker]. +/// +/// > **Deprecated.** Use [SafeTextFilter] and [PhoneNumberChecker] directly. +/// > They offer better modularity, higher performance via the Aho-Corasick +/// > trie, and explicit initialization via [SafeTextFilter.init]. +/// +/// Migration guide: +/// +/// | Old call | New call | +/// |---|---| +/// | `SafeText.filterText(...)` | `SafeTextFilter.filterText(...)` | +/// | `SafeText.containsBadWord(...)` | `SafeTextFilter.containsBadWord(...)` | +/// | `SafeText.containsPhoneNumber(...)` | `PhoneNumberChecker.containsPhoneNumber(...)` | @Deprecated( 'Use SafeTextFilter and PhoneNumberChecker directly for better modularity and high performance.') class SafeText { + /// Filters profanity from [text]. + /// + /// > **Deprecated.** Use [SafeTextFilter.filterText] instead. @Deprecated('Use SafeTextFilter.filterText instead.') static String filterText({ required String text, @@ -35,6 +51,9 @@ class SafeText { ); } + /// Returns `true` if [text] contains a bad word. + /// + /// > **Deprecated.** Use [SafeTextFilter.containsBadWord] instead. @Deprecated('Use SafeTextFilter.containsBadWord instead.') static Future containsBadWord({ required String text, @@ -50,6 +69,9 @@ class SafeText { ); } + /// Returns `true` if [text] contains a phone number. + /// + /// > **Deprecated.** Use [PhoneNumberChecker.containsPhoneNumber] instead. @Deprecated('Use PhoneNumberChecker.containsPhoneNumber instead.') static Future containsPhoneNumber({ required String text, diff --git a/lib/src/aho_corasick.dart b/lib/src/aho_corasick.dart index 9294d6a..ef7ca76 100644 --- a/lib/src/aho_corasick.dart +++ b/lib/src/aho_corasick.dart @@ -1,12 +1,56 @@ +/// A single node in the Aho-Corasick trie. +/// +/// Each node represents a prefix of one or more patterns that have been +/// inserted via [AhoCorasick.addWord]. class TrieNode { + /// Maps a Unicode code point to the child [TrieNode] for that character. final Map children = {}; + + /// Failure (fallback) link — points to the node representing the longest + /// proper suffix of the current path that is also a valid prefix in the trie. + /// + /// Set on all non-root nodes by [AhoCorasick.buildFailureLinks]. TrieNode? fail; + + /// Patterns that terminate at this node. + /// + /// After [AhoCorasick.buildFailureLinks] is called, this list also includes + /// patterns inherited from nodes reachable via [fail] links (the "dictionary + /// suffix links" of the classic algorithm). final List outputs = []; } +/// An implementation of the Aho-Corasick multi-pattern string search algorithm. +/// +/// Aho-Corasick finds all occurrences of a set of patterns in a text in a +/// single linear pass — O(n + m + z), where n is the text length, m is the +/// total length of all patterns, and z is the number of matches. This makes +/// it well-suited for profanity filtering with large word lists. +/// +/// ## Usage +/// +/// ```dart +/// final ac = AhoCorasick(); +/// ac.addWord('bad'); +/// ac.addWord('worse'); +/// ac.buildFailureLinks(); // must be called before search +/// +/// final matches = ac.search('this is bad and worse'); +/// // {10: ['bad'], 20: ['worse']} +/// ``` +/// +/// **Important:** always call [buildFailureLinks] after adding all words and +/// before calling [search]. Omitting this step produces incorrect results. class AhoCorasick { final TrieNode _root = TrieNode(); + /// Inserts [word] into the trie. + /// + /// The word is lowercased before insertion so that [search] can operate on + /// pre-lowercased input. Empty strings are silently ignored. + /// + /// Call this for every pattern you want to detect, then call + /// [buildFailureLinks] once before any calls to [search]. void addWord(String word) { if (word.isEmpty) return; TrieNode current = _root; @@ -17,6 +61,15 @@ class AhoCorasick { current.outputs.add(word.toLowerCase()); } + /// Constructs failure links for all nodes in the trie using a BFS traversal. + /// + /// This is the preprocessing phase of the Aho-Corasick algorithm. It must + /// be called **once**, after all words have been added via [addWord] and + /// before any calls to [search]. + /// + /// Failure links allow the search to fall back to the longest matching + /// suffix instead of restarting from the root on a mismatch, which keeps + /// the search complexity linear in the length of the input. void buildFailureLinks() { final queue = []; @@ -49,9 +102,29 @@ class AhoCorasick { } } - /// Finds all matches in the text. - /// Returns a map where the key is the string index where the match ENDS - /// and the value is a list of matching words. + /// Searches [text] for all patterns previously added via [addWord]. + /// + /// Returns a [Map] where each key is the **zero-based end index** (inclusive) + /// of a match within [text], and the corresponding value is the list of + /// pattern strings that end at that position. + /// + /// The search is case-insensitive — [text] is lowercased internally before + /// matching. + /// + /// [buildFailureLinks] must have been called before invoking this method. + /// + /// ```dart + /// final ac = AhoCorasick() + /// ..addWord('he') + /// ..addWord('she') + /// ..addWord('hers') + /// ..buildFailureLinks(); + /// + /// final result = ac.search('ushers'); + /// // Keys represent end indices; values are matched words at that position. + /// ``` + /// + /// Returns an empty map if no patterns match. Map> search(String text) { final matches = >{}; TrieNode? current = _root; diff --git a/lib/src/models/language.dart b/lib/src/models/language.dart index 5b0d053..8e7707c 100644 --- a/lib/src/models/language.dart +++ b/lib/src/models/language.dart @@ -1,3 +1,24 @@ +/// Supported languages for the profanity word dataset. +/// +/// Pass a value from this enum to [SafeTextFilter.init] to load the +/// corresponding bundled word list. Use [Language.all] to load every +/// available language at once (increases memory usage and initialization +/// time). +/// +/// Language codes follow ISO 639 where available. +/// +/// ```dart +/// // Single language +/// await SafeTextFilter.init(language: Language.english); +/// +/// // Multiple languages +/// await SafeTextFilter.init( +/// languages: [Language.english, Language.spanish, Language.hindi], +/// ); +/// +/// // Every supported language +/// await SafeTextFilter.init(language: Language.all); +/// ``` enum Language { afrikaans, amharic, @@ -83,7 +104,20 @@ enum Language { all, } +/// Utility extension on [Language] for string conversion and asset path resolution. extension LanguageExtension on Language { + /// Returns the [Language] value that corresponds to [languageCode]. + /// + /// Accepts both full language names (e.g. `'english'`) and ISO 639 codes + /// (e.g. `'en'`). Matching is case-insensitive. + /// + /// Falls back to [Language.english] for any unrecognized code. + /// + /// ```dart + /// LanguageExtension.fromString('en'); // Language.english + /// LanguageExtension.fromString('Spanish'); // Language.spanish + /// LanguageExtension.fromString('xyz'); // Language.english (default) + /// ``` static Language fromString(String languageCode) { switch (languageCode.toLowerCase()) { case 'af': @@ -337,6 +371,17 @@ extension LanguageExtension on Language { } } + /// The ISO 639 file code used to locate the bundled asset for this language. + /// + /// Asset files are stored at `assets/data/.txt` inside the + /// package. Returns an empty string for [Language.all], which is a sentinel + /// value and does not correspond to a single file. + /// + /// ```dart + /// Language.english.fileCode; // 'en' + /// Language.spanish.fileCode; // 'es' + /// Language.all.fileCode; // '' + /// ``` String get fileCode { switch (this) { case Language.afrikaans: