<commit_before>// Copyright (c) 2016 The Rouille developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::io;
use std::io::Cursor;
use std::io::Read;
use std::fs::File;
use rustc_serialize;

/// Contains a prototype of a response.
/// The response is only sent when you call `Request::respond`.
pub struct Response {
    /// The status code to return to the user.
    pub status_code: u16,

    /// List of headers to be returned in the response.
    ///
    /// Note that important headers such as `Connection` or `Content-Length` will be ignored
    /// from this list.
    // TODO: document precisely which headers
    pub headers: Vec<(String, String)>,

    /// An opaque type that contains the body of the response.
    pub data: ResponseBody,
}

impl Response {
    /// Returns true if the status code of this `Response` indicates success.
    ///
    /// This is the range [200-399].
    #[inline]
    pub fn success(&self) -> bool {
        self.status_code >= 200 && self.status_code < 400
    }

    /// Shortcut for `!response.success()`.
    #[inline]
    pub fn error(&self) -> bool {
        !self.success()
    }

    /// Builds a `Response` that redirects the user to another URL with a 303 status code.
    #[inline]
    pub fn redirect(target: &str) -> Response {
        Response {
            status_code: 303,
            headers: vec![("Location".to_owned(), target.to_owned())],
            data: ResponseBody::empty(),
        }
    }

    /// Builds a `Response` that outputs HTML.
    #[inline]
    pub fn html<D>(content: D) -> Response where D: Into<Vec<u8>> {
        Response {
            status_code: 200,
            headers: vec![("Content-Type".to_owned(), "text/html; charset=utf8".to_owned())],
            data: ResponseBody::from_data(content),
        }
    }

    /// Builds a `Response` that outputs plain text.
    #[inline]
    pub fn text<S>(text: S) -> Response where S: Into<String> {
        Response {
            status_code: 200,
            headers: vec![("Content-Type".to_owned(), "text/plain; charset=utf8".to_owned())],
            data: ResponseBody::from_string(text),
        }
    }

    /// Builds a `Response` that outputs JSON.
    #[inline]
    pub fn json<T>(content: &T) -> Response where T: rustc_serialize::Encodable {
        let data = rustc_serialize::json::encode(content).unwrap();

        Response {
            status_code: 200,
            headers: vec![("Content-Type".to_owned(), "application/json".to_owned())],
            data: ResponseBody::from_data(data),
        }
    }

    /// Builds a `Response` that returns a `401 Not Authorized` status
    /// and a `WWW-Authenticate` header.
    #[inline]
    pub fn basic_http_auth_login_required(realm: &str) -> Response {
        // TODO: escape the realm
        Response {
            status_code: 401,
            headers: vec![("WWW-Authenticate".to_owned(), format!("Basic realm=\"{}\"", realm))],
            data: ResponseBody::empty(),
        }
    }

    /// Builds an empty `Response` with a 400 status code.
    #[inline]
    pub fn empty_400() -> Response {
        Response {
            status_code: 400,
            headers: vec![],
            data: ResponseBody::empty()
        }
    }

    /// Builds an empty `Response` with a 404 status code.
    #[inline]
    pub fn empty_404() -> Response {
        Response {
            status_code: 404,
            headers: vec![],
            data: ResponseBody::empty()
        }
    }

    /// Changes the status code of the response.
    #[inline]
    pub fn with_status_code(mut self, code: u16) -> Response {
        self.status_code = code;
        self
    }
}

/// An opaque type that represents the body of a response.
pub struct ResponseBody {
    data: Box<Read + Send>,
    data_length: Option<usize>,
}

impl ResponseBody {
    /// UNSTABLE. Extracts the content of the response.
    #[doc(hidden)]
    #[inline]
    pub fn into_inner(self) -> (Box<Read + Send>, Option<usize>) {
        (self.data, self.data_length)
    }

    /// Builds a `ResponseBody` that doesn't return any data.
    #[inline]
    pub fn empty() -> ResponseBody {
        ResponseBody {
            data: Box::new(io::empty()),
            data_length: Some(0),
        }
    }

    /// Builds a new `ResponseBody` that will read the data from a `Read`.
    ///
    /// Note that this is suboptimal compared to other constructors because the length
    /// isn't known in advance.
    #[inline]
    pub fn from_reader<R>(data: R) -> ResponseBody where R: Read + Send + 'static {
        ResponseBody {
            data: Box::new(data),
            data_length: None,
        }
    }

    /// Builds a new `ResponseBody` that returns the given data.
    #[inline]
    pub fn from_data<D>(data: D) -> ResponseBody where D: Into<Vec<u8>> {
        let data = data.into();
        let len = data.len();

        ResponseBody {
            data: Box::new(Cursor::new(data)),
            data_length: Some(len),
        }
    }

    /// Builds a new `ResponseBody` that returns the content of the given file.
    #[inline]
    pub fn from_file(file: File) -> ResponseBody {
        let len = file.metadata().map(|metadata| metadata.len() as usize).ok();

        ResponseBody {
            data: Box::new(file),
            data_length: len,
        }
    }

    /// Builds a new `ResponseBody` that returns an UTF-8 string.
    #[inline]
    pub fn from_string<S>(data: S) -> ResponseBody where S: Into<String> {
        ResponseBody::from_data(data.into().into_bytes())
    }
}
<commit_msg>Add some documentation and lots of examples to Response<commit_after>// Copyright (c) 2016 The Rouille developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::io;
use std::io::Cursor;
use std::io::Read;
use std::fs::File;
use rustc_serialize;

/// Contains a prototype of a response.
///
/// The response is only sent to the client when you return the `Response` object from your
/// request handler. This means that you are free to create as many `Response` objects as you want.
pub struct Response {
    /// The status code to return to the user.
    pub status_code: u16,

    /// List of headers to be returned in the response.
    ///
    /// Note that important headers such as `Connection` or `Content-Length` will be ignored
    /// from this list.
    // TODO: document precisely which headers
    pub headers: Vec<(String, String)>,

    /// An opaque type that contains the body of the response.
    pub data: ResponseBody,
}

impl Response {
    /// Returns true if the status code of this `Response` indicates success.
    ///
    /// This is the range [200-399].
    ///
    /// # Example
    ///
    /// ```
    /// use rouille::Response;
    /// let response = Response::text("hello world");
    /// assert!(response.success());
    /// ```
    #[inline]
    pub fn success(&self) -> bool {
        self.status_code >= 200 && self.status_code < 400
    }

    /// Shortcut for `!response.success()`.
    ///
    /// # Example
    ///
    /// ```
    /// use rouille::Response;
    /// let response = Response::empty_400();
    /// assert!(response.error());
    /// ```
    #[inline]
    pub fn error(&self) -> bool {
        !self.success()
    }

    /// Builds a `Response` that redirects the user to another URL with a 303 status code.
    ///
    /// # Example
    ///
    /// ```
    /// use rouille::Response;
    /// let response = Response::redirect("/foo");
    /// ```
    #[inline]
    pub fn redirect(target: &str) -> Response {
        Response {
            status_code: 303,
            headers: vec![("Location".to_owned(), target.to_owned())],
            data: ResponseBody::empty(),
        }
    }

    /// Builds a `Response` that outputs HTML.
    ///
    /// # Example
    ///
    /// ```
    /// use rouille::Response;
    /// let response = Response::text("<p>hello <strong>world</strong></p>");
    /// ```
    #[inline]
    pub fn html<D>(content: D) -> Response where D: Into<Vec<u8>> {
        Response {
            status_code: 200,
            headers: vec![("Content-Type".to_owned(), "text/html; charset=utf8".to_owned())],
            data: ResponseBody::from_data(content),
        }
    }

    /// Builds a `Response` that outputs plain text.
    ///
    /// # Example
    ///
    /// ```
    /// use rouille::Response;
    /// let response = Response::text("hello world");
    /// ```
    #[inline]
    pub fn text<S>(text: S) -> Response where S: Into<String> {
        Response {
            status_code: 200,
            headers: vec![("Content-Type".to_owned(), "text/plain; charset=utf8".to_owned())],
            data: ResponseBody::from_string(text),
        }
    }

