use std::io;
use std::path::PathBuf;
use crate::account::profiles::ProfileMap;
pub mod edit;
pub mod platform;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Op {
Switch { profile: String },
Minus,
Global { profile: String },
Resync,
Status { print_current: bool },
List,
Add { name: String, dir: Option<String> },
Set { name: String, dir: String },
Remove { name: String },
SetDefault { name: String },
Edit,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Shell {
Zsh,
Pwsh,
}
impl Shell {
pub fn parse(s: &str) -> Option<Self> {
match s.to_ascii_lowercase().as_str() {
"zsh" | "bash" | "sh" => Some(Shell::Zsh),
"pwsh" | "powershell" => Some(Shell::Pwsh),
_ => None,
}
}
pub fn export_line(&self, dir: &str) -> String {
match self {
Shell::Zsh => format!("export CLAUDE_CONFIG_DIR='{dir}'"),
Shell::Pwsh => format!("$env:CLAUDE_CONFIG_DIR = '{dir}'"),
}
}
pub fn error_snippet(&self, msg: &str) -> String {
match self {
Shell::Zsh => {
let escaped = msg.replace('\'', "'\\''");
format!(">&2 printf '%s\\n' '{escaped}'; false")
}
Shell::Pwsh => {
let escaped = msg.replace('\'', "''");
format!("Write-Error '{escaped}'; exit 1")
}
}
}
}
pub fn default_state_file() -> PathBuf {
dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".config")
.join("claude-as")
.join("default")
}
pub fn default_profile(profiles: &ProfileMap) -> String {
profiles.default_name()
}
pub fn write_default_profile(profile: &str, profiles: &ProfileMap) -> io::Result<()> {
let ok = if profiles.is_empty() {
!profile.is_empty()
} else {
profiles.contains(profile)
};
if !ok {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"cas: unknown profile '{profile}' — configured: {}",
profiles.names_sorted().join(", ")
),
));
}
let path = default_state_file();
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
std::fs::write(&path, format!("{profile}\n"))
}
pub fn eval_emit(shell: Shell, op: &Op, profiles: &ProfileMap) -> anyhow::Result<()> {
match op {
Op::Switch { profile } => {
match resolve_profile(profile, profiles) {
Ok(dir) => {
println!("{}", shell.export_line(&dir));
}
Err(e) => {
println!("{}", shell.error_snippet(&e.to_string()));
}
}
}
Op::Minus => {
println!(
"{}",
shell.error_snippet("claude-as: no previous profile to toggle to")
);
}
Op::Global { profile } => {
match resolve_profile(profile, profiles) {
Ok(dir) => {
write_default_profile(profile, profiles)?;
if let Err(e) = platform::apply_global(profile, &dir) {
eprintln!("cas: platform setenv warning: {e}");
}
println!("{}", shell.export_line(&dir));
eprintln!("global default → {profile} ({dir})");
eprintln!("(new shells follow this via ~/.zshenv guard; running claude sessions keep their captured paths)");
}
Err(e) => {
println!("{}", shell.error_snippet(&e.to_string()));
}
}
}
Op::Resync => {
let profile = default_profile(profiles);
match resolve_profile(&profile, profiles) {
Ok(dir) => {
println!("{}", shell.export_line(&dir));
eprintln!("shell → {profile} ({dir})");
}
Err(e) => {
println!("{}", shell.error_snippet(&e.to_string()));
}
}
}
Op::Status { print_current } => {
if *print_current {
let current_dir = std::env::var("CLAUDE_CONFIG_DIR").unwrap_or_default();
let profile_name = if current_dir.is_empty() {
"unknown".to_owned()
} else {
profiles
.iter()
.find(|(_, dir)| *dir == current_dir.as_str())
.map(|(name, _)| name.to_owned())
.unwrap_or_else(|| "unknown".to_owned())
};
println!("{profile_name}");
} else {
print_status(shell, profiles)?;
}
}
Op::List
| Op::Add { .. }
| Op::Set { .. }
| Op::Remove { .. }
| Op::SetDefault { .. }
| Op::Edit => {
anyhow::bail!("internal: {op:?} is a management op — route to manage_emit");
}
}
Ok(())
}
pub fn manage_emit(op: &Op, profiles: &mut ProfileMap) -> anyhow::Result<()> {
match op {
Op::List => {
print_status(Shell::Zsh, profiles)?;
}
Op::Add { name, dir } => {
if !ProfileMap::is_valid_name(name) {
anyhow::bail!(
"add: invalid profile name '{name}' (allowed: letters, digits, . _ -)"
);
}
if profiles.contains(name) {
anyhow::bail!(
"add: profile '{name}' already exists ({}). Use `csm profiles set {name} <dir>` to change its dir.",
profiles.get(name).unwrap_or("")
);
}
let dir = resolve_new_dir(name, dir.as_deref());
std::fs::create_dir_all(&dir)
.map_err(|e| anyhow::anyhow!("add: cannot create dir '{dir}': {e}"))?;
profiles.insert(name.clone(), dir.clone());
profiles.save()?;
eprintln!("added profile '{name}' → {dir}");
}
Op::Set { name, dir } => {
if !ProfileMap::is_valid_name(name) {
anyhow::bail!(
"set: invalid profile name '{name}' (allowed: letters, digits, . _ -)"
);
}
if dir.is_empty() {
anyhow::bail!("set: <dir> is required");
}
std::fs::create_dir_all(dir)
.map_err(|e| anyhow::anyhow!("set: cannot create dir '{dir}': {e}"))?;
let prev = profiles.insert(name.clone(), dir.clone());
profiles.save()?;
match prev {
Some(old) if old != *dir => eprintln!("set profile '{name}' → {dir} (was {old})"),
_ => eprintln!("set profile '{name}' → {dir}"),
}
}
Op::Remove { name } => {
if !profiles.contains(name) {
anyhow::bail!(
"remove: no such profile '{name}' — configured: {}",
profiles.names_sorted().join(", ")
);
}
if profiles.default_name() == *name {
anyhow::bail!(
"remove: '{name}' is the global default — set the default elsewhere first \
(`csm profiles use <other>`)"
);
}
let dir = profiles.remove(name).unwrap_or_default();
profiles.save()?;
eprintln!("removed profile '{name}' (dir retained on disk: {dir})");
}
Op::SetDefault { name } => {
write_default_profile(name, profiles)?;
let dir = resolve_profile(name, profiles)?;
if let Err(e) = platform::apply_global(name, &dir) {
eprintln!("cas: platform setenv warning: {e}");
}
eprintln!("global default → {name} ({dir})");
eprintln!("(new shells + GUI/launchd follow this; your current shell keeps its profile until you run `cas {name}` or open a new shell)");
}
Op::Edit => {
edit::run_interactive(profiles)?;
}
Op::Switch { .. } | Op::Global { .. } | Op::Resync | Op::Minus | Op::Status { .. } => {
anyhow::bail!("internal: {op:?} is not a management op");
}
}
Ok(())
}
fn resolve_new_dir(name: &str, dir: Option<&str>) -> String {
match dir {
Some(d) if !d.is_empty() => d.to_owned(),
_ => dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(format!(".claude.{name}"))
.to_string_lossy()
.into_owned(),
}
}
fn resolve_profile(profile: &str, profiles: &ProfileMap) -> anyhow::Result<String> {
if profiles.is_empty() {
let home = dirs::home_dir()
.ok_or_else(|| anyhow::anyhow!("cas: cannot determine HOME directory"))?;
return Ok(home
.join(format!(".claude.{profile}"))
.to_string_lossy()
.into_owned());
}
profiles.get(profile).map(str::to_owned).ok_or_else(|| {
let available: Vec<&str> = profiles.names_sorted();
anyhow::anyhow!(
"cas: unknown profile '{}' — available: {}",
profile,
available.join(", ")
)
})
}
fn print_status(_shell: Shell, profiles: &ProfileMap) -> anyhow::Result<()> {
let current_dir = std::env::var("CLAUDE_CONFIG_DIR").unwrap_or_default();
let default = default_profile(profiles);
let default_dir = profiles.default_dir().to_string_lossy().into_owned();
let current_name = if current_dir.is_empty() {
"unset".to_owned()
} else {
profiles
.iter()
.find(|(_, dir)| *dir == current_dir.as_str())
.map(|(name, _)| name.to_owned())
.unwrap_or_else(|| "unknown".to_owned())
};
let shell_state = if current_dir.is_empty() {
"unset (no zshenv guard? new shells won't have a profile)".to_owned()
} else {
current_dir.clone()
};
println!("current shell: {current_name} ({shell_state})");
if default_dir.is_empty() {
println!("global default: {default} (~/.config/claude-as/default)");
} else {
println!("global default: {default} ({default_dir})");
}
println!("available:");
if profiles.is_empty() {
println!(" (profiles.json absent — CAS/pick features disabled)");
} else {
for name in profiles.names_sorted() {
let dir = profiles.get(name).unwrap_or("");
let is_current = dir == current_dir.as_str();
let is_default = name == default.as_str();
let mark = match (is_current, is_default) {
(true, true) => "*d",
(true, false) => "* ",
(false, true) => " d",
(false, false) => " ",
};
println!(" {mark} {name:<12} {dir}");
}
println!("(legend: * = current shell, d = global default)");
}
Ok(())
}
#[cfg(test)]
fn parse_cas_args_for_test<S: AsRef<str>>(args: &[S]) -> anyhow::Result<(Shell, Op)> {
let mut shell = Shell::Zsh; let mut i = 0;
let n = args.len();
while i < n {
let a = args[i].as_ref();
match a {
"--eval" => {
i += 1;
}
"--shell" => {
i += 1;
if i >= n {
anyhow::bail!("cas: --shell requires an argument (zsh|bash|pwsh)");
}
let s = args[i].as_ref();
shell = Shell::parse(s).ok_or_else(|| {
anyhow::anyhow!("cas: unknown shell '{}' — expected zsh, bash, or pwsh", s)
})?;
i += 1;
}
"--" => {
i += 1;
break;
}
_ => {
break;
}
}
}
if i >= n {
return Ok((
shell,
Op::Status {
print_current: false,
},
));
}
let cmd = args[i].as_ref();
match cmd {
"-" => Ok((shell, Op::Minus)),
"resync" => Ok((shell, Op::Resync)),
"status" => {
let print_current = (i + 1 < n) && args[i + 1].as_ref() == "--print-current";
Ok((shell, Op::Status { print_current }))
}
"-g" | "--global" => {
i += 1;
if i >= n {
anyhow::bail!("cas: {} requires a profile name", cmd);
}
let profile = args[i].as_ref().to_owned();
Ok((shell, Op::Global { profile }))
}
profile => Ok((
shell,
Op::Switch {
profile: profile.to_owned(),
},
)),
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
use std::io::Write;
use tempfile::NamedTempFile;
fn test_profiles() -> ProfileMap {
let mut m = HashMap::new();
m.insert("personal".to_owned(), "/tmp/.claude.personal".to_owned());
m.insert("elyvian".to_owned(), "/tmp/.claude.elyvian".to_owned());
ProfileMap(m)
}
fn empty_profiles() -> ProfileMap {
ProfileMap::default()
}
fn state_file(content: &str) -> NamedTempFile {
let mut f = NamedTempFile::new().unwrap();
write!(f, "{content}").unwrap();
f
}
#[test]
fn default_name_returns_configured_token() {
let p = test_profiles();
let f = state_file("elyvian\n");
assert_eq!(p.default_name_with(f.path()), "elyvian");
let f = state_file("personal");
assert_eq!(p.default_name_with(f.path()), "personal");
}
#[test]
fn default_name_unknown_token_falls_back_to_preferred() {
let p = test_profiles();
let f = state_file("hacker");
assert_eq!(p.default_name_with(f.path()), "elyvian");
let f = state_file(" ");
assert_eq!(p.default_name_with(f.path()), "elyvian");
}
#[test]
fn default_name_whitespace_trimmed() {
let p = test_profiles();
let f = state_file(" elyvian ");
assert_eq!(p.default_name_with(f.path()), "elyvian");
let f = state_file("\tpersonal\n");
assert_eq!(p.default_name_with(f.path()), "personal");
}
#[test]
fn default_name_empty_map_trusts_any_token() {
let p = empty_profiles();
let f = state_file("whatever\n");
assert_eq!(p.default_name_with(f.path()), "whatever");
}
#[test]
fn default_name_empty_map_absent_token_is_empty() {
let p = empty_profiles();
let f = state_file("");
assert_eq!(p.default_name_with(f.path()), "");
}
#[test]
fn write_default_profile_roundtrip_via_file() {
let mut f = NamedTempFile::new().unwrap();
writeln!(f, "elyvian").unwrap();
let s = std::fs::read_to_string(f.path()).unwrap();
assert_eq!(s.trim(), "elyvian");
}
#[test]
fn profile_map_contains_replaces_allowlist() {
let p = test_profiles();
assert!(p.contains("personal"));
assert!(p.contains("elyvian"));
assert!(!p.contains("toss"));
assert!(!empty_profiles().contains("personal"));
}
#[test]
fn is_valid_name_syntax() {
assert!(ProfileMap::is_valid_name("personal"));
assert!(ProfileMap::is_valid_name("work-2"));
assert!(ProfileMap::is_valid_name("a.b_c"));
assert!(!ProfileMap::is_valid_name(""));
assert!(!ProfileMap::is_valid_name("has space"));
assert!(!ProfileMap::is_valid_name("a/b")); }
#[test]
fn shell_zsh_export_line() {
let line = Shell::Zsh.export_line("/Users/dave/.claude.personal");
assert_eq!(
line,
"export CLAUDE_CONFIG_DIR='/Users/dave/.claude.personal'"
);
}
#[test]
fn shell_pwsh_export_line() {
let line = Shell::Pwsh.export_line(r"C:\Users\dave\.claude.personal");
assert_eq!(
line,
r"$env:CLAUDE_CONFIG_DIR = 'C:\Users\dave\.claude.personal'"
);
}
#[test]
fn shell_parse_zsh_variants() {
assert_eq!(Shell::parse("zsh"), Some(Shell::Zsh));
assert_eq!(Shell::parse("bash"), Some(Shell::Zsh));
assert_eq!(Shell::parse("sh"), Some(Shell::Zsh));
assert_eq!(Shell::parse("ZSH"), Some(Shell::Zsh));
}
#[test]
fn shell_parse_pwsh_variants() {
assert_eq!(Shell::parse("pwsh"), Some(Shell::Pwsh));
assert_eq!(Shell::parse("powershell"), Some(Shell::Pwsh));
assert_eq!(Shell::parse("PWSH"), Some(Shell::Pwsh));
}
#[test]
fn shell_parse_unknown_is_none() {
assert_eq!(Shell::parse("fish"), None);
assert_eq!(Shell::parse(""), None);
}
#[test]
fn resolve_new_dir_explicit_wins() {
assert_eq!(
resolve_new_dir("work", Some("/custom/work")),
"/custom/work"
);
}
#[test]
fn resolve_new_dir_synthesizes_conventional() {
let got = resolve_new_dir("work", None);
assert!(got.ends_with(".claude.work"), "got: {got}");
let got = resolve_new_dir("work", Some(""));
assert!(got.ends_with(".claude.work"), "got: {got}");
}
#[test]
fn shell_zsh_error_snippet_basic() {
let s = Shell::Zsh.error_snippet("cas: unknown profile 'foo'");
assert!(s.starts_with(">&2 printf"), "got: {s}");
assert!(s.ends_with("; false"), "got: {s}");
assert!(s.contains("unknown profile"), "got: {s}");
}
#[test]
fn shell_pwsh_error_snippet_basic() {
let s = Shell::Pwsh.error_snippet("cas: unknown profile 'foo'");
assert!(s.starts_with("Write-Error"), "got: {s}");
assert!(s.ends_with("exit 1"), "got: {s}");
assert!(s.contains("unknown profile"), "got: {s}");
}
#[test]
fn shell_zsh_error_snippet_quote_escaping() {
let s = Shell::Zsh.error_snippet("it's a problem");
assert!(s.contains("it'\\''s"), "expected zsh escape, got: {s}");
}
#[test]
fn shell_pwsh_error_snippet_quote_escaping() {
let s = Shell::Pwsh.error_snippet("it's a problem");
assert!(s.contains("it''s"), "expected pwsh double-quote, got: {s}");
}
#[test]
fn op_variants_constructible() {
let _ = Op::Switch {
profile: "personal".to_owned(),
};
let _ = Op::Minus;
let _ = Op::Global {
profile: "elyvian".to_owned(),
};
let _ = Op::Resync;
let _ = Op::Status {
print_current: false,
};
let _ = Op::Status {
print_current: true,
};
}
#[test]
fn resolve_profile_personal_in_map() {
let profiles = test_profiles();
let result = resolve_profile("personal", &profiles);
assert_eq!(result.unwrap(), "/tmp/.claude.personal");
}
#[test]
fn resolve_profile_elyvian_in_map() {
let profiles = test_profiles();
let result = resolve_profile("elyvian", &profiles);
assert_eq!(result.unwrap(), "/tmp/.claude.elyvian");
}
#[test]
fn resolve_profile_unknown_in_populated_map_errors() {
let profiles = test_profiles();
let result = resolve_profile("hacker", &profiles);
assert!(result.is_err());
let msg = result.unwrap_err().to_string();
assert!(
msg.contains("unknown profile"),
"expected 'unknown profile' in: {msg}"
);
assert!(
msg.contains("personal"),
"expected available profiles in: {msg}"
);
}
#[test]
fn resolve_profile_empty_map_synthesizes_path() {
let profiles = empty_profiles();
let result = resolve_profile("personal", &profiles);
assert!(result.is_ok());
let dir = result.unwrap();
assert!(dir.ends_with(".claude.personal"), "synthesized dir: {dir}");
}
#[test]
fn op_minus_produces_error_snippet_for_zsh() {
let snippet = Shell::Zsh.error_snippet("claude-as: no previous profile to toggle to");
assert!(snippet.contains("no previous profile"), "got: {snippet}");
assert!(snippet.ends_with("; false"), "got: {snippet}");
}
#[test]
fn op_minus_produces_error_snippet_for_pwsh() {
let snippet = Shell::Pwsh.error_snippet("claude-as: no previous profile to toggle to");
assert!(snippet.contains("no previous profile"), "got: {snippet}");
assert!(snippet.ends_with("exit 1"), "got: {snippet}");
}
#[test]
fn unknown_profile_produces_error_snippet_not_panic() {
let profiles = test_profiles();
let err = resolve_profile("badprofile", &profiles).unwrap_err();
let snippet = Shell::Zsh.error_snippet(&err.to_string());
assert!(snippet.contains("unknown profile"), "got: {snippet}");
assert!(snippet.ends_with("; false"), "got: {snippet}");
}
#[test]
fn unknown_profile_error_snippet_for_pwsh() {
let profiles = test_profiles();
let err = resolve_profile("badprofile", &profiles).unwrap_err();
let snippet = Shell::Pwsh.error_snippet(&err.to_string());
assert!(snippet.contains("unknown profile"), "got: {snippet}");
assert!(snippet.ends_with("exit 1"), "got: {snippet}");
}
#[test]
fn toggle_resolves_previous_profile() {
let profiles = test_profiles();
let result = resolve_profile("personal", &profiles);
assert_eq!(result.unwrap(), "/tmp/.claude.personal");
}
#[test]
fn toggle_resolves_other_profile() {
let profiles = test_profiles();
let result = resolve_profile("elyvian", &profiles);
assert_eq!(result.unwrap(), "/tmp/.claude.elyvian");
}
#[test]
fn resync_resolves_via_default_profile_logic() {
let profiles = test_profiles();
let profile = "personal";
let dir = resolve_profile(profile, &profiles).unwrap();
let line = Shell::Zsh.export_line(&dir);
assert_eq!(line, "export CLAUDE_CONFIG_DIR='/tmp/.claude.personal'");
}
#[test]
fn resync_pwsh_form() {
let profiles = test_profiles();
let dir = resolve_profile("elyvian", &profiles).unwrap();
let line = Shell::Pwsh.export_line(&dir);
assert_eq!(line, "$env:CLAUDE_CONFIG_DIR = '/tmp/.claude.elyvian'");
}
#[test]
fn write_default_profile_rejects_unknown_in_populated_map() {
let result = write_default_profile("toss", &test_profiles());
assert!(result.is_err());
let msg = result.unwrap_err().to_string();
assert!(msg.contains("unknown profile"), "got: {msg}");
assert!(msg.contains("configured:"), "got: {msg}");
assert!(
msg.contains("personal") && msg.contains("elyvian"),
"got: {msg}"
);
}
#[test]
fn write_default_profile_empty_map_accepts_any_token() {
let empty = empty_profiles();
assert!(empty.is_empty());
assert!(ProfileMap::is_valid_name("anything"));
}
#[test]
fn parse_args_switch_personal() {
let args = ["--eval", "--shell", "zsh", "--", "personal"];
let (shell, op) = parse_cas_args_for_test(&args).unwrap();
assert_eq!(shell, Shell::Zsh);
assert_eq!(
op,
Op::Switch {
profile: "personal".to_owned()
}
);
}
#[test]
fn parse_args_switch_elyvian() {
let args = ["--eval", "--shell", "zsh", "--", "elyvian"];
let (shell, op) = parse_cas_args_for_test(&args).unwrap();
assert_eq!(shell, Shell::Zsh);
assert_eq!(
op,
Op::Switch {
profile: "elyvian".to_owned()
}
);
}
#[test]
fn parse_args_switch_pwsh() {
let args = ["--eval", "--shell", "pwsh", "--", "personal"];
let (shell, op) = parse_cas_args_for_test(&args).unwrap();
assert_eq!(shell, Shell::Pwsh);
assert_eq!(
op,
Op::Switch {
profile: "personal".to_owned()
}
);
}
#[test]
fn parse_args_minus() {
let args = ["--eval", "--shell", "zsh", "--", "-"];
let (shell, op) = parse_cas_args_for_test(&args).unwrap();
assert_eq!(shell, Shell::Zsh);
assert_eq!(op, Op::Minus);
}
#[test]
fn parse_args_global() {
let args = ["--eval", "--shell", "zsh", "--", "-g", "personal"];
let (shell, op) = parse_cas_args_for_test(&args).unwrap();
assert_eq!(shell, Shell::Zsh);
assert_eq!(
op,
Op::Global {
profile: "personal".to_owned()
}
);
}
#[test]
fn parse_args_global_long_form() {
let args = ["--eval", "--shell", "zsh", "--", "--global", "elyvian"];
let (shell, op) = parse_cas_args_for_test(&args).unwrap();
assert_eq!(shell, Shell::Zsh);
assert_eq!(
op,
Op::Global {
profile: "elyvian".to_owned()
}
);
}
#[test]
fn parse_args_resync() {
let args = ["--eval", "--shell", "zsh", "--", "resync"];
let (shell, op) = parse_cas_args_for_test(&args).unwrap();
assert_eq!(shell, Shell::Zsh);
assert_eq!(op, Op::Resync);
}
#[test]
fn parse_args_status() {
let args = ["--eval", "--shell", "zsh", "--", "status"];
let (shell, op) = parse_cas_args_for_test(&args).unwrap();
assert_eq!(shell, Shell::Zsh);
assert_eq!(
op,
Op::Status {
print_current: false
}
);
}
#[test]
fn parse_args_status_print_current() {
let args = [
"--eval",
"--shell",
"zsh",
"--",
"status",
"--print-current",
];
let (shell, op) = parse_cas_args_for_test(&args).unwrap();
assert_eq!(shell, Shell::Zsh);
assert_eq!(
op,
Op::Status {
print_current: true
}
);
}
#[test]
fn parse_args_no_shell_defaults_to_zsh() {
let args = ["--eval", "--", "personal"];
let (shell, op) = parse_cas_args_for_test(&args).unwrap();
assert_eq!(shell, Shell::Zsh);
assert_eq!(
op,
Op::Switch {
profile: "personal".to_owned()
}
);
}
#[test]
fn parse_args_no_args_is_status() {
let args: [&str; 0] = [];
let (_, op) = parse_cas_args_for_test(&args).unwrap();
assert_eq!(
op,
Op::Status {
print_current: false
}
);
}
#[test]
fn parse_args_global_missing_profile_errors() {
let args = ["--eval", "--shell", "zsh", "--", "-g"];
let result = parse_cas_args_for_test(&args);
assert!(result.is_err(), "expected error for -g without profile");
}
}