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

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

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

/// All settings which are used by both, the client and the daemon
#[derive(PartialEq, Clone, Debug, Default, 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 of pueue's state. \
    /// I.e. task logs, state dumps, etc.
    pub pueue_directory: Option<PathBuf>,
    /// The location where runtime related files will be placed.
    /// Defaults to `pueue_directory` unless `$XDG_RUNTIME_DIR` is set.
    pub runtime_directory: Option<PathBuf>,
    /// If this is set to true, unix sockets will be used.
    /// Otherwise we default to TCP+TLS
    #[cfg(not(target_os = "windows"))]
    #[serde(default = "default_true")]
    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: Option<PathBuf>,

    /// The TCP hostname/ip address.
    #[serde(default = "default_host")]
    pub host: String,
    /// The TCP port.
    #[serde(default = "default_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: Option<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: Option<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: Option<PathBuf>,
}

/// All settings which are used by the client
#[derive(PartialEq, Clone, Debug, Default, Deserialize, Serialize)]
pub struct Client {
    /// If set to true, all tasks will be restart in place, instead of creating a new task.
    /// False is the default, as you'll lose the logs of the previously failed tasks when
    /// restarting tasks in place.
    #[serde(default = "Default::default")]
    pub restart_in_place: bool,
    /// Whether the client should read the logs directly from disk or whether it should
    /// request the data from the daemon via socket.
    #[serde(default = "default_true")]
    pub read_local_logs: bool,
    /// Whether the client should show a confirmation question on potential dangerous actions.
    #[serde(default = "Default::default")]
    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.
    #[serde(default = "Default::default")]
    pub show_expanded_aliases: bool,
    /// Whether the client should use dark shades instead of regular colors.
    #[serde(default = "Default::default")]
    pub dark_mode: bool,
    /// The max amount of lines each task get's in the `pueue status` view.
    pub max_status_lines: Option<usize>,
    /// The format that will be used to display time formats in `pueue status`.
    #[serde(default = "default_status_time_format")]
    pub status_time_format: String,
    /// The format that will be used to display datetime formats in `pueue status`.
    #[serde(default = "default_status_datetime_format")]
    pub status_datetime_format: String,
}

/// All settings which are used by the daemon
#[derive(PartialEq, Clone, Debug, Default, Deserialize, Serialize)]
pub struct Daemon {
    /// Whether a group should be paused as soon as a single task fails
    #[serde(default = "Default::default")]
    pub pause_group_on_failure: bool,
    /// Whether the daemon (and all groups) should be paused as soon as a single task fails
    #[serde(default = "Default::default")]
    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,
    /// The legacy configuration for groups
    #[serde(skip_serializing)]
    #[deprecated(
        since = "1.1.0",
        note = "The configuration for groups is now stored in the state."
    )]
    pub groups: Option<HashMap<String, i64>>,
}

impl Default for Settings {
    fn default() -> Self {
        Settings {
            client: Client {
                read_local_logs: true,
                status_time_format: default_status_time_format(),
                status_datetime_format: default_status_datetime_format(),
                ..Default::default()
            },
            daemon: Daemon {
                callback_log_lines: default_callback_log_lines(),
                ..Default::default()
            },
            shared: Shared {
                #[cfg(not(target_os = "windows"))]
                use_unix_socket: true,
                host: default_host(),
                port: default_port(),
                ..Default::default()
            },
            profiles: HashMap::new(),
        }
    }
}

/// 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,
    #[serde(default = "HashMap::new")]
    pub profiles: HashMap<String, NestedSettings>,
}

/// The nested settings struct for profiles. \
/// In contrast to the normal `Settings` struct, this struct doesn't allow profiles.
/// That way we prevent nested profiles and problems with self-referencing structs.
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct NestedSettings {
    pub client: Client,
    pub daemon: Daemon,
    pub shared: Shared,
}

/// Little helper which expands a given path's `~` characters to a fully qualified path.
pub fn expand_home(old_path: &Path) -> PathBuf {
    PathBuf::from(tilde(&old_path.to_string_lossy()).into_owned())
}

impl Shared {
    pub fn pueue_directory(&self) -> PathBuf {
        if let Some(path) = &self.pueue_directory {
            expand_home(path)
        } else {
            default_pueue_path()
        }
    }

    /// Get the current runtime directory in the following precedence.
    /// 1. Config value
    /// 2. Environment configuration
    /// 3. Pueue directory
    pub fn runtime_directory(&self) -> PathBuf {
        if let Some(path) = &self.runtime_directory {
            expand_home(path)
        } else if let Some(path) = default_runtime_directory() {
            path
        } else {
            default_pueue_path()
        }
    }

    /// The unix socket path can either be explicitly specified or it's simply placed in the
    /// current runtime directory.
    #[cfg(not(target_os = "windows"))]
    pub fn unix_socket_path(&self) -> PathBuf {
        if let Some(path) = &self.unix_socket_path {
            expand_home(path)
        } else {
            self.runtime_directory()
                .join(format!("pueue_{}.socket", whoami::username()))
        }
    }

    pub fn daemon_cert(&self) -> PathBuf {
        if let Some(path) = &self.daemon_cert {
            expand_home(path)
        } else {
            self.pueue_directory().join("certs").join("daemon.cert")
        }
    }

    pub fn daemon_key(&self) -> PathBuf {
        if let Some(path) = &self.daemon_key {
            expand_home(path)
        } else {
            self.pueue_directory().join("certs").join("daemon.key")
        }
    }

    pub fn shared_secret_path(&self) -> PathBuf {
        if let Some(path) = &self.shared_secret_path {
            expand_home(path)
        } else {
            self.pueue_directory().join("shared_secret")
        }
    }
}

impl Settings {
    /// Try to read existing config files, while using default values for non-existing fields.
    /// If successful, this will return a full config as well as a boolean on whether we found an
    /// existing configuration file or not.
    ///
    /// The default local config locations depends on the current target.
    pub fn read(from_file: &Option<PathBuf>) -> Result<(Settings, bool), 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:?}"
                )));
            }

            // Open the file in read-only mode with buffer.
            let file = File::open(path)?;
            let reader = BufReader::new(file);

            let settings = serde_yaml::from_reader(reader)
                .map_err(|err| Error::ConfigDeserialization(err.to_string()))?;
            return Ok((settings, true));
        };

        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:?}");

                // Open the file in read-only mode with buffer.
                let file = File::open(path)?;
                let reader = BufReader::new(file);

                let settings = serde_yaml::from_reader(reader)
                    .map_err(|err| Error::ConfigDeserialization(err.to_string()))?;
                return Ok((settings, true));
            }
        }

        info!("No config file found. Use default config.");
        // Return a default configuration if we couldn't find a file.
        Ok((Settings::default(), false))
    }

    /// 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(())
    }

    /// Try to load a profile. Error if it doesn't exist.
    pub fn load_profile(&mut self, profile: &str) -> Result<(), Error> {
        let profile = self.profiles.remove(profile).ok_or_else(|| {
            Error::ConfigDeserialization(format!("Couldn't find profile with name \"{profile}\""))
        })?;

        self.client = profile.client;
        self.daemon = profile.daemon;
        self.shared = profile.shared;

        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::*;

    /// Check if profiles get loaded correctly.
    #[test]
    fn test_load_profile() {
        // Create some default settings and ensure that default values are loaded.
        let mut settings = Settings::default();
        assert_eq!(
            settings.client.status_time_format,
            default_status_time_format()
        );
        assert_eq!(
            settings.daemon.callback_log_lines,
            default_callback_log_lines()
        );
        assert_eq!(settings.shared.host, default_host());

        // Crate a new profile with slightly different values.
        let mut profile = Settings::default();
        profile.client.status_time_format = "test".to_string();
        profile.daemon.callback_log_lines = 100_000;
        profile.shared.host = "quatschhost".to_string();
        let profile = NestedSettings {
            client: profile.client,
            daemon: profile.daemon,
            shared: profile.shared,
        };

        settings.profiles.insert("testprofile".to_string(), profile);

        // Load the profile and ensure the new values are now loaded.
        settings
            .load_profile("testprofile")
            .expect("We just added the profile");

        assert_eq!(settings.client.status_time_format, "test");
        assert_eq!(settings.daemon.callback_log_lines, 100_000);
        assert_eq!(settings.shared.host, "quatschhost");
    }

    /// A proper pueue [Error] should be thrown if the profile cannot be found.
    #[test]
    fn test_error_on_missing_profile() {
        let mut settings = Settings::default();

        let result = settings.load_profile("doesn't exist");
        let expected_error_message = "Couldn't find profile with name \"doesn't exist\"";
        if let Err(Error::ConfigDeserialization(error_message)) = result {
            assert_eq!(error_message, expected_error_message);
            return;
        }

        panic!("Got unexpected result when expecting missing profile error: {result:?}");
    }
}
<commit_msg>fix: Config sections can be omitted<commit_after>use std::collections::HashMap;
use std::fs::{create_dir_all, File};
use std::io::{prelude::*, BufReader};
use std::path::{Path, PathBuf};

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

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

/// All settings which are used by both, the client and the daemon
#[derive(PartialEq, Clone, Debug, Default, 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 of pueue's state. \
    /// I.e. task logs, state dumps, etc.
    pub pueue_directory: Option<PathBuf>,
    /// The location where runtime related files will be placed.
    /// Defaults to `pueue_directory` unless `$XDG_RUNTIME_DIR` is set.
    pub runtime_directory: Option<PathBuf>,
    /// If this is set to true, unix sockets will be used.
    /// Otherwise we default to TCP+TLS
    #[cfg(not(target_os = "windows"))]
    #[serde(default = "default_true")]
    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: Option<PathBuf>,

    /// The TCP hostname/ip address.
    #[serde(default = "default_host")]
    pub host: String,
    /// The TCP port.
    #[serde(default = "default_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: Option<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: Option<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: Option<PathBuf>,
}

/// All settings which are used by the client
#[derive(PartialEq, Clone, Debug, Default, Deserialize, Serialize)]
pub struct Client {
    /// If set to true, all tasks will be restart in place, instead of creating a new task.
    /// False is the default, as you'll lose the logs of the previously failed tasks when
    /// restarting tasks in place.
    #[serde(default = "Default::default")]
    pub restart_in_place: bool,
    /// Whether the client should read the logs directly from disk or whether it should
    /// request the data from the daemon via socket.
    #[serde(default = "default_true")]
    pub read_local_logs: bool,
    /// Whether the client should show a confirmation question on potential dangerous actions.
    #[serde(default = "Default::default")]
    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.
    #[serde(default = "Default::default")]
    pub show_expanded_aliases: bool,
    /// Whether the client should use dark shades instead of regular colors.
    #[serde(default = "Default::default")]
    pub dark_mode: bool,
    /// The max amount of lines each task get's in the `pueue status` view.
    pub max_status_lines: Option<usize>,
    /// The format that will be used to display time formats in `pueue status`.
    #[serde(default = "default_status_time_format")]
    pub status_time_format: String,
    /// The format that will be used to display datetime formats in `pueue status`.
    #[serde(default = "default_status_datetime_format")]
    pub status_datetime_format: String,
}

