///|
pub enum OptionType {
StringOpt
BoolOpt
IntOpt
Int64Opt
UIntOpt
UInt64Opt
DoubleOpt
} derive(Debug, Eq)
///|
pub impl Show for OptionType with fn output(self, logger) {
match self {
StringOpt => logger.write_string("StringOpt")
BoolOpt => logger.write_string("BoolOpt")
IntOpt => logger.write_string("IntOpt")
Int64Opt => logger.write_string("Int64Opt")
UIntOpt => logger.write_string("UIntOpt")
UInt64Opt => logger.write_string("UInt64Opt")
DoubleOpt => logger.write_string("DoubleOpt")
}
}
///|
pub struct OptionMetadata {
type_ : OptionType
short : Char
description : String
env : String?
required : Bool
default_value : String?
multiple : Bool
interactive : Bool
} derive(Debug)
///|
pub struct PositionMetadata {
type_ : OptionType
description : String
required : Bool
multiple : Bool
interactive : Bool
} derive(Debug)
///|
pub struct ArgDef[_, Metadata] {
name : String
config : String?
metadata : Metadata
} derive(Debug)
///|
pub type OptionDef[T] = ArgDef[T, OptionMetadata]
///|
pub type PositionDef[T] = ArgDef[T, PositionMetadata]
///|
pub struct NonEmptyArray[T] {
first : T
rest : ArrayView[T]
all : ReadOnlyArray[T]
} derive(Debug)
///|
type StoredOption = ArgDef[Unit, OptionMetadata]
///|
type StoredPosition = ArgDef[Unit, PositionMetadata]
///|
trait ToStoredOption {
fn to_stored_option(Self) -> StoredOption
}
///|
pub impl[T] ToStoredOption for ArgDef[T, OptionMetadata] with fn to_stored_option(
self,
) {
{ name: self.name, config: self.config, metadata: self.metadata }
}
///|
trait ToStoredPosition {
fn to_stored_position(Self) -> StoredPosition
}
///|
pub impl[T] ToStoredPosition for ArgDef[T, PositionMetadata] with fn to_stored_position(
self,
) {
{ name: self.name, config: self.config, metadata: self.metadata }
}
///|
pub struct CommandDef {
name : String
description : String
options : Array[StoredOption]
positionals : Array[StoredPosition]
examples : Array[String]
subcommands : Array[CommandDef]
interactive : (async (InteractiveContext) -> Unit)?
run : (async (Context) -> Unit)?
}
///|
pub(all) suberror ConfigLoadFailure {
ConfigLoadFailure(String)
} derive(Debug, Eq)
///|
pub impl Show for ConfigLoadFailure with fn output(self, logger) {
match self {
ConfigLoadFailure(message) => logger.write_string(message)
}
}
///|
pub struct Context {
flags : @immut_hashmap.HashMap[String, Bool]
values : @immut_hashmap.HashMap[String, ReadOnlyArray[String]]
sources : @immut_hashmap.HashMap[String, @argparse.ValueSource]
config : @immut_hashmap.HashMap[String, Json]
interactive_flags : @immut_hashmap.HashMap[String, Bool]
interactive_values : @immut_hashmap.HashMap[String, ReadOnlyArray[String]]
subcommand : (String, Context)?
}
///|
pub fn Context::Context(
flags? : Map[String, Bool] = Map([]),
values? : Map[String, Array[String]] = Map([]),
sources? : Map[String, @argparse.ValueSource] = Map([]),
config? : Map[String, Json] = Map([]),
subcommand? : (String, Context)? = None,
) -> Context {
Context::{
flags: @immut_hashmap.from_iter(flags.iter()),
values: @immut_hashmap.from_iter(
values
.iter()
.map(fn(entry) {
let (key, items) = entry
(key, ReadOnlyArray::from_array(items))
}),
),
sources: @immut_hashmap.from_iter(sources.iter()),
config: @immut_hashmap.from_iter(config.iter()),
interactive_flags: @immut_hashmap.new(),
interactive_values: @immut_hashmap.new(),
subcommand,
}
}
///|
/// Accumulates values selected by a command's interactive input callback.
/// Read `to_context()` before setting values to obtain the resolved argv, environment,
/// configuration, positional, and default values as initial input.
pub struct InteractiveContext {
initial : Context
flags : Map[String, Bool]
values : Map[String, Array[String]]
}
///|
fn Context::with_interactive_values(
self : Context,
flags : Map[String, Bool],
values : Map[String, Array[String]],
) -> Context {
let subcommand = match self.subcommand {
Some((name, context)) =>
Some((name, context.with_interactive_values(flags, values)))
None => None
}
Context::{
..self,
interactive_flags: @immut_hashmap.from_iter(flags.iter()),
interactive_values: @immut_hashmap.from_iter(
values
.iter()
.map(entry => {
let (key, items) = entry
(key, ReadOnlyArray::from_array(items))
}),
),
subcommand,
}
}
///|
/// Returns the current context, including values already selected by this callback.
pub fn InteractiveContext::to_context(self : InteractiveContext) -> Context {
self.initial.with_interactive_values(self.flags, self.values)
}
///|
/// Replaces a boolean option with a value selected interactively.
pub fn InteractiveContext::set_bool(
self : InteractiveContext,
option : OptionDef[Bool],
value : Bool,
) -> Unit {
self.flags[option.name] = value
}
///|
/// Replaces a string option or positional with a value selected interactively.
pub fn[Metadata] InteractiveContext::set_string(
self : InteractiveContext,
argument : ArgDef[String, Metadata],
value : String,
) -> Unit {
self.values[argument.name] = [value]
}
///|
/// Replaces a repeated string option or positional with values selected interactively.
pub fn[Metadata] InteractiveContext::set_strings(
self : InteractiveContext,
argument : ArgDef[Array[String], Metadata],
values : Array[String],
) -> Unit {
self.values[argument.name] = values
}
///|
/// Replaces an integer option or positional with a value selected interactively.
pub fn[Metadata] InteractiveContext::set_int(
self : InteractiveContext,
argument : ArgDef[Int, Metadata],
value : Int,
) -> Unit {
self.values[argument.name] = [value.to_string()]
}
///|
/// Replaces repeated integer input with values selected interactively.
pub fn[Metadata] InteractiveContext::set_ints(
self : InteractiveContext,
argument : ArgDef[Array[Int], Metadata],
values : Array[Int],
) -> Unit {
self.values[argument.name] = values.map(value => value.to_string())
}
///|
/// Replaces a 64-bit integer input with a value selected interactively.
pub fn[Metadata] InteractiveContext::set_int64(
self : InteractiveContext,
argument : ArgDef[Int64, Metadata],
value : Int64,
) -> Unit {
self.values[argument.name] = [value.to_string()]
}
///|
/// Replaces repeated 64-bit integer input with values selected interactively.
pub fn[Metadata] InteractiveContext::set_int64s(
self : InteractiveContext,
argument : ArgDef[Array[Int64], Metadata],
values : Array[Int64],
) -> Unit {
self.values[argument.name] = values.map(value => value.to_string())
}
///|
/// Replaces an unsigned integer input with a value selected interactively.
pub fn[Metadata] InteractiveContext::set_uint(
self : InteractiveContext,
argument : ArgDef[UInt, Metadata],
value : UInt,
) -> Unit {
self.values[argument.name] = [value.to_string()]
}
///|
/// Replaces repeated unsigned integer input with values selected interactively.
pub fn[Metadata] InteractiveContext::set_uints(
self : InteractiveContext,
argument : ArgDef[Array[UInt], Metadata],
values : Array[UInt],
) -> Unit {
self.values[argument.name] = values.map(value => value.to_string())
}
///|
/// Replaces a 64-bit unsigned integer input with a value selected interactively.
pub fn[Metadata] InteractiveContext::set_uint64(
self : InteractiveContext,
argument : ArgDef[UInt64, Metadata],
value : UInt64,
) -> Unit {
self.values[argument.name] = [value.to_string()]
}
///|
/// Replaces repeated 64-bit unsigned integer input with values selected interactively.
pub fn[Metadata] InteractiveContext::set_uint64s(
self : InteractiveContext,
argument : ArgDef[Array[UInt64], Metadata],
values : Array[UInt64],
) -> Unit {
self.values[argument.name] = values.map(value => value.to_string())
}
///|
/// Replaces a floating-point input with a value selected interactively.
pub fn[Metadata] InteractiveContext::set_double(
self : InteractiveContext,
argument : ArgDef[Double, Metadata],
value : Double,
) -> Unit {
self.values[argument.name] = [value.to_string()]
}
///|
/// Replaces repeated floating-point input with values selected interactively.
pub fn[Metadata] InteractiveContext::set_doubles(
self : InteractiveContext,
argument : ArgDef[Array[Double], Metadata],
values : Array[Double],
) -> Unit {
self.values[argument.name] = values.map(value => value.to_string())
}
///|
fn InteractiveContext::InteractiveContext(
context : Context,
) -> InteractiveContext {
let flags : Map[String, Bool] = Map([])
for name, value in context.interactive_flags {
flags[name] = value
}
let values : Map[String, Array[String]] = Map([])
for name, items in context.interactive_values {
values[name] = items.iter().to_array()
}
{ initial: context, flags, values }
}
///|
pub struct CliApp {
name : String
version : String
description : String
root_options : Array[StoredOption]
root_positionals : Array[StoredPosition]
commands : Array[CommandDef]
interactive : (async (InteractiveContext) -> Unit)?
run : (async (Context) -> Unit)?
load_config : (() -> Map[String, Json] raise ConfigLoadFailure)?
}