Skip to content

Commit

Permalink
引入数据结构校验 (#30)
Browse files Browse the repository at this point in the history
* Keep auto scale button.

* Clean code.

* Intro Predicate.

* Clean code.
  • Loading branch information
zhongl authored Sep 21, 2024
1 parent e0c0768 commit ede31c0
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 12 deletions.
29 changes: 17 additions & 12 deletions src/garmin/ActivityId.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,42 @@ import scala.scalajs.js
import org.scalajs.dom.HTMLElement

import core.*
import core.Gauge.{BeatPerMinute as bpm, StepPerMinute as spm}
import core.service.Fetch

opaque type ActivityId = String

object ActivityId:
def apply(s: String): ActivityId = s

given inject(using
given (using
Predicate[(bpm, spm, Intensity), js.Dynamic],
Fetch[Get, js.Dynamic],
Inject[History[js.Dynamic]]
): Inject[List[ActivityId]] with
extension (ids: List[ActivityId])
def inject(e: HTMLElement): Unit =
for case Some(h) :: t <- Future.sequence(ids.map(splits)) do (h -> t.map(_.toList).flatten).inject(e)
for case Some(h) :: t <- Future.sequence(ids.map(splits[(bpm, spm, Intensity)])) do
(h -> t.map(_.toList).flatten).inject(e)

private def splits(id: ActivityId)(using Fetch[Get, js.Dynamic]) =
private def splits[T](id: ActivityId)(using Fetch[Get, js.Dynamic], Predicate[T, js.Dynamic]) =
inline def url = s"https://connect.garmin.cn/activity-service/activity/$id/splits"
inline def referrer = s"https://connect.garmin.cn/modern/activity/$id"

for d <- Get(url, referrer).request yield d.asInstanceOf[Splits].lapDTOs.toList.filter(_.isActive) match
for d <- Get(url, referrer).request
yield d.asInstanceOf[Splits].lapDTOs.toList.filter(Predicate[T, js.Dynamic]) match
case h :: t => Some(h -> t)
case _ => None

private trait Lap extends js.Object:
def intensityType: js.UndefOr[String]

extension (l: Lap)
private inline def isActive =
l.intensityType == "ACTIVE" || l.intensityType == "INTERVAL" || l.intensityType == js.undefined

private trait Splits extends js.Object:
def lapDTOs: js.Array[Lap & js.Dynamic]
def lapDTOs: js.Array[js.Dynamic]

sealed trait Intensity
object Intensity:
private trait Lap extends js.Object:
def intensityType: js.UndefOr[String]

given Predicate[Intensity, js.Dynamic] = Predicate: a =>
val t = a.asInstanceOf[Lap].intensityType
t == "ACTIVE" || t == "INTERVAL" || t == js.undefined
end ActivityId
18 changes: 18 additions & 0 deletions src/garmin/Predicate.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package garmin

import core.Read

opaque type Predicate[T, A] = A => Boolean
object Predicate:
def apply[T, A](f: A => Boolean): Predicate[T, A] = f
def apply[T, A](a: A)(using p: Predicate[T, A]): Boolean = p(a)

given [A]: Predicate[EmptyTuple, A] = _ => true
given [H, T <: Tuple, A](using h: Predicate[H, A], t: Predicate[T, A]): Predicate[H *: T, A] = a => h(a) && t(a)

given [T, A, B](using Read[T, A, B]): Predicate[T, A] = a =>
try
Read[T, A, B](a)
true
catch case _ => false
end Predicate

0 comments on commit ede31c0

Please sign in to comment.