    /// Builds a `Response` that outputs JSON.
    ///
    /// # Example
    ///
    /// ```ignore       // TODO: unignore after https://github.com/rust-lang/rust/issues/36863
    /// extern crate rustc_serialize;
    /// use rouille::Response;
    ///
    /// #[derive(RustcEncodable)]
    /// struct MyStruct {
    ///     field1: String,
    ///     field2: i32,
    /// }
    ///
    /// let response = Response::json(MyStruct { field1: "hello".to_owned(), field2: 5 });
    /// // The Response will contain something like `{ field1: "hello", field2: 5 }`
    /// ```
    #[inline]
    pub fn json<T>(content: &T) -> Response where T: rustc_serialize::Encodable {
        let data = rustc_serialize::json::encode(content).unwrap();

        Response {
            status_code: 200,
            headers: vec![("Content-Type".to_owned(), "application/json".to_owned())],
            data: ResponseBody::from_data(data),
        }
    }

    /// Builds a `Response` that returns a `401 Not Authorized` status
    /// and a `WWW-Authenticate` header.
    ///
    /// # Example
    ///
    /// ```
    /// use rouille::Response;
    /// let response = Response::basic_http_auth_login_required("realm");
    /// ```
    #[inline]
    pub fn basic_http_auth_login_required(realm: &str) -> Response {
        // TODO: escape the realm
        Response {
            status_code: 401,
            headers: vec![("WWW-Authenticate".to_owned(), format!("Basic realm=\"{}\"", realm))],
            data: ResponseBody::empty(),
        }
    }

    /// Builds an empty `Response` with a 400 status code.
    ///
    /// # Example
    ///
    /// ```
    /// use rouille::Response;
    /// let response = Response::empty_400();
    /// ```
    #[inline]
    pub fn empty_400() -> Response {
        Response {
            status_code: 400,
            headers: vec![],
            data: ResponseBody::empty()
        }
    }

    /// Builds an empty `Response` with a 404 status code.
    ///
    /// # Example
    ///
    /// ```
    /// use rouille::Response;
    /// let response = Response::empty_404();
    /// ```
    #[inline]
    pub fn empty_404() -> Response {
        Response {
            status_code: 404,
            headers: vec![],
            data: ResponseBody::empty()
        }
    }

    /// Changes the status code of the response.
    ///
    /// # Example
    ///
    /// ```
    /// use rouille::Response;
    /// let response = Response::text("hello world").with_status_code(500);
    /// ```
    #[inline]
    pub fn with_status_code(mut self, code: u16) -> Response {
        self.status_code = code;
        self
    }
}

/// An opaque type that represents the body of a response.
///
/// You can't access the inside of this struct, but you can build one by using one of the provided
/// constructors.
///
/// # Example
///
/// ```
/// use rouille::ResponseBody;
/// let body = ResponseBody::from_string("hello world");
/// ```
pub struct ResponseBody {
    data: Box<Read + Send>,
    data_length: Option<usize>,
}

impl ResponseBody {
    /// UNSTABLE. Extracts the content of the response. Do not use.
    #[doc(hidden)]
    #[inline]
    pub fn into_inner(self) -> (Box<Read + Send>, Option<usize>) {
        (self.data, self.data_length)
    }

    /// Builds a `ResponseBody` that doesn't return any data.
    ///
    /// # Example
    ///
    /// ```
    /// use rouille::ResponseBody;
    /// let body = ResponseBody::empty();
    /// ```
    #[inline]
    pub fn empty() -> ResponseBody {
        ResponseBody {
            data: Box::new(io::empty()),
            data_length: Some(0),
        }
    }

    /// Builds a new `ResponseBody` that will read the data from a `Read`.
    ///
    /// Note that this is suboptimal compared to other constructors because the length
    /// isn't known in advance.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use std::io;
    /// use std::io::Read;
    /// use rouille::ResponseBody;
    ///
    /// let body = ResponseBody::from_reader(io::stdin().take(128));
    /// ```
    #[inline]
    pub fn from_reader<R>(data: R) -> ResponseBody where R: Read + Send + 'static {
        ResponseBody {
            data: Box::new(data),
            data_length: None,
        }
    }

    /// Builds a new `ResponseBody` that returns the given data.
    ///
    /// # Example
    ///
    /// ```
    /// use rouille::ResponseBody;
    /// let body = ResponseBody::from_data(vec![12u8, 97, 34]);
    /// ```
    #[inline]
    pub fn from_data<D>(data: D) -> ResponseBody where D: Into<Vec<u8>> {
        let data = data.into();
        let len = data.len();

        ResponseBody {
            data: Box::new(Cursor::new(data)),
            data_length: Some(len),
        }
    }

    /// Builds a new `ResponseBody` that returns the content of the given file.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use std::fs::File;
    /// use rouille::ResponseBody;
    ///
    /// let file = File::open("page.html").unwrap();
    /// let body = ResponseBody::from_file(file);
    /// ```
    #[inline]
    pub fn from_file(file: File) -> ResponseBody {
        let len = file.metadata().map(|metadata| metadata.len() as usize).ok();

        ResponseBody {
            data: Box::new(file),
            data_length: len,
        }
    }

    /// Builds a new `ResponseBody` that returns an UTF-8 string.
    ///
    /// # Example
    ///
    /// ```
    /// use rouille::ResponseBody;
    /// let body = ResponseBody::from_string("hello world");
    /// ```
    #[inline]
    pub fn from_string<S>(data: S) -> ResponseBody where S: Into<String> {
        ResponseBody::from_data(data.into().into_bytes())
    }
}
<|endoftext|>


<commit_before>use std::collections::{BTreeMap, HashMap};
use std::fs::{create_dir_all, File};
use std::io::prelude::*;
use std::path::{Path, PathBuf};

use config::Config;
use log::info;
use serde_derive::{Deserialize, Serialize};
use shellexpand::tilde;

use crate::error::Error;
use crate::platform::directories::*;

/// All settings which are used by both, the client and the daemon
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct Shared {
    /// Don't access this property directly, but rather use the getter with the same name.
    /// It's only public to allow proper integration testing.
    ///
    /// The directory that is used for all runtime information. \
    /// I.e. task logs, sockets, state dumps, etc.
    pub pueue_directory: PathBuf,
    /// If this is set to true, unix sockets will be used.
    /// Otherwise we default to TCP+TLS
    #[cfg(not(target_os = "windows"))]
    pub use_unix_socket: bool,
    /// Don't access this property directly, but rather use the getter with the same name.
    /// It's only public to allow proper integration testing.
    ///
    /// The path to the unix socket.
    #[cfg(not(target_os = "windows"))]
    pub unix_socket_path: PathBuf,

    /// The TCP hostname/ip address.
    pub host: String,
    /// The TCP port.
    pub port: String,
    /// Don't access this property directly, but rather use the getter with the same name.
    /// It's only public to allow proper integration testing.
    ///
    /// The path to the TLS certificate used by the daemon. \
    /// This is also used by the client to verify the daemon's identity.
    pub daemon_cert: PathBuf,
    /// Don't access this property directly, but rather use the getter with the same name.
    /// It's only public to allow proper integration testing.
    ///
    /// The path to the TLS key used by the daemon.
    pub daemon_key: PathBuf,
    /// Don't access this property directly, but rather use the getter with the same name.
    /// It's only public to allow proper integration testing.
    ///
    /// The path to the file containing the shared secret used to authenticate the client.
    pub shared_secret_path: PathBuf,
}

/// All settings which are used by the client
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct Client {
    /// Whether the client should read the logs directly from disk or whether it should
    /// request the data from the daemon via socket.
    pub read_local_logs: bool,
    /// Whether the client should show a confirmation question on potential dangerous actions.
    pub show_confirmation_questions: bool,
    /// Whether aliases specified in `pueue_aliases.yml` should be expanded in the `pueue status`
    /// or shown in their short form.
    pub show_expanded_aliases: bool,
    /// Whether the client should use dark shades instead of regular colors.
    #[serde(default = "default_dark_mode")]
    pub dark_mode: bool,
    /// The max amount of lines each task get's in the `pueue status` view.
    pub max_status_lines: Option<usize>,
}

/// All settings which are used by the daemon
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct Daemon {
    /// How many parallel tasks a group should have by default
    pub default_parallel_tasks: usize,
    /// Whether a group should be paused as soon as a single task fails
    pub pause_group_on_failure: bool,
    /// Whether the daemon (and all groups) should be paused as soon as a single task fails
    pub pause_all_on_failure: bool,
    /// The callback that's called whenever a task finishes.
    pub callback: Option<String>,
    /// The amount of log lines from stdout/stderr that are passed to the callback command.
    #[serde(default = "default_callback_log_lines")]
    pub callback_log_lines: usize,
    /// This shouldn't be manipulated manually if the daemon is running.
    /// This represents all known groups and their amount of parallel tasks.
    pub groups: BTreeMap<String, usize>,
}

/// The parent settings struct. \
/// This contains all other setting structs.
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct Settings {
    pub client: Client,
    pub daemon: Daemon,
    pub shared: Shared,
}

