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