1 module localimport;
2 
3 /**
4  * Check if string can be used as a straight module import.
5  */
6 private template IsModuleImport(string import_) {
7     enum IsModuleImport = __traits(compiles, { mixin("import ", import_, ";"); });
8 }
9 
10 /**
11  * Check if the requested symbol is found in the specified module.
12  */
13 private template IsSymbolInModule(string module_, string symbol) {
14     static if (IsModuleImport!module_) {
15         enum import_ = module_ ~ ":" ~ symbol;
16         enum IsSymbolInModule = __traits(compiles, {
17                 mixin("import ", import_, ";");
18             });
19     } else {
20         enum IsSymbolInModule = false;
21     }
22 }
23 
24 /**
25  * Used to generate useful error messages.
26  */
27 private template failedSymbol(string symbol, string module_) {
28     void failedSymbol(Args...)(auto ref Args args) {
29         throw new Exception("Symbol \"" ~ symbol ~ "\" not found in " ~ module_);
30     }
31 }
32 
33 /**
34  * Here the magic happens.
35  * Recursively descends along the dots in usage until it can resolve an imported
36  * module or symbol from module or reaches the end of the dot chain.
37  */
38 private struct FromImpl(string module_) {
39     // opDispatch handles access to missing members of an object. 
40     template opDispatch(string symbol) {
41         // statically check if symbol is in given module
42         static if (IsSymbolInModule!(module_, symbol)) {
43             // Symbol import: emit module import and alias opDispatch to
44             // the symbol.
45             mixin("import ", module_, "; alias opDispatch = ", symbol, ";");
46         } else { // symbol not in module
47             static if (module_.length == 0) {
48                 // module string is of zero length, import of a top level module.
49                 enum opDispatch = FromImpl!(symbol)();
50             } else {
51                 // Check if we have a full module import.
52                 enum import_ = module_ ~ "." ~ symbol;
53                 static if (IsModuleImport!import_) {
54                     // full module import
55                     enum opDispatch = FromImpl!(import_)();
56                 } else {
57                     // failed symbol import as well as full module import and
58                     // the last symbol is of nonzero length.
59                     // -> resolution failed!
60                     alias opDispatch = failedSymbol!(symbol, module_);
61                 }
62             }
63         }
64     }
65 }
66 
67 /**
68  * Used as entry point for local imports.
69  */
70 enum from = FromImpl!null();
71 
72 /**
73  * Test things that should work.
74  */
75 unittest {
76     // use a function from standard library directly.
77     from.std.stdio.writeln("Hallo");
78     // assign something from standard library to a local variable.
79     auto _ = from.std.datetime.stopwatch.AutoStart.yes;
80 }
81 
82 /**
83  * Test things that should not work.
84  */
85 unittest {
86     auto throws = false;
87     try {
88         from.std.stdio.thisFunctionDoesNotExist("Hallo");
89     } catch (Exception ex) {
90         throws = true;
91     }
92     try {
93         from.std.stdio.thisFunctionDoesNotExist(42);
94         throws = false;
95     } catch (Exception ex) {
96         throws = true;
97     }
98 
99     assert(throws);
100 
101 }