struct

  type structure = [
    | `File of string
    | `Directory of (string * structure list)
  ] [@@deriving yojson]

  type t = {
    host: Host.t;
    root: Path.t;
    structure: structure;
  } [@@deriving yojson]
  let create ~host ~root structure = {host; root; structure}
  let file s = `File s
  let dir name contents = `Directory (name, contents)

  let rec all_structure_paths = fun s ->
    match s with
    | `File s -> [Path.relative_file_exn s ]
    | `Directory (name, children) ->
      let children_paths = 
        List.concat_map ~f:all_structure_paths children in
      let this_one = Path.relative_directory_exn name in
      this_one :: List.map ~f:(Path.concat this_one) children_paths

  let all_paths t: Path.t list =
    List.map ~f:(Path.concat t.root) (all_structure_paths t.structure)

  let log_structure structure = 
    let all_paths = all_structure_paths structure |> List.map ~f:Path.to_string in
    let open Log in
    match all_paths with
    | [] -> s "EMPTY"
    | one :: [] -> s "Single path: " % quote one
    | more -> i (List.length more) % sp % s "paths"

  let log {host; root; structure} =
    Log.(braces (
        parens (Ketrew_host.log host) % sp
        % parens (s "Root: " % s (Path.to_string root)) % sp
        % parens (s "Tree: " % log_structure structure)
      ))

  let to_string_hum v =
    Log.to_long_string (log v)

end