dsc-rs 0.3.0

Discourse CLI tool for managing multiple Discourse forums: track installs, run upgrades over SSH, manage emojis, sync topics and categories as Markdown, and more.
Documentation
use crate::api::DiscourseClient;
use crate::api::GroupSummary;
use crate::cli::{ListFormat, StructuredFormat};
use crate::commands::common::{ensure_api_credentials, not_found, select_discourse};
use crate::config::Config;
use crate::utils::{normalize_baseurl, slugify};
use anyhow::Result;

pub fn group_list(
    config: &Config,
    discourse_name: &str,
    format: ListFormat,
    verbose: bool,
) -> Result<()> {
    let discourse = select_discourse(config, Some(discourse_name))?;
    ensure_api_credentials(discourse)?;
    let client = DiscourseClient::new(discourse)?;
    let groups = client.fetch_groups()?;
    match format {
        ListFormat::Text => {
            if groups.is_empty() && !verbose {
                println!("No groups found.");
                return Ok(());
            }
            for group in groups {
                let full_name = group.full_name.unwrap_or_else(|| "-".to_string());
                println!("{} - {} ({})", group.id, group.name, full_name);
            }
        }
        ListFormat::Json => {
            let raw = serde_json::to_string_pretty(&groups)?;
            println!("{}", raw);
        }
        ListFormat::Yaml => {
            let raw = serde_yaml::to_string(&groups)?;
            println!("{}", raw);
        }
    }
    Ok(())
}

pub fn group_info(
    config: &Config,
    discourse_name: &str,
    group_id: u64,
    format: StructuredFormat,
) -> Result<()> {
    let discourse = select_discourse(config, Some(discourse_name))?;
    ensure_api_credentials(discourse)?;
    let client = DiscourseClient::new(discourse)?;
    let group_summary = find_group_summary(&client, group_id)?;
    let group = client.fetch_group_detail(group_summary.id, Some(&group_summary.name))?;
    match format {
        StructuredFormat::Json => {
            let raw = serde_json::to_string_pretty(&group)?;
            println!("{}", raw);
        }
        StructuredFormat::Yaml => {
            let raw = serde_yaml::to_string(&group)?;
            println!("{}", raw);
        }
    }
    Ok(())
}

pub fn group_members(
    config: &Config,
    discourse_name: &str,
    group_id: u64,
    format: ListFormat,
) -> Result<()> {
    let discourse = select_discourse(config, Some(discourse_name))?;
    ensure_api_credentials(discourse)?;
    let client = DiscourseClient::new(discourse)?;
    let group_summary = find_group_summary(&client, group_id)?;
    let members = client.fetch_group_members(group_summary.id, Some(&group_summary.name))?;
    match format {
        ListFormat::Text => {
            if members.is_empty() {
                println!("No group members found.");
                return Ok(());
            }
            for member in members {
                let name = member.name.unwrap_or_else(|| "-".to_string());
                println!("{} - {} ({})", member.id, member.username, name);
            }
        }
        ListFormat::Json => {
            let raw = serde_json::to_string_pretty(&members)?;
            println!("{}", raw);
        }
        ListFormat::Yaml => {
            let raw = serde_yaml::to_string(&members)?;
            println!("{}", raw);
        }
    }
    Ok(())
}

pub fn group_copy(
    config: &Config,
    source: &str,
    target: Option<&str>,
    group_id: u64,
) -> Result<()> {
    let source_discourse = select_discourse(config, Some(source))?;
    let target_discourse_name = target.unwrap_or(source);
    let target_discourse = select_discourse(config, Some(target_discourse_name))?;

    ensure_api_credentials(source_discourse)?;
    ensure_api_credentials(target_discourse)?;

    let source_client = DiscourseClient::new(source_discourse)?;
    let group_summary = find_group_summary(&source_client, group_id)?;
    let mut group =
        source_client.fetch_group_detail(group_summary.id, Some(&group_summary.name))?;
    group.name = format!("{}-copy", slugify(&group.name));
    if let Some(full_name) = group.full_name.clone() {
        group.full_name = Some(format!("Copy of {}", full_name));
    }

    let target_client = DiscourseClient::new(target_discourse)?;
    let new_id = target_client.create_group(&group)?;
    let url = format!(
        "{}/g/{}/{}",
        normalize_baseurl(&target_discourse.baseurl),
        group.name,
        new_id
    );
    println!("{}", url);
    Ok(())
}

fn find_group_summary(client: &DiscourseClient, group_id: u64) -> Result<GroupSummary> {
    let groups = client.fetch_groups()?;
    groups
        .into_iter()
        .find(|item| item.id == group_id)
        .ok_or_else(|| not_found("group", group_id))
}