<commit_before>//! Provides access to the symbolserver config
use std::env;
use std::fs;
use std::path::{Path, PathBuf};

use serde_yaml;
use url::Url;
use rusoto::Region;
use chrono::Duration;
use log::LogLevelFilter;

use super::{Result, ErrorKind};


#[derive(Deserialize, Debug, Default, Clone)]
struct AwsConfig {
    access_key: Option<String>,
    secret_key: Option<String>,
    bucket_url: Option<String>,
    region: Option<String>,
}

#[derive(Deserialize, Debug, Default, Clone)]
struct ServerConfig {
    host: Option<String>,
    port: Option<u16>,
    healthcheck_ttl: Option<u32>,
    sync_interval: Option<u32>,
}

#[derive(Deserialize, Debug, Default, Clone)]
struct LogConfig {
    level: Option<String>,
    file: Option<PathBuf>,
}

/// Central config object that exposes the information from
/// the symbolserver yaml config.
#[derive(Deserialize, Debug, Default, Clone)]
pub struct Config {
    #[serde(default)]
    aws: AwsConfig,
    #[serde(default)]
    server: ServerConfig,
    #[serde(default)]
    log: LogConfig,
    symbol_dir: Option<PathBuf>,
}

impl Config {
    /// Loads a config from a given file
    pub fn load_file<P: AsRef<Path>>(path: P) -> Result<Config> {
        let mut f = fs::File::open(path)?;
        serde_yaml::from_reader(&mut f).map_err(|err| {
            ErrorKind::ConfigError(err).into()
        })
    }

    /// Loads a config from the default location
    pub fn load_default() -> Result<Config> {
        let mut home = match env::home_dir() {
            Some(home) => home,
            None => { return Ok(Default::default()) },
        };
        home.push(".symbolserver.yml");

        Ok(if let Ok(_) = fs::metadata(&home) {
            Config::load_file(&home)?
        } else {
            Default::default()
        })
    }