/// All settings which are used by the daemon
#[derive(PartialEq, Clone, Debug, Default, Deserialize, Serialize)]
pub struct Daemon {
    /// Whether a group should be paused as soon as a single task fails
    #[serde(default = "Default::default")]
    pub pause_group_on_failure: bool,
    /// Whether the daemon (and all groups) should be paused as soon as a single task fails
    #[serde(default = "Default::default")]
    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,
    /// The legacy configuration for groups
    #[serde(skip_serializing)]
    #[deprecated(
        since = "1.1.0",
        note = "The configuration for groups is now stored in the state."
    )]
    pub groups: Option<HashMap<String, i64>>,
}

impl Default for Settings {
    fn default() -> Self {
        Settings {
            client: Client {
                read_local_logs: true,
                status_time_format: default_status_time_format(),
                status_datetime_format: default_status_datetime_format(),
                ..Default::default()
            },
            daemon: Daemon {
                callback_log_lines: default_callback_log_lines(),
                ..Default::default()
            },
            shared: Shared {
                #[cfg(not(target_os = "windows"))]
                use_unix_socket: true,
                host: default_host(),
                port: default_port(),
                ..Default::default()
            },
            profiles: HashMap::new(),
        }
    }
}

/// The parent settings struct. \
/// This contains all other setting structs.
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct Settings {
    #[serde(default = "Default::default")]
    pub client: Client,
    #[serde(default = "Default::default")]
    pub daemon: Daemon,
    pub shared: Shared,
    #[serde(default = "HashMap::new")]
    pub profiles: HashMap<String, NestedSettings>,
}

/// The nested settings struct for profiles. \
/// In contrast to the normal `Settings` struct, this struct doesn't allow profiles.
/// That way we prevent nested profiles and problems with self-referencing structs.
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct NestedSettings {
    #[serde(default = "Default::default")]
    pub client: Client,
    #[serde(default = "Default::default")]
    pub daemon: Daemon,
    #[serde(default = "Default::default")]
    pub shared: Shared,
}

/// Little helper which expands a given path's `~` characters to a fully qualified path.
pub fn expand_home(old_path: &Path) -> PathBuf {
    PathBuf::from(tilde(&old_path.to_string_lossy()).into_owned())
}

impl Shared {
    pub fn pueue_directory(&self) -> PathBuf {
        if let Some(path) = &self.pueue_directory {
            expand_home(path)
        } else {
            default_pueue_path()
        }
    }

    /// Get the current runtime directory in the following precedence.
    /// 1. Config value
    /// 2. Environment configuration
    /// 3. Pueue directory
    pub fn runtime_directory(&self) -> PathBuf {
        if let Some(path) = &self.runtime_directory {
            expand_home(path)
        } else if let Some(path) = default_runtime_directory() {
            path
        } else {
            default_pueue_path()
        }
    }

    /// The unix socket path can either be explicitly specified or it's simply placed in the
    /// current runtime directory.
    #[cfg(not(target_os = "windows"))]
    pub fn unix_socket_path(&self) -> PathBuf {
        if let Some(path) = &self.unix_socket_path {
            expand_home(path)
        } else {
            self.runtime_directory()
                .join(format!("pueue_{}.socket", whoami::username()))
        }
    }

    pub fn daemon_cert(&self) -> PathBuf {
        if let Some(path) = &self.daemon_cert {
            expand_home(path)
        } else {
            self.pueue_directory().join("certs").join("daemon.cert")
        }
    }

    pub fn daemon_key(&self) -> PathBuf {
        if let Some(path) = &self.daemon_key {
            expand_home(path)
        } else {
            self.pueue_directory().join("certs").join("daemon.key")
        }
    }

    pub fn shared_secret_path(&self) -> PathBuf {
        if let Some(path) = &self.shared_secret_path {
            expand_home(path)
        } else {
            self.pueue_directory().join("shared_secret")
        }
    }
}

impl Settings {
    /// Try to read existing config files, while using default values for non-existing fields.
    /// If successful, this will return a full config as well as a boolean on whether we found an
    /// existing configuration file or not.
    ///
    /// The default local config locations depends on the current target.
    pub fn read(from_file: &Option<PathBuf>) -> Result<(Settings, bool), 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:?}"
                )));
            }

            // Open the file in read-only mode with buffer.
            let file = File::open(path)?;
            let reader = BufReader::new(file);

            let settings = serde_yaml::from_reader(reader)
                .map_err(|err| Error::ConfigDeserialization(err.to_string()))?;
            return Ok((settings, true));
        };

        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:?}");

                // Open the file in read-only mode with buffer.
                let file = File::open(path)?;
                let reader = BufReader::new(file);

                let settings = serde_yaml::from_reader(reader)
                    .map_err(|err| Error::ConfigDeserialization(err.to_string()))?;
                return Ok((settings, true));
            }
        }

        info!("No config file found. Use default config.");
        // Return a default configuration if we couldn't find a file.
        Ok((Settings::default(), false))
    }

    /// 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(())
    }

    /// Try to load a profile. Error if it doesn't exist.
    pub fn load_profile(&mut self, profile: &str) -> Result<(), Error> {
        let profile = self.profiles.remove(profile).ok_or_else(|| {
            Error::ConfigDeserialization(format!("Couldn't find profile with name \"{profile}\""))
        })?;

        self.client = profile.client;
        self.daemon = profile.daemon;
        self.shared = profile.shared;

        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::*;

    /// Check if profiles get loaded correctly.
    #[test]
    fn test_load_profile() {
        // Create some default settings and ensure that default values are loaded.
        let mut settings = Settings::default();
        assert_eq!(
            settings.client.status_time_format,
            default_status_time_format()
        );
        assert_eq!(
            settings.daemon.callback_log_lines,
            default_callback_log_lines()
        );
        assert_eq!(settings.shared.host, default_host());

        // Crate a new profile with slightly different values.
        let mut profile = Settings::default();
        profile.client.status_time_format = "test".to_string();
        profile.daemon.callback_log_lines = 100_000;
        profile.shared.host = "quatschhost".to_string();
        let profile = NestedSettings {
            client: profile.client,
            daemon: profile.daemon,
            shared: profile.shared,
        };

        settings.profiles.insert("testprofile".to_string(), profile);

        // Load the profile and ensure the new values are now loaded.
        settings
            .load_profile("testprofile")
            .expect("We just added the profile");

        assert_eq!(settings.client.status_time_format, "test");
        assert_eq!(settings.daemon.callback_log_lines, 100_000);
        assert_eq!(settings.shared.host, "quatschhost");
    }

    /// A proper pueue [Error] should be thrown if the profile cannot be found.
    #[test]
    fn test_error_on_missing_profile() {
        let mut settings = Settings::default();

        let result = settings.load_profile("doesn't exist");
        let expected_error_message = "Couldn't find profile with name \"doesn't exist\"";
        if let Err(Error::ConfigDeserialization(error_message)) = result {
            assert_eq!(error_message, expected_error_message);
            return;
        }

        panic!("Got unexpected result when expecting missing profile error: {result:?}");
    }
}
<|endoftext|>


<commit_before>//! Basic types to build the parsers

use self::IResult::*;
use util::ErrorKind;

#[cfg(feature = "core")]
use std::prelude::v1::*;
use std::boxed::Box;

/*
/// (Experimental) Closure used to hold the temporary state of resumable parsing
pub type IResultClosure<'a,I,O> = Box<FnMut(I) -> IResult<I,O> +'a>;

//cf libcore/fmt/mod.rs:674
impl<'a,I,O> Debug for IResultClosure<'a,I,O> {
  fn fmt(&self, f: &mut Formatter) -> Result {
    Display::fmt("closure", f)
  }
}

impl<'a,I:PartialEq,O:PartialEq> PartialEq for IResultClosure<'a,I,O> {
  #[allow(unused_variables)]
  fn eq<'b>(&self, other: &IResultClosure<'b,I,O>) -> bool {
    false
  }

  #[allow(unused_variables)]
  fn ne<'b>(&self, other: &IResultClosure<'b,I,O>) -> bool {
    false
  }
}

impl<'a,I:Eq,O:Eq> Eq for IResultClosure<'a,I,O> {}
*/
//type IResultClosure<'a,I,O> = |I|:'a -> IResult<'a,I,O>;
//type IResultClosure<'a,I,O> = Fn<I, IResult<'a,I,O>>;

/// Contains the error that a parser can return
#[derive(Debug,PartialEq,Eq,Clone)]
pub enum Err<P,E=u32>{
  Code(ErrorKind<E>),
  Node(ErrorKind<E>, Box<Err<P,E>>),
  Position(ErrorKind<E>, P),
  NodePosition(ErrorKind<E>, P, Box<Err<P,E>>)
}

/// Contains information on needed data if a parser returned `Incomplete`
#[derive(Debug,PartialEq,Eq,Clone)]
pub enum Needed {
  /// needs more data, but we do not know how much
  Unknown,
  /// contains the required data size
  Size(usize)
}

/// Holds the result of parsing functions
///
/// It depends on I, the input types, and O, the output type.
///
/// * Done indicates a correct parsing, the first field containing the rest of the unparsed data,
/// the second field contains the parsed data
///
/// * Error is currently a u32, but should be updated to indicate which parser had a problem,
/// a description, and an eventual stack of parser to know which path failed
///
/// * Incomplete will hold the closure used to restart the computation once more data is available.
/// Current attemps at implementation of Incomplete are progressing, but slowed down by lifetime problems
#[derive(Debug,PartialEq,Eq,Clone)]
pub enum IResult<I,O,E=u32> {
  Done(I,O),
  Error(Err<I,E>),
  //Incomplete(proc(I):'a -> IResult<I,O>)
  Incomplete(Needed)
  //Incomplete(Box<FnMut(I) -> IResult<I,O>>)
  //Incomplete(IResultClosure<I,O>)
  //Incomplete(|I|:'a -> IResult<'a,I,O>)
  //Incomplete(fn(I) -> IResult<'a,I,O>)
}

