use claude_quota::OauthUsageData;
#[ derive( Copy, Clone, PartialEq, Eq, Debug ) ]
pub( crate ) enum SortStrategy { Name, Renew, Renews }
impl SortStrategy
{
pub( crate ) fn parse( s : &str ) -> Result< Self, String >
{
match s
{
"name" => Ok( Self::Name ),
"renew" => Ok( Self::Renew ),
"renews" => Ok( Self::Renews ),
_ => Err( format!(
"invalid sort:: value {s:?}: valid values are `name`, `renew`, `renews`",
) ),
}
}
pub( crate ) fn default_desc( self ) -> bool
{
match self { Self::Name | Self::Renew | Self::Renews => false }
}
}
#[ derive( Copy, Clone, PartialEq, Eq, Debug ) ]
pub( crate ) enum PreferStrategy { Any, Opus, Sonnet }
impl PreferStrategy
{
pub( crate ) fn parse( s : &str ) -> Result< Self, String >
{
match s
{
"any" => Ok( Self::Any ),
"opus" => Ok( Self::Opus ),
"sonnet" => Ok( Self::Sonnet ),
_ => Err( format!(
"invalid prefer:: value {s:?}: valid values are `any`, `opus`, `sonnet`",
) ),
}
}
}
#[ derive( Debug ) ]
#[ allow( clippy::struct_excessive_bools ) ]
pub( crate ) struct ColsVisibility
{
pub( crate ) status : bool,
pub( crate ) expires : bool,
pub( crate ) sub : bool,
pub( crate ) renews : bool,
pub( crate ) h5_left : bool,
pub( crate ) h5_reset : bool,
pub( crate ) d7_left : bool,
pub( crate ) d7_son : bool,
pub( crate ) d7_reset : bool,
pub( crate ) d7_son_reset : bool,
pub( crate ) host : bool,
pub( crate ) role : bool,
pub( crate ) owner : bool,
pub( crate ) next : bool,
}
impl ColsVisibility
{
pub( crate ) fn default_set() -> Self
{
Self
{
status : true,
expires : true,
sub : false,
renews : true,
h5_left : true,
h5_reset : true,
d7_left : true,
d7_son : true,
d7_reset : true,
d7_son_reset : false,
host : false,
role : false,
owner : true,
next : true,
}
}
pub( crate ) fn apply_modifier( &mut self, modifier : &str ) -> Result< (), String >
{
let ( show, id ) = if let Some( rest ) = modifier.strip_prefix( '+' )
{
( true, rest )
}
else if let Some( rest ) = modifier.strip_prefix( '-' )
{
( false, rest )
}
else
{
return Err( format!( "cols:: modifier {modifier:?} must start with `+` or `-`" ) );
};
match id
{
"status" => self.status = show,
"expires" => self.expires = show,
"sub" => self.sub = show,
"renews" => self.renews = show,
"5h_left" => self.h5_left = show,
"5h_reset" => self.h5_reset = show,
"7d_left" => self.d7_left = show,
"7d_son" => self.d7_son = show,
"7d_reset" => self.d7_reset = show,
"7d_son_reset" => self.d7_son_reset = show,
"host" => self.host = show,
"role" => self.role = show,
"owner" => self.owner = show,
"next" => self.next = show,
_ => return Err( format!(
"cols:: unknown column {id:?}: valid IDs are `status`, `expires`, `sub`, `renews`, `5h_left`, `5h_reset`, `7d_left`, `7d_son`, `7d_reset`, `7d_son_reset`, `host`, `role`, `owner`, `next`",
) ),
}
Ok( () )
}
pub( crate ) fn parse( s : &str ) -> Result< Self, String >
{
let mut vis = Self::default_set();
for modifier in s.split( ',' ).map( str::trim ).filter( |m| !m.is_empty() )
{
vis.apply_modifier( modifier )?;
}
Ok( vis )
}
}
#[ allow( clippy::struct_excessive_bools ) ]
pub( crate ) struct AccountQuota
{
pub( crate ) name : String,
pub( crate ) is_current : bool,
pub( crate ) is_active : bool,
pub( crate ) is_occupied_elsewhere : bool,
pub( crate ) expires_at_ms : u64,
pub( crate ) result : Result< OauthUsageData, String >,
pub( crate ) account : Option< claude_quota::OauthAccountData >,
pub( crate ) host : String,
pub( crate ) role : String,
pub( crate ) renewal_at : Option< String >,
pub( crate ) cached : bool,
pub( crate ) cache_age_secs : Option< u64 >,
pub( crate ) is_owned : bool,
pub( crate ) owner : String,
}
#[ derive( Debug ) ]
#[ allow( clippy::struct_excessive_bools ) ]
pub( crate ) struct UsageParams
{
pub( crate ) refresh : i64,
pub( crate ) live : i64,
pub( crate ) interval : u64,
pub( crate ) jitter : u64,
pub( crate ) trace : bool,
pub( crate ) sort : SortStrategy,
pub( crate ) desc : Option< bool >,
pub( crate ) prefer : PreferStrategy,
pub( crate ) cols : ColsVisibility,
pub( crate ) touch : i64,
pub( crate ) imodel : SubprocessModel,
pub( crate ) effort : SubprocessEffort,
pub( crate ) count : u64,
pub( crate ) offset : u64,
pub( crate ) only_active : bool,
pub( crate ) only_next : bool,
pub( crate ) min_5h : u8,
pub( crate ) min_7d : u8,
pub( crate ) only_valid : bool,
pub( crate ) exclude_exhausted : bool,
pub( crate ) format : UsageOutputFormat,
pub( crate ) get : Option< GetField >,
pub( crate ) abs : bool,
pub( crate ) no_color : bool,
pub( crate ) set_model : Option< String >,
pub( crate ) rotate : bool,
pub( crate ) force : bool,
pub( crate ) who : Option< bool >,
pub( crate ) solo : bool,
}
#[ derive( Copy, Clone, PartialEq, Eq, Debug ) ]
pub( crate ) enum UsageOutputFormat
{
Text,
Json,
Tsv,
Plain,
Value,
}
#[ derive( Copy, Clone, PartialEq, Eq, Debug ) ]
pub( crate ) enum GetField
{
FiveHourLeft,
FiveHourReset,
SevenDayLeft,
SevenDaySon,
SevenDayReset,
Expires,
Renews,
Sub,
Status,
Account,
Host,
Role,
NextEventType,
NextEventSecs,
}
impl GetField
{
pub( crate ) fn parse( s : &str ) -> Result< Self, String >
{
match s
{
"5h_left" => Ok( Self::FiveHourLeft ),
"5h_reset" => Ok( Self::FiveHourReset ),
"7d_left" => Ok( Self::SevenDayLeft ),
"7d_son" => Ok( Self::SevenDaySon ),
"7d_reset" => Ok( Self::SevenDayReset ),
"expires" => Ok( Self::Expires ),
"renews" => Ok( Self::Renews ),
"sub" => Ok( Self::Sub ),
"status" => Ok( Self::Status ),
"account" => Ok( Self::Account ),
"host" => Ok( Self::Host ),
"role" => Ok( Self::Role ),
"next_event_type" => Ok( Self::NextEventType ),
"next_event_secs" => Ok( Self::NextEventSecs ),
_ => Err( format!(
"invalid get:: field {s:?}: valid IDs are \
`5h_left`, `5h_reset`, `7d_left`, `7d_son`, `7d_reset`, `expires`, `renews`, \
`sub`, `status`, `account`, `host`, `role`, `next_event_type`, `next_event_secs`",
) ),
}
}
}
#[ derive( Copy, Clone, PartialEq, Eq, Debug ) ]
pub( crate ) enum SubprocessModel { Auto, Sonnet, Opus, Keep, Haiku }
impl SubprocessModel
{
pub( crate ) fn parse( s : &str ) -> Result< Self, String >
{
match s
{
"auto" => Ok( Self::Auto ),
"sonnet" => Ok( Self::Sonnet ),
"opus" => Ok( Self::Opus ),
"keep" => Ok( Self::Keep ),
"haiku" => Ok( Self::Haiku ),
_ => Err( format!( "imodel:: must be one of: auto, sonnet, opus, keep, haiku; got {s:?}" ) ),
}
}
}
#[ derive( Copy, Clone, PartialEq, Eq, Debug ) ]
pub( crate ) enum SubprocessEffort { Auto, High, Max, Low, Normal }
impl SubprocessEffort
{
pub( crate ) fn parse( s : &str ) -> Result< Self, String >
{
match s
{
"auto" => Ok( Self::Auto ),
"high" => Ok( Self::High ),
"max" => Ok( Self::Max ),
"low" => Ok( Self::Low ),
"normal" => Ok( Self::Normal ),
_ => Err( format!( "effort:: must be one of: auto, high, max, low, normal; got {s:?}" ) ),
}
}
}
#[ allow( clippy::option_option ) ]
pub( crate ) fn map_model_shorthand( s : &str ) -> Option< Option< &'static str > >
{
match s
{
"opus" => Some( Some( "claude-opus-4-6" ) ),
"sonnet" => Some( Some( "claude-sonnet-4-6" ) ),
"haiku" => Some( Some( "claude-haiku-4-5-20251001" ) ),
"default" => Some( None ),
_ => None,
}
}
pub( crate ) fn validate_set_model( s : &str ) -> Result< Option< &'static str >, String >
{
map_model_shorthand( s )
.ok_or_else( || format!( "set_model:: must be one of: opus, sonnet, haiku, default; got {s:?}" ) )
}