catala/compiler/catala_utils/file.mli
Louis Gesbert 05752988e6 Clerk: add support for basic configuration files
Not much there at the moment, but being able to specify the include directories
is already pretty useful to run clerk directly e.g. on `catala-examples`.

(you had to explicitely specify variable `CATALA_INCLUDE`, the `-I` flags or to
go through `make` without that)
2024-06-26 10:41:12 +02:00

197 lines
7.8 KiB
OCaml

(* This file is part of the Catala compiler, a specification language for tax
and social benefits computation rules. Copyright (C) 2020 Inria, contributor:
Emile Rolley <emile.rolley@tuta.io>
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of
the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License. *)
type t = string
(** Utility functions used for file manipulation. *)
val with_out_channel : t -> (out_channel -> 'a) -> 'a
(** Runs the given function with the provided file opened, ensuring it is
properly closed afterwards. May raise just as [open_out]. *)
val with_in_channel : t -> (in_channel -> 'a) -> 'a
(** Runs the given function with the provided file opened, ensuring it is
properly closed afterwards. May raise just as [open_in]. *)
(** {2 Formatter wrappers} *)
val with_formatter_of_out_channel :
out_channel -> (Format.formatter -> 'a) -> 'a
(** [with_formatter_of_out_channel oc f] creates an flushes the formatter used
in [f] from the given out_channel [oc]. *)
val with_formatter_of_file : t -> (Format.formatter -> 'a) -> 'a
(** [with_formatter_of_file filename f] manages the formatter created from the
file [filename] used in [f] -- i.e. closes the corresponding out_channel and
flushes the formatter. *)
val with_formatter_of_opt_file : t option -> (Format.formatter -> 'a) -> 'a
(** [with_formatter_of_opt_file filename_opt f] manages the formatter created
from the file [filename_opt] if there is some (see
{!with_formatter_of_file}), otherwise, uses the [Format.std_formatter]. *)
val get_out_channel :
source_file:t Global.input_src ->
output_file:t option ->
?ext:string ->
unit ->
t option * ((out_channel -> 'a) -> 'a)
(** [get_output ~source_file ~output_file ?ext ()] returns the inferred filename
and its corresponding [with_out_channel] function. If the [output_file] is
equal to [Some "-"] returns a wrapper around [stdout]. *)
val get_formatter_of_out_channel :
source_file:t Global.input_src ->
output_file:t option ->
?ext:string ->
unit ->
t option * ((Format.formatter -> 'a) -> 'a)
(** [get_output_format ~source_file ~output_file ?ext ()] returns the inferred
filename and its corresponding [with_formatter_of_out_channel] function. If
the [output_file] is equal to [Some "-"] returns a wrapper around [stdout]. *)
val temp_file : string -> string -> t
(** Like [Filename.temp_file], but registers the file for deletion at program
exit unless Cli.debug_flag is set. *)
val with_temp_file : string -> string -> ?contents:string -> (t -> 'a) -> 'a
(** Creates a temp file (with prefix and suffix like [temp_file], optionally
with the given contents, for the lifetime of the supplied function, then
remove it unconditionally *)
val contents : t -> string
(** Reads the contents of a file as a string *)
val process_out : ?check_exit:(int -> unit) -> string -> string list -> string
(** [process_out cmd args] executes the given command with the specified
arguments, and returns the stdout of the process as a string. [check_exit]
is called on the return code of the sub-process, the default is to fail on
anything but 0. *)
val check_directory : t -> t option
(** Checks if the given directory exists and returns it normalised (as per
[Unix.realpath]). *)
val ensure_dir : t -> unit
(** Creates the directory (and parents recursively) if it doesn't exist already.
Errors out if the file exists but is not a directory *)
val exists : t -> bool
(** Alias for Sys.file_exists*)
val check_file : t -> t option
(** Returns its argument if it exists and is a plain file, [None] otherwise.
Does not do resolution like [check_directory]. *)
val check_exec : t -> t
(** Resolves a command:
- if [t] is a plain name, resolve in PATH
- if [t] is relative, returns its absolute path
- fails with an error explaining that [t] was not found *)
val ( / ) : t -> t -> t
(** [Filename.concat]: Sugar to allow writing
[File.("some" / "relative" / "path")]. As an exception, if the lhs is [.],
returns the rhs unchanged. *)
val dirname : t -> t
(** [Filename.dirname], re-exported for convenience *)
val parent : t -> t
(** Similar to [dirname], except it strips the last **non-"." or ".."** element
in the supplied file name, if it exists *)
val clean_path : t -> t
(** Rewrites a path by removing intermediate relative lookups ("." and "..").
E.g. [../foo/./bar/../baz/] becomes [../foo/baz]. No disk lookup is made by
this function. *)
val reverse_path : ?from_dir:t -> to_dir:t -> t -> t
(** If [to_dir] is a path to a given directory and [f] a path to a file as seen
from absolute path [from_dir], [reverse_path ~from_dir ~to_dir f] is a path
leading to [f] from [to_dir]. The results attempts to be relative to
[to_dir]. *)
val find_in_parents : (t -> bool) -> (t * t) option
(** Checks for the first directory matching the given predicate from the current
directory upwards. Recursion stops at home. Returns a pair [dir, rel_path],
where [dir] is the ancestor directory matching the predicate, and [rel_path]
is a path pointing to it from the current dir. *)
val ( /../ ) : t -> t -> t
(** Sugar for [parent a / b] *)
val ( -.- ) : t -> string -> t
(** Extension replacement: chops the given filename extension, and replaces it
with the given one (which shouldn't contain a dot). No dot is appended if
the provided extension is empty. *)
val path_to_list : t -> string list
(** Empty elements or current-directory (".") are skipped in the resulting list *)
val equal : t -> t -> bool
(** Case-insensitive string comparison (no file resolution whatsoever) *)
val compare : t -> t -> int
(** Case-insensitive string comparison (no file resolution whatsoever) *)
val format : Format.formatter -> t -> unit
(** Formats a filename in a consistent style, with double-quotes and color (when
the output supports) *)
module Set : Set.S with type elt = t
module Map : Map.S with type key = t
val scan_tree : (t -> 'a option) -> t -> (t * t list * 'a list) Seq.t
(** Recursively scans a directory for files. Directories or files matching ".*"
or "_*" are ignored. Unreadable files or subdirectories are ignored with a
debug message. If [t] is a plain file, scan just that non-recursively.
The matching results are returned grouped by directory, case-insensitively
ordered by filename, as a list of non-empty subdirs and a list of extracted
items. *)
module Tree : sig
(** A lazy tree structure mirroring the filesystem ; uses the comparison from
File, so paths are case-insensitive. *)
type path = t
(** Alias for [File.t] *)
type item = F (** Plain file *) | D of t (** Directory with subtree *)
and t = (path * item) Map.t Lazy.t
(** Contents of a directory, lazily loaded. The map keys are the basenames of
the files and subdirectories, while the values contain the original path
(with correct capitalisation) *)
val empty : t
val build : path -> t
(** Lazily builds a [Tree.path] from the files read at [path]. The names in
the maps are qualified (i.e. they all start with ["path/"]) *)
val subtree : t -> path -> t
(** Looks up a path within a lazy tree *)
val lookup : t -> path -> path option
(** Checks if there is a matching plain file (case-insensitively) ; and
returns its path with the correct case if so *)
val union : t -> t -> t
(** Merges two trees. In case of conflict, lhs entry wins *)
end