impl Shared {
    pub fn expand(old_path: &Path) -> PathBuf {
        PathBuf::from(tilde(&old_path.to_string_lossy()).into_owned())
    }

    pub fn pueue_directory(&self) -> PathBuf {
        Shared::expand(&self.pueue_directory)
    }

    #[cfg(not(target_os = "windows"))]
    pub fn unix_socket_path(&self) -> PathBuf {
        Shared::expand(&self.unix_socket_path)
    }

    pub fn daemon_cert(&self) -> PathBuf {
        Shared::expand(&self.daemon_cert)
    }
    pub fn daemon_key(&self) -> PathBuf {
        Shared::expand(&self.daemon_key)
    }
    pub fn shared_secret_path(&self) -> PathBuf {
        Shared::expand(&self.shared_secret_path)
    }
}

impl Settings {
    /// Read from existing config files.
    /// If no config files can be found or fields are missing, an error is returned.
    pub fn read(from_file: &Option<PathBuf>) -> Result<Settings, Error> {
        let config = Config::new();

        parse_config(config, true, from_file)
    }

    /// Try to read existing config files and
    /// This function creates a new configuration instance and
    /// populates it with default values for every option. \
    /// If a local config file already exists, it is parsed and
    /// overrules the default option values.
    ///
    /// The default local config is located at "~/.config/pueue.yml".
    ///
    /// If `require_config` is `true`, an error will be thrown, if no configuration file can be found.
    /// This is utilized by the client, since only the daemon is allowed to touch the configuration
    /// file.
    pub fn read_with_defaults(
        require_config: bool,
        from_file: &Option<PathBuf>,
    ) -> Result<Settings, Error> {
        let config = Settings::default_config()?;

        parse_config(config, require_config, from_file)
    }

    pub fn default_config() -> Result<Config, Error> {
        let mut config = Config::new();
        let pueue_path = default_pueue_path()?;
        config
            .set_default("shared.pueue_directory", pueue_path.clone())
            .unwrap();
        #[cfg(not(target_os = "windows"))]
        config.set_default("shared.use_unix_socket", true).unwrap();
        #[cfg(not(target_os = "windows"))]
        config
            .set_default("shared.unix_socket_path", get_unix_socket_path()?)
            .unwrap();

        config.set_default("shared.host", "127.0.0.1").unwrap();
        config.set_default("shared.port", "6924").unwrap();
        config.set_default("shared.tls_enabled", true).unwrap();
        config
            .set_default(
                "shared.daemon_key",
                pueue_path.clone() + "/certs/daemon.key",
            )
            .unwrap();
        config
            .set_default(
                "shared.daemon_cert",
                pueue_path.clone() + "/certs/daemon.cert",
            )
            .unwrap();
        config
            .set_default("shared.shared_secret_path", pueue_path + "/shared_secret")
            .unwrap();

        // Client specific config
        config.set_default("client.read_local_logs", true).unwrap();
        config
            .set_default("client.show_expanded_aliases", false)
            .unwrap();
        config
            .set_default("client.show_confirmation_questions", false)
            .unwrap();
        config.set_default("client.dark_mode", false).unwrap();
        config
            .set_default("client.max_status_lines", None::<i64>)
            .unwrap();

        // Daemon specific config
        config
            .set_default("daemon.default_parallel_tasks", 1)
            .unwrap();
        config
            .set_default("daemon.pause_group_on_failure", false)
            .unwrap();
        config
            .set_default("daemon.pause_all_on_failure", false)
            .unwrap();
        config
            .set_default("daemon.callback", None::<String>)
            .unwrap();
        config.set_default("daemon.callback_log_lines", 10).unwrap();
        config
            .set_default("daemon.groups", HashMap::<String, i64>::new())
            .unwrap();

        Ok(config)
    }

    /// Save the current configuration as a file to the given path. \
    /// If no path is given, the default configuration path will be used. \
    /// The file is then written to the main configuration directory of the respective OS.
    pub fn save(&self, path: &Option<PathBuf>) -> Result<(), Error> {
        let config_path = if let Some(path) = path {
            path.clone()
        } else {
            default_config_directory()?.join("pueue.yml")
        };
        let config_dir = config_path
            .parent()
            .ok_or_else(|| Error::InvalidPath("Couldn't resolve config directory".into()))?;

        // Create the config dir, if it doesn't exist yet
        if !config_dir.exists() {
            create_dir_all(config_dir)?;
        }

        let content = match serde_yaml::to_string(self) {
            Ok(content) => content,
            Err(error) => {
                return Err(Error::Generic(format!(
                    "Configuration file serialization failed:\n{}",
                    error
                )))
            }
        };
        let mut file = File::create(config_path)?;
        file.write_all(content.as_bytes())?;

        Ok(())
    }
}

/// Get all possible configuration paths and check if there are
/// configuration files at those locations.
/// All configs will be merged by importance.
///
/// If `require_config` is `true`, an error will be thrown, if no configuration file can be found.
fn parse_config(
    mut config: Config,
    require_config: bool,
    from_file: &Option<PathBuf>,
) -> Result<Settings, Error> {
    // Load the config from a very specific file path
    if let Some(path) = from_file {
        if !path.exists() || !path.is_file() {
            return Err(Error::FileNotFound(format!(
                "Couldn't find config at path {:?}",
                path
            )));
        }
        info!("Using config file at: {:?}", path);
        let config_file = config::File::with_name(path.to_str().unwrap());
        config.merge(config_file)?;
        return Ok(config.try_into()?);
    };

    let mut config_found = false;
    info!("Parsing config files");
    for directory in get_config_directories()?.into_iter() {
        let path = directory.join("pueue.yml");
        info!("Checking path: {:?}", &path);

        // Check if the file exists and parse it.
        if path.exists() && path.is_file() {
            info!("Found config file at: {:?}", path);
            config_found = true;
            let config_file = config::File::with_name(path.to_str().unwrap());
            config.merge(config_file)?;
        }
    }

    if require_config && !config_found {
        return Err(Error::FileNotFound(
            "Couldn't find a configuration file. Did you start the daemon yet?".into(),
        ));
    }

    // Try to can deserialize the entire configuration
    Ok(config.try_into()?)
}

/// The default value for the `dark_mode` client settings.
/// Needed to keep backward compatibility between v0.11 and v0.12
fn default_dark_mode() -> bool {
    false
}

/// The default value for the `dark_mode` client settings.
/// Needed to keep backward compatibility between v0.11 and v0.12
fn default_callback_log_lines() -> usize {
    10
}
<commit_msg>feature: Add config for status datetime formatting<commit_after>use std::collections::{BTreeMap, HashMap};
use std::fs::{create_dir_all, File};
use std::io::prelude::*;
use std::path::{Path, PathBuf};

use config::Config;
use log::info;
use serde_derive::{Deserialize, Serialize};
use shellexpand::tilde;

use crate::error::Error;
use crate::platform::directories::*;

/// All settings which are used by both, the client and the daemon
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct Shared {
    /// Don't access this property directly, but rather use the getter with the same name.
    /// It's only public to allow proper integration testing.
    ///
    /// The directory that is used for all runtime information. \
    /// I.e. task logs, sockets, state dumps, etc.
    pub pueue_directory: PathBuf,
    /// If this is set to true, unix sockets will be used.
    /// Otherwise we default to TCP+TLS
    #[cfg(not(target_os = "windows"))]
    pub use_unix_socket: bool,
    /// Don't access this property directly, but rather use the getter with the same name.
    /// It's only public to allow proper integration testing.
    ///
    /// The path to the unix socket.
    #[cfg(not(target_os = "windows"))]
    pub unix_socket_path: PathBuf,

    /// The TCP hostname/ip address.
    pub host: String,
    /// The TCP port.
    pub port: String,
    /// Don't access this property directly, but rather use the getter with the same name.
    /// It's only public to allow proper integration testing.
    ///
    /// The path to the TLS certificate used by the daemon. \
    /// This is also used by the client to verify the daemon's identity.
    pub daemon_cert: PathBuf,
    /// Don't access this property directly, but rather use the getter with the same name.
    /// It's only public to allow proper integration testing.
    ///
    /// The path to the TLS key used by the daemon.
    pub daemon_key: PathBuf,
    /// Don't access this property directly, but rather use the getter with the same name.
    /// It's only public to allow proper integration testing.
    ///
    /// The path to the file containing the shared secret used to authenticate the client.
    pub shared_secret_path: PathBuf,
}

/// All settings which are used by the client
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct Client {
    /// Whether the client should read the logs directly from disk or whether it should
    /// request the data from the daemon via socket.
    pub read_local_logs: bool,
    /// Whether the client should show a confirmation question on potential dangerous actions.
    pub show_confirmation_questions: bool,
    /// Whether aliases specified in `pueue_aliases.yml` should be expanded in the `pueue status`
    /// or shown in their short form.
    pub show_expanded_aliases: bool,
    /// Whether the client should use dark shades instead of regular colors.
    #[serde(default = "default_dark_mode")]
    pub dark_mode: bool,
    /// The max amount of lines each task get's in the `pueue status` view.
    pub max_status_lines: Option<usize>,
    #[serde(default = "default_status_time_format")]
    /// The format that will be used to display time formats in `pueue status`.
    pub status_time_format: String,
    #[serde(default = "default_status_datetime_format")]
    /// The format that will be used to display datetime formats in `pueue status`.
    pub status_datetime_format: String,
}

/// All settings which are used by the daemon
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct Daemon {
    /// How many parallel tasks a group should have by default
    pub default_parallel_tasks: usize,
    /// Whether a group should be paused as soon as a single task fails
    pub pause_group_on_failure: bool,
    /// Whether the daemon (and all groups) should be paused as soon as a single task fails
    pub pause_all_on_failure: bool,
    /// The callback that's called whenever a task finishes.
    pub callback: Option<String>,
    /// The amount of log lines from stdout/stderr that are passed to the callback command.
    #[serde(default = "default_callback_log_lines")]
    pub callback_log_lines: usize,
    /// This shouldn't be manipulated manually if the daemon is running.
    /// This represents all known groups and their amount of parallel tasks.
    pub groups: BTreeMap<String, usize>,
}

/// The parent settings struct. \
/// This contains all other setting structs.
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct Settings {
    pub client: Client,
    pub daemon: Daemon,
    pub shared: Shared,
}

impl Shared {
    pub fn expand(old_path: &Path) -> PathBuf {
        PathBuf::from(tilde(&old_path.to_string_lossy()).into_owned())
    }

    pub fn pueue_directory(&self) -> PathBuf {
        Shared::expand(&self.pueue_directory)
    }

    #[cfg(not(target_os = "windows"))]
    pub fn unix_socket_path(&self) -> PathBuf {
        Shared::expand(&self.unix_socket_path)
    }

    pub fn daemon_cert(&self) -> PathBuf {
        Shared::expand(&self.daemon_cert)
    }
    pub fn daemon_key(&self) -> PathBuf {
        Shared::expand(&self.daemon_key)
    }
    pub fn shared_secret_path(&self) -> PathBuf {
        Shared::expand(&self.shared_secret_path)
    }
}

impl Settings {
    /// Read from existing config files.
    /// If no config files can be found or fields are missing, an error is returned.
    pub fn read(from_file: &Option<PathBuf>) -> Result<Settings, Error> {
        let config = Config::new();

        parse_config(config, true, from_file)
    }

    /// Try to read existing config files and
    /// This function creates a new configuration instance and
    /// populates it with default values for every option. \
    /// If a local config file already exists, it is parsed and
    /// overrules the default option values.
    ///
    /// The default local config is located at "~/.config/pueue.yml".
    ///
    /// If `require_config` is `true`, an error will be thrown, if no configuration file can be found.
    /// This is utilized by the client, since only the daemon is allowed to touch the configuration
    /// file.
    pub fn read_with_defaults(
        require_config: bool,
        from_file: &Option<PathBuf>,
    ) -> Result<Settings, Error> {
        let config = Settings::default_config()?;

        parse_config(config, require_config, from_file)
    }

    pub fn default_config() -> Result<Config, Error> {
        let mut config = Config::new();
        let pueue_path = default_pueue_path()?;
        config
            .set_default("shared.pueue_directory", pueue_path.clone())
            .unwrap();
        #[cfg(not(target_os = "windows"))]
        config.set_default("shared.use_unix_socket", true).unwrap();
        #[cfg(not(target_os = "windows"))]
        config
            .set_default("shared.unix_socket_path", get_unix_socket_path()?)
            .unwrap();

        config.set_default("shared.host", "127.0.0.1").unwrap();
        config.set_default("shared.port", "6924").unwrap();
        config.set_default("shared.tls_enabled", true).unwrap();
        config
            .set_default(
                "shared.daemon_key",
                pueue_path.clone() + "/certs/daemon.key",
            )
            .unwrap();
        config
            .set_default(
                "shared.daemon_cert",
                pueue_path.clone() + "/certs/daemon.cert",
            )
            .unwrap();
        config
            .set_default("shared.shared_secret_path", pueue_path + "/shared_secret")
            .unwrap();

        // Client specific config
        config.set_default("client.read_local_logs", true).unwrap();
        config
            .set_default("client.show_expanded_aliases", false)
            .unwrap();
        config
            .set_default("client.show_confirmation_questions", false)
            .unwrap();
        config.set_default("client.dark_mode", false).unwrap();
        config
            .set_default("client.max_status_lines", None::<i64>)
            .unwrap();
        config
            .set_default("client.newline_between_date_and_time", false)
            .unwrap();

        // Daemon specific config
        config
            .set_default("daemon.default_parallel_tasks", 1)
            .unwrap();
        config
            .set_default("daemon.pause_group_on_failure", false)
            .unwrap();
        config
            .set_default("daemon.pause_all_on_failure", false)
            .unwrap();
        config
            .set_default("daemon.callback", None::<String>)
            .unwrap();
        config.set_default("daemon.callback_log_lines", 10).unwrap();
        config
            .set_default("daemon.groups", HashMap::<String, i64>::new())
            .unwrap();

        Ok(config)
    }

    /// Save the current configuration as a file to the given path. \
    /// If no path is given, the default configuration path will be used. \
    /// The file is then written to the main configuration directory of the respective OS.
    pub fn save(&self, path: &Option<PathBuf>) -> Result<(), Error> {
        let config_path = if let Some(path) = path {
            path.clone()
        } else {
            default_config_directory()?.join("pueue.yml")
        };
        let config_dir = config_path
            .parent()
            .ok_or_else(|| Error::InvalidPath("Couldn't resolve config directory".into()))?;

        // Create the config dir, if it doesn't exist yet
        if !config_dir.exists() {
            create_dir_all(config_dir)?;
        }

        let content = match serde_yaml::to_string(self) {
            Ok(content) => content,
            Err(error) => {
                return Err(Error::Generic(format!(
                    "Configuration file serialization failed:\n{}",
                    error
                )))
            }
        };
        let mut file = File::create(config_path)?;
        file.write_all(content.as_bytes())?;

        Ok(())
    }
}

/// Get all possible configuration paths and check if there are
/// configuration files at those locations.
/// All configs will be merged by importance.
///
/// If `require_config` is `true`, an error will be thrown, if no configuration file can be found.
fn parse_config(
    mut config: Config,
    require_config: bool,
    from_file: &Option<PathBuf>,
) -> Result<Settings, Error> {
    // Load the config from a very specific file path
    if let Some(path) = from_file {
        if !path.exists() || !path.is_file() {
            return Err(Error::FileNotFound(format!(
                "Couldn't find config at path {:?}",
                path
            )));
        }
        info!("Using config file at: {:?}", path);
        let config_file = config::File::with_name(path.to_str().unwrap());
        config.merge(config_file)?;
        return Ok(config.try_into()?);
    };

    let mut config_found = false;
    info!("Parsing config files");
    for directory in get_config_directories()?.into_iter() {
        let path = directory.join("pueue.yml");
        info!("Checking path: {:?}", &path);

        // Check if the file exists and parse it.
        if path.exists() && path.is_file() {
            info!("Found config file at: {:?}", path);
            config_found = true;
            let config_file = config::File::with_name(path.to_str().unwrap());
            config.merge(config_file)?;
        }
    }

    if require_config && !config_found {
        return Err(Error::FileNotFound(
            "Couldn't find a configuration file. Did you start the daemon yet?".into(),
        ));
    }

    // Try to can deserialize the entire configuration
    Ok(config.try_into()?)
}

/// The default value for the `dark_mode` client settings.
/// Needed to keep backward compatibility between v0.11 and v0.12
fn default_dark_mode() -> bool {
    false
}

/// The default value for the `time_format` client settings.
fn default_status_time_format() -> String {
    "%H:%M:%S".into()
}

/// The default value for the `datetime_format` client settings.
fn default_status_datetime_format() -> String {
    "%Y-%m-%d\n%H:%M:%S".into()
}

