1 // Copyright 2016-2021 Doug Moen
2 // Licensed under the Apache License, version 2.0
3 // See accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0
4 
5 #include <libcurv/dir_record.h>
6 
7 #include <libcurv/context.h>
8 #include <libcurv/exception.h>
9 #include <libcurv/import.h>
10 #include <cstdlib>
11 #include <iostream>
12 
13 namespace curv {
14 
15 /*
16 struct Dir_Record : public Record
17 {
18     Filesystem::path dir_;
19     struct File
20     {
21         System::Importer importer_;
22         Filesystem::path path_;
23         Value value_;
24     };
25     Symbol_Map<File> fields_;
26 };
27 */
28 
Dir_Record(Filesystem::path dir,const Context & cx)29 Dir_Record::Dir_Record(Filesystem::path dir, const Context& cx)
30 :
31     Record(Ref_Value::sty_dir_record),
32     dir_(dir),
33     fields_{}
34 {
35     namespace fs = boost::filesystem;
36     System& sys(cx.system());
37 
38     fs::directory_iterator i(dir);
39     fs::directory_iterator end;
40     for (; i != end; ++i) {
41         auto path = i->path();
42         auto name = path.leaf().string();
43         auto cname = name.c_str();
44         if (cname[0] == '.') continue;
45 
46         // Construct filename extension (includes leading '.').
47         std::string ext = path.extension().string();
48         for (char& c : ext)
49             c = std::tolower(c);
50 
51         // Import file based on its filename extension. Filenames with
52         // unknown extensions are ignored.
53         if (ext.empty()) {
54             // If a filename has no extension, it is statted to test if it is
55             // a directory. If yes, it is imported, otherwise it is ignored.
56             boost::system::error_code errcode;
57             if (fs::is_directory(path, errcode))
58                 fields_[make_symbol(cname)] = File{dir_import, path, missing};
59             else if (errcode)
60                 throw Exception(cx, stringify(path,": ",errcode.message()));
61             continue;
62         }
63         auto importp = sys.importers_.find(ext);
64         if (importp != sys.importers_.end())
65             fields_[make_symbol(path.stem().string())] =
66                 File{importp->second, path, missing};
67         // TODO: detect duplicate entries
68     }
69 }
70 
Dir_Record(Filesystem::path dir,Symbol_Map<File> fields)71 Dir_Record::Dir_Record(Filesystem::path dir, Symbol_Map<File> fields)
72 :
73     Record(Ref_Value::sty_dir_record),
74     dir_(dir),
75     fields_(fields)
76 {
77 }
78 
print_repr(std::ostream & out) const79 void Dir_Record::print_repr(std::ostream& out) const
80 {
81     out << "{";
82     bool first = true;
83     for (auto& f : fields_) {
84         if (!first) out << ",";
85         first = false;
86         out << f.first;
87     }
88     out << "}";
89 }
90 
find_field(Symbol_Ref sym,const Context & cx) const91 Value Dir_Record::find_field(Symbol_Ref sym, const Context& cx) const
92 {
93     auto p = fields_.find(sym);
94     if (p == fields_.end())
95         return missing;
96     if (p->second.value_.is_missing())
97         p->second.value_ =
98             import_value(p->second.importer_, p->second.path_, cx);
99     return p->second.value_;
100 }
101 
hasfield(Symbol_Ref sym) const102 bool Dir_Record::hasfield(Symbol_Ref sym) const
103 {
104     return fields_.find(sym) != fields_.end();
105 }
106 
size() const107 size_t Dir_Record::size() const
108 {
109     return fields_.size();
110 }
111 
112 Shared<Record>
clone() const113 Dir_Record::clone() const
114 {
115     return make<Dir_Record>(dir_, fields_);
116 }
117 
118 Value*
ref_field(Symbol_Ref name,bool need_value,const Context & cx)119 Dir_Record::ref_field(Symbol_Ref name, bool need_value, const Context& cx)
120 {
121     auto p = fields_.find(name);
122     if (p == fields_.end()) {
123         throw Exception(cx, stringify(Value{share(*this)},
124             " has no field named ", name));
125     }
126     if (p->second.value_.is_missing()) {
127         if (need_value)
128             p->second.value_ =
129                 import_value(p->second.importer_, p->second.path_, cx);
130     }
131     return &p->second.value_;
132 }
133 
134 /*
135 class Dir_Record::Iter : public Record::Iter
136 {
137 protected:
138     Dir_Record& rec_;
139     Symbol_Map<File>::const_iterator i_;
140 };
141 */
142 
Iter(const Dir_Record & rec)143 Dir_Record::Iter::Iter(const Dir_Record& rec)
144 :
145     rec_{rec},
146     i_{rec.fields_.begin()}
147 {
148     if (i_ != rec_.fields_.end()) {
149         key_ = i_->first;
150         value_ = i_->second.value_;
151     }
152 }
153 
load_value(const Context & cx)154 void Dir_Record::Iter::load_value(const Context& cx)
155 {
156     if (i_ != rec_.fields_.end()) {
157         if (i_->second.value_.is_missing())
158             i_->second.value_ =
159                 import_value(i_->second.importer_, i_->second.path_, cx);
160         value_ = i_->second.value_;
161     }
162 }
163 
next()164 void Dir_Record::Iter::next()
165 {
166     ++i_;
167     if (i_ != rec_.fields_.end()) {
168         key_ = i_->first;
169         value_ = i_->second.value_;
170     } else
171         key_ = Symbol_Ref();
172 }
173 
iter() const174 std::unique_ptr<Record::Iter> Dir_Record::iter() const
175 {
176     return std::make_unique<Dir_Record::Iter>(*this);
177 }
178 
179 }
180