1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
open Ast
(*
--# Populate Pipeline
--#
--# Generates the `_pipeline/` directory with `pipeline.nix` and `dag.json`.
--# Optionally builds the pipeline.
--#
--# @name populate_pipeline
--# @param p :: Pipeline The pipeline to populate.
--# @param build :: Bool (Optional) Whether to trigger the Nix build immediately. Defaults to false.
--# @param verbose :: Int (Optional) Nix build verbosity level. `0` keeps build failures quiet; values above `0` print failed node logs.
--# @return :: String A status message or the output path if build=true.
--# @note `populate_pipeline` performs several validation checks before generating the Nix files:
--# - **File Existence**: Verifies that all files specified in `functions` or `include` arguments of any node actually exist on the file system.
--# - **Custom Function Warning**: Issues a warning to `stderr` if a node uses a custom `serializer` or `deserializer` but does not provide any companion `functions` files.
--# - **Explicit Dependency Declaration**: Checks serializer/runtime requirements up front and asks to add missing entries to `tproject.toml` instead of injecting packages implicitly.
--# @family pipeline
--# @export
*)
let register env =
let populate_fn named_args _env =
let get_arg name pos default named_args =
match List.assoc_opt name (List.filter_map (fun (k, v) -> match k with Some s -> Some (s, v) | None -> None) named_args) with
| Some v -> (true, v)
| None ->
let positionals = List.filter_map (fun (k, v) -> match k with None -> Some v | Some _ -> None) named_args in
if List.length positionals >= pos then (true, List.nth positionals (pos - 1))
else (false, default)
in
let named_keys = List.filter_map (fun (k, _) -> k) named_args in
let positional_count = List.length (List.filter (fun (k, _) -> k = None) named_args) in
match List.find_opt (fun k -> not (List.mem k ["p"; "build"; "verbose"])) named_keys with
| Some k ->
Error.type_error (Printf.sprintf "populate_pipeline: unknown argument '%s'" k)
| None when positional_count > 3 ->
Error.make_error ArityError
(Printf.sprintf "Function `populate_pipeline` accepts at most 3 positional arguments but received %d." positional_count)
| None ->
match get_arg "p" 1 (VNA NAGeneric) named_args with
| (_, VPipeline p) ->
let (build_provided, build_val) = get_arg "build" 2 (VBool false) named_args in
let (verbose_provided, verbose_val) = get_arg "verbose" 3 (VNA NAGeneric) named_args in
let build_result =
match build_val with
| VBool b -> Ok b
| _ when build_provided ->
Error (Error.type_error "Function `populate_pipeline` expects `build` to be a Bool.")
| _ ->
Ok false
in
let verbose_result =
match verbose_val with
| VInt i when i >= 0 -> Ok (Some i)
| VInt _ ->
Error (Error.value_error "Function `populate_pipeline` expects `verbose` to be a non-negative Int.")
| _ when verbose_provided ->
Error (Error.type_error "Function `populate_pipeline` expects `verbose` to be an Int.")
| _ ->
Ok None
in
(match build_result, verbose_result with
| Error e, _ -> e
| _, Error e -> e
| Ok build, Ok verbose ->
match Builder.populate_pipeline ~build ?verbose p with
| Ok out -> VString out
| Error msg -> Error.make_error StructuralError msg)
| _ ->
Error.type_error "Function `populate_pipeline` expects a Pipeline."
in
Env.add "populate_pipeline" (make_builtin_named ~name:"populate_pipeline" ~variadic:true 1 populate_fn) env