From 632f61820b0adc0b82fd69158fc56fca36896c1b Mon Sep 17 00:00:00 2001 From: James Brechtel Date: Tue, 19 May 2026 21:29:41 -0400 Subject: [PATCH] feat: add RecipeSearchEntry JSON encoding for search payload --- src/Roux/RecipeIndex.hs | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/Roux/RecipeIndex.hs b/src/Roux/RecipeIndex.hs index f9a4501..c295625 100644 --- a/src/Roux/RecipeIndex.hs +++ b/src/Roux/RecipeIndex.hs @@ -1,11 +1,17 @@ +{-# LANGUAGE DeriveGeneric #-} + -- | Scanning and indexing of Cooklang recipe files on disk. module Roux.RecipeIndex ( RecipeInfo (..), + RecipeSearchEntry (..), scanRecipes, + toSearchEntry, ) where +import Data.Aeson (ToJSON (..), camelTo2, defaultOptions, fieldLabelModifier, genericToJSON) import Data.Text (Text) import qualified Data.Text as T +import GHC.Generics (Generic) import System.Directory (listDirectory) import System.FilePath (takeBaseName, takeExtension, ()) @@ -45,9 +51,38 @@ loadRecipe dir filename = do , riRecipe = parsed } -{- | Extract a display title from the recipe's metadata or fall back to the -filename. +-- | Search payload for type-ahead / recipe search. +data RecipeSearchEntry = RecipeSearchEntry + { rseTitle :: !Text + , rseFilename :: !Text + , rseCourse :: !(Maybe Text) + , rseTags :: ![Text] + } + deriving stock (Generic) + +instance ToJSON RecipeSearchEntry where + toJSON = genericToJSON defaultOptions{fieldLabelModifier = camelTo2 '_' . drop 3} + +{- | Convert a 'RecipeInfo' into a 'RecipeSearchEntry', extracting course and + tags from the parsed recipe metadata when available. -} +toSearchEntry :: RecipeInfo -> RecipeSearchEntry +toSearchEntry info = + let (course, tags) = case riRecipe info of + Right r -> + ( metaCourse (recipeMetadata r) + , metaTags (recipeMetadata r) + ) + Left _ -> (Nothing, []) + in RecipeSearchEntry + { rseTitle = riTitle info + , rseFilename = T.pack (riFilename info) + , rseCourse = course + , rseTags = tags + } + +-- | Extract a display title from the recipe's metadata or fall back to the +-- filename. extractTitle :: Recipe -> FilePath -> Text extractTitle recipe filename = case metaTitle (recipeMetadata recipe) of