Skip to content

Instantly share code, notes, and snippets.

@denisshevchenko
Last active March 20, 2018 06:46
Show Gist options
  • Save denisshevchenko/e9412ff0b84fb3cb0946e099a95df6fa to your computer and use it in GitHub Desktop.
Save denisshevchenko/e9412ff0b84fb3cb0946e099a95df6fa to your computer and use it in GitHub Desktop.
{-
Вот это наш .yaml-конфиг:
---
API_URL: https://api.nightscout/v2
API_Secret: asdLKJHh0987ljkhLKJlkjhLKJ
-}
-- Это для того, чтобы сырые литералы "" могли автоматически превращаться, например, в Text.
-- Без них литералы станут типом String, а этот тип неэффективный и считается уже антипаттерном.
{-# LANGUAGE OverloadedStrings #-}
-- Из некоторых импортируемых модулей мы можем взять только то, что хотим...
import Data.Maybe ( maybe )
-- ... в то время как из других берём неявно всё.
import Data.Yaml
import Data.Text ( Text, unpack )
import Network.URI
-- Просто псевдоним для существующего типа URI.
-- Мы хотим, чтобы наш APIUrl был не просто тупым текстом,
-- а именно валидным URI, с возможностью манипулировать его частями.
type APIUrl = URI
-- Чтобы было понятно, что это не просто текст, а именно API Secret.
newtype APISecret = APISecret Text
deriving Show
-- Тип, отражающий всю нашу конфигурацию.
data ReceiverConfig = ReceiverConfig APIUrl APISecret
deriving Show
-- Экземпляр класса типов FromJSON (не удивляйтесь имени, этот код может парсить и JSON).
-- Экземпляр нужен для того, чтобы объяснить, как парсить конфиг, переложив сырой текст из
-- файла на наш тип ReceiverConfig.
instance FromJSON ReceiverConfig where
-- Значение values - это словарь, в котором уже лежат все извлечённые из .yaml-файла
-- значения. С помощью оператора (.:) мы извлекаем нужные нам значения, подразумевая,
-- что они точно должны быть в файле.
parseJSON (Object values) = do
url <- values .: "API_URL" -- Явно указываем имя ключа в файле.
secret <- values .: "API_Secret" -- И здесь тоже.
-- Формируем значение типа ReceiverConfig из уже полученных url и secret.
-- Слово return возвращает это значение в "парсинговый контекст", мы как будто
-- объясняем парсеру, мол, если достанешь нужные нам значения, просто сформируй
-- из них ReceiverConfig и держи у себя, пока не попросим.
return $ ReceiverConfig url secret
-- Существует ещё аппликативный стиль парсинга, но это в других примерах...
-- Когда парсер дойдёт до значений URL (см. строку 36)
instance FromJSON URI where
parseJSON (String rawText) =
-- Функция maybe - пример ФВП: она работает с другими функциями как с аргументами.
-- parseURI возвращает опциональное значение типа Maybe URI, то есть либо URI, либо ничего
-- (в том случае, если в конфиге вместо URL будет какой-то хлам).
-- И вот если она вернёт URI, то просто сохраняем его в парсере, пока не попросим.
-- Если же там хлам, тогда сообщаем о проблеме с помощью fail, мол, ну не смогла я!
maybe (fail "Cannot parse URL, please fix it.") -- Не удалось распарсить!
return -- Всё ок, URI уже здесь.
(parseURI . unpack $ rawText) -- С помощью unpack мы превращаем Text в String.
instance FromJSON APISecret where
parseJSON (String rawText) =
-- Мы знаем, что это будет просто текст, но мы создаём на его основе
-- значение типа APISecret, чтобы эта часть конфигурации была
-- самодокументируемой.
return $ APISecret rawText
-- Пытаемся декодировать .yaml-файл. Функция decodeFileEither
-- гарантирует нам, что исключений она не выкинет, а о возможных
-- ошибках конфига она сообщит через Left-значение (см. ниже).
getReceiverConfig :: IO (Either ParseException ReceiverConfig)
getReceiverConfig = decodeFileEither "/tmp/ns.yaml"
main :: IO ()
main = do
result <- getReceiverConfig
-- Получили результат, но пока не знаем, какой он, нужно проверить...
case result of
-- Left-значение говорит о том, что произошла беда, и внутри лежит её описание.
-- Мы окажемся здесь и в том случае, если YAML-структура битая, и в том случае,
-- если со значениями что-то не так (см. fail при парсинге URL).
Left problem -> print problem
-- Right-значение сообщает, что всё ок, и значение типа ReceiverConfig уже здесь.
Right config -> putStrLn "Great, ReceiverConfig is here!"
@chshersh
Copy link

Так есть же, в самом вверху, в многострочном комментарии. ;-)

Прошу прощения, не заметил!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment