platform_path/platform/path/
project.rs1use crate::{Error, Result};
2use camino::Utf8PathBuf;
3use directories::ProjectDirs;
4use std::path::PathBuf;
5use structopt::StructOpt;
6use strum::{Display, EnumIter, EnumProperty, EnumString};
7
8#[derive(Debug, PartialEq, Eq, Hash, Display, StructOpt, EnumString, EnumIter, EnumProperty)]
9#[cfg_attr(
10 feature = "serde1",
11 derive(serde::Serialize, serde::Deserialize),
12 serde(rename_all = "kebab-case")
13)]
14#[strum(serialize_all = "kebab-case")]
15#[structopt(
16 rename_all = "kebab-case",
17 about = "project-specific standard directories"
18)]
19pub enum Project {
20 #[structopt(about = "the project's cache directory")]
21 #[strum(props(linux = "supported", macos = "supported", windows = "supported"))]
22 Cache,
23 #[structopt(about = "the project's config directory")]
24 #[strum(props(linux = "supported", macos = "supported", windows = "supported"))]
25 Config,
26 #[structopt(about = "the project's data directory")]
27 #[strum(props(linux = "supported", macos = "supported", windows = "supported"))]
28 Data,
29 #[structopt(about = "the project's local data directory")]
30 #[strum(props(linux = "supported", macos = "supported", windows = "supported"))]
31 DataLocal,
32 #[structopt(about = "the project's preference directory")]
33 #[strum(props(linux = "supported", macos = "supported", windows = "supported"))]
34 Preference,
35 #[structopt(about = "the project's path fragment")]
36 PathFragment,
37 #[structopt(about = "the project's runtime directory")]
38 #[strum(props(linux = "supported", macos = "unsupported", windows = "unsupported"))]
39 Runtime,
40 #[structopt(about = "the project's state directory")]
41 #[strum(props(linux = "supported", macos = "unsupported", windows = "unsupported"))]
42 State,
43}
44
45#[derive(Debug, StructOpt, Clone)]
46pub struct ProjectOptions {
47 #[structopt(
48 long = "project-qualifier",
49 env = "PROJECT_QUALIFIER",
50 value_name = "string",
51 help = "The reverse domain name notation of the application, excluding the organization or application name itself."
52 )]
53 pub qualifier: Option<String>,
54 #[structopt(
55 long = "project-organization",
56 env = "PROJECT_ORGANIZATION",
57 value_name = "string",
58 help = "The name of the organization that develops this application, or for which the application is developed."
59 )]
60 pub organization: Option<String>,
61 #[structopt(
62 long = "project-application",
63 env = "PROJECT_APPLICATION",
64 value_name = "string",
65 help = "The name of the application itself."
66 )]
67 pub application: String,
68}
69
70impl Project {
71 pub fn dirs(options: &ProjectOptions) -> Result<ProjectDirs> {
72 ProjectDirs::try_from(options)
73 }
74
75 pub(crate) fn path_buf(&self, options: &ProjectOptions) -> Result<PathBuf> {
76 Self::dirs(options).and_then(|project| {
77 match self {
78 Self::Cache => Some(project.cache_dir()),
79 Self::Config => Some(project.config_dir()),
80 Self::Data => Some(project.data_dir()),
81 Self::DataLocal => Some(project.data_local_dir()),
82 Self::Preference => Some(project.preference_dir()),
83 Self::PathFragment => Some(project.project_path()),
84 Self::Runtime => project.runtime_dir(),
85 Self::State => project.state_dir(),
86 }
87 .ok_or(Error::NotDefinedByPlatformStandard)
88 .map(|path| path.to_path_buf())
89 })
90 }
91
92 pub fn utf8_path_buf(&self, options: &ProjectOptions) -> Result<Utf8PathBuf> {
93 self
94 .path_buf(options)
95 .and_then(|path| Ok(Utf8PathBuf::try_from(path)?))
96 }
97}
98
99impl TryFrom<&ProjectOptions> for ProjectDirs {
100 type Error = crate::Error;
101 fn try_from(
102 ProjectOptions {
103 qualifier,
104 organization,
105 application,
106 }: &ProjectOptions,
107 ) -> Result<Self> {
108 let qualifier = match qualifier {
109 Some(qualifier) => qualifier.as_str(),
110 None => "",
111 };
112 let organization = match organization {
113 Some(organization) => organization.as_str(),
114 None => "",
115 };
116 ProjectDirs::from(qualifier, organization, application).ok_or(Error::InvalidHomeDirectory)
117 }
118}