/// The default value for the `dark_mode` client settings.
/// Needed to keep backward compatibility between v0.11 and v0.12
fn default_callback_log_lines() -> usize {
    10
}
<|endoftext|>


<commit_before>/**
    Converts a vector of types that support to_string  into a string
*/
pub fn string_for_vector<T: ToString>(vec: Vec<T>) -> String {
    let mut s = String::new();
    for i in 0..vec.len() {
        s.push_str(&vec[i].to_string());
        if i + 1 < vec.len() {
            s.push(',');
        }
    }
    return s;
}
<commit_msg>Cleaning up<commit_after>/**
    Converts a vector of types that have the ToString trait into
    a pretty string
*/
pub fn string_for_vector<T: ToString>(vec: Vec<T>) -> String {
    let mut s = String::new();
    for x in &vec {
        s.push_str(&x.to_string());
        s.push(',');
    }
    s.pop();
    s
}
<|endoftext|>


<commit_before>//! Module to make Rust subclasses of C++ classes. See [`CppSubclass`]
//! for details.

// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{
    cell::RefCell,
    pin::Pin,
    rc::{Rc, Weak},
};

use cxx::{memory::UniquePtrTarget, UniquePtr};

pub use autocxx_macro::is_subclass;
pub use autocxx_macro::CppSubclassDefault;
pub use autocxx_macro::CppSubclassSelfOwnedDefault;

/// A prelude containing all the traits and macros required to create
/// Rust subclasses of C++ classes. It's recommended that you:
///
/// ```rust
/// use autocxx::subclass::prelude::*;
/// ```
pub mod prelude {
    pub use super::{
        is_subclass, CppPeerConstructor, CppSubclass, CppSubclassDefault, CppSubclassSelfOwned,
        CppSubclassSelfOwnedDefault,
    };
}

#[doc(hidden)]
pub trait CppSubclassCppPeer: UniquePtrTarget {
    fn relinquish_ownership(&self);
}

#[doc(hidden)]
pub enum CppSubclassRustPeerHolder<T> {
    Owned(Rc<RefCell<T>>),
    Unowned(Weak<RefCell<T>>),
}

impl<T> CppSubclassRustPeerHolder<T> {
    pub fn get(&self) -> Option<Rc<RefCell<T>>> {
        match self {
            CppSubclassRustPeerHolder::Owned(strong) => Some(strong.clone()),
            CppSubclassRustPeerHolder::Unowned(weak) => weak.upgrade(),
        }
    }
    pub fn relinquish_ownership(self) -> Self {
        match self {
            CppSubclassRustPeerHolder::Owned(strong) => {
                CppSubclassRustPeerHolder::Unowned(Rc::downgrade(&strong))
            }
            _ => self,
        }
    }
}

#[doc(hidden)]
pub enum CppSubclassCppPeerHolder<CppPeer: CppSubclassCppPeer> {
    Empty,
    Owned(Box<UniquePtr<CppPeer>>),
    Unowned(*mut CppPeer),
}

impl<CppPeer: CppSubclassCppPeer> Default for CppSubclassCppPeerHolder<CppPeer> {
    fn default() -> Self {
        CppSubclassCppPeerHolder::Empty
    }
}

impl<CppPeer: CppSubclassCppPeer> CppSubclassCppPeerHolder<CppPeer> {
    fn pin_mut(&mut self) -> Pin<&mut CppPeer> {
        match self {
            CppSubclassCppPeerHolder::Empty => panic!("Peer not set up"),
            CppSubclassCppPeerHolder::Owned(peer) => peer.pin_mut(),
            CppSubclassCppPeerHolder::Unowned(peer) => unsafe {
                Pin::new_unchecked(peer.as_mut().unwrap())
            },
        }
    }
    fn get(&self) -> &CppPeer {
        match self {
            CppSubclassCppPeerHolder::Empty => panic!("Peer not set up"),
            CppSubclassCppPeerHolder::Owned(peer) => peer.as_ref(),
            CppSubclassCppPeerHolder::Unowned(peer) => unsafe { peer.as_ref().unwrap() },
        }
    }
    fn set_owned(&mut self, peer: UniquePtr<CppPeer>) {
        *self = Self::Owned(Box::new(peer));
    }
    fn set_unowned(&mut self, peer: &mut UniquePtr<CppPeer>) {
        *self = Self::Unowned(unsafe {
            std::pin::Pin::<&mut CppPeer>::into_inner_unchecked(peer.pin_mut())
        });
    }
}

fn make_owning_peer<CppPeer, PeerConstructor, Subclass, PeerBoxer>(
    me: Subclass,
    peer_constructor: PeerConstructor,
    peer_boxer: PeerBoxer,
) -> Rc<RefCell<Subclass>>
where
    CppPeer: CppSubclassCppPeer,
    Subclass: CppSubclass<CppPeer>,
    PeerConstructor:
        FnOnce(&mut Subclass, CppSubclassRustPeerHolder<Subclass>) -> UniquePtr<CppPeer>,
    PeerBoxer: FnOnce(Rc<RefCell<Subclass>>) -> CppSubclassRustPeerHolder<Subclass>,
{
    let me = Rc::new(RefCell::new(me));
    let holder = peer_boxer(me.clone());
    let cpp_side = peer_constructor(&mut me.as_ref().borrow_mut(), holder);
    me.as_ref()
        .borrow_mut()
        .peer_holder_mut()
        .set_owned(cpp_side);
    me
}

/// A trait to be implemented by a subclass which knows how to construct
/// its C++ peer object. Specifically, the implementation here will
/// arrange to call one or other of the `make_unique` methods to be
/// found on the superclass of the C++ object. If the superclass
/// has a single trivial constructor, then this is implemented
/// automatically for you. If there are multiple constructors, or
/// a single constructor which takes parameters, you'll need to implement
/// this trait for your subclass in order to call the correct
/// constructor.
pub trait CppPeerConstructor<CppPeer: CppSubclassCppPeer>: Sized {
    /// Create the C++ peer. This method will be automatically generated
    /// for you *except* in cases where the superclass has multiple constructors,
    /// or its only constructor takes parameters. In such a case you'll need
    /// to implement this by calling a `make_unique` method on the
    /// `<my subclass name>Cpp` type, passing `peer_holder` as the first
    /// argument.
    fn make_peer(&mut self, peer_holder: CppSubclassRustPeerHolder<Self>) -> UniquePtr<CppPeer>;
}

