@inrupt/solid-client
Version:
Make your web apps work with Solid Pods.
389 lines (379 loc) • 13.7 kB
text/typescript
// Copyright Inrupt Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import type { Literal, Quad_Object, NamedNode } from "@rdfjs/types";
import type { Time } from "../datatypes";
import type {
Thing,
ThingLocal,
ThingPersisted,
Url,
UrlString,
} from "../interfaces";
import type { AddOfType } from "./add";
import {
addBoolean,
addDate,
addDatetime,
addTime,
addDecimal,
addInteger,
addIri,
addLiteral,
addNamedNode,
addStringNoLocale,
addStringWithLocale,
addTerm,
addUrl,
} from "./add";
import type { RemoveOfType } from "./remove";
import {
removeAll,
removeBoolean,
removeDate,
removeDatetime,
removeTime,
removeDecimal,
removeInteger,
removeIri,
removeLiteral,
removeNamedNode,
removeStringNoLocale,
removeStringWithLocale,
removeUrl,
} from "./remove";
import type { SetOfType } from "./set";
import {
setBoolean,
setDate,
setDatetime,
setTime,
setDecimal,
setInteger,
setIri,
setLiteral,
setNamedNode,
setStringNoLocale,
setStringWithLocale,
setTerm,
setUrl,
} from "./set";
import type {
CreateThingLocalOptions,
CreateThingOptions,
CreateThingPersistedOptions,
} from "./thing";
import { createThing, isThing } from "./thing";
type Adder<Type, T extends Thing> = (
property: Parameters<AddOfType<Type>>[1],
value: Parameters<AddOfType<Type>>[2],
) => ThingBuilder<T>;
type Setter<Type, T extends Thing> = (
property: Parameters<SetOfType<Type>>[1],
value: Parameters<SetOfType<Type>>[2],
) => ThingBuilder<T>;
type Remover<Type, T extends Thing> = (
property: Parameters<RemoveOfType<Type>>[1],
value: Parameters<RemoveOfType<Type>>[2],
) => ThingBuilder<T>;
// Unfortunately this interface has too many properties for TypeScript to infer,
// hence the duplication between the interface and the implementation method names.
/**
* A Fluent interface to build a [[Thing]].
*
* Add, replace or remove property values using consecutive calls to `.add*()`,
* `.set*()` and `.remove*()`, then finally generate a [[Thing]] with the given
* properties using `.build()`.
* @since 1.9.0
*/
export type ThingBuilder<T extends Thing> = {
build: () => T;
addUrl: Adder<Url | UrlString | Thing, T>;
addIri: Adder<Url | UrlString | Thing, T>;
addBoolean: Adder<boolean, T>;
addDatetime: Adder<Date, T>;
addDate: Adder<Date, T>;
addTime: Adder<Time, T>;
addDecimal: Adder<number, T>;
addInteger: Adder<number, T>;
addStringNoLocale: Adder<string, T>;
addStringEnglish: Adder<string, T>;
addStringWithLocale: (
property: Parameters<typeof addStringWithLocale>[1],
value: Parameters<typeof addStringWithLocale>[2],
locale: Parameters<typeof addStringWithLocale>[3],
) => ThingBuilder<T>;
addNamedNode: Adder<NamedNode, T>;
addLiteral: Adder<Literal, T>;
addTerm: Adder<Quad_Object, T>;
setUrl: Setter<Url | UrlString | Thing, T>;
setIri: Setter<Url | UrlString | Thing, T>;
setBoolean: Setter<boolean, T>;
setDatetime: Setter<Date, T>;
setDate: Setter<Date, T>;
setTime: Setter<Time, T>;
setDecimal: Setter<number, T>;
setInteger: Setter<number, T>;
setStringNoLocale: Setter<string, T>;
setStringEnglish: Setter<string, T>;
setStringWithLocale: (
property: Parameters<typeof setStringWithLocale>[1],
value: Parameters<typeof setStringWithLocale>[2],
locale: Parameters<typeof setStringWithLocale>[3],
) => ThingBuilder<T>;
setNamedNode: Setter<NamedNode, T>;
setLiteral: Setter<Literal, T>;
setTerm: Setter<Quad_Object, T>;
removeAll: (property: Parameters<typeof removeLiteral>[1]) => ThingBuilder<T>;
removeUrl: Remover<Url | UrlString | Thing, T>;
removeIri: Remover<Url | UrlString | Thing, T>;
removeBoolean: Remover<boolean, T>;
removeDatetime: Remover<Date, T>;
removeDate: Remover<Date, T>;
removeTime: Remover<Time, T>;
removeDecimal: Remover<number, T>;
removeInteger: Remover<number, T>;
removeStringNoLocale: Remover<string, T>;
removeStringEnglish: Remover<string, T>;
removeStringWithLocale: (
property: Parameters<typeof removeStringWithLocale>[1],
value: Parameters<typeof removeStringWithLocale>[2],
locale: Parameters<typeof removeStringWithLocale>[3],
) => ThingBuilder<T>;
removeNamedNode: Remover<NamedNode, T>;
removeLiteral: Remover<Literal, T>;
};
/**
* Modify a [[Thing]], setting multiple properties in a single expresssion.
*
* For example, you can initialise several properties of a given Thing as follows:
*
* const me = buildThing(createThing({ name: "profile-vincent" }))
* .addUrl(rdf.type, schema.Person)
* .addStringNoLocale(schema.givenName, "Vincent")
* .build();
*
* Take note of the final call to `.build()` to obtain the actual Thing.
*
* @param init A Thing to modify.
* @returns a [[ThingBuilder]], a Fluent API that allows you to set multiple properties in a single expression.
* @since 1.9.0
*/
export function buildThing(init: ThingLocal): ThingBuilder<ThingLocal>;
/**
* Modify a [[Thing]], setting multiple properties in a single expresssion.
*
* For example, you can initialise several properties of a given Thing as follows:
*
* const me = buildThing(createThing({ url: "https://example.pod/profile#vincent" }))
* .addUrl(rdf.type, schema.Person)
* .addStringNoLocale(schema.givenName, "Vincent")
* .build();
*
* Take note of the final call to `.build()` to obtain the actual Thing.
*
* @param init A Thing to modify.
* @returns a [[ThingBuilder]], a Fluent API that allows you to set multiple properties in a single expression.
* @since 1.9.0
*/
export function buildThing(init: ThingPersisted): ThingBuilder<ThingPersisted>;
/**
* Create a [[Thing]], setting multiple properties in a single expresssion.
*
* For example, you can create a new Thing and initialise several properties as follows:
*
* const me = buildThing({ name: "profile-vincent" })
* .addUrl(rdf.type, schema.Person)
* .addStringNoLocale(schema.givenName, "Vincent")
* .build();
*
* Take note of the final call to `.build()` to obtain the actual Thing.
*
* @param init Options used to initialise a new Thing.
* @returns a [[ThingBuilder]], a Fluent API that allows you to set multiple properties in a single expression.
* @since 1.9.0
*/
export function buildThing(
init: CreateThingLocalOptions,
): ThingBuilder<ThingLocal>;
/**
* Create a [[Thing]], setting multiple properties in a single expresssion.
*
* For example, you can create a new Thing and initialise several properties as follows:
*
* const me = buildThing({ url: "https://example.pod/profile#vincent" })
* .addUrl(rdf.type, schema.Person)
* .addStringNoLocale(schema.givenName, "Vincent")
* .build();
*
* Take note of the final call to `.build()` to obtain the actual Thing.
*
* @param init Optionally pass an existing [[Thing]] to modify the properties of. If left empty, `buildThing` will initialise a new Thing.
* @returns a [[ThingBuilder]], a Fluent API that allows you to set multiple properties in a single expression.
* @since 1.9.0
*/
export function buildThing(
init: CreateThingPersistedOptions,
): ThingBuilder<ThingPersisted>;
/**
* Create a [[Thing]], setting multiple properties in a single expresssion.
*
* For example, you can create a new Thing and initialise several properties as follows:
*
* const me = buildThing()
* .addUrl(rdf.type, schema.Person)
* .addStringNoLocale(schema.givenName, "Vincent")
* .build();
*
* Take note of the final call to `.build()` to obtain the actual Thing.
*
* @returns a [[ThingBuilder]], a Fluent API that allows you to set multiple properties in a single expression.
* @since 1.9.0
*/
export function buildThing(): ThingBuilder<ThingLocal>;
/**
* Create or modify a [[Thing]], setting multiple properties in a single expresssion.
*
* For example, you can create a new Thing and initialise several properties as follows:
*
* const me = buildThing()
* .addUrl(rdf.type, schema.Person)
* .addStringNoLocale(schema.givenName, "Vincent")
* .build();
*
* Take note of the final call to `.build()` to obtain the actual Thing.
*
* @param init Optionally pass an existing [[Thing]] to modify the properties of. If left empty, `buildThing` will initialise a new Thing.
* @returns a [[ThingBuilder]], a Fluent API that allows you to set multiple properties in a single expression.
* @since 1.9.0
*/
export function buildThing(
init: Thing | CreateThingOptions = createThing(),
): ThingBuilder<Thing> {
let thing = isThing(init) ? init : createThing(init);
function getAdder<Type>(adder: AddOfType<Type>) {
return (
property: Parameters<typeof adder>[1],
value: Parameters<typeof adder>[2],
) => {
thing = adder(thing, property, value);
return builder;
};
}
function getSetter<Type>(setter: SetOfType<Type>) {
return (
property: Parameters<typeof setter>[1],
value: Parameters<typeof setter>[2],
) => {
thing = setter(thing, property, value);
return builder;
};
}
function getRemover<Type>(remover: RemoveOfType<Type>) {
return (
property: Parameters<typeof remover>[1],
value: Parameters<typeof remover>[2],
) => {
thing = remover(thing, property, value);
return builder;
};
}
const builder: ThingBuilder<Thing> = {
build: () => thing,
addUrl: getAdder(addUrl),
addIri: getAdder(addIri),
addBoolean: getAdder(addBoolean),
addDatetime: getAdder(addDatetime),
addDate: getAdder(addDate),
addTime: getAdder(addTime),
addDecimal: getAdder(addDecimal),
addInteger: getAdder(addInteger),
addStringNoLocale: getAdder(addStringNoLocale),
addStringEnglish: (
property: Parameters<typeof addStringWithLocale>[1],
value: Parameters<typeof addStringWithLocale>[2],
) => {
thing = addStringWithLocale(thing, property, value, "en");
return builder;
},
addStringWithLocale: (
property: Parameters<typeof addStringWithLocale>[1],
value: Parameters<typeof addStringWithLocale>[2],
locale: Parameters<typeof addStringWithLocale>[3],
) => {
thing = addStringWithLocale(thing, property, value, locale);
return builder;
},
addNamedNode: getAdder(addNamedNode),
addLiteral: getAdder(addLiteral),
addTerm: getAdder(addTerm),
setUrl: getSetter(setUrl),
setIri: getSetter(setIri),
setBoolean: getSetter(setBoolean),
setDatetime: getSetter(setDatetime),
setDate: getSetter(setDate),
setTime: getSetter(setTime),
setDecimal: getSetter(setDecimal),
setInteger: getSetter(setInteger),
setStringNoLocale: getSetter(setStringNoLocale),
setStringEnglish: (
property: Parameters<typeof setStringWithLocale>[1],
value: Parameters<typeof setStringWithLocale>[2],
) => {
thing = setStringWithLocale(thing, property, value, "en");
return builder;
},
setStringWithLocale: (
property: Parameters<typeof setStringWithLocale>[1],
value: Parameters<typeof setStringWithLocale>[2],
locale: Parameters<typeof setStringWithLocale>[3],
) => {
thing = setStringWithLocale(thing, property, value, locale);
return builder;
},
setNamedNode: getSetter(setNamedNode),
setLiteral: getSetter(setLiteral),
setTerm: getSetter(setTerm),
removeAll: (property: Parameters<typeof removeAll>[1]) => {
thing = removeAll(thing, property);
return builder;
},
removeUrl: getRemover(removeUrl),
removeIri: getRemover(removeIri),
removeBoolean: getRemover(removeBoolean),
removeDatetime: getRemover(removeDatetime),
removeDate: getRemover(removeDate),
removeTime: getRemover(removeTime),
removeDecimal: getRemover(removeDecimal),
removeInteger: getRemover(removeInteger),
removeStringNoLocale: getRemover(removeStringNoLocale),
removeStringEnglish: (
property: Parameters<typeof removeStringWithLocale>[1],
value: Parameters<typeof removeStringWithLocale>[2],
) => buildThing(removeStringWithLocale(thing, property, value, "en")),
removeStringWithLocale: (
property: Parameters<typeof removeStringWithLocale>[1],
value: Parameters<typeof removeStringWithLocale>[2],
locale: Parameters<typeof removeStringWithLocale>[3],
) => buildThing(removeStringWithLocale(thing, property, value, locale)),
removeNamedNode: getRemover(removeNamedNode),
removeLiteral: getRemover(removeLiteral),
};
return builder;
}