use crate::cli::{ActorCommand, Cli};
use crate::context::GriteContext;
use crate::output::output_success;
use libgrite_core::{
config::{
actor_dir, list_actors, load_actor_config, load_repo_config, save_actor_config,
save_repo_config,
},
signing::SigningKeyPair,
types::actor::ActorConfig,
types::ids::{generate_actor_id, id_to_hex},
GriteError,
};
use serde::Serialize;
#[derive(Serialize)]
struct ActorInitOutput {
actor_id: String,
label: Option<String>,
data_dir: String,
#[serde(skip_serializing_if = "Option::is_none")]
public_key: Option<String>,
}
#[derive(Serialize)]
struct ActorListOutput {
actors: Vec<ActorInfo>,
}
#[derive(Serialize)]
struct ActorInfo {
actor_id: String,
label: Option<String>,
data_dir: String,
}
#[derive(Serialize)]
struct ActorShowOutput {
actor: ActorDetail,
}
#[derive(Serialize)]
struct ActorDetail {
actor_id: String,
label: Option<String>,
created_ts: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
public_key: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
key_scheme: Option<String>,
}
#[derive(Serialize)]
struct ActorCurrentOutput {
actor_id: String,
data_dir: String,
source: String,
}
#[derive(Serialize)]
struct ActorUseOutput {
default_actor: String,
repo_config: String,
}
pub fn run(cli: &Cli, cmd: ActorCommand) -> Result<(), GriteError> {
match cmd {
ActorCommand::Init {
label,
generate_key,
} => run_init(cli, label, generate_key),
ActorCommand::List => run_list(cli),
ActorCommand::Show { id } => run_show(cli, id),
ActorCommand::Current => run_current(cli),
ActorCommand::Use { id } => run_use(cli, id),
}
}
fn run_init(cli: &Cli, label: Option<String>, generate_key: bool) -> Result<(), GriteError> {
let git_dir = GriteContext::find_git_dir()?;
let actor_id = generate_actor_id();
let actor_id_hex = id_to_hex(&actor_id);
let data_dir = actor_dir(&git_dir, &actor_id_hex);
let mut config = ActorConfig::new(actor_id, label.clone());
let mut public_key = None;
if generate_key {
let keypair = SigningKeyPair::generate();
public_key = Some(keypair.public_key_hex());
config.public_key = public_key.clone();
config.key_scheme = Some("ed25519".to_string());
std::fs::create_dir_all(&data_dir)?;
let signing_key_path = data_dir.join("signing_key");
#[cfg(unix)]
{
use std::fs::OpenOptions;
use std::io::Write;
use std::os::unix::fs::OpenOptionsExt;
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.mode(0o600)
.open(&signing_key_path)?;
file.write_all(keypair.seed_hex().as_bytes())?;
}
#[cfg(not(unix))]
{
std::fs::write(&signing_key_path, keypair.seed_hex())?;
}
}
save_actor_config(&data_dir, &config)?;
output_success(
cli,
ActorInitOutput {
actor_id: actor_id_hex,
label,
data_dir: data_dir.to_string_lossy().to_string(),
public_key,
},
);
Ok(())
}
fn run_list(cli: &Cli) -> Result<(), GriteError> {
let git_dir = GriteContext::find_git_dir()?;
let actors = list_actors(&git_dir)?;
let actor_infos: Vec<ActorInfo> = actors
.into_iter()
.map(|a| {
let data_dir = actor_dir(&git_dir, &a.actor_id);
ActorInfo {
actor_id: a.actor_id,
label: a.label,
data_dir: data_dir.to_string_lossy().to_string(),
}
})
.collect();
output_success(
cli,
ActorListOutput {
actors: actor_infos,
},
);
Ok(())
}
fn run_show(cli: &Cli, id: Option<String>) -> Result<(), GriteError> {
let git_dir = GriteContext::find_git_dir()?;
let actor_id = match id {
Some(id) => id,
None => {
let ctx = GriteContext::resolve(cli)?;
ctx.actor_id
}
};
let data_dir = actor_dir(&git_dir, &actor_id);
let config = load_actor_config(&data_dir)?;
output_success(
cli,
ActorShowOutput {
actor: ActorDetail {
actor_id: config.actor_id,
label: config.label,
created_ts: config.created_ts,
public_key: config.public_key,
key_scheme: config.key_scheme,
},
},
);
Ok(())
}
fn run_current(cli: &Cli) -> Result<(), GriteError> {
let ctx = GriteContext::resolve(cli)?;
output_success(
cli,
ActorCurrentOutput {
actor_id: ctx.actor_id,
data_dir: ctx.data_dir.to_string_lossy().to_string(),
source: ctx.source.as_str().to_string(),
},
);
Ok(())
}
fn run_use(cli: &Cli, id: String) -> Result<(), GriteError> {
let git_dir = GriteContext::find_git_dir()?;
let data_dir = actor_dir(&git_dir, &id);
let _config = load_actor_config(&data_dir)?;
let mut repo_config = load_repo_config(&git_dir)?.unwrap_or_default();
repo_config.default_actor = Some(id.clone());
let repo_config_path = git_dir.join("grite").join("config.toml");
save_repo_config(&git_dir, &repo_config)?;
output_success(
cli,
ActorUseOutput {
default_actor: id,
repo_config: repo_config_path.to_string_lossy().to_string(),
},
);
Ok(())
}