Skip to content

Commit ba4de77

Browse files
committed
Generate TypeScript types from Rust structs using ts-rs
Add ts-rs derives to backend types in gitbutler-branch-actions, gitbutler-operating-modes, gitbutler-edit-mode, and but-api. Types are exported to packages/core/src/generated/ and re-exported from @gitbutler/core/api, replacing hand-written TypeScript interfaces. - Single-line cfg_attr pattern per type: derive(TS) + ts(export) - Manual TS impl for Id<KIND> so StackId resolves to "string" natively - Author renamed to BranchAuthor to avoid collision with author.rs::Author - Consumers updated to import generated types from @gitbutler/core/api - Intermediate re-export files removed (branch.ts deleted) - Select options typed via SelectItemType to avoid unsafe casts
1 parent 222bcce commit ba4de77

57 files changed

Lines changed: 516 additions & 262 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/desktop/src/components/branchesPage/BranchListCard.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<script lang="ts">
22
import BranchesCardLayout from "$components/branchesPage/BranchesCardLayout.svelte";
3-
import { type BranchListing, type BranchListingDetails } from "$lib/branches/branchListing";
43
import { BRANCH_SERVICE } from "$lib/branches/branchService.svelte";
54
import { GIT_CONFIG_SERVICE } from "$lib/config/gitConfigService";
65
import { getPrStatus } from "$lib/forge/interface/prUtils";
@@ -11,6 +10,7 @@
1110
import { gravatarUrlFromEmail } from "@gitbutler/ui/components/avatar/gravatar";
1211
import type { ReviewUnitInfo } from "$lib/forge/interface/forgePrService";
1312
import type { PullRequest } from "$lib/forge/interface/types";
13+
import type { BranchListing, BranchListingDetails } from "@gitbutler/core/api";
1414
1515
interface Props {
1616
reviewUnit: ReviewUnitInfo | undefined;

apps/desktop/src/components/upstream/IntegrateUpstreamModal.svelte

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,7 @@
66
import { DEFAULT_FORGE_FACTORY } from "$lib/forge/forgeFactory.svelte";
77
import {
88
getBaseBranchResolution,
9-
type BaseBranchResolutionApproach,
10-
type Resolution,
11-
type StackStatus,
129
stackFullyIntegrated,
13-
type BranchStatus,
1410
sortStatusInfoV3,
1511
getResolutionApproachV3,
1612
type StackStatusInfoV3,
@@ -40,9 +36,14 @@
4036
import { tick } from "svelte";
4137
import { SvelteMap } from "svelte/reactivity";
4238
import type { PullRequest } from "$lib/forge/interface/types";
39+
import type {
40+
BaseBranchResolutionApproach,
41+
BranchStatus,
42+
Resolution,
43+
StackStatus,
44+
} from "@gitbutler/core/api";
4345
4446
type OperationState = "inert" | "loading" | "completed";
45-
type OperationType = "rebase" | "merge" | "unapply" | "delete";
4647
4748
interface Props {
4849
projectId: string;
@@ -64,6 +65,11 @@
6465
let integratingUpstream = $state<OperationState>("inert");
6566
const results = new SvelteMap<string, Resolution>();
6667
let statuses = $state<StackStatusInfoV3[]>([]);
68+
const baseResolutionOptions = [
69+
{ label: "Rebase", value: "rebase" as const },
70+
{ label: "Merge", value: "merge" as const },
71+
{ label: "Hard reset", value: "hardReset" as const },
72+
];
6773
let baseResolutionApproach = $state<BaseBranchResolutionApproach | undefined>();
6874
let targetCommitOid = $state<string | undefined>(undefined);
6975
let branchStatuses = $state<StackStatusesWithBranchesV3 | undefined>();
@@ -132,7 +138,7 @@
132138
upstreamIntegrationService
133139
.resolveUpstreamIntegrationMutation({
134140
projectId,
135-
resolutionApproach: { type: baseResolutionApproach },
141+
resolutionApproach: baseResolutionApproach,
136142
})
137143
.then((result) => {
138144
targetCommitOid = result;
@@ -174,16 +180,16 @@
174180
return `integrate-upstream-modal:dont-delete-branch:${projectId}:${branchName}`;
175181
}
176182
177-
function handleBaseResolutionSelection(value: string) {
178-
baseResolutionApproach = value as BaseBranchResolutionApproach;
183+
function handleBaseResolutionSelection(value: BaseBranchResolutionApproach["type"]) {
184+
baseResolutionApproach = { type: value };
179185
}
180186
181187
async function integrate() {
182188
integratingUpstream = "loading";
183189
await tick();
184190
const baseResolution = getBaseBranchResolution(
185191
targetCommitOid,
186-
baseResolutionApproach || "hardReset",
192+
baseResolutionApproach ?? { type: "hardReset" },
187193
);
188194
189195
await integrateUpstream({
@@ -314,7 +320,7 @@
314320
maxWidth={130}
315321
onselect={(value) => {
316322
const result = results.get(stackId)!;
317-
results.set(stackId, { ...result, approach: { type: value as OperationType } });
323+
results.set(stackId, { ...result, approach: { type: value } });
318324
}}
319325
options={integrationOptions(stackStatus)}
320326
>
@@ -405,14 +411,10 @@
405411

406412
<div class="target-divergence-action">
407413
<Select
408-
value={baseResolutionApproach}
414+
value={baseResolutionApproach?.type}
409415
placeholder="Choose…"
410416
onselect={handleBaseResolutionSelection}
411-
options={[
412-
{ label: "Rebase", value: "rebase" },
413-
{ label: "Merge", value: "merge" },
414-
{ label: "Hard reset", value: "hardReset" },
415-
]}
417+
options={baseResolutionOptions}
416418
>
417419
{#snippet itemSnippet({ item, highlighted })}
418420
<SelectItem selected={highlighted} {highlighted}>

apps/desktop/src/components/workspace/EditCommitPanel.svelte

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,10 @@
44
import ReduxResult from "$components/shared/ReduxResult.svelte";
55
import { getEditorUri, URL_SERVICE } from "$lib/backend/url";
66
import { splitMessage } from "$lib/commits/commitMessage";
7-
import {
8-
conflictEntryHint,
9-
getConflictState,
10-
type ConflictEntryPresence,
11-
} from "$lib/files/conflictEntryPresence";
7+
import { conflictEntryHint, getConflictState } from "$lib/files/conflictEntryPresence";
128
import { FILE_SERVICE } from "$lib/files/fileService";
139
import { computeChangeStatus } from "$lib/files/fileStatus";
14-
import { MODE_SERVICE, type EditModeMetadata } from "$lib/mode/modeService";
10+
import { MODE_SERVICE } from "$lib/mode/modeService";
1511
import { vscodePath } from "$lib/project/project";
1612
import { PROJECTS_SERVICE } from "$lib/project/projectsService";
1713
import { createCommitSelection } from "$lib/selection/key";
@@ -35,6 +31,8 @@
3531
import { derived, fromStore, readable, toStore, type Readable } from "svelte/store";
3632
import type { FileInfo } from "$lib/files/file";
3733
import type { TreeChange } from "$lib/hunks/change";
34+
import type { EditModeMetadata } from "@gitbutler/core/api";
35+
import type { ConflictEntryPresence } from "@gitbutler/core/api";
3836
import type { FileStatus } from "@gitbutler/ui/components/file/types";
3937
4038
type Props = {

apps/desktop/src/lib/branches/branch.ts

Lines changed: 0 additions & 18 deletions
This file was deleted.

apps/desktop/src/lib/branches/branchEndpoints.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,16 @@ import {
77
} from "$lib/state/tags";
88
import { createEntityAdapter, type EntityState } from "@reduxjs/toolkit";
99
import type { BaseBranch, ForgeProvider, RemoteBranchInfo } from "$lib/baseBranch/baseBranch";
10-
import type { BranchListing, BranchListingDetails } from "$lib/branches/branchListing";
1110
import type { BackendEndpointBuilder } from "$lib/state/backendApi";
1211
import type {
1312
BaseBranchResolution,
1413
BaseBranchResolutionApproach,
15-
BranchStatusesResponse,
14+
BranchListing,
15+
BranchListingDetails,
1616
IntegrationOutcome,
1717
Resolution,
18-
} from "$lib/upstream/types";
18+
StackStatuses,
19+
} from "@gitbutler/core/api";
1920

2021
export function buildBranchEndpoints(build: BackendEndpointBuilder) {
2122
return {
@@ -102,7 +103,7 @@ export function buildBranchEndpoints(build: BackendEndpointBuilder) {
102103

103104
// ── Upstream Integration ─────────────────────────────────────
104105
upstreamIntegrationStatuses: build.query<
105-
BranchStatusesResponse,
106+
StackStatuses,
106107
{ projectId: string; targetCommitOid?: string }
107108
>({
108109
extraOptions: { command: "upstream_integration_statuses" },
@@ -130,7 +131,7 @@ export function buildBranchEndpoints(build: BackendEndpointBuilder) {
130131
}),
131132
resolveUpstreamIntegration: build.mutation<
132133
string,
133-
{ projectId: string; resolutionApproach: { type: BaseBranchResolutionApproach } }
134+
{ projectId: string; resolutionApproach: BaseBranchResolutionApproach }
134135
>({
135136
extraOptions: {
136137
command: `resolve_upstream_integration`,

apps/desktop/src/lib/branches/branchListing.ts

Lines changed: 1 addition & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,110 +1,13 @@
11
import { msSinceDaysAgo } from "$lib/utils/time";
22
import { isDefined } from "@gitbutler/ui/utils/typeguards";
33
import type { ForgeUser, PullRequest } from "$lib/forge/interface/types";
4+
import type { BranchListing } from "@gitbutler/core/api";
45

56
export type GroupedSidebarEntries = Record<
67
"applied" | "authored" | "review" | "today" | "yesterday" | "lastWeek" | "older",
78
SidebarEntrySubject[]
89
>;
910

10-
/**
11-
* Represents a branch that exists for the repository
12-
* This also combines the concept of a remote, local and virtual branch in order to provide a unified interface for the UI
13-
* Branch entry is not meant to contain all of the data a branch can have (e.g. full commit history, all files and diffs, etc.).
14-
* It is intended a summary that can be quickly retrieved and displayed in the UI.
15-
* For more detailed information, each branch can be queried individually for it's `BranchData`.
16-
*/
17-
export type BranchListing = {
18-
/** The name of the branch (e.g. `main`, `feature/branch`), excluding the remote name */
19-
name: string;
20-
/**
21-
* This is a list of remote that this branch can be found on (e.g. `origin`, `upstream` etc.).
22-
* If this branch is a local branch, this list will be empty.
23-
*/
24-
remotes: string[];
25-
/** The branch may or may not have a virtual branch associated with it */
26-
stack?: StackReference | undefined;
27-
/**
28-
* Timestamp in milliseconds since the branch was last updated.
29-
* This includes any commits, uncommitted changes or even updates to the branch metadata (e.g. renaming).
30-
*/
31-
updatedAt: string;
32-
/** The person who committed the head commit */
33-
lastCommiter: Author;
34-
/** Whether or not there is a local branch as part of the grouping */
35-
hasLocal: boolean;
36-
};
37-
38-
/** Represents a reference to an associated virtual branch */
39-
export type StackReference = {
40-
/** A non-normalized name of the branch, set by the user */
41-
givenName: string;
42-
/** Virtual Branch UUID identifier */
43-
id: string;
44-
/** Determines if the virtual branch is applied in the workspace */
45-
inWorkspace: boolean;
46-
/**
47-
List of branch names that are part of the stack
48-
Ordered from newest to oldest (the most recent branch is first in the list)
49-
*/
50-
branches: string[];
51-
/** Pull Request numbes by branch name associated with the stack */
52-
pullRequests: Record<string, number>;
53-
};
54-
55-
/** Represents a "commit author" or "signature", based on the data from there git history */
56-
export type Author = {
57-
/** The name of the author as configured in the git config */
58-
name?: string | undefined;
59-
/** The email of the author as configured in the git config */
60-
email?: string | undefined;
61-
/** The gravatar id of the author */
62-
gravatarUrl?: string | undefined;
63-
};
64-
65-
/** Represents a fat struct with all the data associated with a branch */
66-
export interface BranchListingDetails {
67-
/** The name of the branch (e.g. `main`, `feature/branch`), excluding the remote name */
68-
name: string;
69-
/**
70-
* The number of lines added within the branch
71-
* Since the virtual branch, local branch and the remote one can have different number of lines removed,
72-
* the value from the virtual branch (if present) takes the highest precedence,
73-
* followed by the local branch and then the remote branches (taking the max if there are multiple).
74-
* If this branch has a virtual branch, lines_added does NOT include the uncommitted lines.
75-
*/
76-
linesAdded: number;
77-
/**
78-
* The number of lines removed within the branch
79-
* Since the virtual branch, local branch and the remote one can have different number of lines removed,
80-
* the value from the virtual branch (if present) takes the highest precedence,
81-
* followed by the local branch and then the remote branches (taking the max if there are multiple)
82-
* If this branch has a virtual branch, lines_removed does NOT include the uncommitted lines.
83-
*/
84-
linesRemoved: number;
85-
/**
86-
* The number of files that were modified within the branch
87-
* Since the virtual branch, local branch and the remote one can have different number files modified,
88-
* the value from the virtual branch (if present) takes the highest precedence,
89-
* followed by the local branch and then the remote branches (taking the max if there are multiple)
90-
*/
91-
numberOfFiles: number;
92-
/**
93-
* The number of commits associated with a branch
94-
* Since the virtual branch, local branch and the remote one can have different number of commits,
95-
* the value from the virtual branch (if present) takes the highest precedence,
96-
* followed by the local branch and then the remote branches (taking the max if there are multiple)
97-
*/
98-
numberOfCommits: number;
99-
/**
100-
* A list of authors that have contributes commits to this branch.
101-
* In the case of multiple remote tracking branches, it takes the full list of unique authors.
102-
*/
103-
authors: Author[];
104-
/** The branch may or may not have a virtual branch associated with it */
105-
stack?: StackReference | undefined;
106-
}
107-
10811
type PullRequestEntrySubject = {
10912
type: "pullRequest";
11013
subject: PullRequest;

apps/desktop/src/lib/dragging/stackingReorderDropzoneManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { CommitDropData } from "$lib/dragging/dropHandlers/commitDropHandler";
22
import { InjectionToken } from "@gitbutler/core/context";
3-
import type { StackOrder } from "$lib/branches/branch";
43
import type { DropzoneHandler } from "$lib/dragging/handler";
54
import type { StackService } from "$lib/stacks/stackService.svelte";
5+
import type { StackOrder } from "@gitbutler/core/api";
66

77
export class ReorderCommitDzHandler implements DropzoneHandler {
88
constructor(

apps/desktop/src/lib/files/conflictEntryPresence.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
export interface ConflictEntryPresence {
2-
ours: boolean;
3-
theirs: boolean;
4-
ancestor: boolean;
5-
}
1+
export type { ConflictEntryPresence } from "@gitbutler/core/api";
2+
import type { ConflictEntryPresence } from "@gitbutler/core/api";
63

74
export function emptyConflictEntryPresence(): ConflictEntryPresence {
85
return {

apps/desktop/src/lib/files/conflicts.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import {
2-
emptyConflictEntryPresence,
3-
type ConflictEntryPresence,
4-
} from "$lib/files/conflictEntryPresence";
1+
import { emptyConflictEntryPresence } from "$lib/files/conflictEntryPresence";
2+
import type { ConflictEntryPresence } from "@gitbutler/core/api";
53

64
export class ConflictEntries {
75
public entries: Map<string, ConflictEntryPresence> = new Map();

0 commit comments

Comments
 (0)