platform_path/platform/path/
project.rs

1use 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}