/// A subclass of a C++ type.
///
/// To create a Rust subclass of a C++ class, you must do these things:
/// * Use the `subclass` directive in your [`crate::include_cpp`] macro (or use
///   the auto-allow mode in your `build.rs`)
/// * Create a `struct` to act as your subclass, and add the #[`is_subclass`] attribute.
///   This adds a field to your struct for autocxx record-keeping. You can
///   instead choose to implement [`CppSubclass`] a different way.
/// * Use the [`CppSubclass`] trait, and instantiate the subclass using
///   [`CppSubclass::new_rust_owned`] or [`CppSubclass::new_cpp_owned`]
///   constructors. (You can use [`CppSubclassSelfOwned`] if you need that
///   instead; also, see [`CppSubclassDefaultSelfOwned`] and [`CppSubclassDefault`]
///   to arrange for easier constructors to exist.
/// * You _may_ need to implement [`CppPeerConstructor`] for your subclass,
///   but only if autocxx determines that there are multiple possible superclass
///   constructors so you need to call one explicitly (or if there's a single
///   non-trivial superclass constructor.) autocxx will implemente this trait
///   for you if there's no ambiguity.
///
/// # How to access your Rust structure from outside
///
/// Use [`CppSubclass::new_rust_owned`] then use [`std::cell::RefCell::borrow`]
/// or [`std::cell::RefCell::borrow_mut`] to obtain the underlying Rust struct.
///
/// # How to call C++ methods on the subclass
///
/// Do the same. You should find that your subclass struct `impl`s all the
/// C++ methods belonging to the superclass.
///
/// # How to implement virtual methods
///
/// Simply add an `impl` for the `struct`, implementing the relevant method.
/// The C++ virtual function call will be redirected to your Rust implementation.
///
/// # How _not_ to implement virtual methods
///
/// If you don't want to implement a virtual method, don't: the superclass
/// method will be called instead. Naturally, you must implement any pure virtual
/// methods.
///
/// # How it works
///
/// This actually consists of two objects: this object itself and a C++-side
/// peer. The ownership relationship between those two things can work in three
/// different ways:
/// 1. Neither object is owned by Rust. The C++ peer is owned by a C++
///    [`UniquePtr`] held elsewhere in C++. That C++ peer then owns
///    this Rust-side object via a strong [`Rc`] reference. This is the
///    ownership relationship set up by [`CppSubclass::new_cpp_owned`].
/// 2. The object pair is owned by Rust. Specifically, by a strong
///    [`Rc`] reference to this Rust-side object. In turn, the Rust-side object
///    owns the C++-side peer via a [`UniquePtr`]. This is what's set up by
///    [`CppSubclass::new_rust_owned`]. The C++-side peer _does not_ own the Rust
///    object; it just has a weak pointer. (Otherwise we'd get a reference
///    loop and nothing would ever be freed.)
/// 3. The object pair is self-owned and will stay around forever until
///    [`CppSubclassSelfOwned::delete_self`] is called. In this case there's a strong reference
///    from the C++ to the Rust and from the Rust to the C++. This is useful
///    for cases where the subclass is listening for events, and needs to
///    stick around until a particular event occurs then delete itself.
///
/// # Limitations
///
/// * *Re-entrancy*. The main thing to look out for is re-entrancy. If a
///   (non-const) virtual method is called on your type, which then causes you
///   to call back into C++, which results in a _second_ call into a (non-const)
///   virtual method, we will try to create two mutable references to your
///   subclass which isn't allowed in Rust and will therefore panic.
///
///   A future version of autocxx may provide the option of treating all
///   non-const methods (in C++) as const methods on the Rust side, which will
///   give the option of using interior mutability ([`std::cell::RefCell`])
///   for you to safely handle this situation, whilst remaining compatible
///   with existing C++ interfaces. If you need this, indicate support on
///   [this issue](https://github.com/google/autocxx/issues/622).
///
/// * *Thread safety*. The subclass object is not thread-safe and shouldn't
///   be passed to different threads in C++. A future version of this code
///   will give the option to use `Arc` and `Mutex` internally rather than
///   `Rc` and `RefCell`, solving this problem.
///
/// * *Complex or multiple constructors*. At present, this code doesn't work
///   if the superclass has anything other than a single, zero-argument,
///   constructor. This is
///   https://github.com/google/autocxx/issues/596 and is just a matter of doing
///   a bit of work to plumb this together. (See [`CppPeerConstructor`] for
///   how this will work when it's done.)
///
/// * *Protected methods.* We don't do anything clever here - they're public.
///
/// * *Non-trivial class hierarchies*. We don't yet consider virtual methods
///   on base classes of base classes. This is a temporary limitation,
///   https://github.com/google/autocxx/issues/610.
pub trait CppSubclass<CppPeer: CppSubclassCppPeer>: CppPeerConstructor<CppPeer> {
    /// Return the field which holds the C++ peer object. This is normally
    /// implemented by the #[`is_subclass`] macro, but you're welcome to
    /// implement it yourself if you prefer.
    fn peer_holder(&self) -> &CppSubclassCppPeerHolder<CppPeer>;

    /// Return the field which holds the C++ peer object. This is normally
    /// implemented by the #[`is_subclass`] macro, but you're welcome to
    /// implement it yourself if you prefer.
    fn peer_holder_mut(&mut self) -> &mut CppSubclassCppPeerHolder<CppPeer>;

    /// Return a reference to the C++ part of this object pair.
    /// This can be used to register listeners, etc.
    fn peer(&self) -> &CppPeer {
        self.peer_holder().get()
    }

    /// Return a mutable reference to the C++ part of this object pair.
    /// This can be used to register listeners, etc.
    fn peer_mut(&mut self) -> Pin<&mut CppPeer> {
        self.peer_holder_mut().pin_mut()
    }

    /// Creates a new instance of this subclass. This instance is owned by the
    /// returned [`cxx::UniquePtr`] and thus would typically be returned immediately
    /// to C++ such that it can be owned on the C++ side.
    fn new_cpp_owned(me: Self) -> UniquePtr<CppPeer> {
        let me = Rc::new(RefCell::new(me));
        let holder = CppSubclassRustPeerHolder::Owned(me.clone());
        let mut borrowed = me.as_ref().borrow_mut();
        let mut cpp_side = borrowed.make_peer(holder);
        borrowed.peer_holder_mut().set_unowned(&mut cpp_side);
        cpp_side
    }

    /// Creates a new instance of this subclass. This instance is not owned
    /// by C++, and therefore will be deleted when it goes out of scope in
    /// Rust.
    fn new_rust_owned(me: Self) -> Rc<RefCell<Self>> {
        make_owning_peer(
            me,
            |obj, holder| obj.make_peer(holder),
            |me| CppSubclassRustPeerHolder::Unowned(Rc::downgrade(&me)),
        )
    }
}

/// Trait to be implemented by subclasses which are self-owned, i.e. not owned
/// externally by either Rust or C++ code, and thus need the ability to delete
/// themselves when some virtual function is called.
pub trait CppSubclassSelfOwned<CppPeer: CppSubclassCppPeer>: CppSubclass<CppPeer> {
    /// Creates a new instance of this subclass which owns itself.
    /// This is useful
    /// for observers (etc.) which self-register to listen to events.
    /// If an event occurs which would cause this to want to unregister,
    /// use [`CppSubclassSelfOwned::delete_self`].
    /// The return value may be useful to register this, etc. but can ultimately
    /// be discarded without destroying this object.
    fn new_self_owned(me: Self) -> Rc<RefCell<Self>> {
        make_owning_peer(
            me,
            |obj, holder| obj.make_peer(holder),
            CppSubclassRustPeerHolder::Owned,
        )
    }

    /// Relinquishes ownership from the C++ side. If there are no outstanding
    /// references from the Rust side, this will result in the destruction
    /// of this subclass instance.
    fn delete_self(&self) {
        self.peer().relinquish_ownership()
    }
}

/// Provides default constructors for subclasses which implement `Default`.
pub trait CppSubclassDefault<CppPeer: CppSubclassCppPeer>: CppSubclass<CppPeer> + Default {
    /// Create a Rust-owned instance of this subclass, initializing with default values. See
    /// [`CppSubclass`] for more details of the ownership models available.
    fn default_rust_owned() -> Rc<RefCell<Self>> {
        Self::new_rust_owned(Self::default())
    }

    /// Create a C++-owned instance of this subclass, initializing with default values. See
    /// [`CppSubclass`] for more details of the ownership models available.
    fn default_cpp_owned() -> UniquePtr<CppPeer> {
        Self::new_cpp_owned(Self::default())
    }
}

/// Provides default constructors for subclasses which implement `Default`
/// and are self-owning.
pub trait CppSubclassSelfOwnedDefault<CppPeer: CppSubclassCppPeer>:
    CppSubclassSelfOwned<CppPeer> + Default
{
    /// Create a self-owned instance of this subclass, initializing with default values. See
    /// [`CppSubclass`] for more details of the ownership models available.
    fn default_self_owned() -> Rc<RefCell<Self>> {
        Self::new_self_owned(Self::default())
    }
}
<commit_msg>More comments.<commit_after>//! Module to make Rust subclasses of C++ classes. See [`CppSubclass`]
//! for details.

// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{
    cell::RefCell,
    pin::Pin,
    rc::{Rc, Weak},
};

use cxx::{memory::UniquePtrTarget, UniquePtr};

pub use autocxx_macro::is_subclass;
pub use autocxx_macro::CppSubclassDefault;
pub use autocxx_macro::CppSubclassSelfOwnedDefault;

/// A prelude containing all the traits and macros required to create
/// Rust subclasses of C++ classes. It's recommended that you:
///
/// ```rust
/// use autocxx::subclass::prelude::*;
/// ```
pub mod prelude {
    pub use super::{
        is_subclass, CppPeerConstructor, CppSubclass, CppSubclassDefault, CppSubclassSelfOwned,
        CppSubclassSelfOwnedDefault,
    };
}

#[doc(hidden)]
pub trait CppSubclassCppPeer: UniquePtrTarget {
    fn relinquish_ownership(&self);
}

#[doc(hidden)]
pub enum CppSubclassRustPeerHolder<T> {
    Owned(Rc<RefCell<T>>),
    Unowned(Weak<RefCell<T>>),
}

impl<T> CppSubclassRustPeerHolder<T> {
    pub fn get(&self) -> Option<Rc<RefCell<T>>> {
        match self {
            CppSubclassRustPeerHolder::Owned(strong) => Some(strong.clone()),
            CppSubclassRustPeerHolder::Unowned(weak) => weak.upgrade(),
        }
    }
    pub fn relinquish_ownership(self) -> Self {
        match self {
            CppSubclassRustPeerHolder::Owned(strong) => {
                CppSubclassRustPeerHolder::Unowned(Rc::downgrade(&strong))
            }
            _ => self,
        }
    }
}

