Skip to content

Commit 741579a

Browse files
committed
WIP
1 parent dbad929 commit 741579a

File tree

8 files changed

+127
-95
lines changed

8 files changed

+127
-95
lines changed

src/hexdocs.gleam

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import grille_pain/lustre/toast
1313
import hexdocs/data/model.{type Model, Model}
1414
import hexdocs/data/model/autocomplete
1515
import hexdocs/data/model/route
16+
import hexdocs/data/model/version
1617
import hexdocs/data/msg.{type Msg}
1718
import hexdocs/effects
1819
import hexdocs/loss.{type Loss}
@@ -168,7 +169,7 @@ fn api_returned_initial_latest_packages(
168169
versions: Loss(List(hexpm.Package)),
169170
) -> #(Model, Effect(a)) {
170171
case versions {
171-
Error(_) -> #(model, toast.error("Server error. Retry later."))
172+
Error(_) -> model.replace_search_packages(model)
172173
Ok(versions) ->
173174
model.add_packages_versions(model, versions)
174175
|> model.replace_search_packages
@@ -257,14 +258,7 @@ fn user_edited_packages_filter_version(model: Model, content: String) {
257258
}
258259

259260
fn user_submitted_search_input(model: Model) {
260-
#(model, {
261-
route.push({
262-
route.Search(
263-
q: model.search_input,
264-
packages: model.search_packages_filters,
265-
)
266-
})
267-
})
261+
model.push_search_packages(model)
268262
}
269263

270264
fn user_focused_search(model: Model) {
@@ -301,17 +295,12 @@ fn user_clicked_autocomplete_package(model: Model, package: String) {
301295

302296
fn user_deleted_packages_filter(
303297
model: Model,
304-
filter: #(String, String),
298+
filter: version.Package,
305299
) -> #(Model, Effect(msg)) {
306300
let search_packages_filters =
307301
list.filter(model.search_packages_filters, fn(f) { f != filter })
308-
let model = Model(..model, search_packages_filters:)
309-
#(model, {
310-
route.push(route.Search(
311-
q: model.search_input,
312-
packages: model.search_packages_filters,
313-
))
314-
})
302+
Model(..model, search_packages_filters:)
303+
|> model.push_search_packages
315304
}
316305