impl<I,O> IResult<I,O> {
  pub fn is_done(&self) -> bool {
    match self {
      &Done(_,_) => true,
      _          => false
    }
  }

  pub fn is_err(&self) -> bool {
    match self {
      &Error(_) => true,
      _         => false
    }
  }

  pub fn is_incomplete(&self) -> bool {
    match self {
      &Incomplete(_) => true,
      _              => false
    }
  }
}

pub trait GetInput<I> {
  fn remaining_input(&self) -> Option<I>;
}

pub trait GetOutput<O> {
  fn output(&self) -> Option<O>;
}

impl<'a,I,O> GetInput<&'a[I]> for IResult<&'a[I],O> {
  fn remaining_input(&self) -> Option<&'a[I]> {
    match self {
      &Done(ref i,_) => Some(*i),
      _          => None
    }
  }
}

impl<'a,O> GetInput<()> for IResult<(),O> {
  fn remaining_input(&self) -> Option<()> {
    match self {
      &Done((),_) => Some(()),
      _          => None
    }
  }
}

impl<'a,I,O> GetOutput<&'a[O]> for IResult<I,&'a[O]> {
  fn output(&self) -> Option<&'a[O]> {
    match self {
      &Done(_, ref o) => Some(*o),
      _          => None
    }
  }
}

impl<'a,I> GetOutput<()> for IResult<I,()> {
  fn output(&self) -> Option<()> {
    match self {
      &Done(_,()) => Some(()),
      _          => None
    }
  }
}

<commit_msg>Needed must be Copy<commit_after>//! Basic types to build the parsers

use self::IResult::*;
use util::ErrorKind;

#[cfg(feature = "core")]
use std::prelude::v1::*;
use std::boxed::Box;

/*
/// (Experimental) Closure used to hold the temporary state of resumable parsing
pub type IResultClosure<'a,I,O> = Box<FnMut(I) -> IResult<I,O> +'a>;

//cf libcore/fmt/mod.rs:674
impl<'a,I,O> Debug for IResultClosure<'a,I,O> {
  fn fmt(&self, f: &mut Formatter) -> Result {
    Display::fmt("closure", f)
  }
}

impl<'a,I:PartialEq,O:PartialEq> PartialEq for IResultClosure<'a,I,O> {
  #[allow(unused_variables)]
  fn eq<'b>(&self, other: &IResultClosure<'b,I,O>) -> bool {
    false
  }

  #[allow(unused_variables)]
  fn ne<'b>(&self, other: &IResultClosure<'b,I,O>) -> bool {
    false
  }
}

impl<'a,I:Eq,O:Eq> Eq for IResultClosure<'a,I,O> {}
*/
//type IResultClosure<'a,I,O> = |I|:'a -> IResult<'a,I,O>;
//type IResultClosure<'a,I,O> = Fn<I, IResult<'a,I,O>>;

/// Contains the error that a parser can return
#[derive(Debug,PartialEq,Eq,Clone)]
pub enum Err<P,E=u32>{
  Code(ErrorKind<E>),
  Node(ErrorKind<E>, Box<Err<P,E>>),
  Position(ErrorKind<E>, P),
  NodePosition(ErrorKind<E>, P, Box<Err<P,E>>)
}

/// Contains information on needed data if a parser returned `Incomplete`
#[derive(Debug,PartialEq,Eq,Clone,Copy)]
pub enum Needed {
  /// needs more data, but we do not know how much
  Unknown,
  /// contains the required data size
  Size(usize)
}

/// Holds the result of parsing functions
///
/// It depends on I, the input types, and O, the output type.
///
/// * Done indicates a correct parsing, the first field containing the rest of the unparsed data,
/// the second field contains the parsed data
///
/// * Error is currently a u32, but should be updated to indicate which parser had a problem,
/// a description, and an eventual stack of parser to know which path failed
///
/// * Incomplete will hold the closure used to restart the computation once more data is available.
/// Current attemps at implementation of Incomplete are progressing, but slowed down by lifetime problems
#[derive(Debug,PartialEq,Eq,Clone)]
pub enum IResult<I,O,E=u32> {
  Done(I,O),
  Error(Err<I,E>),
  //Incomplete(proc(I):'a -> IResult<I,O>)
  Incomplete(Needed)
  //Incomplete(Box<FnMut(I) -> IResult<I,O>>)
  //Incomplete(IResultClosure<I,O>)
  //Incomplete(|I|:'a -> IResult<'a,I,O>)
  //Incomplete(fn(I) -> IResult<'a,I,O>)
}

impl<I,O> IResult<I,O> {
  pub fn is_done(&self) -> bool {
    match self {
      &Done(_,_) => true,
      _          => false
    }
  }

  pub fn is_err(&self) -> bool {
    match self {
      &Error(_) => true,
      _         => false
    }
  }

  pub fn is_incomplete(&self) -> bool {
    match self {
      &Incomplete(_) => true,
      _              => false
    }
  }
}

pub trait GetInput<I> {
  fn remaining_input(&self) -> Option<I>;
}

pub trait GetOutput<O> {
  fn output(&self) -> Option<O>;
}

impl<'a,I,O> GetInput<&'a[I]> for IResult<&'a[I],O> {
  fn remaining_input(&self) -> Option<&'a[I]> {
    match self {
      &Done(ref i,_) => Some(*i),
      _          => None
    }
  }
}

impl<'a,O> GetInput<()> for IResult<(),O> {
  fn remaining_input(&self) -> Option<()> {
    match self {
      &Done((),_) => Some(()),
      _          => None
    }
  }
}

impl<'a,I,O> GetOutput<&'a[O]> for IResult<I,&'a[O]> {
  fn output(&self) -> Option<&'a[O]> {
    match self {
      &Done(_, ref o) => Some(*o),
      _          => None
    }
  }
}

impl<'a,I> GetOutput<()> for IResult<I,()> {
  fn output(&self) -> Option<()> {
    match self {
      &Done(_,()) => Some(()),
      _          => None
    }
  }
}

<|endoftext|>


<commit_before>//! Basic types to build the parsers

use self::Needed::*;
use crate::error::ErrorKind;
use core::num::NonZeroUsize;

/// Holds the result of parsing functions
///
/// It depends on I, the input type, O, the output type, and E, the error type (by default u32)
///
/// The `Ok` side is a pair containing the remainder of the input (the part of the data that
/// was not parsed) and the produced value. The `Err` side contains an instance of `nom::Err`.
///
pub type IResult<I, O, E=(I,ErrorKind)> = Result<(I, O), Err<E>>;

/// Contains information on needed data if a parser returned `Incomplete`
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Needed {
  /// needs more data, but we do not know how much
  Unknown,
  /// contains the required data size
  Size(NonZeroUsize),
}

impl Needed {
  /// creates Needed instance, returns `Needed::Unknown` if the argument is zero
  pub fn new(s: usize) -> Self {
    match NonZeroUsize::new(s) {
      Some(sz) => Needed::Size(sz),
      None => Needed::Unknown,
    }
  }

  /// indicates if we know how many bytes we need
  pub fn is_known(&self) -> bool {
    *self != Unknown
  }

  /// Maps a `Needed` to `Needed` by applying a function to a contained `Size` value.
  #[inline]
  pub fn map<F: Fn(NonZeroUsize) -> usize>(self, f: F) -> Needed {
    match self {
      Unknown => Unknown,
      Size(n) => Needed::new(f(n)),
    }
  }
}

/// The `Err` enum indicates the parser was not successful
///
/// It has three cases:
///
/// * `Incomplete` indicates that more data is needed to decide. The `Needed` enum
/// can contain how many additional bytes are necessary. If you are sure your parser
/// is working on full data, you can wrap your parser with the `complete` combinator
/// to transform that case in `Error`
/// * `Error` means some parser did not succeed, but another one might (as an example,
/// when testing different branches of an `alt` combinator)
/// * `Failure` indicates an unrecoverable error. As an example, if you recognize a prefix
/// to decide on the next parser to apply, and that parser fails, you know there's no need
/// to try other parsers, you were already in the right branch, so the data is invalid
///
#[derive(Debug, Clone, PartialEq)]
pub enum Err<E> {
  /// There was not enough data
  Incomplete(Needed),
  /// The parser had an error (recoverable)
  Error(E),
  /// The parser had an unrecoverable error: we got to the right
  /// branch and we know other branches won't work, so backtrack
  /// as fast as possible
  Failure(E),
}

impl<E> Err<E> {
  /// tests if the result is Incomplete
  pub fn is_incomplete(&self) -> bool {
    if let Err::Incomplete(_) = self {
      true
    } else {
      false
    }
  }

  /// Applies the given function to the inner error
  pub fn map<E2, F>(self, f: F) -> Err<E2>
    where F: FnOnce(E) -> E2
  {
    match self {
      Err::Incomplete(n) => Err::Incomplete(n),
      Err::Failure(t) => Err::Failure(f(t)),
      Err::Error(t) => Err::Error(f(t)),
    }
  }

  /// automatically converts between errors if the underlying type supports it
  pub fn convert<F>(e: Err<F>) -> Self
    where E: From<F>
  {
    e.map(Into::into)
  }
}

impl<T> Err<(T, ErrorKind)> {
  /// maps `Err<(T, ErrorKind)>` to `Err<(U, ErrorKind)>` with the given F: T -> U
  pub fn map_input<U, F>(self, f: F) -> Err<(U, ErrorKind)>
    where F: FnOnce(T) -> U {
    match self {
      Err::Incomplete(n) => Err::Incomplete(n),
      Err::Failure((input, k)) => Err::Failure((f(input), k)),
      Err::Error((input, k)) => Err::Error((f(input), k)),
    }
  }
}

#[cfg(feature = "std")]
impl Err<(&[u8], ErrorKind)> {
  /// Obtaining ownership
  pub fn to_owned(self) -> Err<(Vec<u8>, ErrorKind)> {
    self.map_input(ToOwned::to_owned)
  }
}

#[cfg(feature = "std")]
impl Err<(&str, ErrorKind)> {
  /// automatically converts between errors if the underlying type supports it
  pub fn to_owned(self) -> Err<(String, ErrorKind)> {
    self.map_input(ToOwned::to_owned)
  }
}

impl<E: Eq> Eq for Err<E> {}

#[cfg(feature = "std")]
use std::fmt;

