1 module sbylib.engine.project.project;
2 
3 import std;
4 import std : replace; // TODO: is this a compilier's bug???
5 import sbylib.event;
6 import sbylib.engine.project.metainfo;
7 import sbylib.engine.project.moduleunit;
8 import sbylib.engine.project.modulestatus;
9 import sbylib.engine.compiler : CompileErrorException;
10 import sbylib.engine.util : resourcePath;
11 
12 class Project {
13 
14     private alias VModule = Module!(void);
15     alias global this;
16 
17     VModule[string] moduleList;
18     Variant[string] global;
19     private ModuleStatusList status;
20 
21     this() {
22         status = new ModuleStatusList();
23         this.refreshPackage();
24         this.loadAll();
25     }
26 
27     ~this() {
28         foreach (mod; moduleList.values) {
29             mod.destroy();
30         }
31     }
32 
33 	private void loadAll() {
34         foreach (file; this.projectFiles) {
35             this.loadSingle(file);
36         }
37     }
38 
39     private void loadSingle(string file) {
40         auto mod = new VModule(this, status, file.absolutePath.buildNormalizedPath); 
41         moduleList[mod.path] = mod;
42 
43         auto modified = mod.path.hasModified;
44         foreach (d; mod.dependencies)
45             modified = modified | d.hasModified;
46         foreach (f; mod.inputFiles)
47             modified = modified | f.hasModified;
48 
49         when(modified).once({
50             this.loadSingle(file);
51         });
52     }
53 
54     auto get(T)(string name) {
55         if (name !in this) return null;
56         return this[name].get!T;
57     }
58 
59     string[] projectFiles() {
60         enforce(MetaInfo().projectDirectory.exists,
61                 format!`"%s" does not exists.`(MetaInfo().projectDirectory));
62         return MetaInfo().projectDirectory.dirEntries(SpanMode.breadth)
63             .filter!(entry => entry.isFile)
64             .filter!(entry => entry.baseName != "package.d")
65             .filter!(entry => entry.isModule)
66             .map!(entry => cast(string)entry)
67             .array;
68     }
69 
70     void refreshPackage() {
71         import std.file : write;
72 
73         auto projectName = MetaInfo().projectDirectory;
74         if (!projectName.exists)
75             mkdirRecurse(projectName);
76 
77         foreach (entry; projectName.dirEntries(SpanMode.breadth)) {
78             if (entry.isFile) continue;
79 
80             const fileName = entry.buildPath("package.d");
81 
82             auto dirImportList = entry
83                 .dirEntries(SpanMode.shallow)
84                 .filter!(e => e.isDir)
85                 .map!(e => e.relativePath(projectName))
86                 .map!(p => p.replace("/", "."))
87                 .map!(name => name.format!"import %s;")
88                 .array;
89             auto fileImportList = entry
90                 .dirEntries(SpanMode.shallow)
91                 .filter!(e => e.isFile)
92                 .map!(e => e.relativePath(projectName))
93                 .filter!(p => p.extension == ".d")
94                 .filter!(p => p.baseName != "package.d")
95                 .map!(e => e.stripExtension.replace("/", "."))
96                 .map!(name => name.format!"import %s;")
97                 .array;
98 
99             const moduleName = entry.replace("/", ".");
100             const content = resourcePath("package.d").readText
101                 .replace("${moduleName}", moduleName)
102                 .replace("${importList}", (dirImportList ~ fileImportList).join("\n"));
103 
104             fileName.write(content);
105         }
106     }
107 
108     // name -> path
109     string findPath(string name) {
110         auto findResult = moduleList.values.find!(mod => mod.name == name);
111         enforce(!findResult.empty || moduleList.values.any!(m => status[m.path] != ModuleStatus.Compiling), format!"Module '%s' is not found."(name));
112         return findResult.empty ? "" : findResult.front.path;
113     }
114 
115     private auto findLoadedModule(string modName) {
116         auto result = moduleList.values
117             .find!(m => m.name == modName);
118         return result;
119     }
120 }
121 
122 // TODO: implement seriously
123 private bool isModule(string file) 
124     in (file.exists)
125 {
126     return readText(file).split("\n")
127         .map!(chomp)
128         .filter!(line => line.startsWith("//") is false)
129         .filter!(line => line.canFind("mixin"))
130         .filter!(line => line.canFind("Register"))
131         .empty is false;
132 }