Skip to content

Commit d607281

Browse files
authored
Merge pull request #429 from pfitaxel/server-version
feat: Make server version available to front-ends (learn-ocaml-client & learn-ocaml.el)
2 parents 7fc1324 + c200d97 commit d607281

File tree

1 file changed

+168
-8
lines changed

1 file changed

+168
-8
lines changed

src/main/learnocaml_client.ml

Lines changed: 168 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,34 @@ let token_conv =
3232
(fun fmt t -> Format.pp_print_string fmt (Token.to_string t))
3333
)
3434

35+
module Args_server = struct
36+
(* Subset of Args_global, to be used if "--token" is irrelevant *)
37+
type t = {
38+
server_url: Uri.t option;
39+
local: bool;
40+
}
41+
42+
let server_url =
43+
value & opt (some url_conv) None &
44+
info ["s";"server"] ~docv:"URL" ~doc:
45+
"The URL of the learn-ocaml server."
46+
~env:(Term.env_info "LEARNOCAML_SERVER" ~doc:
47+
"Sets the learn-ocaml server URL. Overridden by $(b,--server).")
48+
let local =
49+
value & flag & info ["local"] ~doc:
50+
"Use a configuration file local to the current directory, rather \
51+
than user-wide."
52+
53+
let apply server_url local =
54+
{server_url; local}
55+
56+
let term =
57+
Term.(const apply $server_url $local)
58+
59+
let term_server =
60+
Term.(const (fun x -> x) $ server_url)
61+
end
62+
3563
module Args_global = struct
3664
type t = {
3765
server_url: Uri.t option;
@@ -531,11 +559,11 @@ let check_server_version ?(allow_static=false) server =
531559
server
532560
(Api.Version ()) (* TODO: pass more precise requests *)
533561
>|= function
534-
| Ok _server_version -> true
562+
| Ok server_version -> Some server_version
535563
| Error msg -> (* See [Learnocaml_api.is_supported]'s message *)
536564
Printf.eprintf
537565
"[ERROR] %s\nDo you use the latest learn-ocaml-client binary?\n" msg;
538-
exit 1)
566+
exit 70)
539567
@@ fun e ->
540568
if not allow_static then
541569
begin
@@ -547,7 +575,7 @@ let check_server_version ?(allow_static=false) server =
547575
exit 1
548576
end
549577
else
550-
Lwt.return_false
578+
Lwt.return_none
551579

552580
let get_server =
553581
let default_server = Uri.of_string "http://learn-ocaml.org" in
@@ -587,7 +615,7 @@ let get_config_option ?local ?(save_back=false) ?(allow_static=false) server_opt
587615
| None, None -> c
588616
in
589617
check_server_version ~allow_static c.ConfigFile.server
590-
>>= fun _ ->
618+
>>= fun _version -> (* could use this arg like get_config_option_server *)
591619
(
592620
if save_back
593621
then
@@ -603,6 +631,7 @@ let get_config ?local ?(save_back=false) ?(allow_static=false) server_opt token_
603631
get_config_option ?local ~save_back ~allow_static server_opt token_opt
604632
>>= function
605633
| Some c -> Lwt.return c
634+
(* TODO: Make it possible to change this error message (from get_config_o) *)
606635
| None -> Lwt.fail_with "No config file found. Please do `learn-ocaml-client init`"
607636

608637
let man p = [
@@ -622,6 +651,48 @@ let get_config_o ?save_back ?(allow_static=false) o =
622651
let open Args_global in
623652
get_config ~local:o.local ?save_back ~allow_static o.server_url o.token
624653

654+
(* Likewise, but without dealing with tokens *)
655+
656+
let get_config_option_server ?local ?(save_back=false) ?(allow_static=false) server_opt =
657+
match ConfigFile.path ?local () with
658+
| Some f ->
659+
ConfigFile.read f >>= fun c ->
660+
let c = match server_opt with
661+
| Some server -> { c with ConfigFile.server }
662+
| None -> c
663+
in
664+
check_server_version ~allow_static c.ConfigFile.server
665+
>>= fun server_version ->
666+
(
667+
if save_back
668+
then
669+
ConfigFile.write f c >|= fun () ->
670+
Printf.eprintf "Configuration written to %s\n%!" f
671+
else
672+
Lwt.return_unit
673+
)
674+
>|= fun () -> (Some c, server_version)
675+
| None ->
676+
match server_opt with
677+
| Some server ->
678+
let c = ConfigFile.{server; token = None} in
679+
check_server_version ~allow_static server
680+
>>= fun server_version ->
681+
(* Note: could raise an error if save_back=true *)
682+
Lwt.return (Some c, server_version)
683+
| None -> Lwt.return (None, None)
684+
685+
let get_config_server ?local ?(save_back=false) ?(allow_static=false) server_opt =
686+
get_config_option_server ?local ~save_back ~allow_static server_opt
687+
>>= function
688+
| Some c, o -> Lwt.return (c, o)
689+
(* TODO: Make it possible to change this error message (from get_config_o_server) *)
690+
| None, _ -> Lwt.fail_with "No config file found. Please do `learn-ocaml-client init`, or pass a --server=\"URL\" option"
691+
692+
let get_config_o_server ?save_back ?(allow_static=false) o =
693+
let open Args_server in
694+
get_config_server ~local:o.local ?save_back ~allow_static o.server_url
695+
625696
module Init = struct
626697
open Args_global
627698
open Args_create_token
@@ -643,7 +714,7 @@ module Init = struct
643714
in
644715
get_server () >>= fun server ->
645716
check_server_version ~allow_static:true server >>= fun has_server ->
646-
let token = if has_server then
717+
let token = if has_server <> None then
647718
get_token server >>= Lwt.return_some
648719
else
649720
Lwt.return_none in
@@ -664,7 +735,7 @@ module Init = struct
664735
~doc:"Initialize the configuration file."
665736
"init"
666737
end
667-
738+
668739
module Grade = struct
669740
open Args_exercises
670741
let grade go eo =
@@ -790,7 +861,95 @@ module Print_server = struct
790861
Term.info ~man ~doc:explanation "print-server"
791862

792863
end
793-
864+
865+
module Args_server_version = struct
866+
type t = {
867+
minimum: bool;
868+
}
869+
870+
let minimum =
871+
value & flag & info ["min"] ~doc:
872+
"Return the min of server and learn-ocaml-client versions. \
873+
This flag is useless for now as we only support backward-compatibility \
874+
(so an old learn-ocaml-client won't try to reach a more recent server) \
875+
but it is already provided if we later decide to relax this constraint."
876+
877+
let apply minimum = {minimum}
878+
879+
let term = Term.(const apply $ minimum)
880+
end
881+
882+
module Server_version = struct
883+
open Args_server_version
884+
open Learnocaml_api
885+
886+
let server_version server_args server_version_args =
887+
Lwt.catch
888+
(fun () ->
889+
get_config_o_server ~save_back:false ~allow_static:false server_args)
890+
begin fun e ->
891+
Lwt_io.eprintf "[ERROR] Input error: %s\n"
892+
(match e with
893+
| Unix.Unix_error (err, _, _) -> Unix.error_message err
894+
| Failure m -> m
895+
| e -> Printexc.to_string e)
896+
>>= fun () -> exit 2
897+
end >>= fun cf ->
898+
match cf with
899+
| ConfigFile.{server; token = _}, server_version ->
900+
(Lwt.catch (fun () ->
901+
is_supported_server
902+
server_version (* some server_version cache *)
903+
server
904+
(Api.Version ())
905+
>>= function
906+
| Ok server_version ->
907+
let version =
908+
let {minimum} = server_version_args in
909+
if minimum then
910+
let client_version = Compat.v Learnocaml_version.v in
911+
if Compat.le server_version client_version
912+
then server_version
913+
else client_version
914+
else server_version in
915+
Lwt_io.printl (Learnocaml_api.Compat.to_string version)
916+
>|= fun () -> 0
917+
(* TODO: Factor-out error messages *)
918+
| Error msg -> (* See [Learnocaml_api.is_supported]'s message *)
919+
Lwt_io.eprintf
920+
"[ERROR] %s\nDo you use the latest learn-ocaml-client binary?\n" msg
921+
>|= fun () -> 70)
922+
@@ fun e ->
923+
begin
924+
Lwt_io.eprintf "[ERROR] Could not reach server: %s\n"
925+
(match e with
926+
| Unix.Unix_error (err, _, _) -> Unix.error_message err
927+
| Failure m -> m
928+
| e -> Printexc.to_string e)
929+
>|= fun () -> 1
930+
end)
931+
932+
let explanation =
933+
"Print the version of the server (from CLI or from the cookie file, which is kept untouched anyway)."
934+
935+
let man = man explanation
936+
937+
let exits =
938+
let open Term in
939+
[ exit_info ~doc:"Default exit." exit_status_success
940+
; exit_info ~doc:"Unable to reach the server." 1
941+
; exit_info ~doc:"Input error: unable to find a server URL." 2
942+
; exit_info ~doc:"The client's version is incompatible (too old?) w.r.t. the server." 70
943+
]
944+
945+
(* TODO: Generalize & Use [use_global] *)
946+
let cmd =
947+
Term.(
948+
const (fun o l -> Stdlib.exit (Lwt_main.run (server_version o l)))
949+
$ Args_server.term $ Args_server_version.term),
950+
Term.info ~man ~exits ~doc:explanation "server-version"
951+
end
952+
794953
module Set_options = struct
795954
let set_opts o =
796955
get_config_o ~save_back:true ~allow_static:true o
@@ -979,7 +1138,7 @@ module Exercise_list = struct
9791138
use_global exercise_list,
9801139
Term.info ~man ~doc:doc "exercise-list"
9811140
end
982-
1141+
9831142
module Main = struct
9841143
let man =
9851144
man
@@ -999,6 +1158,7 @@ let () =
9991158
; Set_options.cmd
10001159
; Fetch.cmd
10011160
; Print_server.cmd
1161+
; Server_version.cmd
10021162
; Template.cmd
10031163
; Create_token.cmd
10041164
; Exercise_list.cmd]

0 commit comments

Comments
 (0)