#[cfg(feature = "std")]
impl<E> fmt::Display for Err<E>
where
  E: fmt::Debug,
{
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match self {
      Err::Incomplete(Needed::Size(u)) => write!(f, "Parsing requires {} bytes/chars", u),
      Err::Incomplete(Needed::Unknown) => write!(f, "Parsing requires more data"),
      Err::Failure(c) => write!(f, "Parsing Failure: {:?}", c),
      Err::Error(c) => write!(f, "Parsing Error: {:?}", c),
    }
  }
}

#[cfg(feature = "std")]
use std::error::Error;

#[cfg(feature = "std")]
impl<E> Error for Err<E>
where
  E: fmt::Debug,
{
  fn source(&self) -> Option<&(dyn Error + 'static)> {
    None // no underlying error
  }
}

/// all nom parsers implement ths trait
pub trait Parser<I, O, E> {
  /// a parser takes in input type, and returns a `Result` containing
  /// either the remaining input and the output value, or an error
  fn parse(&mut self, input: I) -> IResult<I, O, E>;
}

impl<'a, I, O, E, F> Parser<I, O, E> for F
where
  F: FnMut(I) -> IResult<I, O, E> + 'a
{
  fn parse(&mut self, i: I) -> IResult<I, O, E> {
    self(i)
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use crate::error::ErrorKind;

  #[doc(hidden)]
  #[macro_export]
  macro_rules! assert_size (
    ($t:ty, $sz:expr) => (
      assert_eq!(crate::lib::std::mem::size_of::<$t>(), $sz);
    );
  );

  #[test]
  #[cfg(target_pointer_width = "64")]
  fn size_test() {
    assert_size!(IResult<&[u8], &[u8], (&[u8], u32)>, 40);
    assert_size!(IResult<&str, &str, u32>, 40);
    assert_size!(Needed, 8);
    assert_size!(Err<u32>, 16);
    assert_size!(ErrorKind, 1);
  }

  #[test]
  fn err_map_test() {
    let e = Err::Error(1);
    assert_eq!(e.map(|v| v + 1), Err::Error(2));
  }

}
<commit_msg>add a map() method on the Parser trait<commit_after>//! Basic types to build the parsers

use self::Needed::*;
use crate::error::ErrorKind;
use core::num::NonZeroUsize;

/// Holds the result of parsing functions
///
/// It depends on I, the input type, O, the output type, and E, the error type (by default u32)
///
/// The `Ok` side is a pair containing the remainder of the input (the part of the data that
/// was not parsed) and the produced value. The `Err` side contains an instance of `nom::Err`.
///
pub type IResult<I, O, E=(I,ErrorKind)> = Result<(I, O), Err<E>>;

/// Contains information on needed data if a parser returned `Incomplete`
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Needed {
  /// needs more data, but we do not know how much
  Unknown,
  /// contains the required data size
  Size(NonZeroUsize),
}

impl Needed {
  /// creates Needed instance, returns `Needed::Unknown` if the argument is zero
  pub fn new(s: usize) -> Self {
    match NonZeroUsize::new(s) {
      Some(sz) => Needed::Size(sz),
      None => Needed::Unknown,
    }
  }

  /// indicates if we know how many bytes we need
  pub fn is_known(&self) -> bool {
    *self != Unknown
  }

  /// Maps a `Needed` to `Needed` by applying a function to a contained `Size` value.
  #[inline]
  pub fn map<F: Fn(NonZeroUsize) -> usize>(self, f: F) -> Needed {
    match self {
      Unknown => Unknown,
      Size(n) => Needed::new(f(n)),
    }
  }
}

/// The `Err` enum indicates the parser was not successful
///
/// It has three cases:
///
/// * `Incomplete` indicates that more data is needed to decide. The `Needed` enum
/// can contain how many additional bytes are necessary. If you are sure your parser
/// is working on full data, you can wrap your parser with the `complete` combinator
/// to transform that case in `Error`
/// * `Error` means some parser did not succeed, but another one might (as an example,
/// when testing different branches of an `alt` combinator)
/// * `Failure` indicates an unrecoverable error. As an example, if you recognize a prefix
/// to decide on the next parser to apply, and that parser fails, you know there's no need
/// to try other parsers, you were already in the right branch, so the data is invalid
///
#[derive(Debug, Clone, PartialEq)]
pub enum Err<E> {
  /// There was not enough data
  Incomplete(Needed),
  /// The parser had an error (recoverable)
  Error(E),
  /// The parser had an unrecoverable error: we got to the right
  /// branch and we know other branches won't work, so backtrack
  /// as fast as possible
  Failure(E),
}

impl<E> Err<E> {
  /// tests if the result is Incomplete
  pub fn is_incomplete(&self) -> bool {
    if let Err::Incomplete(_) = self {
      true
    } else {
      false
    }
  }

  /// Applies the given function to the inner error
  pub fn map<E2, F>(self, f: F) -> Err<E2>
    where F: FnOnce(E) -> E2
  {
    match self {
      Err::Incomplete(n) => Err::Incomplete(n),
      Err::Failure(t) => Err::Failure(f(t)),
      Err::Error(t) => Err::Error(f(t)),
    }
  }

  /// automatically converts between errors if the underlying type supports it
  pub fn convert<F>(e: Err<F>) -> Self
    where E: From<F>
  {
    e.map(Into::into)
  }
}

impl<T> Err<(T, ErrorKind)> {
  /// maps `Err<(T, ErrorKind)>` to `Err<(U, ErrorKind)>` with the given F: T -> U
  pub fn map_input<U, F>(self, f: F) -> Err<(U, ErrorKind)>
    where F: FnOnce(T) -> U {
    match self {
      Err::Incomplete(n) => Err::Incomplete(n),
      Err::Failure((input, k)) => Err::Failure((f(input), k)),
      Err::Error((input, k)) => Err::Error((f(input), k)),
    }
  }
}

#[cfg(feature = "std")]
impl Err<(&[u8], ErrorKind)> {
  /// Obtaining ownership
  pub fn to_owned(self) -> Err<(Vec<u8>, ErrorKind)> {
    self.map_input(ToOwned::to_owned)
  }
}

#[cfg(feature = "std")]
impl Err<(&str, ErrorKind)> {
  /// automatically converts between errors if the underlying type supports it
  pub fn to_owned(self) -> Err<(String, ErrorKind)> {
    self.map_input(ToOwned::to_owned)
  }
}

impl<E: Eq> Eq for Err<E> {}

#[cfg(feature = "std")]
use std::fmt;

#[cfg(feature = "std")]
impl<E> fmt::Display for Err<E>
where
  E: fmt::Debug,
{
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match self {
      Err::Incomplete(Needed::Size(u)) => write!(f, "Parsing requires {} bytes/chars", u),
      Err::Incomplete(Needed::Unknown) => write!(f, "Parsing requires more data"),
      Err::Failure(c) => write!(f, "Parsing Failure: {:?}", c),
      Err::Error(c) => write!(f, "Parsing Error: {:?}", c),
    }
  }
}

#[cfg(feature = "std")]
use std::error::Error;

#[cfg(feature = "std")]
impl<E> Error for Err<E>
where
  E: fmt::Debug,
{
  fn source(&self) -> Option<&(dyn Error + 'static)> {
    None // no underlying error
  }
}

/// all nom parsers implement ths trait
pub trait Parser<I, O, E> {
  /// a parser takes in input type, and returns a `Result` containing
  /// either the remaining input and the output value, or an error
  fn parse(&mut self, input: I) -> IResult<I, O, E>;

  /// maps a function over the result of a parser
  fn map<G,O2>(self, g: G) -> Mapper<Self, G, O>
      where G: Fn(O) -> O2,
            Self: std::marker::Sized, {
    Mapper {
        f: self,
        g,
        phantom: std::marker::PhantomData,
    }
  }
}

impl<'a, I, O, E, F> Parser<I, O, E> for F
where
  F: FnMut(I) -> IResult<I, O, E> + 'a
{
  fn parse(&mut self, i: I) -> IResult<I, O, E> {
    self(i)
  }
}

/// implementation of parser mapping
pub struct Mapper<F, G, O1> {
  f: F,
  g: G,
  phantom: std::marker::PhantomData<O1>,
}