317306
fn user_clicked_go_back(model: Model) -> #(Model, Effect(msg)) {
@@ -327,21 +316,19 @@ fn user_submitted_packages_filter(model: Model) {
327316
|> result.try(list.find(_, fn(r) { r.version == version }))
328317
|> result.map(fn(_) {
329318
let search_packages_filters =
330-
[#(package, version)]
319+
[version.Package(name: package, version: version, resolved: True)]
331320
|> list.append(model.search_packages_filters, _)
332321
|> list.unique
333-
let model =
334-
Model(
335-
..model,
336-
search_packages_filters:,
337-
search_packages_filter_input: "",
338-
search_packages_filter_input_displayed: "",
339-
search_packages_filter_version_input: "",
340-
search_packages_filter_version_input_displayed: "",
341-
)
342-
route.Search(q: model.search_input, packages: model.search_packages_filters)
343-
|> route.push
344-
|> pair.new(model, _)
322+
323+
Model(
324+
..model,
325+
search_packages_filters:,
326+
search_packages_filter_input: "",
327+
search_packages_filter_input_displayed: "",
328+
search_packages_filter_version_input: "",
329+
search_packages_filter_version_input_displayed: "",
330+
)
331+
|> model.push_search_packages
345332
})
346333
|> result.lazy_unwrap(fn() { #(model, effect.none()) })
347334
}

src/hexdocs/data/model.gleam

Lines changed: 73 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ pub type Model {
7171
search_packages_filter_version_input: String,
7272
search_packages_filter_version_input_displayed: String,
7373
/// Store the current set packages filters.
74-
search_packages_filters: List(#(String, String)),
74+
search_packages_filters: List(version.Package),
7575
)
7676
}
7777

@@ -226,47 +226,60 @@ pub fn update_route(model: Model, route: uri.Uri) {
226226
case route {
227227
route.Home | route.NotFound -> #(model, effect.none())
228228
route.Search(q:, packages:) -> {
229-
case string.is_empty(q) {
230-
True -> #(set_search_results(model, #(-1, [])), effect.none())
231-
False -> {
232-
let latest = list.filter(packages, fn(p) { p.1 == "latest" })
233-
Model(..model, search_input: q, search_packages_filters: packages)
234-
|> pair.new({
235-
case latest {
236-
[] -> effects.typesense_search(q, packages)
237-
latest -> {
238-
latest
239-
|> list.map(pair.first)
240-
|> effects.initial_latest_packages
241-
}
229+
let packages = {
230+
use #(package, version) <- list.map(packages)
231+
use <- bool.guard(
232+
when: version != "latest",
233+
return: version.Package(package, version, resolved: True),
234+
)
235+
case dict.get(model.packages_versions, package) {
236+
Error(_) -> version.Package(package, version, resolved: False)
237+
Ok(versions) -> {
238+
case versions.releases {
239+
[] -> version.Package(package, version, resolved: False)
240+
[release, ..] ->
241+
version.Package(package, release.version, resolved: True)
242242
}
243-
})
243+
}
244244
}
245245
}
246+
247+
let latest = list.filter(packages, fn(p) { p.version == "latest" })
248+
249+
let model =
250+
Model(..model, search_input: q, search_packages_filters: packages)
251+
252+
case latest {
253+
[] ->
254+
case string.is_empty(q) {
255+
True ->
256+
pair.new(set_search_results(model, #(-1, [])), effect.none())
257+
False -> pair.new(model, effects.typesense_search(q, packages))
258+
}
259+
260+
latest ->
261+
pair.new(
262+
model,
263+
latest
264+
|> list.map(fn(p) { p.name })
265+
|> effects.initial_latest_packages,
266+
)
267+
}
246268
}
247269
}
248270
}
249271

250272
pub fn replace_search_packages(model: Model) {
251-
let model =
252-
Model(..model, search_packages_filters: {
253-
use #(package, version) <- list.map(model.search_packages_filters)
254-
use <- bool.guard(when: version != "latest", return: #(package, version))
255-
case dict.get(model.packages_versions, package) {
256-
Error(_) -> #(package, version)
257-
Ok(versions) -> {
258-
case versions.releases {
259-
[] -> #(package, version)
260-
[release, ..] -> #(package, release.version)
261-
}
262-
}
263-
}
264-
})
265-
route.Search(q: model.search_input, packages: model.search_packages_filters)
273+
model
274+
|> model_to_search
266275
|> route.replace
267276
|> pair.new(model, _)
268277
}
269278

279+
pub fn push_search_packages(model: Model) {
280+
#(model, route.push(model_to_search(model)))
281+
}
282+
270283
pub fn select_autocomplete_option(model: Model, package: String) {
271284
case model.autocomplete, model.route {
272285
None, _ -> model
@@ -326,9 +339,9 @@ pub fn compute_filters_input(model: Model) -> #(Model, Effect(Msg)) {
326339
let search_input = keep_search_input_non_packages_text(model)
327340
case list.is_empty(packages_to_fetch) {
328341
True -> {
329-
#(Model(..model, search_packages_filters: filters, search_input:), {
330-
route.push(route.Search(q: search_input, packages: filters))
331-
})
342+
let model =
343+
Model(..model, search_packages_filters: filters, search_input:)
344+
push_search_packages(model)
332345
}
333346
False -> #(model, {
334347
use dispatch <- effect.from()
@@ -350,6 +363,15 @@ pub fn compute_filters_input(model: Model) -> #(Model, Effect(Msg)) {
350363
}
351364
}
352365

366+
fn model_to_search(model: Model) {
367+
route.Search(
368+
q: model.search_input,
369+
packages: list.map(model.search_packages_filters, fn(p) {
370+
pair.new(p.name, p.version)
371+
}),
372+
)
373+
}
374+
353375
/// Typical home search input will be something like `foo #phoenix #ecto:1.0.0`.
354376
/// `extract_packages_filters_or_fetches` will extract the `#ecto:1.0.0` part
355377
/// as a filter, and will return a side-effect to fetch `phoenix`, in order to
@@ -358,14 +380,21 @@ pub fn compute_filters_input(model: Model) -> #(Model, Effect(Msg)) {
358380
/// correct model and will reroute to the search page.
359381
fn extract_packages_filters_or_fetches(model: Model) {
360382
let segments = string.split(model.home_input_displayed, on: " ")
361-
let search_packages_filters = list.filter_map(segments, version.match_package)
383+
let search_packages_filters =
384+
list.filter_map(segments, version.input_match_package)
362385
list.fold(search_packages_filters, #([], []), fn(acc, val) {
363386
let #(filters, packages_to_fetch) = acc
364387
let #(package, version) = val
365388
let is_existing_package = list.contains(model.packages, package)
366389
use <- bool.guard(when: !is_existing_package, return: acc)
367390
case version {
368-
Some(version) -> #([#(package, version), ..filters], packages_to_fetch)
391+
Some(version) -> #(
392+
[
393+
version.Package(name: package, version: version, resolved: True),
394+
..filters
395+
],
396+
packages_to_fetch,
397+
)
369398
None -> {
370399
case dict.get(model.packages_versions, package) {
371400
Error(_) -> #(filters, [package, ..packages_to_fetch])
@@ -374,8 +403,14 @@ fn extract_packages_filters_or_fetches(model: Model) {
374403
// That case is impossible, returning the neutral element.
375404
Error(_) -> #(filters, packages_to_fetch)
376405
Ok(release) -> {
377-
let version = release.version
378-
#([#(package, version), ..filters], packages_to_fetch)
406+
let ver = release.version
407+
#(
408+
[
409+
version.Package(name: package, version: ver, resolved: True),
410+
..filters
411+
],
412+
packages_to_fetch,
413+
)
379414
}
380415
}
381416
}
@@ -391,7 +426,7 @@ fn extract_packages_filters_or_fetches(model: Model) {
391426
fn keep_search_input_non_packages_text(model: Model) -> String {
392427
let segments = string.split(model.home_input_displayed, on: " ")
393428
segments
394-
|> list.filter(fn(s) { version.match_package(s) |> result.is_error })
429+
|> list.filter(fn(s) { version.input_match_package(s) |> result.is_error })
395430
|> string.join(with: " ")
396431
}
397432

src/hexdocs/data/model/route.gleam

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import gleam/option.{None, Some}
44
import gleam/result
55
import gleam/string
66
import gleam/uri.{type Uri}
7-
import hexdocs/data/model/version
87
import modem
98

109
pub type Route {
@@ -57,7 +56,7 @@ fn create_query(
5756
packages: List(#(String, String)),
5857
) -> List(#(String, String)) {
5958
use <- bool.guard(when: list.is_empty(packages), return: query)
60-
let packages = list.map(packages, version.to_string)
59+
let packages = list.map(packages, fn(p) { p.0 <> ":" <> p.1 })
6160
let packages = string.join(packages, with: ",")
6261
list.append(query, [#("packages", packages)])
6362
}
@@ -75,10 +74,9 @@ fn search_from_uri(location: Uri) {
7574
|> result.unwrap("")
7675
|> string.split(on: ",")
7776
|> list.filter_map(fn(package) {
78-
case version.match_package(package) {
79-
Ok(#(package, Some(version))) -> Ok(#(package, version))
80-
Ok(_) -> Error(Nil)
81-
Error(Nil) -> Error(Nil)
77+
case string.split(package, ":") {
78+
[name, version] -> Ok(#(name, version))
79+
_ -> Error(Nil)
8280
}
8381
})
8482
})
Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
import gleam/option.{type Option, None, Some}
22
import gleam/regexp
33

4-
const version_regexp = "^#([a-zA-Z_0-9]+)(:((([0-9]+|\\.){1,5})|latest))?"
4+
const input_regexp = "^#(\\w+)(:([0-9]+\\.[\\w+.-]+))?"
55

6-
pub fn match_package(word: String) -> Result(#(String, Option(String)), Nil) {
6+
/// Represents a package filter with its name, version, and resolution status.
7+
pub type Package {
8+
Package(name: String, version: String, resolved: Bool)
9+
}
10+
11+
pub fn input_match_package(
12+
word: String,
13+
) -> Result(#(String, Option(String)), Nil) {
714
let regexp = version_search()
815
case regexp.scan(regexp, word) {
916
[regexp.Match(content: _, submatches:)] -> {
@@ -17,14 +24,13 @@ pub fn match_package(word: String) -> Result(#(String, Option(String)), Nil) {
1724
}
1825
}
1926

20-
pub fn to_string(package: #(String, String)) {
21-
let #(package, version) = package
22-
let package = "#" <> package
23-
package <> ":" <> version
27+
pub fn to_string(package: Package) {
28+
let package_name = "#" <> package.name
29+
package_name <> ":" <> package.version
2430
}
2531

2632
fn version_search() {
2733
let options = regexp.Options(case_insensitive: False, multi_line: False)
28-
let assert Ok(regexp) = regexp.compile(version_regexp, with: options)
34+
let assert Ok(regexp) = regexp.compile(input_regexp, with: options)
2935
regexp
3036
}

src/hexdocs/data/msg.gleam

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import gleam/dynamic.{type Dynamic}
22
import gleam/hexpm
33
import gleam/uri
4+
import hexdocs/data/model/version
45
import hexdocs/loss.{type Loss}
56

67
pub type Msg {
@@ -32,7 +33,7 @@ pub type Msg {
3233
UserSubmittedAutocomplete
3334

3435
// Search page messages.
35-
UserDeletedPackagesFilter(#(String, String))
36+
UserDeletedPackagesFilter(version.Package)
3637
UserEditedPackagesFilterInput(String)
3738
UserEditedPackagesFilterVersion(String)
3839
UserEditedSearchInput(search_input: String)

src/hexdocs/effects.gleam

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import gleam/http/response.{type Response}
44
import gleam/javascript/promise
55
import gleam/list
66
import gleam/result
7+
import hexdocs/data/model/version
78
import hexdocs/data/msg
89
import hexdocs/loss.{type Loss}
910
import hexdocs/services/hex
@@ -54,7 +55,7 @@ pub fn subscribe_blurred_search() {
5455
|> dispatch
5556
}
5657

57-
pub fn typesense_search(query: String, packages: List(#(String, String))) {
58+
pub fn typesense_search(query: String, packages: List(version.Package)) {
5859
use dispatch <- effect.from()
5960
use _ <- function.tap(Nil)
6061
use response <- promise.map(hexdocs.typesense_search(query, packages, 1))

0 commit comments

Comments
 (0)