11open Lwt
22open Cohttp
33open Cohttp_lwt
4- // open AutocompleteBS
54
65module Client = Cohttp_lwt_jsoo . Client
76module ReactDOM = React . Dom
@@ -20,6 +19,29 @@ let paramState_to_string = (p: paramState) => {
2019
2120let is_successful = (p: paramState ) => p == Executed ;
2221
22+ type inputState = Blacklisted | Malformed | Empty | Ok ;
23+
24+ let inputState_to_string = (i: inputState ) => {
25+ switch i {
26+ | Blacklisted => "Blacklisted options are not allowed" ;
27+ | Malformed => "Options are malformed. Check the input again"
28+ | Empty => "At least one parameter has to be entered" ;
29+ | Ok => "" ;
30+ }
31+ }
32+
33+ let is_ok = (i: inputState ) => i == Ok ;
34+
35+ let option_whitelist = [
36+ "incremental.force-reanalyze.funs" ,
37+ "incremental.reluctant.enabled" ,
38+ "incremental.compare" ,
39+ "incremental.detect-renames" ,
40+ "incremental.restart" ,
41+ "incremental.postsolver" ,
42+ "annotation.goblint_precision"
43+ ] ;
44+
2345let scheme = "http" ;
2446let host = "127.0.0.1" ;
2547let port = 8000 ;
@@ -41,11 +63,11 @@ let rev_arr = (array) => array |> Array.to_list |> List.rev |> Array.of_list;
4163let make = (~goblint_path, ~parameters, ~history, ~setHistory) => {
4264
4365 let (value , setValue ) = React . useState(_ => parameters |> ParameterUtils . concat_parameter_list);
44- // Linked to cancelation, see reasons below for why it is commented out
66+ // Linked to cancelation, see reasons below in on_cancel() for why it is commented out
4567 // let (disableCancel, setDisableCancel) = React.useState(_ => true);
4668 let (disableRun , setDisableRun ) = React . useState(_ => false );
69+ let (inputState , setInputState ) = React . useState(_ => Ok );
4770 let (sortDesc , setSortDesc ) = React . useState(_ => true );
48- let (hasServerOpts , setHasServerOpts ) = React . useState(_ => false );
4971
5072 React . useEffect1(() => {
5173 None
@@ -61,26 +83,64 @@ let make = (~goblint_path, ~parameters, ~history, ~setHistory) => {
6183 setSortDesc(_ => ! sortDesc);
6284 }
6385
64-
65- let on_change = (new_value) => {
66- let server_opt_regex = Str . regexp_string("server." );
67- let server_opts_found = switch (Str . search_forward(server_opt_regex, new_value, 0 )) {
68- | exception Not_found => false ;
69- | _ => true
86+ let is_input_invalid = (parameter_list: list ((string , string )), is_malformed, input_val): inputState => {
87+ if (String . length(input_val) == 0 ) {
88+ Empty
89+ } else if (is_malformed || List . length(parameter_list) == 0 ) {
90+ Malformed
91+ } else {
92+ let has_found_option =
93+ parameter_list
94+ |> List . for_all(((option, _)) => {
95+ let result =
96+ option_whitelist
97+ |> List . map (whitelisted_option => String . starts_with(~prefix= whitelisted_option, option))
98+ |> List . find_opt (b => b == true );
99+
100+ switch (result) {
101+ | Some (b ) => b;
102+ | None => false ;
103+ }
104+ });
105+
106+ if (! has_found_option) {
107+ Blacklisted
108+ } else {
109+ Ok
110+ }
70111 }
71- setHasServerOpts(_ => server_opts_found);
72- setDisableRun(_ => server_opts_found);
112+ }
113+
114+ let react_on_input = (parameter_list, is_malformed, value) => {
115+ let input_state = is_input_invalid(parameter_list, is_malformed, value);
116+ setInputState(_ => input_state);
117+
118+ let isInvalid = ! is_ok(input_state)
119+ setDisableRun(_ => isInvalid);
73120
121+ isInvalid
122+ }
123+
124+ let on_change = (new_value) => {
125+ let (tuple_parameter_list , is_malformed ) =
126+ new_value
127+ |> ParameterUtils . construct_parameters
128+ |> ((p, b)) => (p |> ParameterUtils . tuples_from_parameters, b);
129+
130+ let _ = react_on_input(tuple_parameter_list, is_malformed, new_value);
74131 setValue(_ => new_value);
75132 };
76133
77134 let on_submit = () => {
78- if (! hasServerOpts) {
79- let parameter_list =
80- value
81- |> ParameterUtils . construct_parameters
82- |> ParameterUtils . group_parameters
83-
135+ let (parameter_list , tuple_parameter_list , is_malformed ) =
136+ value
137+ |> ParameterUtils . construct_parameters
138+ |> ((p, b)) => (p, p |> ParameterUtils . tuples_from_parameters, b);
139+
140+ // To prevent invalid default input to be executed, with i.e. blacklisted options, we check the input value first
141+ let isInvalid = react_on_input(tuple_parameter_list, is_malformed, value);
142+
143+ if (! isInvalid) {
84144 let time = Time . get_local_time() ;
85145 let element = (parameter_list, time, Executing );
86146
@@ -135,14 +195,18 @@ let make = (~goblint_path, ~parameters, ~history, ~setHistory) => {
135195 };
136196
137197 let config_api_call = (config_opts: list ((string , string ))) => {
138- config_opts
139- |> List . map(((k, v)) => {
140- ` List ([ ` String (k), ` String (v)] )
141- |> Yojson . Safe . to_string
142- |> Body . of_string;
143- })
144- |> List . map(inner_config_api_call)
145- |> Lwt . npick;
198+ if (List . length(config_opts) == 0 ) {
199+ Lwt . return([ Error ] )
200+ } else {
201+ config_opts
202+ |> List . map(((k, v)) => {
203+ ` List ([ ` String (k), Yojson . Safe . from_string(v)] )
204+ |> Yojson . Safe . to_string
205+ |> Body . of_string;
206+ })
207+ |> List . map(inner_config_api_call)
208+ |> Lwt . npick;
209+ }
146210 };
147211
148212 let inner_analyze_api_call = (analyze_body): Lwt . t(paramState) => {
@@ -165,15 +229,14 @@ let make = (~goblint_path, ~parameters, ~history, ~setHistory) => {
165229 |> Body . of_string;
166230
167231 let analyze_api_call = () => {
168- let config_opts = parameter_list |> ParameterUtils . tuples_from_parameters;
169-
170- config_opts
232+ tuple_parameter_list
171233 |> config_api_call >>=
172234 (result) => {
173- let result_state = result
174- |> List . map(is_successful)
175- |> List . fold_left((a, b) => a && b, true )
176- |> ((s) => if (s) { Executed } else { Error });
235+ let result_state =
236+ result
237+ |> List . map(is_successful)
238+ |> List . fold_left((a, b) => a && b, true )
239+ |> ((s) => if (s) { Executed } else { Error });
177240
178241 Lwt . return(result_state);
179242 } >>=
@@ -189,8 +252,7 @@ let make = (~goblint_path, ~parameters, ~history, ~setHistory) => {
189252 };
190253 };
191254
192- let () = analyze_api_call() |> ignore ;
193-
255+ ignore (analyze_api_call() );
194256 }
195257 };
196258
@@ -243,12 +305,6 @@ let make = (~goblint_path, ~parameters, ~history, ~setHistory) => {
243305
244306 let list_elements = history |> map_history_entry_to_list_entry;
245307
246- let icon_for_sort_dir = if (sortDesc) {
247- <IconArrowUp on_click= on_sort />
248- } else {
249- <IconArrowDown on_click= on_sort />
250- };
251-
252308 <div >
253309 <div className= "input-group mb-2 has-validation" >
254310 {playButton}
@@ -260,17 +316,19 @@ let make = (~goblint_path, ~parameters, ~history, ~setHistory) => {
260316
261317 <label data= "tooltip" title= goblint_path className= "input-group-text" type_= "tooltip_path" >{"./goblint" |> React . string}</label >
262318 // Input and tooltip are seperated due to display issues
263- {switch hasServerOpts {
264- | true => <Input class_= [ "form-control" , "is-invalid" ] value on_change on_submit key= "tooltip_path" /* style={ReactDOM.Style.make(~maxWidth="100%", ())}*/ />
265- | false => <Input value on_change on_submit key= "tooltip_path" />;
319+ {switch inputState {
320+ | Malformed
321+ | Blacklisted
322+ | Empty => <Input class_= [ "form-control" , "is-invalid" ] value on_change on_submit key= "tooltip_path" /* id=input_id style={ReactDOM.Style.make(~maxWidth="100%", ())}*/ />
323+ | Ok => <Input value on_change on_submit key= "tooltip_path" /* id=input_id*/ />;
266324 }}
267- {switch hasServerOpts {
268- | true => {
325+ {switch inputState {
326+ | Ok => React . null;
327+ | _ => {
269328 <div className= "invalid-tooltip" >
270- {"Server options are not allowed" |> React . string}
329+ {inputState |> inputState_to_string |> React . string}
271330 </div >
272331 };
273- | _ => React . null;
274332 }}
275333 </div >
276334
@@ -283,9 +341,13 @@ let make = (~goblint_path, ~parameters, ~history, ~setHistory) => {
283341 <div className= "col-2" >
284342 {"Status" |> React . string}
285343 </div >
286- <div className= "col-2" >
344+ <div className= "col-2" onClick = on_sort style = { ReactDOM . Style . make(~cursor = "pointer" , () )} >
287345 {"Time " |> React . string}
288- {icon_for_sort_dir}
346+ {switch sortDesc {
347+ | true => <IconArrowUp />
348+ | _ => <IconArrowDown />
349+ };
350+ }
289351 </div >
290352 <div className= "col" >
291353 {"Parameters" |> React . string}
0 commit comments