impl<'a, I, O1, O2, E, F: Parser<I, O1, E>, G: Fn(O1) -> O2> Parser<I, O2, E> for Mapper<F, G, O1>
{
  fn parse(&mut self, i: I) -> IResult<I, O2, E> {
    match self.f.parse(i) {
        Err(e) => Err(e),
        Ok((i, o)) => Ok((i, (self.g)(o))),
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use crate::error::ErrorKind;

  #[doc(hidden)]
  #[macro_export]
  macro_rules! assert_size (
    ($t:ty, $sz:expr) => (
      assert_eq!(crate::lib::std::mem::size_of::<$t>(), $sz);
    );
  );

  #[test]
  #[cfg(target_pointer_width = "64")]
  fn size_test() {
    assert_size!(IResult<&[u8], &[u8], (&[u8], u32)>, 40);
    assert_size!(IResult<&str, &str, u32>, 40);
    assert_size!(Needed, 8);
    assert_size!(Err<u32>, 16);
    assert_size!(ErrorKind, 1);
  }

  #[test]
  fn err_map_test() {
    let e = Err::Error(1);
    assert_eq!(e.map(|v| v + 1), Err::Error(2));
  }

}
<|endoftext|>


<commit_before>/*
Module: list

A standard linked list
*/

import option::{some, none};

/* Section: Types */

/*
Tag: list
*/
tag list<T> {
    /* Variant: cons */
    cons(T, @list<T>);
    /* Variant: nil */
    nil;
}

/*Section: Operations */

/*
Function: from_vec

Create a list from a vector
*/
fn from_vec<T>(v: [mutable? T]) -> list<T> {
    let l = nil::<T>;
    // FIXME: This would be faster and more space efficient if it looped over
    // a reverse vector iterator. Unfortunately generic iterators seem not to
    // work yet.

    for item: T in vec::reversed(v) { l = cons::<T>(item, @l); }
    ret l;
}

/*
Function: foldl

Left fold

Applies `f` to the first argument in the list and `u`, then applies
`f` to the second argument and the result of the previous call,
and so on, returning the accumulated result.

Parameters:

ls_ - The list to fold
u - The initial value
f - The function to apply
*/
fn foldl<T, U>(ls_: list<T>, u: U, f: block(T, U) -> U) -> U {
    let accum: U = u;
    let ls = ls_;
    while true {
        alt ls {
          cons(hd, tl) { accum = f(hd, accum); ls = *tl; }
          nil. { break; }
        }
    }
    ret accum;
}

/*
Function: find

Search for an element that matches a given predicate

Apply function `f` to each element of `v`, starting from the first.
When function `f` returns true then an option containing the element
is returned. If `f` matches no elements then none is returned.
*/
fn find<T, U>(ls_: list<T>, f: block(T) -> option::t<U>) -> option::t<U> {
    let ls = ls_;
    while true {
        alt ls {
          cons(hd, tl) {
            alt f(hd) { none. { ls = *tl; } some(rs) { ret some(rs); } }
          }
          nil. { break; }
        }
    }
    ret none;
}

/*
Function: has

Returns true if a list contains an element with the given value
*/
fn has<T>(ls_: list<T>, elt: T) -> bool {
    let ls = ls_;
    while true {
        alt ls {
          cons(hd, tl) { if elt == hd { ret true; } else { ls = *tl; } }
          nil. { break; }
        }
    }
    ret false;
}

/*
Function: length

Returns the length of a list
*/
fn length<T>(ls: list<T>) -> uint {
    fn count<T>(_t: T, &&u: uint) -> uint { ret u + 1u; }
    ret foldl(ls, 0u, bind count(_, _));
}

/*
Function: cdr

Returns all but the first element of a list
*/
fn cdr<T>(ls: list<T>) -> list<T> {
    alt ls { cons(_, tl) { ret *tl; } nil. { fail "list empty" } }
}

/*
Function: car

Returns the first element of a list
*/
fn car<T>(ls: list<T>) -> T {
    alt ls { cons(hd, _) { ret hd; } nil. { fail "list empty" } }
}

/*
Function: append

Appends one list to another
*/
fn append<T>(l: list<T>, m: list<T>) -> list<T> {
    alt l {
      nil. { ret m; }
      cons(x, xs) { let rest = append(*xs, m); ret cons(x, @rest); }
    }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>stdlib: Make list::from_vec more efficient<commit_after>/*
Module: list

A standard linked list
*/

import option::{some, none};

/* Section: Types */

/*
Tag: list
*/
tag list<T> {
    /* Variant: cons */
    cons(T, @list<T>);
    /* Variant: nil */
    nil;
}

/*Section: Operations */

/*
Function: from_vec

Create a list from a vector
*/
fn from_vec<T>(v: [mutable? T]) -> list<T> {
    *vec::foldr({ |h, t| @cons(h, t) }, @nil::<T>, v)
}

/*
Function: foldl

Left fold

Applies `f` to the first argument in the list and `u`, then applies
`f` to the second argument and the result of the previous call,
and so on, returning the accumulated result.

Parameters:

ls_ - The list to fold
u - The initial value
f - The function to apply
*/
fn foldl<T, U>(ls_: list<T>, u: U, f: block(T, U) -> U) -> U {
    let accum: U = u;
    let ls = ls_;
    while true {
        alt ls {
          cons(hd, tl) { accum = f(hd, accum); ls = *tl; }
          nil. { break; }
        }
    }
    ret accum;
}

/*
Function: find

Search for an element that matches a given predicate

Apply function `f` to each element of `v`, starting from the first.
When function `f` returns true then an option containing the element
is returned. If `f` matches no elements then none is returned.
*/
fn find<T, U>(ls_: list<T>, f: block(T) -> option::t<U>) -> option::t<U> {
    let ls = ls_;
    while true {
        alt ls {
          cons(hd, tl) {
            alt f(hd) { none. { ls = *tl; } some(rs) { ret some(rs); } }
          }
          nil. { break; }
        }
    }
    ret none;
}

/*
Function: has

Returns true if a list contains an element with the given value
*/
fn has<T>(ls_: list<T>, elt: T) -> bool {
    let ls = ls_;
    while true {
        alt ls {
          cons(hd, tl) { if elt == hd { ret true; } else { ls = *tl; } }
          nil. { break; }
        }
    }
    ret false;
}

/*
Function: length

Returns the length of a list
*/
fn length<T>(ls: list<T>) -> uint {
    fn count<T>(_t: T, &&u: uint) -> uint { ret u + 1u; }
    ret foldl(ls, 0u, bind count(_, _));
}

/*
Function: cdr

Returns all but the first element of a list
*/
fn cdr<T>(ls: list<T>) -> list<T> {
    alt ls { cons(_, tl) { ret *tl; } nil. { fail "list empty" } }
}

/*
Function: car

Returns the first element of a list
*/
fn car<T>(ls: list<T>) -> T {
    alt ls { cons(hd, _) { ret hd; } nil. { fail "list empty" } }
}

/*
Function: append

Appends one list to another
*/
fn append<T>(l: list<T>, m: list<T>) -> list<T> {
    alt l {
      nil. { ret m; }
      cons(x, xs) { let rest = append(*xs, m); ret cons(x, @rest); }
    }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Some crumminess to make sure we link correctly

#[cfg(target_os = "linux")]
#[link(name = "azure", kind = "static")]
#[link(name = "stdc++")]
#[link(name = "freetype")]
#[link(name = "bz2")]
// fontconfig must come before expat for linking to succeed
#[link(name = "fontconfig")]
#[link(name = "expat")]
#[link(name = "X11")]
extern { }

#[cfg(target_os = "android")]
#[link(name = "azure", kind = "static")]
#[link(name = "stdc++")]
#[link(name = "expat")]
#[link(name = "fontconfig")]
#[link(name = "EGL")]
extern { }

#[cfg(target_os = "macos")]
#[link(name = "azure", kind = "static")]
#[link(name = "stdc++")]
#[link(name = "objc")]
#[link(name = "IOSurface", kind = "framework")]
#[link(name = "OpenGL", kind = "framework")]
#[link(name = "Foundation", kind = "framework")]
#[link(name = "QuartzCore", kind = "framework")]
#[link(name = "ApplicationServices", kind = "framework")]
extern { }

<commit_msg>Auto merge of #297 - pcwalton:mojave, r=Manishearth<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Some crumminess to make sure we link correctly

#[cfg(target_os = "linux")]
#[link(name = "azure", kind = "static")]
#[link(name = "stdc++")]
#[link(name = "freetype")]
#[link(name = "bz2")]
// fontconfig must come before expat for linking to succeed
#[link(name = "fontconfig")]
#[link(name = "expat")]
#[link(name = "X11")]
extern { }

#[cfg(target_os = "android")]
#[link(name = "azure", kind = "static")]
#[link(name = "stdc++")]
#[link(name = "expat")]
#[link(name = "fontconfig")]
#[link(name = "EGL")]
extern { }

#[cfg(target_os = "macos")]
#[link(name = "azure", kind = "static")]
#[link(name = "c++")]
#[link(name = "objc")]
#[link(name = "IOSurface", kind = "framework")]
#[link(name = "OpenGL", kind = "framework")]
#[link(name = "Foundation", kind = "framework")]
#[link(name = "QuartzCore", kind = "framework")]
#[link(name = "ApplicationServices", kind = "framework")]
extern { }

<|endoftext|>


<commit_before>//! The Mach-o binary format parser and raw struct definitions

use scroll::{Pread};

use error;
//use container::{self, Container};

pub mod header;
pub mod constants;
pub mod fat;
pub mod utils;
pub mod load_command;
pub mod symbols;
pub mod exports;

pub use self::constants::cputype as cputype;

#[derive(Debug)]
/// A zero-copy, endian-aware, 32/64 bit Mach-o binary parser
pub struct MachO<'a> {
    pub header: header::Header,
    pub load_commands: Vec<load_command::LoadCommand>,
    pub symbols: Option<symbols::Symbols<'a>>,
    pub libs: Vec<&'a str>,
    pub entry: u64,
    export_trie: Option<exports::ExportTrie<'a>>,
}

impl<'a> MachO<'a> {
    /// Return the exported symbols in this binary (if any)
    pub fn exports(&self) -> error::Result<Vec<exports::Export>> {
        if let Some(ref trie) = self.export_trie {
            trie.exports(self.libs.as_slice())
        } else {
            Ok(vec![])
        }
    }
    /// Parses the Mach-o binary from `buffer` at `offset`
    pub fn parse<'b, B: AsRef<[u8]>> (buffer: &'b B, mut offset: usize) -> error::Result<MachO<'b>> {
        let offset = &mut offset;
        let header: header::Header = buffer.pread(*offset)?;
        let ctx = header.ctx()?;
        *offset = *offset + header.size();
        let ncmds = header.ncmds;
        let mut cmds: Vec<load_command::LoadCommand> = Vec::with_capacity(ncmds);
        let mut symbols = None;
        let mut libs = Vec::new();
        let mut export_trie = None;
        let mut entry = 0x0;
        for _ in 0..ncmds {
            let cmd = load_command::LoadCommand::parse(buffer, offset, ctx.le)?;
            match cmd.command {
                load_command::CommandVariant::Symtab(command) => {
                    symbols = Some(symbols::Symbols::parse(buffer, &command, ctx)?);
                },
                  load_command::CommandVariant::LoadDylib    (command)
                | load_command::CommandVariant::ReexportDylib(command)
                | load_command::CommandVariant::LazyLoadDylib(command) => {
                    let lib = buffer.pread::<&str>(cmd.offset + command.dylib.name as usize)?;
                    libs.push(lib);
                },
                  load_command::CommandVariant::DyldInfo    (command)
                | load_command::CommandVariant::DyldInfoOnly(command) => {
                    export_trie = Some(exports::ExportTrie::new(buffer, &command)?);
                },
                load_command::CommandVariant::Main(command) => {
                    entry = command.entryoff;
                }
                _ => ()
            }
            cmds.push(cmd)
        }
        Ok(MachO {
            header: header,
            load_commands: cmds,
            symbols: symbols,
            libs: libs,
            export_trie: export_trie,
            entry: entry,
        })
    }
}

#[derive(Debug)]
/// Either a fat collection of architectures, or a single mach-o binary
pub enum Mach<'a> {
    Fat(Vec<fat::FatArch>),
    Binary(MachO<'a>)
}

impl<'a> Mach<'a> {
    pub fn parse<'b, B: AsRef<[u8]>>(buffer: &'b B) -> error::Result<Mach<'b>> {
        let size = buffer.as_ref().len();
        if size < 4 {
            let error = error::Error::Malformed(
                                       format!("size is smaller than a magical number"));
            return Err(error);
        }
        let magic = utils::peek_magic(&buffer, 0)?;
        match magic {
            fat::FAT_CIGAM => {
                let arches = fat::FatArch::parse(&buffer)?;
                println!("{:?}", arches);
                Ok(Mach::Fat(arches))
            },
            // we're a regular binary
            _ => {
                let binary = MachO::parse(buffer, 0)?;
                Ok(Mach::Binary(binary))
            }
        }
    }
}
<commit_msg>mach: add name to parser and add upward dylibs for parsed libs<commit_after>//! The Mach-o binary format parser and raw struct definitions

