module Net::IMAP::StringPrep

def self.[](table)

Returns a Regexp matching the given +table+ name.
def self.[](table)
  Tables::REGEXPS.fetch(table)
end

def check_bidi!(string, c_8: false, profile: nil)

added to any exception that is raised (it does not affect behavior).
requirements are met. +profile+ is an optional string which will be
Raises either ProhibitedCodepoint or BidiStringError unless all

checked when c_8: true.
This is usually combined with #check_prohibited!, so table "C.8" is only

RandALCat character MUST be the last character of the string.
character MUST be the first character of the string, and a
* If a string contains any RandALCat character, a RandALCat
contain any LCat character.
* If a string contains any RandALCat character, the string MUST NOT
* The characters in \StringPrep\[\"C.8\"] MUST be prohibited

requirements in RFC-3454, ยง6:
Checks that +string+ obeys all of the "Bidirectional Characters"
def check_bidi!(string, c_8: false, profile: nil)
  check_prohibited!(string, "C.8", profile: profile) if c_8
  if Tables::BIDI_FAILS_REQ2.match?(string)
    raise BidiStringError.new(
      Tables::BIDI_DESC_REQ2, string: string, profile: profile,
    )
  elsif Tables::BIDI_FAILS_REQ3.match?(string)
    raise BidiStringError.new(
      Tables::BIDI_DESC_REQ3, string: string, profile: profile,
    )
  end
end

def check_prohibited!(string,

is raised (it does not affect behavior).
+profile+ is an optional string which will be added to any exception that

raise a BidiStringError.
Also checks bidirectional characters, when bidi: true, which may

ProhibitedCodepoint describing the first matching table.
Checks +string+ for any codepoint in +tables+. Raises a
def check_prohibited!(string,
                      *tables,
                      bidi: false,
                      unassigned: "A.1",
                      stored: false,
                      profile: nil)
  tables  = Tables::TITLES.keys.grep(/^C/) if tables.empty?
  tables |= [unassigned] if stored
  tables |= %w[C.8] if bidi
  table   = tables.find {|t|
    case t
    when String then Tables::REGEXPS.fetch(t).match?(string)
    when Regexp then t.match?(string)
    else raise ArgumentError, "only table names and regexps can be checked"
    end
  }
  if table
    raise ProhibitedCodepoint.new(
      table, string: string, profile: profile
    )
  end
  check_bidi!(string, profile: profile) if bidi
end

def map_tables!(string, *tables)

def map_tables!(string, *tables)
  tables.each do |table|
    regexp, replacements = Tables::MAPPINGS.fetch(table)
    string.gsub!(regexp, replacements)
  end
  string
end

def stringprep(string,


this specification.
The above steps MUST be performed in the order given to comply with

error. This is described in section 6.
satisfy the requirements for bidirectional strings, return an
requirements for bidirectional strings. If the string does not
any are found, make sure that the whole string satisfies the
4. Check bidi -- Possibly check for right-to-left characters, and if

section 5.
output. If any are found, return an error. This is described in
3. Prohibit -- Check for any characters that are not allowed in the

normalization. This is described in section 4.
2. Normalize -- Possibly normalize the result of step 1 using Unicode

section 3.
and, if so, replace it with its mapping. This is described in
1. Map -- For each character in the input, check if it has a mapping
>>>
def stringprep(string,
               maps:,
               normalization:,
               prohibited:,
               **opts)
  string = string.encode("UTF-8") # also dups (and raises invalid encoding)
  map_tables!(string, *maps)                     if maps
  string.unicode_normalize!(normalization)       if normalization
  check_prohibited!(string, *prohibited, **opts) if prohibited
  string
end