-- | Module    : Text.Sass
-- Stability   : experimental
--
-- This module provides support for <http://sass-lang.com/ Sass>, a CSS
-- extension language. It supports basic compilation, functions, importers and
-- headers, so it should suffice for most of the work.
--
-- Code used in this document is testable - see
-- @test\/Text\/Sass\/TutorialSpec.hs@.
module Text.Sass
  (
    -- * Compilation
    -- $compilation
    module Text.Sass.Compilation
    -- * Options
    -- $options
  , module Text.Sass.Options
    -- * Values
    -- $values
  , module Text.Sass.Values
    -- * Functions, headers, importers
    -- $functions_basic

    -- ** Functions
    -- $functions

    -- ** Headers and importers
    -- $headers_and_importers

  , module Text.Sass.Functions
  , module Data.Default.Class
  ) where

import           Data.Default.Class    (def)
import           Text.Sass.Compilation
import           Text.Sass.Functions
import           Text.Sass.Options
import           Text.Sass.Values

-- $compilation
-- Compilation of a Sass source is very easy - you only have to use
-- 'compileFile' or 'compileString' and leave the work to hsass. 'compileFile'
-- a takes path to the file as a first parameter, while 'compileString' takes a
-- source code. Both functions take 'SassOptions' as a second parameter, so if
-- you want to customize compilation behaviour, you may use it.
--
-- The result of both functions is 'Either' @Left 'SassError'@, indicating that
-- something went wrong, @Right 'String'@ with resulting code or
-- @Right 'SassExtendedResult'@ with a compiled code, a list of included files
-- and optionally a source map. Compilation functions are polymorphic in their
-- return type (there are no separate functions based on return type), but the
-- compiler should be able to infer the type easily.
-- You can examine 'SassError' to gain more knowledge about the error.
--
-- 'SassOptions' is instance of 'Default' class, so you may use 'def' function
-- to get defaults.
--
-- For example, this code:
--
-- > compileString "foo { margin: 2 * 14px; }" def
--
-- Will result in something like
--
-- > "foo { margin: 28px; }"
--
-- When you want to compile source code instead of file, consider setting
-- 'sassIncludePaths' - it will allow to resolve @includes without custom
-- importers.
--
-- If you want access to the extended result, you have to use 'resultString',
-- 'resultIncludes' and 'resultSourcemap' on the result to extract desirable
-- information.
--
-- Note that if the compiled output contains non-ASCII characters, by default it
-- will be prefixed with either a @\@charset@ rule indicating UTF-8 encoding or
-- a byte-order mark (depending on the output style). If this is inconvenient,
-- you can suppress it by enabling 'sassStripEncodingInfo'.

-- $options
-- 'SassOptions' wraps <http://libsass.org libsass> context options. It does not
-- try to be smarter than libsass, so it is mostly 1-1 mapping. See
-- "Text.Sass.Options" documentation for more info.

-- $values
-- "Text.Sass.Values" module wraps native values that libsass uses. It provides
-- an easy way to manage them in pure Haskell code.

-- $functions_basic
-- This is the most advanced stuff in the library (even though it is quite
-- simple). It allows you to define functions in Haskell and use them from Sass
-- source, provide custom resolution functions for @import statements and
-- include custom headers in files.

