M
Modular•4mo ago
toasty

Prism: CLI Library (inspired by Golang's Cobra)

Prism This is a library for building CLI tools written purely in Mojo! It's inspired by the Cobra package from the Golang ecosystem. https://github.com/thatstoasty/prism eg
from prism import Command


fn test(command: Arc[Command], args: List[String]) -> None:
print("Pass chromeria as a subcommand!")


fn hello(command: Arc[Command], args: List[String]) -> None:
print("Hello from Chromeria!")


fn main():
var root_command = Arc(
Command(
name="hello",
description="This is a dummy command!",
run=test,
)
)

var hello_command = Arc(Command(name="chromeria", description="This is a dummy command!", run=hello))

root_command[].add_command(hello_command)
root_command[].execute()
from prism import Command


fn test(command: Arc[Command], args: List[String]) -> None:
print("Pass chromeria as a subcommand!")


fn hello(command: Arc[Command], args: List[String]) -> None:
print("Hello from Chromeria!")


fn main():
var root_command = Arc(
Command(
name="hello",
description="This is a dummy command!",
run=test,
)
)

var hello_command = Arc(Command(name="chromeria", description="This is a dummy command!", run=hello))

root_command[].add_command(hello_command)
root_command[].execute()
mojo build my_tool.mojo -o my_tool
./my_tool # Prints Pass chromeria as a subcommand!
./my_tool chromeria # Prints Hello from Chromeria!
mojo build my_tool.mojo -o my_tool
./my_tool # Prints Pass chromeria as a subcommand!
./my_tool chromeria # Prints Hello from Chromeria!
4 Replies
Melody Daniel
Melody Daniel•4mo ago
How far along are you on this?
toasty
toasty•4mo ago
Fairly far along at this point, if you check out the readme headers you'll be able to glance through the implemented features. The API might change slightly as I tweak things for an easier developer experience. Open to contributors if anyone wants to jump in 🙂
jmky
jmky•4mo ago
cool, in your description could you also elaborate on what Cobra is doing or more directly, what are you trying to accomplish?may not be easily appreciated by the majority who dont know what cobra is
toasty
toasty•3mo ago
@jmky will do, I’ll remove that bit so it’s not confusing. The library was modeled after a the golang CLI package named Cobra. Prism is a package to build CLI tools with in Mojo, so it would help to clarify that in the description! Prism has been updated for Mojo 24.4! Due to changes with how Arc works, and with self references or lists of references being difficult/impossible. I had to modify the flow for a user a bit. For example
fn init() -> None:
var root_command = Arc(
Command(
name="my",
description="This is a dummy command!",
run=test,
)
)
root_command[].persistent_flags.add_bool_flag(name="required", shorthand="r", usage="Always required.")
root_command[].persistent_flags.add_string_flag(name="host", shorthand="h", usage="Host")
root_command[].persistent_flags.add_string_flag(name="port", shorthand="p", usage="Port")
root_command[].mark_persistent_flag_required("required")

var tool_command = Arc(Command(name="tool", description="This is a dummy command!", run=tool_func))
tool_command[].flags.add_bool_flag(name="also", shorthand="a", usage="Also always required.")
tool_command[].flags.add_string_flag(name="uri", shorthand="u", usage="URI")
root_command[].add_command(tool_command)

# Make sure to add the child command to the parent before marking flags.
# add_command() will merge persistent flags from the parent into the child's flags.
tool_command[].mark_flag_required("also")
tool_command[].mark_flags_required_together("host", "port")
tool_command[].mark_flags_mutually_exclusive("host", "uri")

root_command[].execute()
fn init() -> None:
var root_command = Arc(
Command(
name="my",
description="This is a dummy command!",
run=test,
)
)
root_command[].persistent_flags.add_bool_flag(name="required", shorthand="r", usage="Always required.")
root_command[].persistent_flags.add_string_flag(name="host", shorthand="h", usage="Host")
root_command[].persistent_flags.add_string_flag(name="port", shorthand="p", usage="Port")
root_command[].mark_persistent_flag_required("required")

var tool_command = Arc(Command(name="tool", description="This is a dummy command!", run=tool_func))
tool_command[].flags.add_bool_flag(name="also", shorthand="a", usage="Also always required.")
tool_command[].flags.add_string_flag(name="uri", shorthand="u", usage="URI")
root_command[].add_command(tool_command)

# Make sure to add the child command to the parent before marking flags.
# add_command() will merge persistent flags from the parent into the child's flags.
tool_command[].mark_flag_required("also")
tool_command[].mark_flags_required_together("host", "port")
tool_command[].mark_flags_mutually_exclusive("host", "uri")

root_command[].execute()
Commands must be turned into an Arc[Command] when initially created. This is so the relationship between commands can be preserved when you call add_command. Also, modifying a commands flagset through command methods has been removed. Users should alter a command's flags through the flags field(s) directly. Like so
root_command[].persistent_flags.add_bool_flag(name="required", shorthand="r", usage="Always required.")
tool_command[].flags.add_string_flag(name="uri", shorthand="u", usage="URI")
root_command[].persistent_flags.add_bool_flag(name="required", shorthand="r", usage="Always required.")
tool_command[].flags.add_string_flag(name="uri", shorthand="u", usage="URI")
Want results from more Discord servers?
Add your server