Skip to content

Commit

Permalink
add Url module
Browse files Browse the repository at this point in the history
  • Loading branch information
KhraksMamtsov committed Dec 20, 2024
1 parent 0c91dd6 commit b0b4ed5
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 1 deletion.
8 changes: 8 additions & 0 deletions .changeset/thin-coats-bake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@effect/platform": patch
---

`Url` module has been introduced:

- immutable setters with dual-function api
- integration with `UrlParams`
2 changes: 1 addition & 1 deletion packages/platform/src/Error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export declare namespace PlatformError {
export interface Base {
readonly [PlatformErrorTypeId]: typeof PlatformErrorTypeId
readonly _tag: string
readonly module: "Clipboard" | "Command" | "FileSystem" | "KeyValueStore" | "Path" | "Stream" | "Terminal"
readonly module: "Clipboard" | "Command" | "FileSystem" | "KeyValueStore" | "Path" | "Stream" | "Terminal" | "Url"
readonly method: string
readonly message: string
}
Expand Down
150 changes: 150 additions & 0 deletions packages/platform/src/Url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/**
* @since 1.0.0
*/
import * as Either from "effect/Either"
import { dual } from "effect/Function"
import * as Error from "./Error.js"
import * as UrlParams from "./UrlParams.js"

/** @internal */
const immutableSetter = <M>(clone: (mutable: M) => M) =>
<P extends keyof M>(property: P): {
(value: M[P]): (mutable: M) => M
(mutable: M, value: M[P]): M
} =>
dual(2, (mutable: M, value: M[P]) => {
const result = clone(mutable)
result[property] = value
return result
})
/** @internal */
const immutableURLSetter = immutableSetter<URL>((url) => new URL(url))
/**
* @since 1.0.0
* @category constructors
*/
export const make: {
(url: string | URL, base?: string | URL | undefined): Either.Either<URL, Error.BadArgument>
} = (url, base) =>
Either.try({
try: () => new URL(url, base),
catch: (cause) =>
Error.BadArgument({
module: "Url",
method: "make",
message: cause instanceof globalThis.Error ? cause.message : "Invalid input"
})
})
/**
* @since 1.0.0
* @category utils
*/
export const setHash: {
(hash: string): (url: URL) => URL
(url: URL, hash: string): URL
} = immutableURLSetter("hash")
/**
* @since 1.0.0
* @category utils
*/
export const setHost: {
(host: string): (url: URL) => URL
(url: URL, host: string): URL
} = immutableURLSetter("host")
/**
* @since 1.0.0
* @category utils
*/
export const setHostname: {
(hostname: string): (url: URL) => URL
(url: URL, hostname: string): URL
} = immutableURLSetter("hostname")
/**
* @since 1.0.0
* @category utils
*/
export const setHref: {
(href: string): (url: URL) => URL
(url: URL, href: string): URL
} = immutableURLSetter("href")
/**
* @since 1.0.0
* @category utils
*/
export const setPassword: {
(password: string): (url: URL) => URL
(url: URL, password: string): URL
} = immutableURLSetter("password")
/**
* @since 1.0.0
* @category utils
*/
export const setPathname: {
(pathname: string): (url: URL) => URL
(url: URL, pathname: string): URL
} = immutableURLSetter("pathname")
/**
* @since 1.0.0
* @category utils
*/
export const setPort: {
(port: string): (url: URL) => URL
(url: URL, port: string): URL
} = immutableURLSetter("port")
/**
* @since 1.0.0
* @category utils
*/
export const setProtocol: {
(protocol: string): (url: URL) => URL
(url: URL, protocol: string): URL
} = immutableURLSetter("protocol")
/**
* @since 1.0.0
* @category utils
*/
export const setSearch: {
(search: string): (url: URL) => URL
(url: URL, search: string): URL
} = immutableURLSetter("search")
/**
* @since 1.0.0
* @category utils
*/
export const setUsername: {
(username: string): (url: URL) => URL
(url: URL, username: string): URL
} = immutableURLSetter("username")
/**
* @since 1.0.0
* @category utils
*/
export const setUrlParams: {
(searchParams: UrlParams.UrlParams): (url: URL) => URL
(url: URL, username: UrlParams.UrlParams): URL
} = dual(2, (url: URL, searchParams: UrlParams.UrlParams) => {
const result = new URL(url)
result.search = UrlParams.toString(searchParams)
return result
})
/**
* @since 1.0.0
* @category getters
*/
export const urlParams: {
(url: URL): UrlParams.UrlParams
} = (url) => UrlParams.fromInput(url.searchParams)
/**
* @since 1.0.0
* @category utils
*/
export const modifyUrlParams: {
(f: (urlParams: UrlParams.UrlParams) => UrlParams.UrlParams): (url: URL) => URL
(url: URL, f: (urlParams: UrlParams.UrlParams) => UrlParams.UrlParams): URL
} = dual(2, (url: URL, f: (urlParams: UrlParams.UrlParams) => UrlParams.UrlParams) => {
const urlParams = UrlParams.fromInput(url.searchParams)
const newUrlParams = f(urlParams)
const result = new URL(url)
result.search = UrlParams.toString(newUrlParams)
return result
})
5 changes: 5 additions & 0 deletions packages/platform/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,11 @@ export * as Terminal from "./Terminal.js"
*/
export * as Transferable from "./Transferable.js"

/**
* @since 1.0.0
*/
export * as Url from "./Url.js"

/**
* @since 1.0.0
*/
Expand Down
28 changes: 28 additions & 0 deletions packages/platform/test/Url.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as Url from "@effect/platform/Url"
import * as UrlParams from "@effect/platform/UrlParams"
import { assert, describe, it } from "@effect/vitest"
import { Effect } from "effect"

describe("Url", () => {
const testURL = new URL("https://example.com/test")

describe("make", () => {
it.effect("immutable", () =>
Effect.gen(function*() {
const url = yield* Url.make(testURL)
assert.notStrictEqual(url, testURL)
}))
})
describe("setters", () => {
it("immutable", () => {
const hashUrl = Url.setHash(testURL, "test")
assert.notStrictEqual(hashUrl, testURL)
assert.strictEqual(hashUrl.toString(), "https://example.com/test#test")
})
})
it("modifyUrlParams", () => {
const paramsUrl = Url.modifyUrlParams(testURL, (x) => UrlParams.append(x, "key", "value"))
assert.notStrictEqual(paramsUrl, testURL)
assert.strictEqual(paramsUrl.toString(), "https://example.com/test?key=value")
})
})

0 comments on commit b0b4ed5

Please sign in to comment.