001/*
002 * Copyright (C) 2022 - 2023, the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *    http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.github.ascopes.jct.workspaces;
017
018import java.io.File;
019import java.io.InputStream;
020import java.net.URL;
021import java.nio.charset.Charset;
022import java.nio.file.Path;
023import org.apiguardian.api.API;
024import org.apiguardian.api.API.Status;
025
026/**
027 * Chainable builder for creating individual files.
028 *
029 * @author Ashley Scopes
030 * @since 0.0.1
031 */
032@API(since = "0.0.1", status = Status.STABLE)
033@SuppressWarnings("unused")
034public interface FileBuilder {
035
036  /**
037   * Create the file with the given contents as UTF-8.
038   * 
039   * <p>If you are using multi-line strings, an example of usage would be:
040   * 
041   * <pre><code>
042   *   directory
043   *      .createFile("org", "example", "HelloWorld.java")
044   *      .withContents("""
045   *        package org.example;
046   *        
047   *        public class HelloWorld {
048   *          public static void main(String[] args) {
049   *            System.out.println("Hello, World!");
050   *          }
051   *        }
052   *      """);
053   * </code></pre>
054   * 
055   * <p>If the Java language level of your tests does not support multi-line strings, you can
056   * alternatively pass each line of text to write as a separate string. These will be written
057   * to the file using line-feed {@code '\n'} separators. For example:
058   *
059   * <pre><code>
060   *   directory
061   *      .createFile("org", "example", "HelloWorld.java")
062   *      .withContents(
063   *        "package org.example;",
064   *        "",
065   *        "public class HelloWorld {",
066   *        "  public static void main(String[] args) {",
067   *        "    System.out.println(\"Hello, World!\");",
068   *        "  }",
069   *        "}"
070   *      );
071   * </code></pre>
072   *
073   * @param lines the lines to write using the default charset.
074   * @return the root managed directory for further configuration.
075   * @see #withContents(Charset, String...)
076   * @see #withContents(byte[])
077   */
078  ManagedDirectory withContents(String... lines);
079
080  /**
081   * Create the file with the given contents.
082   *
083   * <pre><code>
084   *   directory
085   *      .createFile("org", "example", "HelloWorld.java")
086   *      .withContents(StandardCharsets.US_ASCII, """
087   *        package org.example;
088   *
089   *        public class HelloWorld {
090   *          public static void main(String[] args) {
091   *            System.out.println("Hello, World!");
092   *          }
093   *        }
094   *      """);
095   * </code></pre>
096   *
097   * <p>If the Java language level of your tests does not support multi-line strings, you can
098   * alternatively pass each line of text to write as a separate string. These will be written
099   * to the file using line-feed {@code '\n'} separators. For example:
100   *
101   * <pre><code>
102   *   directory
103   *      .createFile("org", "example", "HelloWorld.java")
104   *      .withContents(
105   *        StandardCharsets.US_ASCII,
106   *        "package org.example;",
107   *        "",
108   *        "public class HelloWorld {",
109   *        "  public static void main(String[] args) {",
110   *        "    System.out.println(\"Hello, World!\");",
111   *        "  }",
112   *        "}"
113   *      );
114   * </code></pre>
115   *
116   * @param charset the character encoding to use.
117   * @param lines   the lines to write.
118   * @return the root managed directory for further configuration.
119   * @see #withContents(String...)
120   * @see #withContents(byte[])
121   */
122  ManagedDirectory withContents(Charset charset, String... lines);
123
124  /**
125   * Create the file with the given byte contents.
126   *
127   * <pre><code>
128   *   var classFile = new byte[]{
129   *      -54, -2, -70, -66, 0, 0, 0, 63, 0, 29, 10, 0, 2, 0, 3, 7, 0, 4, 12, 0, 5, 0, 6, 1, 0,
130   *      16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 1, 0, 6,
131   *      60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 9, 0, 8, 0, 9, 7, 0, 10, 12, 0, 11,
132   *      0, 12, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 121, 115, 116, 101,
133   *      109, 1, 0, 3, 111, 117, 116, 1, 0, 21, 76, 106, 97, 118, 97, 47, 105, 111, 47, 80, 114,
134   *      105, 110, 116, 83, 116, 114, 101, 97, 109, 59, 8, 0, 14, 1, 0, 13, 72, 101, 108, 108,
135   *      111, 44, 32, 87, 111, 114, 108, 100, 33, 10, 0, 16, 0, 17, 7, 0, 18, 12, 0, 19, 0, 20,
136   *      1, 0, 19, 106, 97, 118, 97, 47, 105, 111, 47, 80, 114, 105, 110, 116, 83, 116, 114, 101,
137   *      97, 109, 1, 0, 7, 112, 114, 105, 110, 116, 108, 110, 1, 0, 21, 40, 76, 106, 97, 118, 97,
138   *      47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 86, 7, 0, 22, 1, 0, 10,
139   *      72, 101, 108, 108, 111, 87, 111, 114, 108, 100, 1, 0, 4, 67, 111, 100, 101, 1, 0, 15, 76,
140   *      105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 4, 109, 97, 105,
141   *      110, 1, 0, 22, 40, 91, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114,
142   *      105, 110, 103, 59, 41, 86, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1,
143   *      0, 15, 72, 101, 108, 108, 111, 87, 111, 114, 108, 100, 46, 106, 97, 118, 97, 0, 33, 0,
144   *      21, 0, 2, 0, 0, 0, 0, 0, 2, 0, 1, 0, 5, 0, 6, 0, 1, 0, 23, 0, 0, 0, 29, 0, 1, 0, 1, 0,
145   *      0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 1, 0, 24, 0, 0, 0, 6, 0, 1, 0, 0, 0, 1, 0, 9, 0,
146   *      25, 0, 26, 0, 1, 0, 23, 0, 0, 0, 37, 0, 2, 0, 1, 0, 0, 0, 9, -78, 0, 7, 18, 13, -74, 0,
147   *      15, -79, 0, 0, 0, 1, 0, 24, 0, 0, 0, 10, 0, 2, 0, 0, 0, 3, 0, 8, 0, 4, 0, 1, 0, 27, 0,
148   *      0, 0, 2, 0, 28
149   *   };
150   *
151   *   directory
152   *       .createFile("HelloWorld.class")
153   *       .withContents(classFile);
154   * </code></pre>
155   *
156   * @param contents the bytes to write.
157   * @return the root managed directory for further configuration.
158   */
159  ManagedDirectory withContents(byte[] contents);
160
161  /**
162   * Copy a resource from the class loader on the current thread into the file system.
163   *
164   * <pre><code>
165   *   directory
166   *       .createFile("org", "example", "HelloWorld.class")
167   *       .copiedFromClassPath("org/example/HelloWorld.class");
168   * </code></pre>
169   *
170   * @param resource the resource to copy.
171   * @return the root managed directory for further configuration.
172   */
173  ManagedDirectory copiedFromClassPath(String resource);
174
175  /**
176   * Copy a resource from the given class loader into the file system.
177   *
178   * <pre><code>
179   *   var classLoader = getClass().getClassLoader();
180   *
181   *   directory
182   *       .createFile("org", "example", "HelloWorld.class")
183   *       .copiedFromClassPath(classLoader, "org/example/HelloWorld.class");
184   * </code></pre>
185   *
186   * @param classLoader the class loader to use.
187   * @param resource    the resource to copy.
188   * @return the root managed directory for further configuration.
189   */
190  ManagedDirectory copiedFromClassPath(ClassLoader classLoader, String resource);
191
192  /**
193   * Copy the contents from the given file into the file system.
194   *
195   * <pre><code>
196   *   var file = new File("src/test/resources/code/HelloWorld.java");
197   *
198   *   directory
199   *       .createFile("org", "example", "HelloWorld.java")
200   *       .copiedFromFile(file);
201   * </code></pre>
202   *
203   * @param file the file to read.
204   * @return the root managed directory for further configuration.
205   */
206  ManagedDirectory copiedFromFile(File file);
207
208  /**
209   * Copy the contents from the given path into the file system.
210   *
211   * <pre><code>
212   *   var file = Path.of("src", "test", "resources", "code", "HelloWorld.java");
213   *
214   *   directory
215   *       .createFile("org", "example", "HelloWorld.java")
216   *       .copiedFromFile(file);
217   * </code></pre>
218   *
219   * @param file the file to read.
220   * @return the root managed directory for further configuration.
221   */
222  ManagedDirectory copiedFromFile(Path file);
223
224  /**
225   * Copy the contents from the given URL into the file system.
226   *
227   * <pre><code>
228   *   var url = URI.create("file:///code/org/example/HelloWorld.java").toURI();
229   *
230   *   directory
231   *       .createFile("org", "example", "HelloWorld.java")
232   *       .copiedFromUrl(url);
233   * </code></pre>
234   *
235   * @param url the URL to read.
236   * @return the root managed directory for further configuration.
237   */
238  ManagedDirectory copiedFromUrl(URL url);
239
240  /**
241   * Create the file but do not put anything in it.
242   *
243   * <p>The resultant file will be 0 bytes long.
244   *
245   * <pre><code>
246   *   directory
247   *       .createFile(".gitkeep")
248   *       .thatIsEmpty();
249   * </code></pre>
250   *
251   * @return the root managed directory for further configuration.
252   */
253  ManagedDirectory thatIsEmpty();
254
255  /**
256   * Copy the contents from the given input stream into the file system.
257   *
258   * <p>The input stream will be closed when reading completes.
259   *
260   * <pre><code>
261   *   try (var is = getClass().getResourceAsStream("code/examples/HelloWorld.java")) {
262   *     directory
263   *         .createFile("org", "example", "HelloWorld.java")
264   *         .fromInputStream(is);
265   *   }
266   * </code></pre>
267   *
268   * @param inputStream the input stream to read.
269   * @return the root managed directory for further configuration.
270   */
271  ManagedDirectory fromInputStream(InputStream inputStream);
272}