Repository: gitbutlerapp/gitbutler
File: crates/but-core/src/settings.rs (Rust)

Code before:
    const GIT_SIGN_COMMITS: &str = "commit.gpgsign";
    const GITBUTLER_SIGN_COMMITS: &str = "gitbutler.signCommits";
    const GITBUTLER_GERRIT_MODE: &str = "gitbutler.gerritMode";
    const GITBUTLER_FORGE_TEMPLATE_PATH: &str = "gitbutler.forgeReviewTemplatePath";
    const GITBUTLER_GITLAB_PROJECT_ID: &str = "gitbutler.gitlabProjectId";
    const GITBUTLER_GITLAB_UPSTREAM_PROJECT_ID: &str = "gitbutler.gitlabUpstreamProjectId";
    const SIGNING_KEY: &str = "user.signingKey";
    const SIGNING_FORMAT: &str = "gpg.format";
    const GPG_PROGRAM: &str = "gpg.program";
    const GPG_SSH_PROGRAM: &str = "gpg.ssh.program";

    /// UI types
    pub mod ui {
        use but_serde::BStringForFrontend;

        /// See [`GitConfigSettings`](crate::GitConfigSettings) for the docs.
        #[derive(Debug, PartialEq, Clone, Default, serde::Serialize, serde::Deserialize)]
        #[cfg_attr(feature = "export-ts", derive(ts_rs::TS))]
        #[serde(rename_all = "camelCase")]
        #[cfg_attr(feature = "export-ts", ts(export, export_to = "./settings/gitConfigSettings.ts"))]
        #[expect(missing_docs)]
        pub struct GitConfigSettings {
            #[serde(rename = "signCommits")]
            pub gitbutler_sign_commits: Option<bool>,
            pub gitbutler_gerrit_mode: Option<bool>,
            #[cfg_attr(feature = "export-ts", ts(type = "string | null"))]
            pub gitbutler_forge_review_template_path: Option<BStringForFrontend>,
            #[cfg_attr(feature = "export-ts", ts(type = "string | null"))]
            pub gitbutler_gitlab_project_id: Option<BStringForFrontend>,
            #[cfg_attr(feature = "export-ts", ts(type = "string | null"))]
            pub gitbutler_gitlab_upstream_project_id: Option<BStringForFrontend>,
            #[cfg_attr(feature = "export-ts", ts(type = "string | null"))]
            pub signing_key: Option<BStringForFrontend>,
            #[cfg_attr(feature = "export-ts", ts(type = "string | null"))]
            pub signing_format: Option<BStringForFrontend>,
            #[cfg_attr(feature = "export-ts", ts(type = "string | null"))]
            pub gpg_program: Option<BStringForFrontend>,
            #[cfg_attr(feature = "export-ts", ts(type = "string | null"))]
            pub gpg_ssh_program: Option<BStringForFrontend>,
        }

        impl From<crate::GitConfigSettings> for GitConfigSettings {
            fn from(
                crate::GitConfigSettings {
                    gitbutler_sign_commits,
                    gitbutler_gerrit_mode,
                    gitbutler_forge_review_template_path,
                    gitbutler_gitlab_project_id,
                    gitbutler_gitlab_upstream_project_id,
                    signing_key,
                    signing_format,

Review comment (suggestion):
~~While having seen that before, it's strange to me that `ts-rs` won't do this automatically. Are we sure it' can't turn an option into `null` - that should be the point of it.~~

Edit: I think I understand now that the problem is not the Option, the problem is the `BStringForFrontend`. It would be nice if there was a way to configure ts-rs more centrally, and configure it once.

Code after:
    const GIT_SIGN_COMMITS: &str = "commit.gpgsign";
    const GITBUTLER_SIGN_COMMITS: &str = "gitbutler.signCommits";
    const GITBUTLER_GERRIT_MODE: &str = "gitbutler.gerritMode";
    const GITBUTLER_FORGE_TEMPLATE_PATH: &str = "gitbutler.forgeReviewTemplatePath";
    const GITBUTLER_GITLAB_PROJECT_ID: &str = "gitbutler.gitlabProjectId";
    const GITBUTLER_GITLAB_UPSTREAM_PROJECT_ID: &str = "gitbutler.gitlabUpstreamProjectId";
    const SIGNING_KEY: &str = "user.signingKey";
    const SIGNING_FORMAT: &str = "gpg.format";
    const GPG_PROGRAM: &str = "gpg.program";
    const GPG_SSH_PROGRAM: &str = "gpg.ssh.program";

    /// UI types
    pub mod ui {
        use but_serde::BStringForFrontend;

        /// See [`GitConfigSettings`](crate::GitConfigSettings) for the docs.
        #[derive(Debug, PartialEq, Clone, Default, serde::Serialize, serde::Deserialize)]
        #[cfg_attr(feature = "export-ts", derive(ts_rs::TS))]
        #[serde(rename_all = "camelCase")]
        #[cfg_attr(feature = "export-ts", ts(export, export_to = "./settings/gitConfigSettings.ts"))]
        #[expect(missing_docs)]
        pub struct GitConfigSettings {
            #[serde(rename = "signCommits")]
            pub gitbutler_sign_commits: Option<bool>,
            pub gitbutler_gerrit_mode: Option<bool>,
            #[cfg_attr(feature = "export-ts", ts(type = "string | null"))]
            pub gitbutler_forge_review_template_path: Option<BStringForFrontend>,
            pub gitbutler_gitlab_project_id: Option<String>,
            pub gitbutler_gitlab_upstream_project_id: Option<String>,
            #[cfg_attr(feature = "export-ts", ts(type = "string | null"))]
            pub signing_key: Option<BStringForFrontend>,
            #[cfg_attr(feature = "export-ts", ts(type = "string | null"))]
            pub signing_format: Option<BStringForFrontend>,
            #[cfg_attr(feature = "export-ts", ts(type = "string | null"))]
            pub gpg_program: Option<BStringForFrontend>,
            #[cfg_attr(feature = "export-ts", ts(type = "string | null"))]
            pub gpg_ssh_program: Option<BStringForFrontend>,
        }

        impl From<crate::GitConfigSettings> for GitConfigSettings {
            fn from(
                crate::GitConfigSettings {
                    gitbutler_sign_commits,
                    gitbutler_gerrit_mode,
                    gitbutler_forge_review_template_path,
                    gitbutler_gitlab_project_id,
                    gitbutler_gitlab_upstream_project_id,
                    signing_key,
                    signing_format,
                    gpg_program,
                    gpg_ssh_program,