1 module commandcontroller;
2 import std.stdio;
3 import std.range;
4 import std.algorithm;
5 import std.algorithm.comparison;
6 import std.conv;
7 import std.array;
8 import std.typecons;
9 import std.traits;
10 import std.string;
11 import std.uri;
12 import url;
13 import std.algorithm.iteration;
14 import clients.sourceclient;
15 import clients.opmlclient;
16 import clients.feedsclient;
17 import models;
18 import apierror;
19 
20 class CommandController {
21 
22     protected SourceClient _sourceClient;
23     protected OpmlClient _opmlClient;
24     protected FeedsClient _feedsClient;
25 
26     private static uint defaultSize = 10;
27 
28     public this(
29         SourceClient sourceClient,
30         OpmlClient opmlClient,
31         FeedsClient feedsClient
32     ) {
33         _sourceClient = sourceClient;
34         _opmlClient = opmlClient;
35         _feedsClient = feedsClient;
36     }
37 
38     public void all() {
39        auto result = _sourceClient.getAllSources();
40        foreach (source; result) {
41          writeln(to!string(source.id) ~ ": " ~ source.url);
42        }
43     }
44 
45     public void latest() {
46         auto page = _sourceClient.latest();
47         printPage(page, "No entries");
48     }
49 
50     public void favorites() {
51         auto page = _feedsClient.favorites();
52         printPage(page, "No new entries");
53     }
54 
55     private static void printPage(Page page, string whenEmpty) {
56         auto xs = page.resources;
57         if (page.isEmpty()) {
58             writeln("No new entries");
59             return;
60         }
61         ulong max = 0;
62 
63         foreach (x; xs) {
64             auto len = x.title.walkLength;
65             if (len > max) {
66                 max = len;
67             }
68         }
69 
70         foreach (x; xs) {
71             auto len = x.title.walkLength;
72             auto diffSize = max - len + defaultSize;
73             auto between = replicate(" ", diffSize);
74             writeln(x.title ~ between ~ x.url);
75         }
76         writeln("Total: " ~ to!string(page.total));
77     }
78 
79     public void download() {
80         _opmlClient.download();
81     }
82 
83     public void add(string url, string name, string interval) {
84         auto tmpInterval = 8;
85         auto tmpName = url.parseURL.host;
86 
87         if (uriLength(url) < 0) {
88             writeln(url ~ " is not a valid URL!");
89             return;
90         }
91         if (!interval.empty) {
92             tmpInterval = conv( interval);
93             if (tmpInterval <= 0) {
94                 writeln("Interval must be positive integer");
95                 return;
96             }
97         }
98         if (!name.empty) {
99             tmpName = name;
100         }
101 
102         auto newSource = NewSource(url, tmpName, tmpInterval);
103         try {
104             auto result = _sourceClient.addSource(newSource);
105             if (result) {
106                 writeln("Source " ~ url ~ " was added");
107             } else {
108                 writeln("Source " ~ url ~ " was not added");
109             }
110         } catch(Exception ex) {
111             writeln("Can not add new source " ~ url ~ " because: " ~ ex.message);
112         }
113     }
114 
115     private static int conv(string str) {
116         try {
117             return to!int(str);
118         } catch (Exception ex) {
119             return -1;
120         }
121     }
122 
123     public void overview(string str) {
124         int id = 0;
125         try {
126             id = to!int(str);
127             if (id <= 0) {
128                 overviewHelp(str);
129                 return;
130             }
131             auto source = _sourceClient.getOne(id);
132             auto overview = _sourceClient.overview(id);
133             auto tmp = max(findMax(source), findMax(overview));
134             print(source, tmp);
135             print(overview, tmp);
136 
137         } catch(ConvException ex) {
138             overviewHelp(str);
139         } catch(ApiError error) {
140             if (error.is404()) {
141                 writeln("Source " ~ to!string(id) ~ " not found");
142                 return;
143             }
144             writeln(error.message());
145         }
146     }
147 
148     private ulong findMax(T)(T instance) {
149         ulong max = 0;
150         foreach (member; __traits(allMembers, T)) {
151             auto len = member.walkLength;
152             if (len > max) {
153                 max = len;
154             }
155         }
156         return max;
157     }
158 
159     private T print(T)(T instance, ulong max) {
160         foreach (member; __traits(allMembers, T)) {
161             auto value = __traits(getMember, instance, member);
162             auto len = to!string(member).walkLength;
163             auto attrs = __traits(getAttributes, __traits(getMember, T, member));
164             auto field = member.capitalize;
165             foreach(attr; attrs) {
166                 static if (is(typeof(attr) == Alias)) {
167                     field = attr.name;
168                 }
169             }
170             if (!hasUDA!(__traits(getMember, T, member), Ignore)) {
171                 print(max, to!string(value), field);
172             }
173         }
174         return instance;
175     }
176 
177     private static void print(ulong max, string value, string member) {
178         auto len = member.walkLength;
179         auto diffSize = max - len + defaultSize;
180         auto between = replicate(" ", diffSize);
181         writeln(member ~ between ~ to!string(value));
182     }
183 
184     private static overviewHelp(string str) {
185         writeln("Positive number is required, given: " ~ str);
186     }
187 
188 }