use scroll::{Pread};

use error;
//use container::{self, Container};

pub mod header;
pub mod constants;
pub mod fat;
pub mod utils;
pub mod load_command;
pub mod symbols;
pub mod exports;

pub use self::constants::cputype as cputype;

#[derive(Debug)]
/// A zero-copy, endian-aware, 32/64 bit Mach-o binary parser
pub struct MachO<'a> {
    pub header: header::Header,
    pub load_commands: Vec<load_command::LoadCommand>,
    pub symbols: Option<symbols::Symbols<'a>>,
    pub libs: Vec<&'a str>,
    pub entry: u64,
    pub name: Option<&'a str>,
    export_trie: Option<exports::ExportTrie<'a>>,
}

impl<'a> MachO<'a> {
    /// Return the exported symbols in this binary (if any)
    pub fn exports(&self) -> error::Result<Vec<exports::Export>> {
        if let Some(ref trie) = self.export_trie {
            trie.exports(self.libs.as_slice())
        } else {
            Ok(vec![])
        }
    }
    /// Parses the Mach-o binary from `buffer` at `offset`
    pub fn parse<'b, B: AsRef<[u8]>> (buffer: &'b B, mut offset: usize) -> error::Result<MachO<'b>> {
        let offset = &mut offset;
        let header: header::Header = buffer.pread(*offset)?;
        let ctx = header.ctx()?;
        *offset = *offset + header.size();
        let ncmds = header.ncmds;
        let mut cmds: Vec<load_command::LoadCommand> = Vec::with_capacity(ncmds);
        let mut symbols = None;
        let mut libs = Vec::new();
        let mut export_trie = None;
        let mut entry = 0x0;
        let mut name = None;
        for _ in 0..ncmds {
            let cmd = load_command::LoadCommand::parse(buffer, offset, ctx.le)?;
            match cmd.command {
                load_command::CommandVariant::Symtab(command) => {
                    symbols = Some(symbols::Symbols::parse(buffer, &command, ctx)?);
                },
                  load_command::CommandVariant::LoadDylib      (command)
                | load_command::CommandVariant::LoadUpwardDylib(command)
                | load_command::CommandVariant::ReexportDylib  (command)
                | load_command::CommandVariant::LazyLoadDylib  (command) => {
                    let lib = buffer.pread::<&str>(cmd.offset + command.dylib.name as usize)?;
                    libs.push(lib);
                },
                  load_command::CommandVariant::DyldInfo    (command)
                | load_command::CommandVariant::DyldInfoOnly(command) => {
                    export_trie = Some(exports::ExportTrie::new(buffer, &command)?);
                },
                load_command::CommandVariant::Main(command) => {
                    entry = command.entryoff;
                },
                load_command::CommandVariant::IdDylib(command) => {
                    name = Some(buffer.pread::<&str>(cmd.offset + command.dylib.name as usize)?);
                },
                _ => ()
            }
            cmds.push(cmd)
        }
        Ok(MachO {
            header: header,
            load_commands: cmds,
            symbols: symbols,
            libs: libs,
            export_trie: export_trie,
            entry: entry,
            name: name,
        })
    }
}

#[derive(Debug)]
/// Either a fat collection of architectures, or a single mach-o binary
pub enum Mach<'a> {
    Fat(Vec<fat::FatArch>),
    Binary(MachO<'a>)
}

impl<'a> Mach<'a> {
    pub fn parse<'b, B: AsRef<[u8]>>(buffer: &'b B) -> error::Result<Mach<'b>> {
        let size = buffer.as_ref().len();
        if size < 4 {
            let error = error::Error::Malformed(
                                       format!("size is smaller than a magical number"));
            return Err(error);
        }
        let magic = utils::peek_magic(&buffer, 0)?;
        match magic {
            fat::FAT_CIGAM => {
                let arches = fat::FatArch::parse(&buffer)?;
                println!("{:?}", arches);
                Ok(Mach::Fat(arches))
            },
            // we're a regular binary
            _ => {
                let binary = MachO::parse(buffer, 0)?;
                Ok(Mach::Binary(binary))
            }
        }
    }
}
<|endoftext|>


<commit_before>// build.rs

extern crate pkg_config;
extern crate bindgen;
extern crate cmake;
extern crate cc;

use std::env;
use std::path::Path;

use std::fs::File;
use std::io::Write;

fn format_write(builder: bindgen::Builder, output: &str) {
    let s = builder
        .generate()
        .unwrap()
        .to_string()
        .replace("/**", "/*")
        .replace("/*!", "/*");

    let mut file = File::create(output).unwrap();

    let _ = file.write(s.as_bytes());
}

fn main() {
    let cargo_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let build_path = Path::new(&cargo_dir).join("aom_build/aom");

    let cfg = cmake::Config::new(build_path)
        .define("CONFIG_AV1_ENCODER", "0")
        .define("CONFIG_DEBUG", "1")
        .define("CONFIG_EXPERIMENTAL", "1")
        .define("CONFIG_UNIT_TESTS", "0")
        .define("CONFIG_EXT_INTRA", "0")
        .define("CONFIG_EXT_PARTITION", "0")
        .define("CONFIG_EXT_PARTITION_TYPES", "0")
        .define("CONFIG_INTRA_EDGE", "0")
        .define("CONFIG_CFL", "0")
        .define("CONFIG_KF_CTX", "0")
        .define("CONFIG_EXT_INTRA_MOD", "0")
        .define("CONFIG_SIMPLIFY_TX_MODE", "0")
        .define("CONFIG_OBU", "0")
        .define("CONFIG_FILTER_INTRA", "0")
        .define("CONFIG_EXT_SKIP", "0")
        .define("CONFIG_LV_MAP", "0")
        .define("CONFIG_LV_MAP_MULTI", "0")
        .define("CONFIG_ANALYZER", "0")
        .build();

    env::set_var("PKG_CONFIG_PATH", cfg.join("lib/pkgconfig"));

    let libs = pkg_config::Config::new().statik(true).probe("aom").unwrap();
    let headers = libs.include_paths.clone();

    let mut builder = bindgen::builder()
        .raw_line("#![allow(dead_code)]")
        .raw_line("#![allow(non_camel_case_types)]")
        .raw_line("#![allow(non_snake_case)]")
        .raw_line("#![allow(non_upper_case_globals)]")
        .blacklist_type("max_align_t")
        .header("data/aom.h");

    for header in headers {
        builder = builder.clang_arg("-I").clang_arg(header.to_str().unwrap());
    }

    // Manually fix the comment so rustdoc won't try to pick them
    format_write(builder, "tests/aom.rs");

    cc::Build::new()
        .file("aom_build/aom/aom_dsp/fwd_txfm.c")
        .file("aom_build/aom/av1/encoder/dct.c")
        .include("aom_build")
        .include("aom_build/aom")
        .flag("-std=c99")
        .compile("libntr.a");
}
<commit_msg>build: Explicitly ask to rerun build.rs if that directory is changed<commit_after>// build.rs

extern crate pkg_config;
extern crate bindgen;
extern crate cmake;
extern crate cc;

use std::env;
use std::path::Path;

use std::fs::File;
use std::io::Write;

fn format_write(builder: bindgen::Builder, output: &str) {
    let s = builder
        .generate()
        .unwrap()
        .to_string()
        .replace("/**", "/*")
        .replace("/*!", "/*");

    let mut file = File::create(output).unwrap();

    let _ = file.write(s.as_bytes());
}

fn main() {
    let cargo_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let build_path = Path::new(&cargo_dir).join("aom_build/aom");

    let cfg = cmake::Config::new(build_path)
        .define("CONFIG_AV1_ENCODER", "0")
        .define("CONFIG_DEBUG", "1")
        .define("CONFIG_EXPERIMENTAL", "1")
        .define("CONFIG_UNIT_TESTS", "0")
        .define("CONFIG_EXT_INTRA", "0")
        .define("CONFIG_EXT_PARTITION", "0")
        .define("CONFIG_EXT_PARTITION_TYPES", "0")
        .define("CONFIG_INTRA_EDGE", "0")
        .define("CONFIG_CFL", "0")
        .define("CONFIG_KF_CTX", "0")
        .define("CONFIG_EXT_INTRA_MOD", "0")
        .define("CONFIG_SIMPLIFY_TX_MODE", "0")
        .define("CONFIG_OBU", "0")
        .define("CONFIG_FILTER_INTRA", "0")
        .define("CONFIG_EXT_SKIP", "0")
        .define("CONFIG_LV_MAP", "0")
        .define("CONFIG_LV_MAP_MULTI", "0")
        .define("CONFIG_ANALYZER", "0")
        .build();

    env::set_var("PKG_CONFIG_PATH", cfg.join("lib/pkgconfig"));

    let libs = pkg_config::Config::new().statik(true).probe("aom").unwrap();
    let headers = libs.include_paths.clone();

    let mut builder = bindgen::builder()
        .raw_line("#![allow(dead_code)]")
        .raw_line("#![allow(non_camel_case_types)]")
        .raw_line("#![allow(non_snake_case)]")
        .raw_line("#![allow(non_upper_case_globals)]")
        .blacklist_type("max_align_t")
        .header("data/aom.h");

    for header in headers {
        builder = builder.clang_arg("-I").clang_arg(header.to_str().unwrap());
    }

    // Manually fix the comment so rustdoc won't try to pick them
    format_write(builder, "tests/aom.rs");

    println!("cargo:rerun-if-changed=aom_build/aom/build");

    cc::Build::new()
        .file("aom_build/aom/aom_dsp/fwd_txfm.c")
        .file("aom_build/aom/av1/encoder/dct.c")
        .include("aom_build")
        .include("aom_build/aom")
        .flag("-std=c99")
        .compile("libntr.a");
}
<|endoftext|>


<commit_before>extern crate cc;
extern crate glob;

#[cfg(not(feature = "orangepi"))]
const TARGET: &'static str = "wiringPi";
#[cfg(feature = "orangepi")]
const TARGET: &'static str = "WiringOP";

