-module(caffeine_lang@parser@blueprints). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/caffeine_lang/parser/blueprints.gleam"). -export([validate_blueprints/2, blueprints_from_json/2, parse_from_json_string/2, parse_from_json_file/2]). -export_type([blueprint/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. -type blueprint() :: {blueprint, binary(), list(binary()), gleam@dict:dict(binary(), caffeine_lang@common@accepted_types:accepted_types()), gleam@dict:dict(binary(), gleam@dynamic:dynamic_())}. -file("src/caffeine_lang/parser/blueprints.gleam", 145). ?DOC(" Map each blueprint to its list of referenced artifacts.\n"). -spec map_blueprints_to_artifacts( list(blueprint()), list(caffeine_lang@parser@artifacts:artifact()) ) -> list({blueprint(), list(caffeine_lang@parser@artifacts:artifact())}). map_blueprints_to_artifacts(Blueprints, Artifacts) -> Artifact_map = begin _pipe = Artifacts, _pipe@1 = gleam@list:map(_pipe, fun(A) -> {erlang:element(2, A), A} end), maps:from_list(_pipe@1) end, _pipe@2 = Blueprints, gleam@list:map( _pipe@2, fun(Blueprint) -> Artifact_list = begin _pipe@3 = erlang:element(3, Blueprint), gleam@list:filter_map( _pipe@3, fun(Ref) -> gleam_stdlib:map_get(Artifact_map, Ref) end ) end, {Blueprint, Artifact_list} end ). -file("src/caffeine_lang/parser/blueprints.gleam", 164). ?DOC(" Merge params from multiple artifacts into a single dict.\n"). -spec merge_artifact_params(list(caffeine_lang@parser@artifacts:artifact())) -> gleam@dict:dict(binary(), caffeine_lang@common@accepted_types:accepted_types()). merge_artifact_params(Artifact_list) -> _pipe = Artifact_list, gleam@list:fold( _pipe, maps:new(), fun(Acc, Artifact) -> maps:merge(Acc, erlang:element(3, Artifact)) end ). -file("src/caffeine_lang/parser/blueprints.gleam", 174). ?DOC(" Validate that no blueprint has duplicate artifact refs.\n"). -spec validate_no_duplicate_artifact_refs(list(blueprint())) -> {ok, boolean()} | {error, caffeine_lang@common@errors:compilation_error()}. validate_no_duplicate_artifact_refs(Blueprints) -> Duplicates = begin _pipe = Blueprints, gleam@list:filter_map( _pipe, fun(Blueprint) -> Refs = erlang:element(3, Blueprint), Unique_refs = begin _pipe@1 = Refs, gleam@list:unique(_pipe@1) end, case erlang:length(Refs) =:= erlang:length(Unique_refs) of true -> {error, nil}; false -> Duplicate_refs = begin _pipe@2 = Refs, _pipe@3 = gleam@list:group(_pipe@2, fun(R) -> R end), _pipe@4 = gleam@dict:filter( _pipe@3, fun(_, V) -> erlang:length(V) > 1 end ), _pipe@5 = maps:keys(_pipe@4), gleam@string:join(_pipe@5, <<", "/utf8>>) end, {ok, Duplicate_refs} end end ) end, case Duplicates of [] -> {ok, true}; [First | _] -> {error, {parser_duplicate_error, <<"Duplicate artifact references in blueprint: "/utf8, First/binary>>}} end. -file("src/caffeine_lang/parser/blueprints.gleam", 227). ?DOC(" Find param names that have different types across artifacts.\n"). -spec find_conflicting_params(list(caffeine_lang@parser@artifacts:artifact())) -> {ok, binary()} | {error, nil}. find_conflicting_params(Artifacts) -> All_params = begin _pipe = Artifacts, gleam@list:flat_map( _pipe, fun(A) -> maps:to_list(erlang:element(3, A)) end ) end, Grouped = begin _pipe@1 = All_params, gleam@list:group(_pipe@1, fun(Pair) -> erlang:element(1, Pair) end) end, Conflicting_names = begin _pipe@2 = Grouped, _pipe@3 = maps:to_list(_pipe@2), gleam@list:filter_map( _pipe@3, fun(Group) -> {Name, Pairs} = Group, Types = begin _pipe@4 = Pairs, gleam@list:map(_pipe@4, fun(P) -> erlang:element(2, P) end) end, Unique_types = begin _pipe@5 = Types, gleam@list:unique(_pipe@5) end, case erlang:length(Unique_types) > 1 of true -> {ok, Name}; false -> {error, nil} end end ) end, case Conflicting_names of [] -> {error, nil}; [First | _] -> {ok, First} end. -file("src/caffeine_lang/parser/blueprints.gleam", 207). ?DOC(" Validate that artifacts referenced by a blueprint don't have conflicting param types.\n"). -spec validate_no_conflicting_params( list({blueprint(), list(caffeine_lang@parser@artifacts:artifact())}) ) -> {ok, boolean()} | {error, caffeine_lang@common@errors:compilation_error()}. validate_no_conflicting_params(Blueprint_artifacts_collection) -> Conflicts = begin _pipe = Blueprint_artifacts_collection, gleam@list:filter_map( _pipe, fun(Pair) -> {_, Artifact_list} = Pair, find_conflicting_params(Artifact_list) end ) end, case Conflicts of [] -> {ok, true}; [First | _] -> {error, {parser_duplicate_error, <<"Conflicting param types across artifacts: "/utf8, First/binary>>}} end. -file("src/caffeine_lang/parser/blueprints.gleam", 57). ?DOC(false). -spec validate_blueprints( list(blueprint()), list(caffeine_lang@parser@artifacts:artifact()) ) -> {ok, list(blueprint())} | {error, caffeine_lang@common@errors:compilation_error()}. validate_blueprints(Blueprints, Artifacts) -> gleam@result:'try'( caffeine_lang@common@validations:validate_relevant_uniqueness( Blueprints, fun(B) -> erlang:element(2, B) end, <<"blueprint names"/utf8>> ), fun(_) -> gleam@result:'try'( validate_no_duplicate_artifact_refs(Blueprints), fun(_) -> Blueprint_artifacts_collection = map_blueprints_to_artifacts( Blueprints, Artifacts ), gleam@result:'try'( validate_no_conflicting_params( Blueprint_artifacts_collection ), fun(_) -> Blueprint_merged_artifact_collection = begin _pipe = Blueprint_artifacts_collection, gleam@list:map( _pipe, fun(Pair) -> {Blueprint, Artifact_list} = Pair, Merged_params = merge_artifact_params( Artifact_list ), {Blueprint, {artifact, <<"merged"/utf8>>, Merged_params}} end ) end, gleam@result:'try'( caffeine_lang@common@validations:validate_inputs_for_collection( Blueprint_merged_artifact_collection, fun(Blueprint@1) -> erlang:element(5, Blueprint@1) end, fun(Artifact) -> erlang:element(3, Artifact) end, true ), fun(_) -> Overshadow_params_error = begin _pipe@1 = Blueprint_merged_artifact_collection, _pipe@2 = gleam@list:filter_map( _pipe@1, fun(Blueprint_artifact_pair) -> {Blueprint@2, Merged_artifact} = Blueprint_artifact_pair, case caffeine_lang@common@validations:check_collection_key_overshadowing( erlang:element( 4, Blueprint@2 ), erlang:element( 3, Merged_artifact ), <<"Blueprint overshadowing inherited_params from artifact: "/utf8>> ) of {ok, _} -> {error, nil}; {error, Msg} -> {ok, Msg} end end ), gleam@string:join( _pipe@2, <<", "/utf8>> ) end, gleam@result:'try'( case Overshadow_params_error of <<""/utf8>> -> {ok, true}; _ -> {error, {parser_duplicate_error, <<"Overshadowed inherited_params in blueprint error: "/utf8, Overshadow_params_error/binary>>}} end, fun(_) -> Merged_param_blueprints = begin _pipe@3 = Blueprint_artifacts_collection, gleam@list:map( _pipe@3, fun( Blueprint_artifacts_pair ) -> {Blueprint@3, Artifact_list@1} = Blueprint_artifacts_pair, All_params = begin _pipe@4 = merge_artifact_params( Artifact_list@1 ), maps:merge( _pipe@4, erlang:element( 4, Blueprint@3 ) ) end, {blueprint, erlang:element( 2, Blueprint@3 ), erlang:element( 3, Blueprint@3 ), All_params, erlang:element( 5, Blueprint@3 )} end ) end, {ok, Merged_param_blueprints} end ) end ) end ) end ) end ). -file("src/caffeine_lang/parser/blueprints.gleam", 260). ?DOC(false). -spec blueprints_from_json( binary(), list(caffeine_lang@parser@artifacts:artifact()) ) -> {ok, list(blueprint())} | {error, gleam@json:decode_error()}. blueprints_from_json(Json_string, Artifacts) -> Blueprint_decoded = begin gleam@dynamic@decode:field( <<"name"/utf8>>, caffeine_lang@common@decoders:non_empty_string_decoder(), fun(Name) -> gleam@dynamic@decode:field( <<"artifact_refs"/utf8>>, caffeine_lang@common@decoders:non_empty_named_reference_list_decoder( Artifacts, fun(A) -> erlang:element(2, A) end ), fun(Artifact_refs) -> gleam@dynamic@decode:field( <<"params"/utf8>>, gleam@dynamic@decode:dict( {decoder, fun gleam@dynamic@decode:decode_string/1}, caffeine_lang@common@decoders:accepted_types_decoder( ) ), fun(Params) -> gleam@dynamic@decode:field( <<"inputs"/utf8>>, gleam@dynamic@decode:dict( {decoder, fun gleam@dynamic@decode:decode_string/1}, {decoder, fun gleam@dynamic@decode:decode_dynamic/1} ), fun(Inputs) -> gleam@dynamic@decode:success( {blueprint, Name, Artifact_refs, Params, Inputs} ) end ) end ) end ) end ) end, Blueprints_decoded = begin gleam@dynamic@decode:field( <<"blueprints"/utf8>>, gleam@dynamic@decode:list(Blueprint_decoded), fun(Blueprints) -> gleam@dynamic@decode:success(Blueprints) end ) end, gleam@json:parse(Json_string, Blueprints_decoded). -file("src/caffeine_lang/parser/blueprints.gleam", 39). ?DOC(false). -spec parse_from_json_string( binary(), list(caffeine_lang@parser@artifacts:artifact()) ) -> {ok, list(blueprint())} | {error, caffeine_lang@common@errors:compilation_error()}. parse_from_json_string(Json_string, Artifacts) -> gleam@result:'try'(case blueprints_from_json(Json_string, Artifacts) of {ok, Blueprints} -> {ok, Blueprints}; {error, Err} -> {error, caffeine_lang@common@errors:format_json_decode_error(Err)} end, fun(Blueprints@1) -> validate_blueprints(Blueprints@1, Artifacts) end). -file("src/caffeine_lang/parser/blueprints.gleam", 28). ?DOC(false). -spec parse_from_json_file( binary(), list(caffeine_lang@parser@artifacts:artifact()) ) -> {ok, list(blueprint())} | {error, caffeine_lang@common@errors:compilation_error()}. parse_from_json_file(File_path, Artifacts) -> gleam@result:'try'( caffeine_lang@common@helpers:json_from_file(File_path), fun(Json_string) -> parse_from_json_string(Json_string, Artifacts) end ).