#[doc(hidden)]
pub enum CppSubclassCppPeerHolder<CppPeer: CppSubclassCppPeer> {
    Empty,
    Owned(Box<UniquePtr<CppPeer>>),
    Unowned(*mut CppPeer),
}

impl<CppPeer: CppSubclassCppPeer> Default for CppSubclassCppPeerHolder<CppPeer> {
    fn default() -> Self {
        CppSubclassCppPeerHolder::Empty
    }
}

impl<CppPeer: CppSubclassCppPeer> CppSubclassCppPeerHolder<CppPeer> {
    fn pin_mut(&mut self) -> Pin<&mut CppPeer> {
        match self {
            CppSubclassCppPeerHolder::Empty => panic!("Peer not set up"),
            CppSubclassCppPeerHolder::Owned(peer) => peer.pin_mut(),
            CppSubclassCppPeerHolder::Unowned(peer) => unsafe {
                Pin::new_unchecked(peer.as_mut().unwrap())
            },
        }
    }
    fn get(&self) -> &CppPeer {
        match self {
            CppSubclassCppPeerHolder::Empty => panic!("Peer not set up"),
            CppSubclassCppPeerHolder::Owned(peer) => peer.as_ref(),
            CppSubclassCppPeerHolder::Unowned(peer) => unsafe { peer.as_ref().unwrap() },
        }
    }
    fn set_owned(&mut self, peer: UniquePtr<CppPeer>) {
        *self = Self::Owned(Box::new(peer));
    }
    fn set_unowned(&mut self, peer: &mut UniquePtr<CppPeer>) {
        *self = Self::Unowned(unsafe {
            std::pin::Pin::<&mut CppPeer>::into_inner_unchecked(peer.pin_mut())
        });
    }
}

fn make_owning_peer<CppPeer, PeerConstructor, Subclass, PeerBoxer>(
    me: Subclass,
    peer_constructor: PeerConstructor,
    peer_boxer: PeerBoxer,
) -> Rc<RefCell<Subclass>>
where
    CppPeer: CppSubclassCppPeer,
    Subclass: CppSubclass<CppPeer>,
    PeerConstructor:
        FnOnce(&mut Subclass, CppSubclassRustPeerHolder<Subclass>) -> UniquePtr<CppPeer>,
    PeerBoxer: FnOnce(Rc<RefCell<Subclass>>) -> CppSubclassRustPeerHolder<Subclass>,
{
    let me = Rc::new(RefCell::new(me));
    let holder = peer_boxer(me.clone());
    let cpp_side = peer_constructor(&mut me.as_ref().borrow_mut(), holder);
    me.as_ref()
        .borrow_mut()
        .peer_holder_mut()
        .set_owned(cpp_side);
    me
}

/// A trait to be implemented by a subclass which knows how to construct
/// its C++ peer object. Specifically, the implementation here will
/// arrange to call one or other of the `make_unique` methods to be
/// found on the superclass of the C++ object. If the superclass
/// has a single trivial constructor, then this is implemented
/// automatically for you. If there are multiple constructors, or
/// a single constructor which takes parameters, you'll need to implement
/// this trait for your subclass in order to call the correct
/// constructor.
pub trait CppPeerConstructor<CppPeer: CppSubclassCppPeer>: Sized {
    /// Create the C++ peer. This method will be automatically generated
    /// for you *except* in cases where the superclass has multiple constructors,
    /// or its only constructor takes parameters. In such a case you'll need
    /// to implement this by calling a `make_unique` method on the
    /// `<my subclass name>Cpp` type, passing `peer_holder` as the first
    /// argument.
    fn make_peer(&mut self, peer_holder: CppSubclassRustPeerHolder<Self>) -> UniquePtr<CppPeer>;
}

/// A subclass of a C++ type.
///
/// To create a Rust subclass of a C++ class, you must do these things:
/// * Use the `subclass` directive in your [`crate::include_cpp`] macro (or use
///   the auto-allow mode in your `build.rs`)
/// * Create a `struct` to act as your subclass, and add the #[`is_subclass`] attribute.
///   This adds a field to your struct for autocxx record-keeping. You can
///   instead choose to implement [`CppSubclass`] a different way.
/// * Use the [`CppSubclass`] trait, and instantiate the subclass using
///   [`CppSubclass::new_rust_owned`] or [`CppSubclass::new_cpp_owned`]
///   constructors. (You can use [`CppSubclassSelfOwned`] if you need that
///   instead; also, see [`CppSubclassDefaultSelfOwned`] and [`CppSubclassDefault`]
///   to arrange for easier constructors to exist.
/// * You _may_ need to implement [`CppPeerConstructor`] for your subclass,
///   but only if autocxx determines that there are multiple possible superclass
///   constructors so you need to call one explicitly (or if there's a single
///   non-trivial superclass constructor.) autocxx will implemente this trait
///   for you if there's no ambiguity.
///
/// # How to access your Rust structure from outside
///
/// Use [`CppSubclass::new_rust_owned`] then use [`std::cell::RefCell::borrow`]
/// or [`std::cell::RefCell::borrow_mut`] to obtain the underlying Rust struct.
///
/// # How to call C++ methods on the subclass
///
/// Do the same. You should find that your subclass struct `impl`s all the
/// C++ methods belonging to the superclass.
///
/// # How to implement virtual methods
///
/// Simply add an `impl` for the `struct`, implementing the relevant method.
/// The C++ virtual function call will be redirected to your Rust implementation.
///
/// # How _not_ to implement virtual methods
///
/// If you don't want to implement a virtual method, don't: the superclass
/// method will be called instead. Naturally, you must implement any pure virtual
/// methods.
///
/// # How it works
///
/// This actually consists of two objects: this object itself and a C++-side
/// peer. The ownership relationship between those two things can work in three
/// different ways:
/// 1. Neither object is owned by Rust. The C++ peer is owned by a C++
///    [`UniquePtr`] held elsewhere in C++. That C++ peer then owns
///    this Rust-side object via a strong [`Rc`] reference. This is the
///    ownership relationship set up by [`CppSubclass::new_cpp_owned`].
/// 2. The object pair is owned by Rust. Specifically, by a strong
///    [`Rc`] reference to this Rust-side object. In turn, the Rust-side object
///    owns the C++-side peer via a [`UniquePtr`]. This is what's set up by
///    [`CppSubclass::new_rust_owned`]. The C++-side peer _does not_ own the Rust
///    object; it just has a weak pointer. (Otherwise we'd get a reference
///    loop and nothing would ever be freed.)
/// 3. The object pair is self-owned and will stay around forever until
///    [`CppSubclassSelfOwned::delete_self`] is called. In this case there's a strong reference
///    from the C++ to the Rust and from the Rust to the C++. This is useful
///    for cases where the subclass is listening for events, and needs to
///    stick around until a particular event occurs then delete itself.
///
/// # Limitations
///
/// * *Re-entrancy*. The main thing to look out for is re-entrancy. If a
///   (non-const) virtual method is called on your type, which then causes you
///   to call back into C++, which results in a _second_ call into a (non-const)
///   virtual method, we will try to create two mutable references to your
///   subclass which isn't allowed in Rust and will therefore panic.
///
///   A future version of autocxx may provide the option of treating all
///   non-const methods (in C++) as const methods on the Rust side, which will
///   give the option of using interior mutability ([`std::cell::RefCell`])
///   for you to safely handle this situation, whilst remaining compatible
///   with existing C++ interfaces. If you need this, indicate support on
///   [this issue](https://github.com/google/autocxx/issues/622).
///
/// * *Thread safety*. The subclass object is not thread-safe and shouldn't
///   be passed to different threads in C++. A future version of this code
///   will give the option to use `Arc` and `Mutex` internally rather than
///   `Rc` and `RefCell`, solving this problem.
///
/// * *Complex or multiple constructors*. At present, this code doesn't work
///   if the superclass has anything other than a single, zero-argument,
///   constructor. This is
///   https://github.com/google/autocxx/issues/596 and is just a matter of doing
///   a bit of work to plumb this together. (See [`CppPeerConstructor`] for
///   how this will work when it's done.)
///
/// * *Protected methods.* We don't do anything clever here - they're public.
///
/// * *Non-trivial class hierarchies*. We don't yet consider virtual methods
///   on base classes of base classes. This is a temporary limitation,
///   https://github.com/google/autocxx/issues/610.
///
/// * *Namespaces.* Superclasses in namespaces are not yet supported:
///   https://github.com/google/autocxx/issues/599.
pub trait CppSubclass<CppPeer: CppSubclassCppPeer>: CppPeerConstructor<CppPeer> {
    /// Return the field which holds the C++ peer object. This is normally
    /// implemented by the #[`is_subclass`] macro, but you're welcome to
    /// implement it yourself if you prefer.
    fn peer_holder(&self) -> &CppSubclassCppPeerHolder<CppPeer>;

