1 /**
2  * Getting applications paths where desktop files are stored.
3  *
4  * Authors:
5  *  $(LINK2 https://github.com/FreeSlave, Roman Chistokhodov)
6  * Copyright:
7  *  Roman Chistokhodov, 2015-2017
8  * License:
9  *  $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
10  * See_Also:
11  *  $(LINK2 https://www.freedesktop.org/wiki/Specifications/desktop-entry-spec/, Desktop Entry Specification)
12  */
13 
14 module desktopfile.paths;
15 
16 private {
17     import isfreedesktop;
18     import xdgpaths;
19 
20     import std.algorithm;
21     import std.array;
22     import std.path;
23     import std.range;
24 }
25 
26 version(unittest) {
27     import std.process : environment;
28 
29     package struct EnvGuard
30     {
31         this(string env, string newValue) {
32             envVar = env;
33             envValue = environment.get(env);
34             environment[env] = newValue;
35         }
36 
37         ~this() {
38             if (envValue is null) {
39                 environment.remove(envVar);
40             } else {
41                 environment[envVar] = envValue;
42             }
43         }
44 
45         string envVar;
46         string envValue;
47     }
48 }
49 
50 /**
51  * Applications paths based on data paths.
52  * This function is available on all platforms, but requires dataPaths argument (e.g. C:\ProgramData\KDE\share on Windows)
53  * Returns: Array of paths, based on dataPaths with "applications" directory appended.
54  */
55 string[] applicationsPaths(Range)(Range dataPaths) if (isInputRange!Range && is(ElementType!Range : string)) {
56     return dataPaths.map!(p => buildPath(p, "applications")).array;
57 }
58 
59 ///
60 unittest
61 {
62     assert(equal(applicationsPaths(["share", buildPath("local", "share")]), [buildPath("share", "applications"), buildPath("local", "share", "applications")]));
63 }
64 
65 static if (isFreedesktop)
66 {
67     /**
68      * ditto, but returns paths based on known data paths.
69      * This function is defined only on freedesktop systems to avoid confusion with other systems that have data paths not compatible with Desktop Entry Spec.
70      */
71     @trusted string[] applicationsPaths() nothrow {
72         return xdgAllDataDirs("applications");
73     }
74 
75     ///
76     unittest
77     {
78         import std.process : environment;
79         auto dataHomeGuard = EnvGuard("XDG_DATA_HOME", "/home/user/data");
80         auto dataDirsGuard = EnvGuard("XDG_DATA_DIRS", "/usr/local/data:/usr/data");
81 
82         assert(applicationsPaths() == ["/home/user/data/applications", "/usr/local/data/applications", "/usr/data/applications"]);
83     }
84 
85     /**
86      * Path where .desktop files can be stored by user.
87      * This function is defined only on freedesktop systems.
88      * Note: it does not check if returned path exists and appears to be directory.
89      */
90     @safe string writableApplicationsPath() nothrow {
91         return xdgDataHome("applications");
92     }
93 
94     ///
95     unittest
96     {
97         import std.process : environment;
98         auto dataHomeGuard = EnvGuard("XDG_DATA_HOME", "/home/user/data");
99         assert(writableApplicationsPath() == "/home/user/data/applications");
100     }
101 }