Implement most of the spec reusing scribe & nefthera code

This commit is contained in:
Juno Takano 2025-04-09 01:59:32 -03:00
commit 612a98cfde
16 changed files with 225 additions and 25 deletions

View file

@ -1,2 +1,6 @@
(library
(name tori))
(name tori)
(libraries unix)
)
(include_subdirs qualified)

View file

@ -0,0 +1,23 @@
let interpret (past: Schema.schema) (input: string list): Schema.schema =
let future = { past with output = { message = "" } } in
(* say is useful when the only change to future is the output message *)
let say (message: string) =
{ future with output = { message = message } } in
(*
TODO: return a schema with orders, instead of calling side-effects
directly, making this more of a parser and less of a glorified switch
*)
match input with
| "pkg" :: tail -> System.Package.merge past tail
| "os" :: _ -> say (System.File.read "/etc/os-release")
| "host" :: _ -> say (System.Process.Reader.read [||] "hostname").output
| "echo" :: tail -> say (String.concat " " tail)
| ("version" | "-v" | "--version") :: _ ->
say (Schema.format_version future.meta.version)
| ("help" | "-h" | "--help") :: _ -> say future.meta.help.long
| head :: _ ->
say ("Unrecognized command: " ^ head ^ "\n" ^ future.meta.help.short)
| _ -> future

2
ocaml/lib/qol.ml Normal file
View file

@ -0,0 +1,2 @@
let str_int = string_of_int
let print = print_endline

View file

@ -0,0 +1,38 @@
open Qol
type version = { major: int; minor: int; patch: int }
type help = { short: string; long: string }
type meta = { version: version; help: help }
type output = { message: string; }
type os = Unknown | FreeBSD | Void | Alpine
type host = { os: os; name: string; }
type schema = { meta: meta; output: output; host: host; }
let seed: schema = {
meta = {
version = {
major = 0;
minor = 8;
patch = 0;
};
help = {
short = "Use 'tori help' for usage instructions";
long = "<help message>";
};
};
output = {
message = "Use command 'help' for help";
};
host = {
os = Unknown;
name = "Unknown Host";
};
}
let format_version (version: version): string =
"v" ^ str_int version.major ^
"." ^ str_int version.minor ^
"." ^ str_int version.patch

14
ocaml/lib/system/file.ml Normal file
View file

@ -0,0 +1,14 @@
let read_channel channel =
let buffer = Buffer.create 4096 in
let rec read () =
let line = input_line channel in
Buffer.add_string buffer line;
Buffer.add_char buffer '\n';
read ()
in
try read () with
End_of_file -> Buffer.contents buffer
let read path =
let channel = open_in path in
read_channel channel

View file

@ -0,0 +1,15 @@
let merge (schema: Schema.schema) (packages: string list) =
match packages with
| [] -> { schema with output = { message = "No packages provided" } }
| _ ->
let in_targets = List.flatten [["doas"; "apk"; "-i"; "add"]; packages] and
out_targets = List.flatten [["doas"; "apk"; "-i"; "del"]; packages] in
Process.Fork.run "doas" in_targets;
Process.Fork.run "doas" out_targets;
{ schema with output = {
message = "Done: " ^ (String.concat "\n" packages)
}}

View file

@ -0,0 +1,11 @@
open Qol
let run (command: string) (arguments: string list) =
match Unix.fork () with
| 0 -> Unix.execvp command (Array.of_list arguments)
| pid -> let (_, status) = Unix.waitpid [] pid in
match status with
| Unix.WEXITED 0 -> ()
| Unix.WEXITED n -> print ("Process exited with code " ^ str_int n)
| Unix.WSIGNALED n -> print ("Process terminated by signal " ^ str_int n)
| Unix.WSTOPPED n -> print ("Process stopped by signal " ^ str_int n)

View file

@ -0,0 +1,45 @@
open Qol
type output = { output: string; error: string; status: string; }
let handle_exit_status (status: Unix.process_status): string =
match status with
| Unix.WEXITED n -> "Exit " ^ str_int n
| Unix.WSIGNALED n -> "Kill " ^ str_int n
| Unix.WSTOPPED n -> "Stopped " ^ str_int n
let read (env: string array) (command: string): output =
let stdout, stdin, stderr = Unix.open_process_full command env in
let in_buffer = Buffer.create 4096 in
let err_buffer = Buffer.create 4096 in
let rec read_in () =
let in_line = input_line stdout in
Buffer.add_string in_buffer in_line;
Buffer.add_char in_buffer '\n';
read_in ()
in
try read_in () with End_of_file -> ();
let rec read_err () =
let err_line = input_line stderr in
Buffer.add_string err_buffer err_line;
Buffer.add_char err_buffer '\n';
read_err ()
in
try read_err () with
End_of_file -> let exit_status =
handle_exit_status (Unix.close_process_full (stdout, stdin, stderr))
in
{
output = String.trim (Buffer.contents in_buffer);
error = Buffer.contents err_buffer;
status = exit_status;
}
let format (output: output): string =
match output with
| { output = o; error = _; status = "Exit 0" } -> o
| { output = ""; error = e; status = s } -> "[" ^ s ^ "]" ^ " " ^ e
| { output = o; error = _; status = s } -> "[" ^ s ^ "]" ^ " " ^ o