Extract Cooklang types into Data.CookLang module
- Create src/Data/CookLang.hs with all recipe types (Recipe, Section, StepItem, Ingredient, Cookware, Timer, RecipeRef, Quantity, Duration, Metadata, emptyMetadata) - Data.CookLang has zero Roux imports — can be extracted to its own package - Roux.Types now just re-exports from Data.CookLang - Update all Roux.* modules and tests to import Data.CookLang directly - All 30 tests passing, hlint clean
This commit is contained in:
@@ -16,6 +16,7 @@ build-type: Simple
|
|||||||
|
|
||||||
library
|
library
|
||||||
exposed-modules:
|
exposed-modules:
|
||||||
|
Data.CookLang
|
||||||
Roux
|
Roux
|
||||||
Roux.Html
|
Roux.Html
|
||||||
Roux.NYTimes
|
Roux.NYTimes
|
||||||
|
|||||||
@@ -0,0 +1,239 @@
|
|||||||
|
{- | Data types for the Cooklang recipe markup language.
|
||||||
|
|
||||||
|
Models the structure of a parsed @.cook@ recipe file as specified at
|
||||||
|
<https://cooklang.org/docs/spec/>. This module has no dependencies on
|
||||||
|
Roux-specific modules and can be extracted to its own package.
|
||||||
|
|
||||||
|
Hierarchy:
|
||||||
|
|
||||||
|
@
|
||||||
|
Recipe
|
||||||
|
Metadata — YAML front matter (title, tags, servings, …)
|
||||||
|
NonEmpty Section — at least one section per recipe
|
||||||
|
|
||||||
|
Section
|
||||||
|
Maybe Text — optional section name ("Dough", "Filling")
|
||||||
|
NonEmpty SectionBodyItem — interleaved steps, comments, and notes
|
||||||
|
|
||||||
|
SectionBodyItem
|
||||||
|
SecStep Step — a cooking step (paragraph)
|
||||||
|
SecComment Text — full-line @--@ comment
|
||||||
|
SecNote Text — @>@ note / commentary
|
||||||
|
|
||||||
|
Step
|
||||||
|
[StepItem]
|
||||||
|
|
||||||
|
StepItem
|
||||||
|
StepText — plain text
|
||||||
|
StepIngredient — \@name{amount%unit}(preparation)
|
||||||
|
StepCookware — \#name{}
|
||||||
|
StepTimer — \~name{duration%unit}
|
||||||
|
StepRecipeRef — \@.\/path\/to\/recipe{amount}
|
||||||
|
StepEndComment — @-- end of line comment@
|
||||||
|
StepComment — @[- block comment -]@
|
||||||
|
StepBreak — \\ at end of line
|
||||||
|
@
|
||||||
|
-}
|
||||||
|
module Data.CookLang (
|
||||||
|
-- * Recipe
|
||||||
|
Recipe (..),
|
||||||
|
|
||||||
|
-- * Sections and body items
|
||||||
|
Section (..),
|
||||||
|
SectionBodyItem (..),
|
||||||
|
Step (..),
|
||||||
|
|
||||||
|
-- * Inline elements
|
||||||
|
StepItem (..),
|
||||||
|
Ingredient (..),
|
||||||
|
Cookware (..),
|
||||||
|
Timer (..),
|
||||||
|
RecipeRef (..),
|
||||||
|
|
||||||
|
-- * Quantities and durations
|
||||||
|
Quantity (..),
|
||||||
|
Duration (..),
|
||||||
|
|
||||||
|
-- * Metadata
|
||||||
|
Metadata (..),
|
||||||
|
emptyMetadata,
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.List.NonEmpty (NonEmpty)
|
||||||
|
import Data.Map.Strict (Map)
|
||||||
|
import qualified Data.Map.Strict as Map
|
||||||
|
import Data.Text (Text)
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------------------------
|
||||||
|
-- Top-level
|
||||||
|
-- ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | A fully parsed Cooklang recipe file.
|
||||||
|
data Recipe = Recipe
|
||||||
|
{ recipeMetadata :: !Metadata
|
||||||
|
, recipeSections :: !(NonEmpty Section)
|
||||||
|
}
|
||||||
|
deriving stock (Eq, Show)
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------------------------
|
||||||
|
-- Sections and body items
|
||||||
|
-- ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
{- | A named or unnamed section within a recipe. Each section contains
|
||||||
|
interleaved steps, full-line comments, and notes.
|
||||||
|
-}
|
||||||
|
data Section = Section
|
||||||
|
{ sectionName :: !(Maybe Text)
|
||||||
|
, sectionBody :: !(NonEmpty SectionBodyItem)
|
||||||
|
}
|
||||||
|
deriving stock (Eq, Show)
|
||||||
|
|
||||||
|
-- | An item that appears at the top-level of a section (between blank lines).
|
||||||
|
data SectionBodyItem
|
||||||
|
= SecStep !Step
|
||||||
|
| SecComment !Text
|
||||||
|
| SecNote !Text
|
||||||
|
deriving stock (Eq, Show)
|
||||||
|
|
||||||
|
{- | A single cooking step — one paragraph of text in the Cooklang file,
|
||||||
|
separated from other steps by a blank line.
|
||||||
|
-}
|
||||||
|
newtype Step = Step
|
||||||
|
{ unStep :: [StepItem]
|
||||||
|
}
|
||||||
|
deriving stock (Eq, Show)
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------------------------
|
||||||
|
-- Inline elements
|
||||||
|
-- ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | An element that appears within a step.
|
||||||
|
data StepItem
|
||||||
|
= StepText !Text
|
||||||
|
| StepIngredient !Ingredient
|
||||||
|
| StepCookware !Cookware
|
||||||
|
| StepTimer !Timer
|
||||||
|
| StepRecipeRef !RecipeRef
|
||||||
|
| StepEndComment !Text
|
||||||
|
| StepComment !Text
|
||||||
|
| StepBreak
|
||||||
|
deriving stock (Eq, Show)
|
||||||
|
|
||||||
|
{- | An ingredient reference.
|
||||||
|
|
||||||
|
@\@salt\@ground black pepper{}\@potato{2}\@bacon strips{1%kg}@
|
||||||
|
-}
|
||||||
|
data Ingredient = Ingredient
|
||||||
|
{ ingName :: !Text
|
||||||
|
, ingQuantity :: !(Maybe Quantity)
|
||||||
|
, ingPreparation :: !(Maybe Text)
|
||||||
|
}
|
||||||
|
deriving stock (Eq, Show)
|
||||||
|
|
||||||
|
{- | A piece of cookware.
|
||||||
|
|
||||||
|
@\#pot\#potato masher{}@
|
||||||
|
-}
|
||||||
|
newtype Cookware = Cookware
|
||||||
|
{ cwName :: Text
|
||||||
|
}
|
||||||
|
deriving stock (Eq, Show)
|
||||||
|
|
||||||
|
{- | A timer.
|
||||||
|
|
||||||
|
@\{25%minutes\}\~eggs{3%minutes}@
|
||||||
|
-}
|
||||||
|
data Timer = Timer
|
||||||
|
{ timerName :: !(Maybe Text)
|
||||||
|
, timerDuration :: !Duration
|
||||||
|
}
|
||||||
|
deriving stock (Eq, Show)
|
||||||
|
|
||||||
|
{- | A reference to another recipe, signalled by a path starting with @.\/@.
|
||||||
|
|
||||||
|
@\@.\/sauces\/Hollandaise{150%g}@
|
||||||
|
-}
|
||||||
|
data RecipeRef = RecipeRef
|
||||||
|
{ refPath :: !FilePath
|
||||||
|
, refQuantity :: !(Maybe Quantity)
|
||||||
|
}
|
||||||
|
deriving stock (Eq, Show)
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------------------------
|
||||||
|
-- Quantities and durations
|
||||||
|
-- ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
{- | An ingredient quantity with optional unit and scaling lock.
|
||||||
|
|
||||||
|
@{2}\{1%kg}\{1\/2%tbsp}\{=1%tsp}@
|
||||||
|
-}
|
||||||
|
data Quantity = Quantity
|
||||||
|
{ quantityAmount :: !Rational
|
||||||
|
-- ^ e.g. @1\/2@, @3@, @3\/4@
|
||||||
|
, quantityUnit :: !(Maybe Text)
|
||||||
|
-- ^ e.g. @\"kg\"@, @\"tbsp\"@
|
||||||
|
, quantityFixed :: !Bool
|
||||||
|
-- ^ @{=1%tsp}@ — locked from scaling
|
||||||
|
}
|
||||||
|
deriving stock (Eq, Show)
|
||||||
|
|
||||||
|
{- | A timer duration — structurally similar to 'Quantity' but semantically
|
||||||
|
distinct. Timers are not scaled with serving size.
|
||||||
|
-}
|
||||||
|
data Duration = Duration
|
||||||
|
{ durationAmount :: !Rational
|
||||||
|
-- ^ e.g. @25@, @3@
|
||||||
|
, durationUnit :: !(Maybe Text)
|
||||||
|
-- ^ e.g. @\"minutes\"@, @\"hours\"@
|
||||||
|
}
|
||||||
|
deriving stock (Eq, Show)
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------------------------
|
||||||
|
-- Metadata
|
||||||
|
-- ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
{- | Metadata parsed from YAML front matter (the @---@ … @---@ block at the
|
||||||
|
top of a @.cook@ file).
|
||||||
|
|
||||||
|
Canonical metadata keys are lifted to dedicated fields; everything else
|
||||||
|
lives in 'metaCustom'.
|
||||||
|
-}
|
||||||
|
data Metadata = Metadata
|
||||||
|
{ metaTitle :: !(Maybe Text)
|
||||||
|
, metaServings :: !(Maybe (Int, Maybe Text))
|
||||||
|
, metaAuthor :: !(Maybe Text)
|
||||||
|
, metaSource :: !(Maybe Text)
|
||||||
|
, metaTotalTime :: !(Maybe Duration)
|
||||||
|
, metaPrepTime :: !(Maybe Duration)
|
||||||
|
, metaCookTime :: !(Maybe Duration)
|
||||||
|
, metaDifficulty :: !(Maybe Text)
|
||||||
|
, metaCuisine :: !(Maybe Text)
|
||||||
|
, metaCourse :: !(Maybe Text)
|
||||||
|
, metaDiet :: ![Text]
|
||||||
|
, metaTags :: ![Text]
|
||||||
|
, metaImage :: !(Maybe Text)
|
||||||
|
, metaDescription :: !(Maybe Text)
|
||||||
|
, metaCustom :: !(Map Text Text)
|
||||||
|
}
|
||||||
|
deriving stock (Eq, Show)
|
||||||
|
|
||||||
|
-- | Metadata with all fields set to their default / empty values.
|
||||||
|
emptyMetadata :: Metadata
|
||||||
|
emptyMetadata =
|
||||||
|
Metadata
|
||||||
|
{ metaTitle = Nothing
|
||||||
|
, metaServings = Nothing
|
||||||
|
, metaAuthor = Nothing
|
||||||
|
, metaSource = Nothing
|
||||||
|
, metaTotalTime = Nothing
|
||||||
|
, metaPrepTime = Nothing
|
||||||
|
, metaCookTime = Nothing
|
||||||
|
, metaDifficulty = Nothing
|
||||||
|
, metaCuisine = Nothing
|
||||||
|
, metaCourse = Nothing
|
||||||
|
, metaDiet = []
|
||||||
|
, metaTags = []
|
||||||
|
, metaImage = Nothing
|
||||||
|
, metaDescription = Nothing
|
||||||
|
, metaCustom = Map.empty
|
||||||
|
}
|
||||||
+1
-1
@@ -5,4 +5,4 @@ module Roux (
|
|||||||
) where
|
) where
|
||||||
|
|
||||||
import Roux.Server (app)
|
import Roux.Server (app)
|
||||||
import Roux.Types as X
|
import Data.CookLang as X
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ import Text.Blaze.Html5 as H
|
|||||||
import Text.Blaze.Html5.Attributes as A
|
import Text.Blaze.Html5.Attributes as A
|
||||||
|
|
||||||
import qualified Roux.RecipeIndex as Idx
|
import qualified Roux.RecipeIndex as Idx
|
||||||
import Roux.Types
|
import Data.CookLang
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
-- Sort mode for the index page
|
-- Sort mode for the index page
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ import qualified Data.Text as T
|
|||||||
import qualified Text.Parsec as P
|
import qualified Text.Parsec as P
|
||||||
import Text.Parsec.Text (Parser)
|
import Text.Parsec.Text (Parser)
|
||||||
|
|
||||||
import Roux.Types
|
import Data.CookLang
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
-- Internal raw-block types (parser only)
|
-- Internal raw-block types (parser only)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import System.Directory (listDirectory)
|
|||||||
import System.FilePath (takeBaseName, takeExtension, (</>))
|
import System.FilePath (takeBaseName, takeExtension, (</>))
|
||||||
|
|
||||||
import Roux.Parser (parseCookFile)
|
import Roux.Parser (parseCookFile)
|
||||||
import Roux.Types
|
import Data.CookLang
|
||||||
|
|
||||||
-- | Metadata about a single recipe discovered on disk.
|
-- | Metadata about a single recipe discovered on disk.
|
||||||
data RecipeInfo = RecipeInfo
|
data RecipeInfo = RecipeInfo
|
||||||
|
|||||||
+3
-234
@@ -1,236 +1,5 @@
|
|||||||
{- | Core types for Roux. Models the structure of a parsed Cooklang (.cook)
|
{- | Re-exports all types from 'Data.CookLang' for backward compatibility.
|
||||||
recipe file as specified at <https://cooklang.org/docs/spec/>.
|
|
||||||
|
|
||||||
Hierarchy:
|
|
||||||
|
|
||||||
@
|
|
||||||
Recipe
|
|
||||||
Metadata — YAML front matter (title, tags, servings, …)
|
|
||||||
NonEmpty Section — at least one section per recipe
|
|
||||||
|
|
||||||
Section
|
|
||||||
Maybe Text — optional section name ("Dough", "Filling")
|
|
||||||
NonEmpty SectionBodyItem — interleaved steps, comments, and notes
|
|
||||||
|
|
||||||
SectionBodyItem
|
|
||||||
SecStep Step — a cooking step (paragraph)
|
|
||||||
SecComment Text — full-line @--@ comment
|
|
||||||
SecNote Text — @>@ note / commentary
|
|
||||||
|
|
||||||
Step
|
|
||||||
[StepItem]
|
|
||||||
|
|
||||||
StepItem
|
|
||||||
StepText — plain text
|
|
||||||
StepIngredient — \@name{amount%unit}(preparation)
|
|
||||||
StepCookware — \#name{}
|
|
||||||
StepTimer — \~name{duration%unit}
|
|
||||||
StepRecipeRef — \@.\/path\/to\/recipe{amount}
|
|
||||||
StepEndComment — @-- end of line comment@
|
|
||||||
StepComment — @[- block comment -]@
|
|
||||||
StepBreak — \\ at end of line
|
|
||||||
@
|
|
||||||
-}
|
-}
|
||||||
module Roux.Types (
|
module Roux.Types (module X) where
|
||||||
-- * Recipe
|
|
||||||
Recipe (..),
|
|
||||||
|
|
||||||
-- * Sections and body items
|
import Data.CookLang as X
|
||||||
Section (..),
|
|
||||||
SectionBodyItem (..),
|
|
||||||
Step (..),
|
|
||||||
|
|
||||||
-- * Inline elements
|
|
||||||
StepItem (..),
|
|
||||||
Ingredient (..),
|
|
||||||
Cookware (..),
|
|
||||||
Timer (..),
|
|
||||||
RecipeRef (..),
|
|
||||||
|
|
||||||
-- * Quantities and durations
|
|
||||||
Quantity (..),
|
|
||||||
Duration (..),
|
|
||||||
|
|
||||||
-- * Metadata
|
|
||||||
Metadata (..),
|
|
||||||
emptyMetadata,
|
|
||||||
) where
|
|
||||||
|
|
||||||
import Data.List.NonEmpty (NonEmpty)
|
|
||||||
import Data.Map.Strict (Map)
|
|
||||||
import qualified Data.Map.Strict as Map
|
|
||||||
import Data.Text (Text)
|
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------------
|
|
||||||
-- Top-level
|
|
||||||
-- ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-- | A fully parsed Cooklang recipe file.
|
|
||||||
data Recipe = Recipe
|
|
||||||
{ recipeMetadata :: !Metadata
|
|
||||||
, recipeSections :: !(NonEmpty Section)
|
|
||||||
}
|
|
||||||
deriving stock (Eq, Show)
|
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------------
|
|
||||||
-- Sections and body items
|
|
||||||
-- ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
{- | A named or unnamed section within a recipe. Each section contains
|
|
||||||
interleaved steps, full-line comments, and notes.
|
|
||||||
-}
|
|
||||||
data Section = Section
|
|
||||||
{ sectionName :: !(Maybe Text)
|
|
||||||
, sectionBody :: !(NonEmpty SectionBodyItem)
|
|
||||||
}
|
|
||||||
deriving stock (Eq, Show)
|
|
||||||
|
|
||||||
-- | An item that appears at the top-level of a section (between blank lines).
|
|
||||||
data SectionBodyItem
|
|
||||||
= SecStep !Step
|
|
||||||
| SecComment !Text
|
|
||||||
| SecNote !Text
|
|
||||||
deriving stock (Eq, Show)
|
|
||||||
|
|
||||||
{- | A single cooking step — one paragraph of text in the Cooklang file,
|
|
||||||
separated from other steps by a blank line.
|
|
||||||
-}
|
|
||||||
newtype Step = Step
|
|
||||||
{ unStep :: [StepItem]
|
|
||||||
}
|
|
||||||
deriving stock (Eq, Show)
|
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------------
|
|
||||||
-- Inline elements
|
|
||||||
-- ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-- | An element that appears within a step.
|
|
||||||
data StepItem
|
|
||||||
= StepText !Text
|
|
||||||
| StepIngredient !Ingredient
|
|
||||||
| StepCookware !Cookware
|
|
||||||
| StepTimer !Timer
|
|
||||||
| StepRecipeRef !RecipeRef
|
|
||||||
| StepEndComment !Text
|
|
||||||
| StepComment !Text
|
|
||||||
| StepBreak
|
|
||||||
deriving stock (Eq, Show)
|
|
||||||
|
|
||||||
{- | An ingredient reference.
|
|
||||||
|
|
||||||
@\@salt\@ground black pepper{}\@potato{2}\@bacon strips{1%kg}@
|
|
||||||
-}
|
|
||||||
data Ingredient = Ingredient
|
|
||||||
{ ingName :: !Text
|
|
||||||
, ingQuantity :: !(Maybe Quantity)
|
|
||||||
, ingPreparation :: !(Maybe Text)
|
|
||||||
}
|
|
||||||
deriving stock (Eq, Show)
|
|
||||||
|
|
||||||
{- | A piece of cookware.
|
|
||||||
|
|
||||||
@\#pot\#potato masher{}@
|
|
||||||
-}
|
|
||||||
newtype Cookware = Cookware
|
|
||||||
{ cwName :: Text
|
|
||||||
}
|
|
||||||
deriving stock (Eq, Show)
|
|
||||||
|
|
||||||
{- | A timer.
|
|
||||||
|
|
||||||
@\{25%minutes\}\~eggs{3%minutes}@
|
|
||||||
-}
|
|
||||||
data Timer = Timer
|
|
||||||
{ timerName :: !(Maybe Text)
|
|
||||||
, timerDuration :: !Duration
|
|
||||||
}
|
|
||||||
deriving stock (Eq, Show)
|
|
||||||
|
|
||||||
{- | A reference to another recipe, signalled by a path starting with @.\/@.
|
|
||||||
|
|
||||||
@\@.\/sauces\/Hollandaise{150%g}@
|
|
||||||
-}
|
|
||||||
data RecipeRef = RecipeRef
|
|
||||||
{ refPath :: !FilePath
|
|
||||||
, refQuantity :: !(Maybe Quantity)
|
|
||||||
}
|
|
||||||
deriving stock (Eq, Show)
|
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------------
|
|
||||||
-- Quantities and durations
|
|
||||||
-- ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
{- | An ingredient quantity with optional unit and scaling lock.
|
|
||||||
|
|
||||||
@{2}\{1%kg}\{1\/2%tbsp}\{=1%tsp}@
|
|
||||||
-}
|
|
||||||
data Quantity = Quantity
|
|
||||||
{ quantityAmount :: !Rational
|
|
||||||
-- ^ e.g. @1\/2@, @3@, @3\/4@
|
|
||||||
, quantityUnit :: !(Maybe Text)
|
|
||||||
-- ^ e.g. @\"kg\"@, @\"tbsp\"@
|
|
||||||
, quantityFixed :: !Bool
|
|
||||||
-- ^ @{=1%tsp}@ — locked from scaling
|
|
||||||
}
|
|
||||||
deriving stock (Eq, Show)
|
|
||||||
|
|
||||||
{- | A timer duration — structurally similar to 'Quantity' but semantically
|
|
||||||
distinct. Timers are not scaled with serving size.
|
|
||||||
-}
|
|
||||||
data Duration = Duration
|
|
||||||
{ durationAmount :: !Rational
|
|
||||||
-- ^ e.g. @25@, @3@
|
|
||||||
, durationUnit :: !(Maybe Text)
|
|
||||||
-- ^ e.g. @\"minutes\"@, @\"hours\"@
|
|
||||||
}
|
|
||||||
deriving stock (Eq, Show)
|
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------------
|
|
||||||
-- Metadata
|
|
||||||
-- ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
{- | Metadata parsed from YAML front matter (the @---@ … @---@ block at the
|
|
||||||
top of a @.cook@ file).
|
|
||||||
|
|
||||||
Canonical metadata keys are lifted to dedicated fields; everything else
|
|
||||||
lives in 'metaCustom'.
|
|
||||||
-}
|
|
||||||
data Metadata = Metadata
|
|
||||||
{ metaTitle :: !(Maybe Text)
|
|
||||||
, metaServings :: !(Maybe (Int, Maybe Text))
|
|
||||||
, metaAuthor :: !(Maybe Text)
|
|
||||||
, metaSource :: !(Maybe Text)
|
|
||||||
, metaTotalTime :: !(Maybe Duration)
|
|
||||||
, metaPrepTime :: !(Maybe Duration)
|
|
||||||
, metaCookTime :: !(Maybe Duration)
|
|
||||||
, metaDifficulty :: !(Maybe Text)
|
|
||||||
, metaCuisine :: !(Maybe Text)
|
|
||||||
, metaCourse :: !(Maybe Text)
|
|
||||||
, metaDiet :: ![Text]
|
|
||||||
, metaTags :: ![Text]
|
|
||||||
, metaImage :: !(Maybe Text)
|
|
||||||
, metaDescription :: !(Maybe Text)
|
|
||||||
, metaCustom :: !(Map Text Text)
|
|
||||||
}
|
|
||||||
deriving stock (Eq, Show)
|
|
||||||
|
|
||||||
-- | Metadata with all fields set to their default / empty values.
|
|
||||||
emptyMetadata :: Metadata
|
|
||||||
emptyMetadata =
|
|
||||||
Metadata
|
|
||||||
{ metaTitle = Nothing
|
|
||||||
, metaServings = Nothing
|
|
||||||
, metaAuthor = Nothing
|
|
||||||
, metaSource = Nothing
|
|
||||||
, metaTotalTime = Nothing
|
|
||||||
, metaPrepTime = Nothing
|
|
||||||
, metaCookTime = Nothing
|
|
||||||
, metaDifficulty = Nothing
|
|
||||||
, metaCuisine = Nothing
|
|
||||||
, metaCourse = Nothing
|
|
||||||
, metaDiet = []
|
|
||||||
, metaTags = []
|
|
||||||
, metaImage = Nothing
|
|
||||||
, metaDescription = Nothing
|
|
||||||
, metaCustom = Map.empty
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import qualified Data.Text.IO as TIO
|
|||||||
import Test.Hspec (Spec, describe, expectationFailure, it, shouldBe)
|
import Test.Hspec (Spec, describe, expectationFailure, it, shouldBe)
|
||||||
|
|
||||||
import Roux.Parser (parseCookFile)
|
import Roux.Parser (parseCookFile)
|
||||||
import Roux.Types
|
import Data.CookLang
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = do
|
spec = do
|
||||||
|
|||||||
Reference in New Issue
Block a user