packages feed

telescope-0.3.0: src/Telescope/Asdf/Core.hs

{-# LANGUAGE OverloadedLists #-}

module Telescope.Asdf.Core where

import Data.String (fromString)
import Data.Text (Text)
import Data.Version (showVersion)
import Effectful
import Effectful.Error.Static
import GHC.Generics (Generic)
import Paths_telescope (version)
import Telescope.Asdf.Class
import Telescope.Asdf.Error (AsdfError (..))
import Telescope.Asdf.Node
import Telescope.Data.Parser (expected)


{- | VOUnit: https://www.ivoa.net/documents/VOUnits/20231215/REC-VOUnits-1.1.html
 -
Unrecognised units should be accepted by parsers, as long as they are parsed giving preference to the syntaxes and prefixes described here.
-}
data Unit
  = Count
  | Pixel
  | Degrees
  | Nanometers
  | Meters
  | Kilometers
  | Arcseconds
  | Unit Text
  deriving (Eq)


instance ToAsdf Unit where
  schema _ = "!unit/unit-1.0.0"
  toValue = \case
    Count -> "count"
    Pixel -> "pixel"
    Degrees -> "deg"
    Nanometers -> "nm"
    Kilometers -> "km"
    Arcseconds -> "arcsec"
    Meters -> "m"
    (Unit t) -> String t
instance FromAsdf Unit where
  parseValue = \case
    String "count" -> pure Count
    String "deg" -> pure Degrees
    String "pixel" -> pure Pixel
    String "pix" -> pure Pixel
    String "nm" -> pure Nanometers
    String "km" -> pure Kilometers
    String "m" -> pure Meters
    String "arcsec" -> pure Arcseconds
    String t -> pure $ Unit t
    val -> expected "String" val


-- | Tag a value with a 'Unit'
data Quantity = Quantity
  { unit :: Unit
  , value :: Value
  }
  deriving (Generic)


instance ToAsdf Quantity where
  schema _ = "!unit/quantity-1.1.0"
instance FromAsdf Quantity


--
-- -- TEST: this is probably index dependent
-- data BoundingBox = BoundingBox Double Double
--
--
-- instance ToAsdf BoundingBox where
--   toValue (BoundingBox a b) =
--     Array
--       [ toNode $ Quantity Pixel (toValue a)
--       , toNode $ Quantity Pixel (toValue b)
--       ]
--
--
-- instance FromAsdf BoundingBox where
--   parseValue = \case
--     Array [n1, n2] -> do
--       BoundingBox <$> parseNode n1 <*> parseNode n2
--     node -> expected "BoundingBox" node

-- | Required Software node at the top-level
data Software = Software
  { author :: Maybe Text
  , homepage :: Maybe Text
  , name :: Text
  , version :: Text
  }
  deriving (Show, Eq, Generic, FromAsdf)


instance ToAsdf Software where
  schema _ = "!core/software-1.0.0"


-- | Root ASDF node
data Asdf = Asdf
  { history :: History
  , library :: Software
  , tree :: Tree
  }


instance ToAsdf Asdf where
  schema _ = "!core/asdf-1.1.0"
  toValue a =
    let Tree tree = a.tree
     in -- these two required fields are first, then merge keys from the tree
        Object $
          [ ("asdf_library", toNode a.library)
          , ("history", toNode a.history)
          ]
            <> tree
instance FromAsdf Asdf where
  parseValue = \case
    Object o -> do
      library <- o .: "asdf_library"
      history <- o .: "history"
      let tree = Tree $ filter (not . isLibraryField) o
      pure $ Asdf{history, library, tree}
    val -> expected "Asdf" val
   where
    isLibraryField ("asdf_library", _) = True
    isLibraryField ("history", _) = True
    isLibraryField _ = False


-- | Convert any ToAsdf into a raw Asdf document
toAsdfDoc :: (ToAsdf a, Error AsdfError :> es) => a -> Eff es Asdf
toAsdfDoc a =
  case toValue a of
    Object o -> do
      let history = History []
      let library = telescopeLibrary
      pure $ Asdf{history, library, tree = Tree o}
    value -> throwError $ EncodeError $ "Expected Top-level Tree Object, but got: " ++ show value
 where
  telescopeLibrary :: Software
  telescopeLibrary =
    Software
      { author = Just "DKIST Data Center"
      , homepage = Just "https://github.com/dkistdc/telescope.hs"
      , name = "telescope.hs"
      , version = fromString $ showVersion version
      }


data History = History
  { extensions :: [ExtensionMetadata]
  }
  deriving (Show, Generic, FromAsdf, ToAsdf)


data ExtensionMetadata = ExtensionMetadata
  { extension_class :: Text
  , software :: Software
  }
  deriving (Show, Generic, FromAsdf)
instance ToAsdf ExtensionMetadata where
  schema _ = "!core/extension_metadata-1.0.0"