@@ -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+
3563module 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\n Do 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
552580let 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
608637let 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+
625696module 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"
666737end
667-
738+
668739module 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
792863end
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\n Do 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+
794953module 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"
9811140end
982-
1141+
9831142module 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