-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathal.ml
More file actions
172 lines (159 loc) · 5.84 KB
/
Copy pathal.ml
File metadata and controls
172 lines (159 loc) · 5.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
(* Alias Manager CLI - Manage shell aliases *)
let home_dir = Sys.getenv "HOME"
let config_dir = Filename.concat home_dir ".config/alias-manager"
let aliases_file = Filename.concat config_dir "aliases.json"
let shell_script = Filename.concat config_dir "aliases.sh"
(* Create directory and parents if they don't exist *)
let rec mkdir_p path =
if Sys.file_exists path then ()
else begin
let parent = Filename.dirname path in
if parent <> path then mkdir_p parent;
(try Unix.mkdir path 0o755
with Unix.Unix_error (Unix.EEXIST, _, _) -> ())
end
(* Ensure config directory exists *)
let ensure_config_dir () = mkdir_p config_dir
(* Load aliases from JSON file *)
let load_aliases () : (string * string) list =
ensure_config_dir ();
if Sys.file_exists aliases_file then
try
let json = Yojson.Basic.from_file aliases_file in
match json with
| `Assoc pairs ->
List.filter_map (fun (name, value) ->
match value with
| `String cmd -> Some (name, cmd)
| _ -> None
) pairs
| _ -> []
with
| Yojson.Json_error msg ->
Printf.eprintf "Warning: Failed to parse %s: %s\n" aliases_file msg;
[]
| Sys_error msg ->
Printf.eprintf "Warning: Failed to read %s: %s\n" aliases_file msg;
[]
else
[]
(* Save aliases to JSON file *)
let save_aliases (aliases : (string * string) list) =
ensure_config_dir ();
let json = `Assoc (List.map (fun (name, cmd) -> (name, `String cmd)) aliases) in
let oc = open_out aliases_file in
Fun.protect ~finally:(fun () -> close_out oc) (fun () ->
output_string oc (Yojson.Basic.pretty_to_string json)
)
(* Generate shell script for sourcing *)
let generate_shell_script (aliases : (string * string) list) =
ensure_config_dir ();
let oc = open_out shell_script in
Fun.protect ~finally:(fun () -> close_out oc) (fun () ->
output_string oc "# Auto-generated by alias-manager\n";
output_string oc "# Source this file in your shell config (.bashrc, .zshrc, etc.)\n\n";
List.iter (fun (name, cmd) ->
(* Escape single quotes in the command *)
let escaped_cmd = String.concat "'\\''" (String.split_on_char '\'' cmd) in
Printf.fprintf oc "alias %s='%s'\n" name escaped_cmd
) aliases
)
(* Validate that a name is a valid shell alias *)
let is_valid_alias_name name =
String.length name > 0 &&
(match name.[0] with 'a'..'z' | 'A'..'Z' | '_' -> true | _ -> false) &&
String.to_seq name |> Seq.for_all (fun c ->
match c with 'a'..'z' | 'A'..'Z' | '0'..'9' | '_' | '-' | '.' -> true | _ -> false
)
(* Add a new alias *)
let add_alias name command =
if not (is_valid_alias_name name) then begin
Printf.eprintf "Error: '%s' is not a valid alias name.\n" name;
Printf.eprintf "Alias names must start with a letter or underscore and contain only letters, digits, underscores, hyphens, or dots.\n";
exit 1
end;
let aliases = load_aliases () in
(* Check if alias already exists *)
let existing = List.assoc_opt name aliases in
let new_aliases = match existing with
| Some _ ->
(* Replace existing alias *)
List.map (fun (n, c) -> if n = name then (n, command) else (n, c)) aliases
| None ->
(* Add new alias *)
aliases @ [(name, command)]
in
save_aliases new_aliases;
generate_shell_script new_aliases;
match existing with
| Some old_cmd ->
Printf.printf "Updated alias '%s': '%s' -> '%s'\n" name old_cmd command
| None ->
Printf.printf "Created alias '%s' = '%s'\n" name command
(* List all aliases *)
let list_aliases () =
let aliases = load_aliases () in
if aliases = [] then
print_endline "No aliases defined."
else begin
print_endline "Defined aliases:";
print_endline (String.make 50 '-');
List.iter (fun (name, cmd) ->
Printf.printf " %s = %s\n" name cmd
) aliases
end
(* Delete an alias *)
let delete_alias name =
let aliases = load_aliases () in
if List.assoc_opt name aliases = None then
Printf.printf "Alias '%s' not found.\n" name
else begin
let new_aliases = List.filter (fun (n, _) -> n <> name) aliases in
save_aliases new_aliases;
generate_shell_script new_aliases;
Printf.printf "Deleted alias '%s'.\n" name
end
(* Show shell integration instructions *)
let show_init () =
ensure_config_dir ();
let aliases = load_aliases () in
generate_shell_script aliases;
print_endline "Run ./install.sh to set up shell integration.";
print_endline "";
print_endline "This will add the al function to your shell config,";
print_endline "which auto-sources aliases after add/delete operations."
(* Print usage information *)
let print_usage () =
print_endline "al - Manage shell aliases";
print_endline "";
print_endline "Usage:";
print_endline " al add <name> <command> Create or update an alias";
print_endline " al list List all aliases";
print_endline " al delete <name> Delete an alias";
print_endline " al init Show shell integration setup";
print_endline " al help Show this help message";
print_endline "";
print_endline "Examples:";
print_endline " al add ll 'ls -la'";
print_endline " al add gs 'git status'";
print_endline " al list";
print_endline " al delete ll"
(* Main entry point *)
let () =
let args = Array.to_list Sys.argv |> List.tl in
match args with
| ["add"; name; command] -> add_alias name command
| "add" :: name :: rest when List.length rest > 0 ->
(* Allow command with spaces without quotes *)
let command = String.concat " " rest in
add_alias name command
| ["list"] -> list_aliases ()
| ["delete"; name] -> delete_alias name
| ["init"] -> show_init ()
| ["help"] | ["-h"] | ["--help"] -> print_usage ()
| [] -> print_usage ()
| _ ->
print_endline "Error: Invalid command or arguments.";
print_endline "";
print_usage ();
exit 1