1# Copyright (c) 2018 Ultimaker B.V.
2# libCharon is released under the terms of the LGPLv3 or higher.
3from typing import Any, Dict, List, IO, Optional, Callable
4
5from Charon.OpenMode import OpenMode
6
7
8##  An interface for accessing files.
9#
10#   This interface is designed to be able to access 3D-printing related files,
11#   and for container-type files to access the resources therein.
12class FileInterface:
13    stream_handler = open # type: Callable[[str, str], IO[bytes]]
14
15    mime_type = ""
16
17    ##  Opens a file for reading or writing.
18    #
19    #   After opening the file, this instance will represent that file from then
20    #   on, meaning that the metadata getters/setters and the streams will be
21    #   functioning on that file.
22    #   \param path The path to the file on local disk, relative or absolute.
23    #   \param mode The mode with which to open the file (see OpenMode).
24    def open(self, path: str, mode: OpenMode = OpenMode.ReadOnly) -> None:
25        raise NotImplementedError("The open() function of " + self.__class__.__qualname__ + " is not implemented.")
26
27    ##  Opens a stream for reading or writing.
28    #
29    #   After opening the stream, this instance will represent that stream from
30    #   then on, meaning that the metadata getters/setters and the streams will
31    #   be functioning on that stream.
32    #   \param stream The stream to read from or write to.
33    #   \param mime The MIME type of the stream. This determines what implementation is used to read/write it.
34    #   \param mode The mode with which to open the file (see OpenMode).
35    def openStream(self, stream: IO[bytes], mime: str, mode: OpenMode = OpenMode.ReadOnly) -> None:
36        raise NotImplementedError("The openStream() function of " + self.__class__.__qualname__ + " is not implemented.")
37
38    ##  Closes the opened file, releasing the resources in use for it.
39    #
40    #   After the file is closed, this instance can no longer be used until the ``open`` method is called again.
41    def close(self) -> None:
42        raise NotImplementedError("The close() function of " + self.__class__.__qualname__ + " is not implemented.")
43
44    ##  Ensures that no buffered data is still pending to be read or written.
45    def flush(self) -> None:
46        raise NotImplementedError("The flush() function of " + self.__class__.__qualname__ + " is not implemented.")
47
48    ##  Returns a list of all resources and metadata in the file.
49    def listPaths(self) -> List[str]:
50        raise NotImplementedError("The listPaths() function of " + self.__class__.__qualname__ + " is not implemented.")
51
52    ##  Gets the data stored at the specified virtual path and all its descendants.
53    #
54    #   The returned dictionary may contain normal resources as well as
55    #   metadata. If it is a normal resource, the value will contain the
56    #   serialised data (either ``bytes`` or ``str``, depending on whether the
57    #   file opens in binary mode or not). If it is metadata, all metadata keys
58    #   under the specified path are returned (all descendants in the tree). If
59    #   there is no metadata and no resource under the selected virtual path, an
60    #   empty dictionary is returned.
61    #   \param virtual_path The path inside the file to get the data from.
62    #   \return The data and metadata under the specified virtual path.
63    def getData(self, virtual_path: str) -> Dict[str, Any]:
64        raise NotImplementedError("The getData() function of " + self.__class__.__qualname__ + " is not implemented.")
65
66    ##  Sets the data of several virtual paths at once.
67    #
68    #   The ``data`` parameter provides a dictionary mapping virtual paths to
69    #   the new data that should be provided in the path.
70    def setData(self, data: Dict[str, Any]) -> None:
71        raise NotImplementedError("The setData() function of " + self.__class__.__qualname__ + " is not implemented.")
72
73    ##  Gets metadata entries in the opened file.
74    #
75    #   The metadata is a dictionary, where the keys are virtual paths in the
76    #   subtree of the resource tree specified by ``virtual_path``. For
77    #   instance, when requesting the metadata of the resource with virtual path
78    #   ``/metadata``, this function could return a dictionary containing:
79    #   * ``/metadata/size``: 12354
80    #   * ``/metadata/toolpath/default/size``: 12000
81    #   * ``/metadata/toolpath/default/machine_type``: ``ultimaker3``
82    #   * ``/metadata/toolpath/default/print_time``: 121245
83    #   * ``/metadata/toolpath/default/print_size``: (0, 0, 0) x (100, 100, 100)
84    #
85    #   But a subtree can be requested as well, such as
86    #   ``/metadata/toolpath/default/size``, which would then return a
87    #   dictionary containing only the key ``/metadata/toolpath/default/size``
88    #   and its value, because there are no other subitems in that subtree.
89    #
90    #   If there is no metadata in the requested path, an empty dictionary is
91    #   returned.
92    #   \param virtual_path The subtree of metadata entries to get the metadata
93    #   of.
94    #   \return A dictionary of all the metadata entries in the selected
95    #   subtree.
96    def getMetadata(self, virtual_path: str) -> Dict[str, Any]:
97        raise NotImplementedError("The getMetadata() function of " + self.__class__.__qualname__ + " is not implemented.")
98
99    ##  Changes some metadata entries in the opened file.
100    #
101    #   The provided dictionary must have the full virtual paths of the metadata
102    #   entries it wants to change as its keys, and the new values along with
103    #   every key.
104    #
105    #   If a metadata entry didn't exist yet, it is created.
106    #
107    #   If a metadata entry by cannot be changed (such as the file size of a
108    #   resource) then a ``ReadOnlyError`` must be raised for that resource, and
109    #   none of the changes of this function call may be applied (or everything
110    #   must be undone).
111    #   \param metadata A dictionary of metadata entries to change.
112    #   \raises ReadOnlyError A metadata entry cannot be changed (such as the
113    #   file size of a resource).
114    def setMetadata(self, metadata: Dict[str, Any]) -> None:
115        raise NotImplementedError("The setMetadata() function of " + self.__class__.__qualname__ + " is not implemented.")
116
117    ##  Gets an I/O stream to the resource or metadata at the specified virtual
118    #   path.
119    #
120    #   This stream may be a normal resource or it may be metadata. If it is
121    #   metadata, a stream will be returned in the form of a JSON document
122    #   (encoded in UTF-8 for binary streams) containing all the metadata that
123    #   would be returned by the getMetadata method.
124    #
125    #   Whether the returned stream is an input or an output stream depends on
126    #   the mode that was provided in the ``open`` method. This determines
127    #   whether you can read from and/or write to the stream.
128    #
129    #   If a resource didn't exist and you can write, the resource is created.
130    #   \param virtual_path The virtual path to the resource that you want to
131    #   read or write.
132    #   \raises ReadOnlyError The resource doesn't exist and there are no write
133    #   permissions to create it.
134    def getStream(self, virtual_path: str) -> IO[bytes]:
135        raise NotImplementedError("The getStream() function of " + self.__class__.__qualname__ + " is not implemented.")
136
137    ##  Gets a bytes representation of the file.
138    #
139    #   Resources inside the file are not supported by this method. Use
140    #   ``getStream`` for that.
141    #   \param offset The number of bytes to skip at the beginning of the file.
142    #   \param count The maximum number of bytes to return. If the file is
143    #   longer than this, it is truncated. If the file is shorter than this,
144    #   fewer bytes than this might be returned. If not specified, the entire
145    #   file will be returned except the initial offset.
146    #   \return bytes A bytes array representing the file or a part of it.
147    def toByteArray(self, offset: int = 0, count: int = -1) -> bytes:
148        raise NotImplementedError("The toByteArray() function of " + self.__class__.__qualname__ + " is not implemented.")