Skip to content

Commit

Permalink
Make preprint DOI data available
Browse files Browse the repository at this point in the history
Refs #26
  • Loading branch information
thewilkybarkid committed Jul 2, 2024
1 parent 5888b0c commit d368eb4
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/data/requests.json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import { HttpClient, Terminal } from '@effect/platform'
import { NodeTerminal } from '@effect/platform-node'
import { Schema } from '@effect/schema'
import { Effect } from 'effect'
import * as Doi from '../lib/Doi.js'
import * as LanguageCode from '../lib/LanguageCode.js'
import { DomainIdSchema, FieldIdSchema, SubfieldIdSchema } from '../lib/OpenAlex.js'
import * as Temporal from '../lib/Temporal.js'

const Requests = Schema.Array(
Schema.Struct({
timestamp: Temporal.InstantFromStringSchema,
preprint: Doi.DoiSchema,
language: Schema.optional(LanguageCode.LanguageCodeSchema, { nullable: true }),
fields: Schema.Array(FieldIdSchema),
subfields: Schema.Array(SubfieldIdSchema),
Expand Down
2 changes: 2 additions & 0 deletions src/data/reviews.json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { HttpClient, Terminal } from '@effect/platform'
import { NodeTerminal } from '@effect/platform-node'
import { Schema } from '@effect/schema'
import { Config, Effect, Redacted } from 'effect'
import * as Doi from '../lib/Doi.js'
import * as Temporal from '../lib/Temporal.js'

const Reviews = Schema.Array(
Schema.Struct({
createdAt: Temporal.PlainDateFromStringSchema,
preprint: Doi.ParseDoiSchema,
}),
)

Expand Down
47 changes: 47 additions & 0 deletions src/lib/Doi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { ParseResult, Schema } from '@effect/schema'
import { Brand, Either, Option, type Predicate, String, flow } from 'effect'

export type Doi = Brand.Branded<string, 'Doi'>

const isDoi: Predicate.Refinement<unknown, Doi> = (u): u is Doi =>
typeof u === 'string' && /^10[.][0-9]{2,}(?:[.][0-9]+)*\/\S+$/.test(u) && !u.endsWith('/.') && !u.endsWith('/..')

export const Doi = Brand.refined<Doi>(isDoi, s => Brand.error(`Expected ${s} to be a DOI`))

const parse: (s: string) => Option.Option<Doi> = flow(String.trim, s => {
if (isDoi(s)) {
return Option.some(s)
}

if (s.startsWith('doi:')) {
return Option.liftPredicate(isDoi)(s.substring(4))
}

try {
const url = new URL(s)

if (!['http:', 'https:'].includes(url.protocol) || !['doi.org', 'dx.doi.org'].includes(url.hostname)) {
return Option.none()
}

return Option.liftPredicate(isDoi)(decodeURIComponent(url.pathname).substring(1))
} catch {
return Option.liftPredicate(isDoi)(s)
}
})

const LowercasedStringSchema = Schema.transform(Schema.String, Schema.String.pipe(Schema.lowercased()), {
decode: s => s.toLowerCase(),
encode: s => s,
})

export const DoiSchema = LowercasedStringSchema.pipe(Schema.fromBrand(Doi))

export const ParseDoiSchema: Schema.Schema<Doi, string> = Schema.transformOrFail(
LowercasedStringSchema,
Schema.typeSchema(DoiSchema),
{
decode: s => Either.fromOption(parse(s), () => new ParseResult.Type(DoiSchema.ast, s)),
encode: ParseResult.succeed,
},
)

0 comments on commit d368eb4

Please sign in to comment.