    /// Return the field which holds the C++ peer object. This is normally
    /// implemented by the #[`is_subclass`] macro, but you're welcome to
    /// implement it yourself if you prefer.
    fn peer_holder_mut(&mut self) -> &mut CppSubclassCppPeerHolder<CppPeer>;

    /// Return a reference to the C++ part of this object pair.
    /// This can be used to register listeners, etc.
    fn peer(&self) -> &CppPeer {
        self.peer_holder().get()
    }

    /// Return a mutable reference to the C++ part of this object pair.
    /// This can be used to register listeners, etc.
    fn peer_mut(&mut self) -> Pin<&mut CppPeer> {
        self.peer_holder_mut().pin_mut()
    }

    /// Creates a new instance of this subclass. This instance is owned by the
    /// returned [`cxx::UniquePtr`] and thus would typically be returned immediately
    /// to C++ such that it can be owned on the C++ side.
    fn new_cpp_owned(me: Self) -> UniquePtr<CppPeer> {
        let me = Rc::new(RefCell::new(me));
        let holder = CppSubclassRustPeerHolder::Owned(me.clone());
        let mut borrowed = me.as_ref().borrow_mut();
        let mut cpp_side = borrowed.make_peer(holder);
        borrowed.peer_holder_mut().set_unowned(&mut cpp_side);
        cpp_side
    }

    /// Creates a new instance of this subclass. This instance is not owned
    /// by C++, and therefore will be deleted when it goes out of scope in
    /// Rust.
    fn new_rust_owned(me: Self) -> Rc<RefCell<Self>> {
        make_owning_peer(
            me,
            |obj, holder| obj.make_peer(holder),
            |me| CppSubclassRustPeerHolder::Unowned(Rc::downgrade(&me)),
        )
    }
}

/// Trait to be implemented by subclasses which are self-owned, i.e. not owned
/// externally by either Rust or C++ code, and thus need the ability to delete
/// themselves when some virtual function is called.
pub trait CppSubclassSelfOwned<CppPeer: CppSubclassCppPeer>: CppSubclass<CppPeer> {
    /// Creates a new instance of this subclass which owns itself.
    /// This is useful
    /// for observers (etc.) which self-register to listen to events.
    /// If an event occurs which would cause this to want to unregister,
    /// use [`CppSubclassSelfOwned::delete_self`].
    /// The return value may be useful to register this, etc. but can ultimately
    /// be discarded without destroying this object.
    fn new_self_owned(me: Self) -> Rc<RefCell<Self>> {
        make_owning_peer(
            me,
            |obj, holder| obj.make_peer(holder),
            CppSubclassRustPeerHolder::Owned,
        )
    }

    /// Relinquishes ownership from the C++ side. If there are no outstanding
    /// references from the Rust side, this will result in the destruction
    /// of this subclass instance.
    fn delete_self(&self) {
        self.peer().relinquish_ownership()
    }
}

/// Provides default constructors for subclasses which implement `Default`.
pub trait CppSubclassDefault<CppPeer: CppSubclassCppPeer>: CppSubclass<CppPeer> + Default {
    /// Create a Rust-owned instance of this subclass, initializing with default values. See
    /// [`CppSubclass`] for more details of the ownership models available.
    fn default_rust_owned() -> Rc<RefCell<Self>> {
        Self::new_rust_owned(Self::default())
    }

    /// Create a C++-owned instance of this subclass, initializing with default values. See
    /// [`CppSubclass`] for more details of the ownership models available.
    fn default_cpp_owned() -> UniquePtr<CppPeer> {
        Self::new_cpp_owned(Self::default())
    }
}

/// Provides default constructors for subclasses which implement `Default`
/// and are self-owning.
pub trait CppSubclassSelfOwnedDefault<CppPeer: CppSubclassCppPeer>:
    CppSubclassSelfOwned<CppPeer> + Default
{
    /// Create a self-owned instance of this subclass, initializing with default values. See
    /// [`CppSubclass`] for more details of the ownership models available.
    fn default_self_owned() -> Rc<RefCell<Self>> {
        Self::new_self_owned(Self::default())
    }
}
<|endoftext|>


<commit_before>use clap::{Arg, App, SubCommand};

pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
    app
        .subcommand(SubCommand::with_name("add")
                    .about("Add a reference to a file outside of the store")
                    .version("0.1")
                    .arg(Arg::with_name("path")
                         .long("path")
                         .short("p")
                         .takes_value(true)
                         .required(true)
                         .help("The path of the file")
                         .value_name("PATH"))
                    .arg(Arg::with_name("track-content")
                         .long("content-hash")
                         .short("C")
                         .takes_value(false)
                         .required(false)
                         .help("Hash the content for the reference"))
                    .arg(Arg::with_name("track-permissions")
                         .long("permission-tracking")
                         .short("P")
                         .takes_value(false)
                         .required(false)
                         .help("Rememeber the permissions of the referenced file"))
                    )

        .subcommand(SubCommand::with_name("remove")
                .about("Remove a reference")
                .version("0.1")
                .arg(Arg::with_name("hash")
                     .long("hash")
                     .short("h")
                     .takes_value(true)
                     .required(true)
                     .help("Remove the reference with this hash")
                     .value_name("HASH"))

                .arg(Arg::with_name("yes")
                     .long("yes")
                     .short("y")
                     .help("Don't ask whether this really should be done"))
                )

        .subcommand(SubCommand::with_name("list")
                    .about("List references in the store")
                    .version("0.1")

                    .arg(Arg::with_name("check-dead")
                         .long("check-dead")
                         .short("d")
                         .help("Check each reference whether it is dead"))

                    .arg(Arg::with_name("check-changed")
                         .long("check-changed")
                         .short("c")
                         .help("Check whether a reference had changed (content or permissions)"))

                    .arg(Arg::with_name("check-changed-content")
                         .long("check-changed-content")
                         .short("C")
                         .help("Check whether the content of the referenced file changed"))

                    .arg(Arg::with_name("check-changed-permissions")
                         .long("check-changed-perms")
                         .short("P")
                         .help("Check whether the permissions of the referenced file changed"))

                    )
}
<commit_msg>imag-ref: Use validator is_existing_path<commit_after>use clap::{Arg, App, SubCommand};

use libimagutil::cli_validators::is_existing_path;

pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
    app
        .subcommand(SubCommand::with_name("add")
                    .about("Add a reference to a file outside of the store")
                    .version("0.1")
                    .arg(Arg::with_name("path")
                         .long("path")
                         .short("p")
                         .takes_value(true)
                         .required(true)
                         .help("The path of the file")
                         .validator(is_existing_path)
                         .value_name("PATH"))
                    .arg(Arg::with_name("track-content")
                         .long("content-hash")
                         .short("C")
                         .takes_value(false)
                         .required(false)
                         .help("Hash the content for the reference"))
                    .arg(Arg::with_name("track-permissions")
                         .long("permission-tracking")
                         .short("P")
                         .takes_value(false)
                         .required(false)
                         .help("Rememeber the permissions of the referenced file"))
                    )

        .subcommand(SubCommand::with_name("remove")
                .about("Remove a reference")
                .version("0.1")
                .arg(Arg::with_name("hash")
                     .long("hash")
                     .short("h")
                     .takes_value(true)
                     .required(true)
                     .help("Remove the reference with this hash")
                     .value_name("HASH"))

                .arg(Arg::with_name("yes")
                     .long("yes")
                     .short("y")
                     .help("Don't ask whether this really should be done"))
                )

        .subcommand(SubCommand::with_name("list")
                    .about("List references in the store")
                    .version("0.1")

                    .arg(Arg::with_name("check-dead")
                         .long("check-dead")
                         .short("d")
                         .help("Check each reference whether it is dead"))

                    .arg(Arg::with_name("check-changed")
                         .long("check-changed")
                         .short("c")
                         .help("Check whether a reference had changed (content or permissions)"))

                    .arg(Arg::with_name("check-changed-content")
                         .long("check-changed-content")
                         .short("C")
                         .help("Check whether the content of the referenced file changed"))

                    .arg(Arg::with_name("check-changed-permissions")
                         .long("check-changed-perms")
                         .short("P")
                         .help("Check whether the permissions of the referenced file changed"))

                    )
}
<|endoftext|>