import { Effect, Layer, Option } from "effect";import { SourceControlProviderError, type ChangeRequest } from "@t3tools/contracts"; import { GitLabCli, type GitLabCliError, type GitLabMergeRequestSummary } from "./GitLabCli.ts";import { SourceControlProvider, type SourceControlRefSelector } from "./SourceControlProvider.ts"; function providerError(operation: string, cause: GitLabCliError): SourceControlProviderError { return new SourceControlProviderError({ provider: "gitlab", operation, detail: cause.detail, cause, });} function toChangeRequest(summary: GitLabMergeRequestSummary): ChangeRequest { return { provider: "gitlab", number: summary.number, title: summary.title, url: summary.url, baseRefName: summary.baseRefName, headRefName: summary.headRefName, state: summary.state ?? "open", updatedAt: summary.updatedAt ?? Option.none(), ...(summary.isCrossRepository !== undefined ? { isCrossRepository: summary.isCrossRepository } : {}), ...(summary.headRepositoryNameWithOwner !== undefined ? { headRepositoryNameWithOwner: summary.headRepositoryNameWithOwner } : {}), ...(summary.headRepositoryOwnerLogin !== undefined ? { headRepositoryOwnerLogin: summary.headRepositoryOwnerLogin } : {}), };} function sourceFromInput(input: { readonly headSelector: string; readonly source?: SourceControlRefSelector;}): SourceControlRefSelector | undefined { if (input.source) { return input.source; } const match = /^([^:/\s]+):(.+)$/u.exec(input.headSelector.trim()); const owner = match?.[1]?.trim(); const refName = match?.[2]?.trim(); return owner && refName ? { owner, refName } : undefined;} export const make = Effect.fn("makeGitLabSourceControlProvider")(function* () { const gitlab = yield* GitLabCli; return SourceControlProvider.of({ kind: "gitlab", listChangeRequests: (input) => { const source = sourceFromInput(input); return gitlab .listMergeRequests({ cwd: input.cwd, headSelector: input.headSelector, ...(source ? { source } : {}), state: input.state, ...(input.limit !== undefined ? { limit: input.limit } : {}), }) .pipe( Effect.map((items) => items.map(toChangeRequest)), Effect.mapError((error) => providerError("listChangeRequests", error)), ); }, getChangeRequest: (input) => gitlab.getMergeRequest(input).pipe( Effect.map(toChangeRequest), Effect.mapError((error) => providerError("getChangeRequest", error)), ), createChangeRequest: (input) => { const source = sourceFromInput(input); return gitlab .createMergeRequest({ cwd: input.cwd, baseBranch: input.baseRefName, headSelector: input.headSelector, ...(source ? { source } : {}), ...(input.target ? { target: input.target } : {}), title: input.title, bodyFile: input.bodyFile, }) .pipe(Effect.mapError((error) => providerError("createChangeRequest", error))); }, getRepositoryCloneUrls: (input) => gitlab .getRepositoryCloneUrls(input) .pipe(Effect.mapError((error) => providerError("getRepositoryCloneUrls", error))), createRepository: (input) => gitlab .createRepository(input) .pipe(Effect.mapError((error) => providerError("createRepository", error))), getDefaultBranch: (input) => gitlab .getDefaultBranch(input) .pipe(Effect.mapError((error) => providerError("getDefaultBranch", error))), checkoutChangeRequest: (input) => gitlab .checkoutMergeRequest(input) .pipe(Effect.mapError((error) => providerError("checkoutChangeRequest", error))), });}); export const layer = Layer.effect(SourceControlProvider, make());
import { Effect, Layer, Option } from "effect";
import { SourceControlProviderError, type ChangeRequest } from "@t3tools/contracts";
import { GitLabCli, type GitLabCliError, type GitLabMergeRequestSummary } from "./GitLabCli.ts";
import { SourceControlProvider, type SourceControlRefSelector } from "./SourceControlProvider.ts";
function providerError(operation: string, cause: GitLabCliError): SourceControlProviderError {
return new SourceControlProviderError({
provider: "gitlab",
operation,
detail: cause.detail,
cause,
});
}
function toChangeRequest(summary: GitLabMergeRequestSummary): ChangeRequest {
return {
provider: "gitlab",
number: summary.number,
title: summary.title,
url: summary.url,
baseRefName: summary.baseRefName,
headRefName: summary.headRefName,
state: summary.state ?? "open",
updatedAt: summary.updatedAt ?? Option.none(),
...(summary.isCrossRepository !== undefined
? { isCrossRepository: summary.isCrossRepository }
: {}),
...(summary.headRepositoryNameWithOwner !== undefined
? { headRepositoryNameWithOwner: summary.headRepositoryNameWithOwner }
: {}),
...(summary.headRepositoryOwnerLogin !== undefined
? { headRepositoryOwnerLogin: summary.headRepositoryOwnerLogin }
: {}),
};
}
function sourceFromInput(input: {
readonly headSelector: string;
readonly source?: SourceControlRefSelector;
}): SourceControlRefSelector | undefined {
if (input.source) {
return input.source;
}
const match = /^([^:/\s]+):(.+)$/u.exec(input.headSelector.trim());
const owner = match?.[1]?.trim();
const refName = match?.[2]?.trim();
return owner && refName ? { owner, refName } : undefined;
}
export const make = Effect.fn("makeGitLabSourceControlProvider")(function* () {
const gitlab = yield* GitLabCli;
return SourceControlProvider.of({
kind: "gitlab",
listChangeRequests: (input) => {
const source = sourceFromInput(input);
return gitlab
.listMergeRequests({
cwd: input.cwd,
headSelector: input.headSelector,
...(source ? { source } : {}),
state: input.state,
...(input.limit !== undefined ? { limit: input.limit } : {}),
})
.pipe(
Effect.map((items) => items.map(toChangeRequest)),
Effect.mapError((error) => providerError("listChangeRequests", error)),
);
},
getChangeRequest: (input) =>
gitlab.getMergeRequest(input).pipe(
Effect.map(toChangeRequest),
Effect.mapError((error) => providerError("getChangeRequest", error)),
),
createChangeRequest: (input) => {
const source = sourceFromInput(input);
return gitlab
.createMergeRequest({
cwd: input.cwd,
baseBranch: input.baseRefName,
headSelector: input.headSelector,
...(source ? { source } : {}),
...(input.target ? { target: input.target } : {}),
title: input.title,
bodyFile: input.bodyFile,
})
.pipe(Effect.mapError((error) => providerError("createChangeRequest", error)));
},
getRepositoryCloneUrls: (input) =>
gitlab
.getRepositoryCloneUrls(input)
.pipe(Effect.mapError((error) => providerError("getRepositoryCloneUrls", error))),
createRepository: (input) =>
gitlab
.createRepository(input)
.pipe(Effect.mapError((error) => providerError("createRepository", error))),
getDefaultBranch: (input) =>
gitlab
.getDefaultBranch(input)
.pipe(Effect.mapError((error) => providerError("getDefaultBranch", error))),
checkoutChangeRequest: (input) =>
gitlab
.checkoutMergeRequest(input)
.pipe(Effect.mapError((error) => providerError("checkoutChangeRequest", error))),
});
});
export const layer = Layer.effect(SourceControlProvider, make());