fn main() {
    if cfg!(feature = "development") {
        return;
    }

    cc::Build::new()
        .files(
            glob::glob(&format!("{}/wiringPi/*.c", TARGET))
                .unwrap()
                .filter_map(|x| x.ok()),
        )
        .include(format!("{}/", TARGET))
        .include(format!("{}/wiringPi/", TARGET))
        .static_flag(true)
        .compile("wiringpi");
}
<commit_msg>Check to only build wiringpi/wiringop on arm/armv7 platforms<commit_after>extern crate cc;
extern crate glob;

use std::env;

#[cfg(not(feature = "orangepi"))]
const TARGET: &'static str = "wiringPi";
#[cfg(feature = "orangepi")]
const TARGET: &'static str = "WiringOP";

fn main() {
    if cfg!(feature = "development") {
        return;
    }

    // only build wiringpi/wiringop for arm/armv7 platforms

    let target = std::env::var("TARGET").unwrap();
    if !(target.starts_with("arm-") || target.starts_with("armv7-")) {
        return;
    }

    cc::Build::new()
        .files(
            glob::glob(&format!("{}/wiringPi/*.c", TARGET))
                .unwrap()
                .filter_map(|x| x.ok()),
        )
        .include(format!("{}/", TARGET))
        .include(format!("{}/wiringPi/", TARGET))
        .static_flag(true)
        .compile("wiringpi");
}
<|endoftext|>


<commit_before>use std::env;
use std::path::Path;
use std::process::{Stdio, Command};

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let dst = Path::new(&out_dir);

    setup();
    configure();
    build();
    install(&dst);
    clean();
    println!("cargo:rustc-link-search={}", dst.join("lib").display());
    println!("cargo:rustc-link-lib=static=termbox");
}

fn setup() {
    let mut cmd = Command::new("git");
    cmd.arg("clone");
    cmd.arg("https://github.com/nsf/termbox");
    cmd.arg(".termbox");
    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let cargo_dir = Path::new(&manifest_dir);
    cmd.current_dir(&cargo_dir);

    run(&mut cmd);
}

fn clean() {
    let mut cmd = Command::new("rm");
    cmd.arg("-rf");
    cmd.arg(".termbox");
    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let cargo_dir = Path::new(&manifest_dir);
    cmd.current_dir(&cargo_dir);
    run(&mut cmd);
}

fn configure() {
    let mut cmd = waf();
    cmd.arg("configure");
    cmd.arg("--prefix=/");

    let target = env::var("TARGET").unwrap();
    let mut cflags;
    if target.contains("i686") {
        cflags = "-m32"
    } else if target.contains("x86_64") {
        cflags = "-m64 -fPIC"
    } else {
        cflags = "-fPIC"
    }
    println!("waf configure: setting CFLAGS to: `{}`", cflags);
    env::set_var("CFLAGS", cflags);

    run(&mut cmd);
    env::remove_var("CFLAGS");
}

fn build() {
    let mut cmd = waf();
    cmd.arg("build");
    cmd.arg("--targets=termbox_static");
    run(&mut cmd);
}

fn install(dst: &Path) {
    let mut cmd = waf();
    cmd.arg("install");
    cmd.arg("--targets=termbox_static");
    cmd.arg(format!("--destdir={}", dst.display()));
    run(&mut cmd);
}

fn waf() -> Command {
    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let cargo_dir = Path::new(&manifest_dir);
    let termbox_dir = cargo_dir.join(".termbox");
    let mut cmd = Command::new("./waf");
    cmd.current_dir(&termbox_dir);
    cmd
}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    assert!(cmd.stdout(Stdio::inherit())
                .stderr(Stdio::inherit())
                .status()
                .unwrap()
                .success());
}
<commit_msg>Run clean before setup as proposed in issue #11<commit_after>use std::env;
use std::path::Path;
use std::process::{Stdio, Command};

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let dst = Path::new(&out_dir);

    clean();
    setup();
    configure();
    build();
    install(&dst);
    clean();
    println!("cargo:rustc-link-search={}", dst.join("lib").display());
    println!("cargo:rustc-link-lib=static=termbox");
}

fn setup() {
    let mut cmd = Command::new("git");
    cmd.arg("clone");
    cmd.arg("https://github.com/nsf/termbox");
    cmd.arg(".termbox");
    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let cargo_dir = Path::new(&manifest_dir);
    cmd.current_dir(&cargo_dir);

    run(&mut cmd);
}

fn clean() {
    let mut cmd = Command::new("rm");
    cmd.arg("-rf");
    cmd.arg(".termbox");
    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let cargo_dir = Path::new(&manifest_dir);
    cmd.current_dir(&cargo_dir);
    run(&mut cmd);
}

fn configure() {
    let mut cmd = waf();
    cmd.arg("configure");
    cmd.arg("--prefix=/");

    let target = env::var("TARGET").unwrap();
    let mut cflags;
    if target.contains("i686") {
        cflags = "-m32"
    } else if target.contains("x86_64") {
        cflags = "-m64 -fPIC"
    } else {
        cflags = "-fPIC"
    }
    println!("waf configure: setting CFLAGS to: `{}`", cflags);
    env::set_var("CFLAGS", cflags);

    run(&mut cmd);
    env::remove_var("CFLAGS");
}

fn build() {
    let mut cmd = waf();
    cmd.arg("build");
    cmd.arg("--targets=termbox_static");
    run(&mut cmd);
}

fn install(dst: &Path) {
    let mut cmd = waf();
    cmd.arg("install");
    cmd.arg("--targets=termbox_static");
    cmd.arg(format!("--destdir={}", dst.display()));
    run(&mut cmd);
}

fn waf() -> Command {
    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let cargo_dir = Path::new(&manifest_dir);
    let termbox_dir = cargo_dir.join(".termbox");
    let mut cmd = Command::new("./waf");
    cmd.current_dir(&termbox_dir);
    cmd
}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    assert!(cmd.stdout(Stdio::inherit())
                .stderr(Stdio::inherit())
                .status()
                .unwrap()
                .success());
}
<|endoftext|>


<commit_before>extern crate nasm_rs;
// use std::process::Command;
use std::env;
// use std::path::Path;
//
// enum Arch { X86_64
//           , X86
//           , ArmV7
//           }
//
// impl Arch {
//
//     /// Convert an LLVM target triple into an `Arch`.
//     fn from_triple(triple: &str) -> Result<Arch, &str> {
//         let triple_arch_string = triple.split("-").next();
//
//         match triple_arch_string {
//             Some("x86_64") => Ok(Arch::X86_64)
//           , Some("x86") => Ok(Arch::X86)
//           , Some("armv7") => Ok(Arch::ArmV7)
//           , Some(thing_weird) =>
//                 Err(&format!("Got weird architecture: `{}`"), thing_weird)
//           , None =>
//                 Err(&format!("Target triple {} contained no valid arch part"
//                             , triple)
//
//         }
//     }
//
//     fn asm_extension(&self) -> &str {
//         match self {
//             &Arch::X86_64 => ".asm"
//           , &Arch::X86 => ".asm"
//           , &Arch::ArmV7 => unimplemented!()
//         }
//     }
//
//     fn nasm_flag(&self) -> &str {
//         match self {
//             &Arch::X86_64 => "-felf64"
//           , &Arch::X86 => "-felf32"
//           , &Arch::ArmV7 => unimplemented!()
//       }
//     }
//
//     fn arch_dir(&self) -> &str {
//         match self {
//             &Arch::X86_64 => "x86_64"
//           , &Arch::X86 => "x86"
//           , &Arch::ArmV7 => "armv7"
//       }
//     }
//
//     fn is_x86_64(&self) -> bool {
//         match self {
//             &Arch::X86_64 => true
//           , _ => false
//         }
//     }
//
//     fn is_x86_32(&self) -> bool {
//         match self {
//             &Arch::X86 => true
//           , _ => false
//         }
//     }
//
//     fn is_x86(&self) -> bool {
//         match self {
//             &Arch::X86 | &Arch::x86_64 => true
//           , _ => false
//         }
//     }
//
// }

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    //
    // let maybe_target = env::var("TARGET");
    //
    // let arch = maybe_target.map(Arch::from_triple).unwrap();
    // let arch_nasm_flags = arch.nasm_flag();
    //
    // let asm_dir = Path::new("src").join("arch")
    //                               .join(arch.arch_dir());
    //
    // let asm_ext = arch.asm_extension();
    //
    // fn nasm(filename: &str) -> Result<Command::ExitStatus, &str> {
    //     // TODO: can we refactor this to use the nasm-rs crate?
    //     let path = asm_dir.with_file_name(format!("{}{}", filename, asm_ext));
    //                       .to_str()?;
    //
    //     Command::new("nasm")
    //         .args(&[ path
    //                , arch_nasm_flags
    //                , "-o"
    //                , out_dir.with_file_name(format!("{}{}", filename, ".o"))
    //                         .to_str().unwrap()
    //                ])
    //         .status()
    //         .map(|ok| {
    //             println!("cargo:rustc-link-lib=static={}", filename);
    //             println!("cargo:rerun-if-changed={}", path);
    //             ok
    //         })
    // }
    //
    // nasm("boot").unwrap();
    //
    // if arch.is_x86_64() {
    //         nasm("start_64").unwrap();
    //         nasm("multiboot").unwrap();
    // };
    //
    nasm_rs::compile_library_args( "libboot.a"
                                 , &[ "src/arch/x86_64/multiboot.asm"
                                    , "src/arch/x86_64/boot.asm"
                                    , "src/arch/x86_64/interrupt_handlers.asm"]
                                 , &[ "-felf64" ]);

    println!("cargo:rustc-link-search=native={}", out_dir);
    println!("cargo:rustc-link-lib=static=boot");

    println!("cargo:rerun-if-changed=src/arch/x86_64/linker.ld");
    println!("cargo:rerun-if-changed=src/arch/x86_64/multiboot.asm");
    println!("cargo:rerun-if-changed=src/arch/x86_64/boot.asm");
    println!("cargo:rerun-if-changed=src/arch/x86_64/interrupt_handlers.asm");

}
<commit_msg>[build] build.rs now no longer hard-codes the name of ASM files<commit_after>extern crate nasm_rs;
// use std::process::Command;
use std::env;
use std::path::{Path, PathBuf};
use std::fs::DirEntry;
use std::io;
use std::ffi::OsStr;
// use std::path::Path;
//
// enum Arch { X86_64
//           , X86
//           , ArmV7
//           }
//
// impl Arch {
//
//     /// Convert an LLVM target triple into an `Arch`.
//     fn from_triple(triple: &str) -> Result<Arch, &str> {
//         let triple_arch_string = triple.split("-").next();
//
//         match triple_arch_string {
//             Some("x86_64") => Ok(Arch::X86_64)
//           , Some("x86") => Ok(Arch::X86)
//           , Some("armv7") => Ok(Arch::ArmV7)
//           , Some(thing_weird) =>
//                 Err(&format!("Got weird architecture: `{}`"), thing_weird)
//           , None =>
//                 Err(&format!("Target triple {} contained no valid arch part"
//                             , triple)
//
//         }
//     }
//
//     fn asm_extension(&self) -> &str {
//         match self {
//             &Arch::X86_64 => ".asm"
//           , &Arch::X86 => ".asm"
//           , &Arch::ArmV7 => unimplemented!()
//         }
//     }
//
//     fn nasm_flag(&self) -> &str {
//         match self {
//             &Arch::X86_64 => "-felf64"
//           , &Arch::X86 => "-felf32"
//           , &Arch::ArmV7 => unimplemented!()
//       }
//     }
//
//     fn arch_dir(&self) -> &str {
//         match self {
//             &Arch::X86_64 => "x86_64"
//           , &Arch::X86 => "x86"
//           , &Arch::ArmV7 => "armv7"
//       }
//     }
//
//     fn is_x86_64(&self) -> bool {
//         match self {
//             &Arch::X86_64 => true
//           , _ => false
//         }
//     }
//
//     fn is_x86_32(&self) -> bool {
//         match self {
//             &Arch::X86 => true
//           , _ => false
//         }
//     }
//
//     fn is_x86(&self) -> bool {
//         match self {
//             &Arch::X86 | &Arch::x86_64 => true
//           , _ => false
//         }
//     }
//
// }

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();

    let target = env::var("TARGET").unwrap();

    let arch_name = target.split("-").next()
                          .expect("Couldn't parse target triple!");

    let src_path = env::var("CARGO_MANIFEST_DIR")
                    .map(|path| PathBuf::from(path).join("src") )
                    .unwrap();

    let asm_dir = src_path.as_path()
                    .join("arch")
                    .join(arch_name);

    let asm_files = asm_dir.read_dir().unwrap()
                        .filter(|entry| match entry {
                            &Ok(ref file) =>
                                if let Some(ext) = file.path().extension() {
                                    "asm" == ext
                                } else {
                                    false
                                }
                          , &Err(_) => false
                        })
                        .map(|entry| entry.unwrap().path())
                        .collect::<Vec<PathBuf>>();

    //
    // let arch = maybe_target.map(Arch::from_triple).unwrap();
    // let arch_nasm_flags = arch.nasm_flag();
    //
    // let asm_dir = Path::new("src").join("arch")
    //                               .join(arch.arch_dir());
    //
    // let asm_ext = arch.asm_extension();
    //
    // fn nasm(filename: &str) -> Result<Command::ExitStatus, &str> {
    //     // TODO: can we refactor this to use the nasm-rs crate?
    //     let path = asm_dir.with_file_name(format!("{}{}", filename, asm_ext));
    //                       .to_str()?;
    //
    //     Command::new("nasm")
    //         .args(&[ path
    //                , arch_nasm_flags
    //                , "-o"
    //                , out_dir.with_file_name(format!("{}{}", filename, ".o"))
    //                         .to_str().unwrap()
    //                ])
    //         .status()
    //         .map(|ok| {
    //             println!("cargo:rustc-link-lib=static={}", filename);
    //             println!("cargo:rerun-if-changed={}", path);
    //             ok
    //         })
    // }
    //
    // nasm("boot").unwrap();
    //
    // if arch.is_x86_64() {
    //         nasm("start_64").unwrap();
    //         nasm("multiboot").unwrap();
    // };
    //

    nasm_rs::compile_library_args( "libboot.a"
                                //  , &[ "src/arch/x86_64/multiboot.asm"
                                //     , "src/arch/x86_64/boot.asm"
                                //     , "src/arch/x86_64/interrupt_handlers.asm"]
                                 , asm_files.iter()
                                            .map(|path| path.to_str().unwrap())
                                            .collect::<Vec<&str>>()
                                            .as_slice()
                                 , &[ "-felf64" ]);

    println!("cargo:rustc-link-search=native={}", out_dir);
    println!("cargo:rustc-link-lib=static=boot");

    for asm_file in asm_files {
        println!("cargo:rerun-if-changed={}", asm_file.to_str().unwrap());
    }

}
<|endoftext|>


