# Path: fimfarchive/flavors.py
# class DataFormat(Flavor):
#     """
#     Indicates the file format of story data.
#     """
#     EPUB = ()
#     FPUB = ()
#     HTML = ()
#     JSON = ()
# 
# Path: fimfarchive/stories.py
# class Story:
#     """
#     Represents a story.
#     """
# 
#     def __init__(
#             self,
#             key: int,
#             fetcher: Fetcher = None,
#             meta: Dict[str, Any] = None,
#             data: bytes = None,
#             flavors: Iterable[Flavor] = (),
#             ) -> None:
#         """
#         Constructor.
# 
#         Args:
#             key: Primary key of the story.
#             fetcher: Fetcher to use for lazy fetching.
#             meta: Meta to populate the story with.
#             data: Data to populate the story with.
#             flavors: Content type hints.
#         """
#         if fetcher is None and (data is None or meta is None):
#             raise ValueError("Story must contain fetcher if lazy.")
# 
#         self.key = key
#         self.fetcher = fetcher
#         self.flavors = set(flavors)
#         self._meta = meta
#         self._data = data
# 
#     @property
#     def is_fetched(self) -> bool:
#         """
#         True if no more fetches are necessary.
#         """
#         return self.has_meta and self.has_data
# 
#     @property
#     def has_meta(self) -> bool:
#         """
#         True if story meta has been fetched.
#         """
#         return self._meta is not None
# 
#     @property
#     def meta(self) -> Dict[str, Any]:
#         """
#         Returns the story meta.
# 
#         Meta may be fetched if this story instance is lazy.
# 
#         Raises:
#             InvalidStoryError: If a valid story is not found.
#             StorySourceError: If source does not return any data.
#         """
#         if self._meta is None and self.fetcher:
#             self._meta = self.fetcher.fetch_meta(self.key)
# 
#         if self._meta is None:
#             raise StorySourceError("Meta is missing.")
# 
#         return self._meta
# 
#     @property
#     def has_data(self) -> bool:
#         """
#         True if story data has been fetched.
#         """
#         return self._data is not None
# 
#     @property
#     def data(self) -> bytes:
#         """
#         Returns the story data.
# 
#         Data may be fetched if this story instance is lazy.
# 
#         Raises:
#             InvalidStoryError: If a valid story is not found.
#             StorySourceError: If source does not return any data.
#         """
#         if self._data is None and self.fetcher:
#             self._data = self.fetcher.fetch_data(self.key)
# 
#         if self._data is None:
#             raise StorySourceError("Data is missing.")
# 
#         return self._data
# 
#     def merge(self, **params) -> 'Story':
#         """
#         Returns a shallow copy, optionally replacing attributes.
# 
#         Args:
#             **params: Overrides parameters from the current instance.
# 
#         Raises:
#             TypeError: If passed an unexpected parameter.
#         """
#         kwargs = {k.lstrip('_'): v for k, v in vars(self).items()}
#         kwargs.update(params)
# 
#         return type(self)(**kwargs)
# 
# Path: fimfarchive/utils.py
# def get_path(source: Union[None, Path, str]) -> Optional[Path]:
#     """
#     Creates a path from an object, if one is supplied.
# 
#     Args:
#         source: Object to create a path from.
# 
#     Returns:
#         A resolved path instance, or None.
#     """
#     if source is None:
#         return None
# 
#     return Path(source).resolve()
# 
# Path: fimfarchive/converters/base.py
# class Converter(ABC):
#     """
#     Converter for stories.
#     """
# 
#     @abstractmethod
#     def __call__(self, story: Story) -> Story:
#         """
#         Applies the conversion.
# 
#         Args:
#             *story: The story to convert.
# 
#         Returns:
#             A converted story.
#         """


