{-# LANGUAGE OverloadedStrings #-} -- | Core types and error definitions for Docster module Docster.Types ( -- * Error Types DocsterError(..) -- * Output Format , OutputFormat(..) -- * Domain Types , SourceDir(..) , OutputDir(..) , OutputPath(..) , DiagramId(..) , DiagramConfig(..) -- * Traversal State , TraversalState(..) , initialTraversalState , normalizeHeading -- * Path Utilities , computeOutputDir , ensureOutputDir ) where import Data.Text (Text) import qualified Data.Text as T import Data.Char (isAlphaNum, isSpace) import Data.Map.Strict (Map) import qualified Data.Map.Strict as Map import Control.Exception (Exception) import System.FilePath (takeDirectory, takeBaseName, ()) import System.Directory (createDirectoryIfMissing) -- | Custom error types for comprehensive error handling data DocsterError = InvalidUsage Text | FileError Text | PDFGenerationError Text | ProcessError Text deriving (Show) instance Exception DocsterError -- | Output format for document generation data OutputFormat = PDF | HTML | DOCX deriving (Show, Eq) -- | Type-safe wrapper for source directory paths newtype SourceDir = SourceDir FilePath deriving (Show, Eq) -- | Type-safe wrapper for output directory paths newtype OutputDir = OutputDir FilePath deriving (Show, Eq) -- | Type-safe wrapper for output file paths newtype OutputPath = OutputPath FilePath deriving (Show, Eq) -- | Type-safe wrapper for diagram identifiers newtype DiagramId = DiagramId Text deriving (Show, Eq) -- | Configuration for diagram generation data DiagramConfig = DiagramConfig { dcSourceDir :: SourceDir , dcOutputDir :: OutputDir , dcOutputFormat :: OutputFormat } deriving (Show) -- | Compute output directory from input file path -- "docs/readme.md" -> "docs/output/readme" computeOutputDir :: FilePath -> OutputDir computeOutputDir inputPath = let dir = takeDirectory inputPath baseName = takeBaseName inputPath in OutputDir $ if null dir || dir == "." then "output" baseName else dir "output" baseName -- | Ensure output directory exists ensureOutputDir :: OutputDir -> IO () ensureOutputDir (OutputDir dir) = createDirectoryIfMissing True dir -- | State for heading-aware diagram naming during AST traversal data TraversalState = TraversalState { tsCurrentHeading :: Maybe Text -- ^ Current heading text (normalized) , tsHeadingCounters :: Map Text Int -- ^ Counter for diagrams per heading , tsDocumentName :: Text -- ^ Fallback name when no heading } deriving (Show, Eq) -- | Create initial traversal state with document name as fallback initialTraversalState :: Text -> TraversalState initialTraversalState docName = TraversalState { tsCurrentHeading = Nothing , tsHeadingCounters = Map.empty , tsDocumentName = docName } -- | Normalize heading text for use as a filename -- "File Flow Diagram!" -> "file_flow_diagram" normalizeHeading :: Text -> Text normalizeHeading = T.intercalate "_" . T.words . T.filter (\c -> isAlphaNum c || isSpace c) . T.toLower