<commit_before>extern crate nasm_rs;
// use std::process::Command;
use std::env;
use std::path::{Path, PathBuf};
use std::fs::DirEntry;
use std::io;
use std::ffi::OsStr;
// use std::path::Path;
//
// enum Arch { X86_64
//           , X86
//           , ArmV7
//           }
//
// impl Arch {
//
//     /// Convert an LLVM target triple into an `Arch`.
//     fn from_triple(triple: &str) -> Result<Arch, &str> {
//         let triple_arch_string = triple.split("-").next();
//
//         match triple_arch_string {
//             Some("x86_64") => Ok(Arch::X86_64)
//           , Some("x86") => Ok(Arch::X86)
//           , Some("armv7") => Ok(Arch::ArmV7)
//           , Some(thing_weird) =>
//                 Err(&format!("Got weird architecture: `{}`"), thing_weird)
//           , None =>
//                 Err(&format!("Target triple {} contained no valid arch part"
//                             , triple)
//
//         }
//     }
//
//     fn asm_extension(&self) -> &str {
//         match self {
//             &Arch::X86_64 => ".asm"
//           , &Arch::X86 => ".asm"
//           , &Arch::ArmV7 => unimplemented!()
//         }
//     }
//
//     fn nasm_flag(&self) -> &str {
//         match self {
//             &Arch::X86_64 => "-felf64"
//           , &Arch::X86 => "-felf32"
//           , &Arch::ArmV7 => unimplemented!()
//       }
//     }
//
//     fn arch_dir(&self) -> &str {
//         match self {
//             &Arch::X86_64 => "x86_64"
//           , &Arch::X86 => "x86"
//           , &Arch::ArmV7 => "armv7"
//       }
//     }
//
//     fn is_x86_64(&self) -> bool {
//         match self {
//             &Arch::X86_64 => true
//           , _ => false
//         }
//     }
//
//     fn is_x86_32(&self) -> bool {
//         match self {
//             &Arch::X86 => true
//           , _ => false
//         }
//     }
//
//     fn is_x86(&self) -> bool {
//         match self {
//             &Arch::X86 | &Arch::x86_64 => true
//           , _ => false
//         }
//     }
//
// }

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();

    let target = env::var("TARGET").unwrap();

    let arch_name = target.split("-").next()
                          .expect("Couldn't parse target triple!");

    let src_path = env::var("CARGO_MANIFEST_DIR")
                    .map(|path| PathBuf::from(path).join("src") )
                    .unwrap();

    let asm_dir = src_path.as_path()
                    .join("arch")
                    .join(arch_name);

    let asm_files = asm_dir.read_dir().unwrap()
                        .filter(|entry| match entry {
                            &Ok(ref file) =>
                                if let Some(ext) = file.path().extension() {
                                    "asm" == ext
                                } else {
                                    false
                                }
                          , &Err(_) => false
                        })
                        .map(|entry| entry.unwrap().path())
                        .collect::<Vec<PathBuf>>();

    //
    // let arch = maybe_target.map(Arch::from_triple).unwrap();
    // let arch_nasm_flags = arch.nasm_flag();
    //
    // let asm_dir = Path::new("src").join("arch")
    //                               .join(arch.arch_dir());
    //
    // let asm_ext = arch.asm_extension();
    //
    // fn nasm(filename: &str) -> Result<Command::ExitStatus, &str> {
    //     // TODO: can we refactor this to use the nasm-rs crate?
    //     let path = asm_dir.with_file_name(format!("{}{}", filename, asm_ext));
    //                       .to_str()?;
    //
    //     Command::new("nasm")
    //         .args(&[ path
    //                , arch_nasm_flags
    //                , "-o"
    //                , out_dir.with_file_name(format!("{}{}", filename, ".o"))
    //                         .to_str().unwrap()
    //                ])
    //         .status()
    //         .map(|ok| {
    //             println!("cargo:rustc-link-lib=static={}", filename);
    //             println!("cargo:rerun-if-changed={}", path);
    //             ok
    //         })
    // }
    //
    // nasm("boot").unwrap();
    //
    // if arch.is_x86_64() {
    //         nasm("start_64").unwrap();
    //         nasm("multiboot").unwrap();
    // };
    //

    nasm_rs::compile_library_args( "libboot.a"
                                //  , &[ "src/arch/x86_64/multiboot.asm"
                                //     , "src/arch/x86_64/boot.asm"
                                //     , "src/arch/x86_64/interrupt_handlers.asm"]
                                 , asm_files.iter()
                                            .map(|path| path.to_str().unwrap())
                                            .collect::<Vec<&str>>()
                                            .as_slice()
                                 , &[ "-felf64" ]);

    println!("cargo:rustc-link-search=native={}", out_dir);
    println!("cargo:rustc-link-lib=static=boot");

    for asm_file in asm_files {
        println!("cargo:rerun-if-changed={}", asm_file.to_str().unwrap());
    }

}
<commit_msg>[build] remove unneeded imports & dead code from build.rs<commit_after>extern crate nasm_rs;
use std::env;
use std::path::PathBuf;

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();

    let target = env::var("TARGET").unwrap();

    let arch_name = target.split("-").next()
                          .expect("Couldn't parse target triple!");

    let src_path = env::var("CARGO_MANIFEST_DIR")
                    .map(|path| PathBuf::from(path).join("src") )
                    .unwrap();

    let asm_dir = src_path.as_path()
                    .join("arch")
                    .join(arch_name);

    let asm_files = asm_dir.read_dir().unwrap()
                        .filter(|entry| match entry {
                            &Ok(ref file) =>
                                if let Some(ext) = file.path().extension() {
                                    "asm" == ext
                                } else {
                                    false
                                }
                          , &Err(_) => false
                        })
                        .map(|entry| entry.unwrap().path())
                        .collect::<Vec<PathBuf>>();

    nasm_rs::compile_library_args( "libboot.a"
                                 , asm_files.iter()
                                            .map(|path| path.to_str().unwrap())
                                            .collect::<Vec<&str>>()
                                            .as_slice()
                                 , &[ "-felf64" ]);

    println!("cargo:rustc-link-search=native={}", out_dir);
    println!("cargo:rustc-link-lib=static=boot");

    for asm_file in asm_files {
        println!("cargo:rerun-if-changed={}", asm_file.to_str().unwrap());
    }

}
<|endoftext|>