namespace PureGym open System.Threading.Tasks open Fastenshtein /// Identifies a gym, possibly non-uniquely and possibly ambiguously. [] type GymSelector = /// The ID of this gym, according to the PureGym internal mapping. | Id of int /// The user-specified name of this gym. | Name of string /// The home gym of the authenticated user. | Home /// Methods for manipulating GymSelector. [] module GymSelector = /// Get the canonical PureGym ID for this user-specified gym. let canonicalId (client : IPureGymApi) (gym : GymSelector) : int Task = match gym with | GymSelector.Home -> async { let! ct = Async.CancellationToken let! self = Async.AwaitTask (client.GetMember ct) return self.HomeGymId } |> Async.StartAsTask | GymSelector.Id i -> Task.FromResult<_> i | GymSelector.Name name -> async { let! ct = Async.CancellationToken let! allGyms = Async.AwaitTask (client.GetGyms ct) if allGyms.IsEmpty then return failwith "PureGym API returned no gyms!" else let distance = Levenshtein (name.ToLowerInvariant ()) let bestDistance, bestGym = allGyms |> Seq.map (fun gym -> distance.DistanceFrom (gym.Name.ToLowerInvariant ()), gym) |> Seq.sortBy fst |> Seq.head if bestDistance <> 0 then System.Console.Error.WriteLine $"Autocorrected gym from %s{name} to %s{bestGym.Name}" return bestGym.Id } |> Async.StartAsTask