-- $functions
-- Let's start with explanation of functions. For example, assume that we want
-- to use function @max3@, that takes three numbers and returns the largest. In
-- Haskell, we would write it like this:
--
-- > max3 :: Int -> Int -> Int -> Int
-- > max3 a b c = max a $ max b c
--
-- Unfortunately, sass would not be able to use that. We must rewrite this
-- function so that it operates on 'SassValue's. If we want to use a function
-- in sass, it has to have following signature:
--
-- > func :: SassValue -> IO SassValue
--
-- It takes an argument that is 'SassList' with all of its arguments and returns
-- computed 'SassValue'. With this in mind, we may write following code:
--
-- > max3 (SassList (SassNumber a _:SassNumber b _:SassNumber c _:_) _) =
-- >     return $ SassNumber (max a $ max b c) "px"
-- > max3 _ = SassError "invalid arguments"
--
-- Having this function, we may proceed and define its signature:
--
-- > max3sig = SassFunction "max3($a, $b, $c)" max3
--
-- This description allows the compiler to map Haskell function (@max3@) to a
-- form that may be used in sass. Signature consists of function name (@max3@),
-- an opening parenthesis, a list of arguments (dollar sign and its name)
-- separated by commas and a closing parenthesis. It is the same as a function
-- definition in sass code.
--
-- We can now tell the compiler to use our function. In order to do this, we
-- must replace 'sassFunctions' field in 'SassOptions':
--
-- > opts = def { sassFunctions = Just [max3sig] }
--
-- Now, we may compile code that uses function max3:
--
-- > compileString "foo { margin: max3(1px, 2px, 3px); }" opts
--
-- And we will get
--
-- > "foo { margin: 3px; }"
--
-- There exists several functions that are special:
--
-- * @*@ - fallback implementation
-- * @@warn@ - overload warn statement
-- * @@error@ - overload error statement
-- * @@debug@ - overload debug statement
--
-- See <https://github.com/sass/libsass/wiki/API-Sass-Function libsass>
-- documentation for more information.

-- $headers_and_importers
-- Importers are functions that override default behaviour of @@import@
-- statements.  For example, you may implement rewrite rules or even download
-- stylesheets from a remote server.
--
-- Headers allow you to insert arbitrary sass at the beginning of the file being
-- compiled.
--
-- Let's say that we want to inject a path to the currently compiled file. We
-- may write the following header:
--
-- > header src = return [makeSourceImport $ "$file: " ++ src ++ ";"]
--
-- It simply returns sass code that defines a @$file@ variable set to a path to
-- the current file (first argument of the function).
-- Then, we must define header signature and override options:
--
-- > headerSig = SassHeader 1 header
-- > opts = def { sassHeaders = Just [headerSig], sassInputPath = Just "path" }
--
-- We set 'sassInputPath', because we will be compiling a string and it won't be
-- set automatically.
-- Now, executing
--
-- > compileString "foo { prop: $file; }" opts
--
-- Will produce following result:
--
-- > "foo { prop: path; }"
--
-- Importers are defined and act similarly, but they take two arguments. The
-- first argument is the path to the file being imported, and the second
-- argument is the path to the importing file. For example
--
-- > importer imp src = return
-- >     [makeSourceImport $ "/* imported " ++ imp ++ " into " ++ src ++ " */"]
-- > sassImporter = Just [SassImporter 1 importer]
-- > opts = def { sassImporters = sassImporter, sassInputPath = Just "file" }
-- > compileString "@import \"relative/path\"" opts
--
-- Gives
--
-- > "/* imported relative/path into file */"
--
-- The first argument to 'SassHeader' or 'SassImporter' is its priority - if two
-- importers return source for some file, the one with higher priority wins. For
-- example
--
-- > importer1 imp _ = return [makeSourceImport $ "$file: " ++ imp ++ "1;"]
-- > importer2 imp _ = return [makeSourceImport $ "$file: " ++ imp ++ "2;"]
-- > importerSigs = [SassImporter 0.5 importer1, SassImporter 1 importer2]
-- > opts = def { sassImporters = Just importerSigs }
-- > compileString "@import \"file\";\nfoo { prop: $file; }" opts
--
-- Will result in
--
-- > "foo { prop: file2; }"
--
-- Instead of providing source code for imports, you may provide a path to a
-- file and leave loading to the library. This is done by settings only
-- 'importPath' and 'importBase' in 'SassImport' or using 'makePathImport'
-- instead of 'makeSourceImport'. This may be useful to implement just rewrite
-- rules and not full loading.