yog-ids 0.4.0

Yog's shared names — namespaces, content kinds, schemes, separators, its own ids — defined once, exported to TypeScript and Java.
Documentation
//! The names for the other languages: TypeScript for the IDE, Java for the hosts.

use crate::groups;

const HEADER: &str = "Generated by yog-ids from Yog-Mod-Loader/rust/crates/yog-ids — don't edit; regenerate with `cargo run -p yog-ids --bin yog-ids-export`.";

/// The hosts' class of the names, and its package.
pub const JAVA_CLASS: &str = "YogNames";
pub const JAVA_PACKAGE: &str = "dev.yog";

/// The Java expression naming `value` in class `class`
/// (`YogNames.EventBlock.BREAK`), if a name has it.
pub fn java_ref(class: &str, value: &str) -> Option<String> {
    groups().find_map(|(group, names)| {
        names
            .iter()
            .find(|(_, v)| *v == value)
            .map(|(name, _)| format!("{class}.{}.{name}", pascal(group)))
    })
}

/// A TypeScript module: one `as const` object per group.
pub fn typescript() -> String {
    let mut out = format!("// {HEADER}\n");
    for (group, names) in groups() {
        out.push_str(&format!("\nexport const {} = {{\n", camel(group)));
        for (name, value) in *names {
            out.push_str(&format!("  {name}: {value:?},\n"));
        }
        out.push_str("} as const;\n");
    }
    out.push_str(
        "\n/** Yog's file formats: extension, noun, and whether files of it are Yog Data — binary on disk, text in the editor (`Format`). */\nexport const formats = {\n",
    );
    for f in crate::Format::ALL {
        out.push_str(&format!(
            "  {}: {{ ext: {:?}, noun: {:?}, isData: {} }},\n",
            camel(&format!("{f:?}").to_lowercase()),
            f.ext(),
            f.noun(),
            f.is_data()
        ));
    }
    out.push_str("} as const;\n");
    out
}

/// A Java class (`package`): a nested class of constants per group.
pub fn java(package: &str, class: &str) -> String {
    let mut out = format!(
        "// {HEADER}\npackage {package};\n\n/** The names Yog's code shares (yog-ids). */\npublic final class {class} {{\n    private {class}() {{}}\n"
    );
    for (group, names) in groups() {
        out.push_str(&format!(
            "\n    public static final class {} {{\n        private {}() {{}}\n\n",
            pascal(group),
            pascal(group)
        ));
        for (name, value) in *names {
            out.push_str(&format!(
                "        public static final String {name} = {value:?};\n"
            ));
        }
        let all: Vec<&str> = names.iter().map(|(name, _)| *name).collect();
        out.push_str(&format!(
            "\n        /** Every name of the group, in order. */\n        public static final java.util.List<String> ALL_VALUES = java.util.List.of({});\n    }}\n",
            all.join(", ")
        ));
    }
    out.push_str("\n    /** Yog's file formats' extensions (`Format`). */\n    public static final class Ext {\n        private Ext() {}\n\n");
    for f in crate::Format::ALL {
        out.push_str(&format!(
            "        public static final String {} = {:?};\n",
            format!("{f:?}").to_uppercase(),
            f.ext()
        ));
    }
    out.push_str("    }\n}\n");
    out
}

fn pascal(s: &str) -> String {
    s.split('_')
        .map(|w| w[..1].to_uppercase() + &w[1..])
        .collect()
}

fn camel(s: &str) -> String {
    let p = pascal(s);
    p[..1].to_lowercase() + &p[1..]
}