(**
 * Copyright (c) 2013-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *)

(*
 * An ExactCover.t represents an exact cover over 0 or more files.
 *
 * See https://en.wikipedia.org/wiki/Exact_cover; the short version is that an
 * ExactCover.t represents a set of adjacent, nonoverlapping, nonempty,
 * exhaustive ranges over 0 or more files.
 *
 * ExactCover.t is a read-only structure for efficient querying.
 *
 * ExactCover.builder is a write-only structure for efficient construction
 * (under some assuptions) that can be baked into an ExactCover.t.
 *)

open Lints
open Severity

(* Supports O(log(n)) queries to get the value associated with a loc. *)
type 'a t

(* Cover over 0 files. *)
val empty: 'a t
(* Given a filename and a value, generate a cover associating that value with that entire file. *)
val file_cover: File_key.t -> 'a -> 'a t
(* Gets the value associated with a certain location in the code. To resolve
 * ambiguity, this looks at the location of the first character in the provided
 * location. Errors if queried for a file not contained in this cover. *)
val find: Loc.t -> 'a t -> 'a
(* Combines two disjoint covers. Behavior is undefined if the provided covers
 * aren't disjoint. *)
val union: 'a t -> 'a t -> 'a t
(* Combines covers collated by filename into one cover. Behavior is undefined if
 * the provided covers aren't disjoint. *)
val union_all : 'a t Utils_js.FilenameMap.t -> 'a t


(* Supports O(j*(j+k)) operations to modify a range of a cover being constructed,
 * where j is the number of ranges in the builder intersecting the range being
 * modified and k is the number of ranges in the builder after the range being
 * modified. Note that this means that operations are O(1) in the case where
 * ranges are modified in order from the beginning of the file to the end, which
 * is how this builder is used. *)
(* Better complexity modifications are possible, but because the builder is only
 * used to modify ranges in order, giving O(1) complexity, there was no reason
 * to make them. *)
(* The builder structure is only intended to construct a cover for a single
 * file. If a cover for multiple files is needed, construct and bake a cover for
 * each file and use the union functions to combine them. *)
type 'a builder
(* Create a new builder for the provided file. The resultant builder is an exact
 * cover with a single range associating the whole file with the provided value. *)
val new_builder: File_key.t -> 'a -> 'a builder
(* Change the value in the provided range by applying the provided mapping
 * function. Ranges in the builder that are completely contained in the provided
 * range have their values replaced using the mapping function. Ranges in the
 * builder that have no overlap with the provided range are left untouched.
 * Ranges in the builder that intersect with the provided range are split into
 * two ranges: one that is completely contained in the provided range and one
 * that has no overlap with the provided range. If the provided range is
 * completely contained within a range in the builder, the range in the builder
 * is split into three ranges: one that matches the provided range and two that
 * have no overlap wth the provided range. *)
val update_range:
  Loc.t -> ('a -> 'a) -> 'a builder -> 'a builder

(* Change the settings in the provided range by adding the provided settings list.
* In the settings list, the kind is the type of lint, the value is the value to set to,
* and the location is the position of the setting in the source code. *)
val update_settings:
  Loc.t -> (* Range to operate on *)
  (lint_kind * ('a * Loc.t)) list -> (* List of settings to add *)
  'a LintSettings.t builder -> (* Builder to work from *)
  'a LintSettings.t builder (* Resultant builder *)
(* Works similarly to update_settings, but takes two additional parameters: a running
 * LintSettings object and an error handling function. The LintSettings object is updated with
 * the new lint settings (in addition to the builder being updated), and if any redundant
 * settings are encountered, the error function is called with an error message and the
 * location of the error. Additionally, takes the lint settings in unflattened form so
 * that errors can be properly reported. *)
(* This function only checks for settings that are redundant because they don't change
 * anything. It doesn't check for settings that are redundant because they are
 * immediately overwritten. (That's done elsewhere.) *)
val update_settings_and_running:
  'a LintSettings.t -> (* Running lint settings *)
  (LintSettings.lint_parse_error -> unit) -> (* Parse error handler *)
  Loc.t -> (* Range to operate on *)
  (lint_kind * ('a * Loc.t)) list list -> (* Unflattened list of settings to add *)
  'a LintSettings.t builder -> (* Builder to work from *)
  'a LintSettings.t builder (* Resultant builder *)
    * 'a LintSettings.t (* Resultant running lint settings *)

val bake: 'a builder -> 'a t


(* `severity LintSettings.t`-specific functions *)

type lint_severity_cover = severity LintSettings.t t

(* Given a filename, generate a cover that applies the default lint severities
 * across the entire file. *)
val default_file_cover: File_key.t -> lint_severity_cover
(* Gets the severity of the provided lint kind at the provided location. Errors
 * if queried for a file not contained in this cover. *)
val get_severity: lint_kind -> Loc.t -> lint_severity_cover -> severity
(* True iff the provided lint kind has severity `Off` at the provided location.
 * Errors if queried for a file not contained in this cover. *)
val is_suppressed: lint_kind -> Loc.t -> lint_severity_cover -> bool
(* True iff the severity for the provided lint kind has been explicitly set at
 * the provided location. Errors if queried for a file not contained in this
 * cover. *)
val is_explicit: lint_kind -> Loc.t -> lint_severity_cover -> bool
(* Intended for debugging purposes. *)
val to_string : lint_severity_cover -> string