    /// Return the AWS access key
    pub fn get_aws_access_key<'a>(&'a self) -> Option<&str> {
        self.aws.access_key.as_ref().map(|x| &**x)
    }

    /// Return the AWS secret key
    pub fn get_aws_secret_key<'a>(&'a self) -> Option<&str> {
        self.aws.secret_key.as_ref().map(|x| &**x)
    }

    /// Return the AWS S3 bucket URL
    pub fn get_aws_bucket_url<'a>(&'a self) -> Result<Url> {
        let url = if let Some(value) = self.aws.bucket_url.as_ref() {
            Url::parse(value)?
        } else {
            return Err(ErrorKind::MissingConfigKey(
                "aws.bucket_url", None).into());
        };
        if url.scheme() != "s3" {
            return Err(ErrorKind::BadConfigKey(
                "aws.bucket_url", "The scheme for the bucket URL needs to be s3").into());
        } else if url.host_str().is_none() {
            return Err(ErrorKind::BadConfigKey(
                "aws.bucket_url", "The bucket URL is missing a name").into());
        }
        Ok(url)
    }

    /// Return the AWS region
    pub fn get_aws_region(&self) -> Result<Region> {
        match self.aws.region {
            Some(ref region) => {
                if let Ok(rv) = region.parse() {
                    Ok(rv)
                } else {
                    Err(ErrorKind::BadConfigKey(
                        "aws.region", "An unknown AWS region was provided").into())
                }
            }
            None => Ok(Region::UsEast1)
        }
    }

    /// Return the path where symbols are stored.
    pub fn get_symbol_dir(&self) -> Result<&Path> {
        if let Some(ref path) = self.symbol_dir {
            Ok(path.as_path())
        } else {
            Err(ErrorKind::MissingConfigKey("symbol_dir", None).into())
        }
    }

    /// Return the bind target for the http server
    pub fn get_server_socket_addr(&self) -> Result<(&str, u16)> {<commit_msg>Pick up region from envvar too<commit_after>//! Provides access to the symbolserver config
use std::env;
use std::fs;
use std::path::{Path, PathBuf};

use serde_yaml;
use url::Url;
use rusoto::Region;
use chrono::Duration;
use log::LogLevelFilter;

use super::{Result, ErrorKind};


#[derive(Deserialize, Debug, Default, Clone)]
struct AwsConfig {
    access_key: Option<String>,
    secret_key: Option<String>,
    bucket_url: Option<String>,
    region: Option<String>,
}

#[derive(Deserialize, Debug, Default, Clone)]
struct ServerConfig {
    host: Option<String>,
    port: Option<u16>,
    healthcheck_ttl: Option<u32>,
    sync_interval: Option<u32>,
}

#[derive(Deserialize, Debug, Default, Clone)]
struct LogConfig {
    level: Option<String>,
    file: Option<PathBuf>,
}

/// Central config object that exposes the information from
/// the symbolserver yaml config.
#[derive(Deserialize, Debug, Default, Clone)]
pub struct Config {
    #[serde(default)]
    aws: AwsConfig,
    #[serde(default)]
    server: ServerConfig,
    #[serde(default)]
    log: LogConfig,
    symbol_dir: Option<PathBuf>,
}

impl Config {
    /// Loads a config from a given file
    pub fn load_file<P: AsRef<Path>>(path: P) -> Result<Config> {
        let mut f = fs::File::open(path)?;
        serde_yaml::from_reader(&mut f).map_err(|err| {
            ErrorKind::ConfigError(err).into()
        })
    }

    /// Loads a config from the default location
    pub fn load_default() -> Result<Config> {
        let mut home = match env::home_dir() {
            Some(home) => home,
            None => { return Ok(Default::default()) },
        };
        home.push(".symbolserver.yml");

        Ok(if let Ok(_) = fs::metadata(&home) {
            Config::load_file(&home)?
        } else {
            Default::default()
        })
    }

    /// Return the AWS access key
    pub fn get_aws_access_key<'a>(&'a self) -> Option<&str> {
        self.aws.access_key.as_ref().map(|x| &**x)
    }

    /// Return the AWS secret key
    pub fn get_aws_secret_key<'a>(&'a self) -> Option<&str> {
        self.aws.secret_key.as_ref().map(|x| &**x)
    }

    /// Return the AWS S3 bucket URL
    pub fn get_aws_bucket_url<'a>(&'a self) -> Result<Url> {
        let url = if let Some(value) = self.aws.bucket_url.as_ref() {
            Url::parse(value)?
        } else {
            return Err(ErrorKind::MissingConfigKey(
                "aws.bucket_url", None).into());
        };
        if url.scheme() != "s3" {
            return Err(ErrorKind::BadConfigKey(
                "aws.bucket_url", "The scheme for the bucket URL needs to be s3").into());
        } else if url.host_str().is_none() {
            return Err(ErrorKind::BadConfigKey(
                "aws.bucket_url", "The bucket URL is missing a name").into());
        }
        Ok(url)
    }

    /// Return the AWS region
    pub fn get_aws_region(&self) -> Result<Region> {
        let region_opt = self.aws.region
            .as_ref()
            .map(|x| x.to_string())
            .or_else(|| env::var("AWS_DEFAULT_REGION").ok());

        if let Some(region) = region_opt {
            if let Ok(rv) = region.parse() {
                Ok(rv)
            } else {
                Err(ErrorKind::BadConfigKey(
                    "aws.region", "An unknown AWS region was provided").into())
            }
        } else {
            Ok(Region::UsEast1)
        }
    }

    /// Return the path where symbols are stored.
    pub fn get_symbol_dir(&self) -> Result<&Path> {
        if let Some(ref path) = self.symbol_dir {
            Ok(path.as_path())
        } else {
            Err(ErrorKind::MissingConfigKey("symbol_dir", None).into())
        }
    }

    /// Return the bind target for the http server
    pub fn get_server_socket_addr(&self) -> Result<(&str, u16)> {
{'fraction_non_alphanumeric': 0.14469666799415903, 'fraction_numerical': 0.0031859816806053365, 'mean_word_length': 2.2985989492119088, 'pattern_counts': {'":': 0, '<': 51, '<?xml version=': 0, '>': 73, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '53354', 'n_tokens_mistral': 2362, 'n_tokens_neox': 2349, 'n_words': 581}