module Sass
  module CacheStores
    # An abstract base class for backends for the Sass cache.
    # Any key-value store can act as such a backend;
    # it just needs to implement the
    # \{#_store} and \{#_retrieve} methods.
    #
    # To use a cache store with Sass,
    # use the {file:SASS_REFERENCE.md#cache_store-option `:cache_store` option}.
    #
    # @abstract
    class Base
      # Store cached contents for later retrieval
      # Must be implemented by all CacheStore subclasses
      #
      # Note: cache contents contain binary data.
      #
      # @param key [String] The key to store the contents under
      # @param version [String] The current sass version.
      #                Cached contents must not be retrieved across different versions of sass.
      # @param sha [String] The sha of the sass source.
      #                Cached contents must not be retrieved if the sha has changed.
      # @param contents [String] The contents to store.
      def _store(key, version, sha, contents)
        raise "#{self.class} must implement #_store."
      end

      # Retrieved cached contents.
      # Must be implemented by all subclasses.
      # 
      # Note: if the key exists but the sha or version have changed,
      # then the key may be deleted by the cache store, if it wants to do so.
      #
      # @param key [String] The key to retrieve
      # @param version [String] The current sass version.
      #                Cached contents must not be retrieved across different versions of sass.
      # @param sha [String] The sha of the sass source.
      #                Cached contents must not be retrieved if the sha has changed.
      # @return [String] The contents that were previously stored.
      # @return [NilClass] when the cache key is not found or the version or sha have changed.
      def _retrieve(key, version, sha)
        raise "#{self.class} must implement #_retrieve."
      end

      # Store a {Sass::Tree::RootNode}.
      #
      # @param key [String] The key to store it under.
      # @param sha [String] The checksum for the contents that are being stored.
      # @param obj [Object] The object to cache.
      def store(key, sha, root)
        _store(key, Sass::VERSION, sha, Marshal.dump(root))
      rescue TypeError, LoadError => e
        Sass::Util.sass_warn "Warning. Error encountered while saving cache #{path_to(key)}: #{e}"
        nil
      end

      # Retrieve a {Sass::Tree::RootNode}.
      #
      # @param key [String] The key the root element was stored under.
      # @param sha [String] The checksum of the root element's content.
      # @return [Object] The cached object.
      def retrieve(key, sha)
        contents = _retrieve(key, Sass::VERSION, sha)
        Marshal.load(contents) if contents
      rescue EOFError, TypeError, ArgumentError, LoadError => e
        Sass::Util.sass_warn "Warning. Error encountered while reading cache #{path_to(key)}: #{e}"
        nil
      end

      # Return the key for the sass file.
      #
      # The `(sass_dirname, sass_basename)` pair
      # should uniquely identify the Sass document,
      # but otherwise there are no restrictions on their content.
      #
      # @param sass_dirname [String]
      #   The fully-expanded location of the Sass file.
      #   This corresponds to the directory name on a filesystem.
      # @param sass_basename [String] The name of the Sass file that is being referenced.
      #   This corresponds to the basename on a filesystem.
      def key(sass_dirname, sass_basename)
        dir = Digest::SHA1.hexdigest(sass_dirname)
        filename = "#{sass_basename}c"
        "#{dir}/#{filename}"
      end
    end
  end
end