from functools import partial
from pathlib import Path
from shutil import rmtree
from subprocess import DEVNULL, STDOUT, run
from tempfile import mkdtemp
from typing import Union
from fimfarchive.flavors import DataFormat
from fimfarchive.stories import Story
from fimfarchive.utils import get_path
from .base import Converter
    """
    parent = Path(mkdtemp())
    source = parent / SOURCE
    target = parent / TARGET

    command = (PROGRAM, str(source), str(target), *ARGUMENTS)

    try:
        source.write_bytes(data)
        proc(command, stdout=pipe)
    except Exception:
        raise
    else:
        return target.read_bytes()
    finally:
        rmtree(parent)


class FpubEpubConverter(Converter):
    """
    Converts story data from FPUB to EPUB.
    """

    def __init__(self, logdir: Union[Path, str] = None) -> None:
        self.logdir = get_path(logdir)

        if self.logdir and not self.logdir.is_dir():
            raise ValueError("Logdir must be a directory.")

    def __call__(self, story: Story) -> Story:

# Path: fimfarchive/flavors.py
# class DataFormat(Flavor):
#     """
#     Indicates the file format of story data.
#     """
#     EPUB = ()
#     FPUB = ()
#     HTML = ()
#     JSON = ()
# 
# Path: fimfarchive/stories.py
# class Story:
#     """
#     Represents a story.
#     """
# 
#     def __init__(
#             self,
#             key: int,
#             fetcher: Fetcher = None,
#             meta: Dict[str, Any] = None,
#             data: bytes = None,
#             flavors: Iterable[Flavor] = (),
#             ) -> None:
#         """
#         Constructor.
# 
#         Args:
#             key: Primary key of the story.
#             fetcher: Fetcher to use for lazy fetching.
#             meta: Meta to populate the story with.
#             data: Data to populate the story with.
#             flavors: Content type hints.
#         """
#         if fetcher is None and (data is None or meta is None):
#             raise ValueError("Story must contain fetcher if lazy.")
# 
#         self.key = key
#         self.fetcher = fetcher
#         self.flavors = set(flavors)
#         self._meta = meta
#         self._data = data
# 
#     @property
#     def is_fetched(self) -> bool:
#         """
#         True if no more fetches are necessary.
#         """
#         return self.has_meta and self.has_data
# 
#     @property
#     def has_meta(self) -> bool:
#         """
#         True if story meta has been fetched.
#         """
#         return self._meta is not None
# 
#     @property
#     def meta(self) -> Dict[str, Any]:
#         """
#         Returns the story meta.
# 
#         Meta may be fetched if this story instance is lazy.
# 
#         Raises:
#             InvalidStoryError: If a valid story is not found.
#             StorySourceError: If source does not return any data.
#         """
#         if self._meta is None and self.fetcher:
#             self._meta = self.fetcher.fetch_meta(self.key)
# 
#         if self._meta is None:
#             raise StorySourceError("Meta is missing.")
# 
#         return self._meta
# 
#     @property
#     def has_data(self) -> bool:
#         """
#         True if story data has been fetched.
#         """
#         return self._data is not None
# 
#     @property
#     def data(self) -> bytes:
#         """
#         Returns the story data.
# 
#         Data may be fetched if this story instance is lazy.
# 
#         Raises:
#             InvalidStoryError: If a valid story is not found.
#             StorySourceError: If source does not return any data.
#         """
#         if self._data is None and self.fetcher:
#             self._data = self.fetcher.fetch_data(self.key)
# 
#         if self._data is None:
#             raise StorySourceError("Data is missing.")
# 
#         return self._data
# 
#     def merge(self, **params) -> 'Story':
#         """
#         Returns a shallow copy, optionally replacing attributes.
# 
#         Args:
#             **params: Overrides parameters from the current instance.
# 
#         Raises:
#             TypeError: If passed an unexpected parameter.
#         """
#         kwargs = {k.lstrip('_'): v for k, v in vars(self).items()}
#         kwargs.update(params)
# 
#         return type(self)(**kwargs)
# 
# Path: fimfarchive/utils.py
# def get_path(source: Union[None, Path, str]) -> Optional[Path]:
#     """
#     Creates a path from an object, if one is supplied.
# 
#     Args:
#         source: Object to create a path from.
# 
#     Returns:
#         A resolved path instance, or None.
#     """
#     if source is None:
#         return None
# 
#     return Path(source).resolve()
# 
# Path: fimfarchive/converters/base.py
# class Converter(ABC):
#     """
#     Converter for stories.
#     """
# 
#     @abstractmethod
#     def __call__(self, story: Story) -> Story:
#         """
#         Applies the conversion.
# 
#         Args:
#             *story: The story to convert.
# 
#         Returns:
#             A converted story.
#         """


# Path: fimfarchive/converters/fpub_epub.py
from functools import partial
from pathlib import Path
from shutil import rmtree
from subprocess import DEVNULL, STDOUT, run
from tempfile import mkdtemp
from typing import Union
from fimfarchive.flavors import DataFormat
from fimfarchive.stories import Story
from fimfarchive.utils import get_path
from .base import Converter

    """
    parent = Path(mkdtemp())
    source = parent / SOURCE
    target = parent / TARGET

    command = (PROGRAM, str(source), str(target), *ARGUMENTS)

    try:
        source.write_bytes(data)
        proc(command, stdout=pipe)
    except Exception:
        raise
    else:
        return target.read_bytes()
    finally:
        rmtree(parent)


class FpubEpubConverter(Converter):
    """
    Converts story data from FPUB to EPUB.
    """

    def __init__(self, logdir: Union[Path, str] = None) -> None:
        self.logdir = get_path(logdir)

        if self.logdir and not self.logdir.is_dir():
            raise ValueError("Logdir must be a directory.")

    def __call__(self, story: Story) -> Story:

        if DataFormat.FPUB not in story.flavors: