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

//! Aggregator example for Project Oak.
//!
//! This shows how an Oak Node can aggregate data samples and report aggregated values if there are
//! enough samples to hide individual contributors (enforces k-anonymity).
//!
//! Clients invoke the module by providing data samples that contain a bucket ID
//! and a Sparse Vector - a dictionary with integer keys.

pub mod proto {
    pub mod oak {
        pub use oak::proto::oak::invocation;
        pub mod examples {
            pub mod aggregator {
                include!(concat!(env!("OUT_DIR"), "/oak.examples.aggregator.rs"));
                include!(concat!(env!("OUT_DIR"), "/oak.examples.aggregator_init.rs"));
            }
        }
    }
}

pub mod data;

use aggregator_common::ThresholdAggregator;
use data::SparseVector;
use log::{debug, error, info};
use oak::{
    grpc,
    io::{Receiver, ReceiverExt, SenderExt},
    proto::oak::invocation::{GrpcInvocation, GrpcInvocationReceiver, GrpcInvocationSender},
};
use oak_abi::{label::Label, proto::oak::application::ConfigMap};
use proto::oak::examples::aggregator::{
    Aggregator, AggregatorClient, AggregatorDispatcher, AggregatorInit, Sample,
};
use std::{collections::HashMap, convert::TryFrom};

/// Currently threshold value is hardcoded.
pub const SAMPLE_THRESHOLD: u64 = 5;

/// Oak Node that collects and aggregates data.
/// Data is collected in the `aggregators` map where keys are buckets and values are instances of a
/// `ThresholdAggregator`. `Option` is used to keep note of the outdated buckets: once an
/// Aggregator has sent its data to the Backend, it's replaced with `None` and all subsequent client
/// requests corresponding to its bucket are discarded.
pub struct AggregatorNode {
    aggregator_client: AggregatorClient,
    aggregators: HashMap<String, Option<ThresholdAggregator<SparseVector>>>,
}

impl AggregatorNode {
    fn new(aggregator_client: oak::grpc::client::Client) -> Self {
        AggregatorNode {
            aggregator_client: AggregatorClient(aggregator_client),
            aggregators: HashMap::new(),
        }
    }

    /// Submit a data sample (Sparse Vector `svec`) to an Aggregator corresponding to the `bucket`.
    /// If the Aggregator has enough data samples, then report the aggregated value to the backend
    /// server and replace the Aggregator with `None`, so all subsequent client requests
    /// corresponding to the `bucket` will be discarded.
    fn submit(&mut self, bucket: String, svec: &SparseVector) -> Result<(), String> {
        match self.aggregators.get_mut(&bucket) {
            Some(entry) => match *entry {
                Some(ref mut aggregator) => {
                    aggregator.submit(svec);
                    if let Some(aggregated_data) = aggregator.take() {
                        *entry = None;
                        if let Err(err) = self.report(bucket, aggregated_data) {
                            // Backend report errors are not returned to the clients.
                            error!("Backend report error: {:?}", err);
                        }
                    }
                }
                None => return Err(format!("Outdated bucket: {}", bucket)),
            },
            None => {
                let mut aggregator = ThresholdAggregator::<SparseVector>::new(SAMPLE_THRESHOLD);
                aggregator.submit(svec);
                self.aggregators.insert(bucket, Some(aggregator));
            }
        }
        Ok(())
    }

    /// Try to report the aggregated value to the backend server via gRPC.
    /// Return an error if the backend server is not available.
    fn report(&self, bucket: String, svec: SparseVector) -> Result<(), String> {
        debug!(
            "Reporting data to the backend: bucket {}, sparse vector: {:?}",
            bucket, svec
        );
        let result = self.aggregator_client.submit_sample(Sample {
            bucket,
            data: Some(svec.into()),
        });
        match result {
            Ok(response) => {
                debug!("Data report response: {:?}", response);
                Ok(())
            }
            Err(err) => Err(format!("gRPC send error: {:?}", err)),
        }
    }
}

/// A gRPC service implementation for the Aggregator example.
impl Aggregator for AggregatorNode {
    fn submit_sample(&mut self, sample: Sample) -> grpc::Result<()> {
        match sample.data {
            Some(data) => match SparseVector::try_from(&data) {
                Ok(svec) => {
                    debug!(
                        "Received data: bucket {}, sparse vector: {:?}",
                        sample.bucket, svec
                    );
                    match self.submit(sample.bucket, &svec) {
                        Ok(_) => Ok(()),
                        Err(err) => {
                            let err = format!("Submit sample error: {}", err);
                            Err(grpc::build_status(grpc::Code::InvalidArgument, &err))
                        }
                    }
                }
                Err(err) => {
                    let err = format!("Data deserialization error: {}", err);
                    Err(grpc::build_status(grpc::Code::InvalidArgument, &err))
                }
            },
            None => {
                let err = "No data specified";
                Err(grpc::build_status(grpc::Code::InvalidArgument, &err))
            }
        }
    }
}

oak::entrypoint!(aggregator<AggregatorInit> => |init_receiver: Receiver<AggregatorInit>| {
    oak::logger::init_default();

    // Receive the initialization message.
    let init_message: AggregatorInit = init_receiver.receive().expect("Couldn't receive init message");
    let grpc_server_invocation_receiver = init_message
        .grpc_server_invocation_receiver
        .expect("Couldn't receive gRPC invocation receiver")
        .receiver
        .expect("Empty gRPC invocation sender");
    let grpc_client_invocation_sender = init_message
        .grpc_client_invocation_sender
        .expect("Couldn't receive gRPC invocation sender")
        .sender
        .expect("Empty gRPC invocation sender");

    // Run event loop and handle incoming invocations.
    let node = AggregatorNode::new(
        oak::grpc::client::Client {
            invocation_sender: grpc_client_invocation_sender
        }
    );
    let dispatcher = AggregatorDispatcher::new(node);
    oak::run_command_loop(dispatcher, grpc_server_invocation_receiver);
});

#[derive(Debug, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
    pub grpc_server_listen_address: String,
}

/// Create initialization message for Aggregator Node.
fn create_init_message(
    receiver: &oak::io::Receiver<GrpcInvocation>,
    sender: &oak::io::Sender<GrpcInvocation>,
) -> AggregatorInit {
    AggregatorInit {
        grpc_server_invocation_receiver: Some(GrpcInvocationReceiver {
            receiver: Some(receiver.clone()),
        }),
        grpc_client_invocation_sender: Some(GrpcInvocationSender {
            sender: Some(sender.clone()),
        }),
    }
}

oak::entrypoint!(oak_main<ConfigMap> => |receiver: Receiver<ConfigMap>| {
    oak::logger::init_default();

    // Parse config.
    let config_map = receiver.receive().expect("Couldn't read config map");
    let config: Config = toml::from_slice(&config_map.items.get("config").expect("Couldn't find config")).expect("Couldn't parse TOML config file");
    info!("Parsed config: {:?}", config);
    let grpc_server_channel =
        oak::grpc::server::init(&config.grpc_server_listen_address).expect("Couldn't create gRPC server pseudo-Node");

    // Create an Aggregator Node.
    let (init_sender, init_receiver) = oak::io::channel_create::<AggregatorInit>()
        .expect("Couldn't create initialization channel");
    oak::node_create(&oak::node_config::wasm("app", "aggregator"), init_receiver.handle)
        .expect("Couldn't create gRPC worker node");
    oak::channel_close(init_receiver.handle.handle).expect("Couldn't close receiver channel");

    // Create a gRPC client Node with a less restrictive label than the current Node.
    // In particular, the confidentiality component of the current Node label includes the
    // "aggregator" WebAssembly hash, which is declassified as part of the gRPC client
    // Node creation. This is only allowed if the current Node actually has the
    // appropriate capability (i.e. the correct WebAssembly module hash) as specified by
    // the label component being removed, as set by the client.
    // TODO(#814): Uncomment and use correct confidentiality labels.
    // let label = Label {
    //     confidentiality_tags: vec![Tag {
    //         tag: Some(tag::Tag::TlsEndpointTag(TlsEndpointTag {
    //             certificate_subject_alternative_name: "google.com".to_string(),
    //         })),
    //     }],
    //     integrity_tags: vec![],
    // };
    let grpc_client = oak::grpc::client::Client::new_with_label(
        &oak::node_config::grpc_client("https://localhost:8888"),
        &Label::public_untrusted(),
    )
    .expect("Couldn't create gRPC client");

    // Send the initialization message to Aggregator Node containing a gRPC server invocation
    // receiver and a gRPC client invocation sender.
    debug!("Sending the initialization message to handler Node");
    let (invocation_sender, invocation_receiver) = oak::io::channel_create::<grpc::Invocation>()
        .expect("Couldn't create gRPC invocation channel");
    let init_message = create_init_message(&invocation_receiver, &grpc_client.invocation_sender);
    init_sender.send(&init_message).expect("Couldn't send the initialization message to Aggregator Node");

    // Route gRPC invocations to Aggregator Node.
    while let Ok(invocation) = grpc_server_channel.receive() {
        invocation_sender
            .send(&invocation)
            .expect("Couldn't send invocation to worker node");
    }
});
<commit_msg>Use default gRPC client label in Aggregator (#1611)<commit_after>//
// Copyright 2020 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! Aggregator example for Project Oak.
//!
//! This shows how an Oak Node can aggregate data samples and report aggregated values if there are
//! enough samples to hide individual contributors (enforces k-anonymity).
//!
//! Clients invoke the module by providing data samples that contain a bucket ID
//! and a Sparse Vector - a dictionary with integer keys.

pub mod proto {
    pub mod oak {
        pub use oak::proto::oak::invocation;
        pub mod examples {
            pub mod aggregator {
                include!(concat!(env!("OUT_DIR"), "/oak.examples.aggregator.rs"));
                include!(concat!(env!("OUT_DIR"), "/oak.examples.aggregator_init.rs"));
            }
        }
    }
}

pub mod data;

use aggregator_common::ThresholdAggregator;
use data::SparseVector;
use log::{debug, error, info};
use oak::{
    grpc,
    io::{Receiver, ReceiverExt, SenderExt},
    proto::oak::invocation::{GrpcInvocation, GrpcInvocationReceiver, GrpcInvocationSender},
};
use oak_abi::proto::oak::application::ConfigMap;
use proto::oak::examples::aggregator::{
    Aggregator, AggregatorClient, AggregatorDispatcher, AggregatorInit, Sample,
};
use std::{collections::HashMap, convert::TryFrom};

/// Currently threshold value is hardcoded.
pub const SAMPLE_THRESHOLD: u64 = 5;

/// Oak Node that collects and aggregates data.
/// Data is collected in the `aggregators` map where keys are buckets and values are instances of a
/// `ThresholdAggregator`. `Option` is used to keep note of the outdated buckets: once an
/// Aggregator has sent its data to the Backend, it's replaced with `None` and all subsequent client
/// requests corresponding to its bucket are discarded.
pub struct AggregatorNode {
    aggregator_client: AggregatorClient,
    aggregators: HashMap<String, Option<ThresholdAggregator<SparseVector>>>,
}

impl AggregatorNode {
    fn new(aggregator_client: oak::grpc::client::Client) -> Self {
        AggregatorNode {
            aggregator_client: AggregatorClient(aggregator_client),
            aggregators: HashMap::new(),
        }
    }

    /// Submit a data sample (Sparse Vector `svec`) to an Aggregator corresponding to the `bucket`.
    /// If the Aggregator has enough data samples, then report the aggregated value to the backend
    /// server and replace the Aggregator with `None`, so all subsequent client requests
    /// corresponding to the `bucket` will be discarded.
    fn submit(&mut self, bucket: String, svec: &SparseVector) -> Result<(), String> {
        match self.aggregators.get_mut(&bucket) {
            Some(entry) => match *entry {
                Some(ref mut aggregator) => {
                    aggregator.submit(svec);
                    if let Some(aggregated_data) = aggregator.take() {
                        *entry = None;
                        if let Err(err) = self.report(bucket, aggregated_data) {
                            // Backend report errors are not returned to the clients.
                            error!("Backend report error: {:?}", err);
                        }
                    }
                }
                None => return Err(format!("Outdated bucket: {}", bucket)),
            },
            None => {
                let mut aggregator = ThresholdAggregator::<SparseVector>::new(SAMPLE_THRESHOLD);
                aggregator.submit(svec);
                self.aggregators.insert(bucket, Some(aggregator));
            }
        }
        Ok(())
    }

    /// Try to report the aggregated value to the backend server via gRPC.
    /// Return an error if the backend server is not available.
    fn report(&self, bucket: String, svec: SparseVector) -> Result<(), String> {
        debug!(
            "Reporting data to the backend: bucket {}, sparse vector: {:?}",
            bucket, svec
        );
        let result = self.aggregator_client.submit_sample(Sample {
            bucket,
            data: Some(svec.into()),
        });
        match result {
            Ok(response) => {
                debug!("Data report response: {:?}", response);
                Ok(())
            }
            Err(err) => Err(format!("gRPC send error: {:?}", err)),
        }
    }
}

/// A gRPC service implementation for the Aggregator example.
impl Aggregator for AggregatorNode {
    fn submit_sample(&mut self, sample: Sample) -> grpc::Result<()> {
        match sample.data {
            Some(data) => match SparseVector::try_from(&data) {
                Ok(svec) => {
                    debug!(
                        "Received data: bucket {}, sparse vector: {:?}",
                        sample.bucket, svec
                    );
                    match self.submit(sample.bucket, &svec) {
                        Ok(_) => Ok(()),
                        Err(err) => {
                            let err = format!("Submit sample error: {}", err);
                            Err(grpc::build_status(grpc::Code::InvalidArgument, &err))
                        }
                    }
                }
                Err(err) => {
                    let err = format!("Data deserialization error: {}", err);
                    Err(grpc::build_status(grpc::Code::InvalidArgument, &err))
                }
            },
            None => {
                let err = "No data specified";
                Err(grpc::build_status(grpc::Code::InvalidArgument, &err))
            }
        }
    }
}

oak::entrypoint!(aggregator<AggregatorInit> => |init_receiver: Receiver<AggregatorInit>| {
    oak::logger::init_default();

    // Receive the initialization message.
    let init_message: AggregatorInit = init_receiver.receive().expect("Couldn't receive init message");
    let grpc_server_invocation_receiver = init_message
        .grpc_server_invocation_receiver
        .expect("Couldn't receive gRPC invocation receiver")
        .receiver
        .expect("Empty gRPC invocation sender");
    let grpc_client_invocation_sender = init_message
        .grpc_client_invocation_sender
        .expect("Couldn't receive gRPC invocation sender")
        .sender
        .expect("Empty gRPC invocation sender");

    // Run event loop and handle incoming invocations.
    let node = AggregatorNode::new(
        oak::grpc::client::Client {
            invocation_sender: grpc_client_invocation_sender
        }
    );
    let dispatcher = AggregatorDispatcher::new(node);
    oak::run_command_loop(dispatcher, grpc_server_invocation_receiver);
});

#[derive(Debug, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
    pub grpc_server_listen_address: String,
}

/// Create initialization message for Aggregator Node.
fn create_init_message(
    receiver: &oak::io::Receiver<GrpcInvocation>,
    sender: &oak::io::Sender<GrpcInvocation>,
) -> AggregatorInit {
    AggregatorInit {
        grpc_server_invocation_receiver: Some(GrpcInvocationReceiver {
            receiver: Some(receiver.clone()),
        }),
        grpc_client_invocation_sender: Some(GrpcInvocationSender {
            sender: Some(sender.clone()),
        }),
    }
}

oak::entrypoint!(oak_main<ConfigMap> => |receiver: Receiver<ConfigMap>| {
    oak::logger::init_default();

    // Parse config.
    let config_map = receiver.receive().expect("Couldn't read config map");
    let config: Config = toml::from_slice(&config_map.items.get("config").expect("Couldn't find config")).expect("Couldn't parse TOML config file");
    info!("Parsed config: {:?}", config);
    let grpc_server_channel =
        oak::grpc::server::init(&config.grpc_server_listen_address).expect("Couldn't create gRPC server pseudo-Node");

    // Create an Aggregator Node.
    let (init_sender, init_receiver) = oak::io::channel_create::<AggregatorInit>()
        .expect("Couldn't create initialization channel");
    oak::node_create(&oak::node_config::wasm("app", "aggregator"), init_receiver.handle)
        .expect("Couldn't create gRPC worker node");
    oak::channel_close(init_receiver.handle.handle).expect("Couldn't close receiver channel");

    // Create a gRPC client Node with a less restrictive label than the current Node.
    // In particular, the confidentiality component of the current Node label includes the
    // "aggregator" WebAssembly hash, which is declassified as part of the gRPC client
    // Node creation. This is only allowed if the current Node actually has the
    // appropriate capability (i.e. the correct WebAssembly module hash) as specified by
    // the label component being removed, as set by the client.
    // TODO(#814): Uncomment and use correct confidentiality labels.
    // let label = Label {
    //     confidentiality_tags: vec![Tag {
    //         tag: Some(tag::Tag::TlsEndpointTag(TlsEndpointTag {
    //             certificate_subject_alternative_name: "google.com".to_string(),
    //         })),
    //     }],
    //     integrity_tags: vec![],
    // };
    let grpc_client = oak::grpc::client::Client::new(
        &oak::node_config::grpc_client("https://localhost:8888"),
    )
    .expect("Couldn't create gRPC client");

    // Send the initialization message to Aggregator Node containing a gRPC server invocation
    // receiver and a gRPC client invocation sender.
    debug!("Sending the initialization message to handler Node");
    let (invocation_sender, invocation_receiver) = oak::io::channel_create::<grpc::Invocation>()
        .expect("Couldn't create gRPC invocation channel");
    let init_message = create_init_message(&invocation_receiver, &grpc_client.invocation_sender);
    init_sender.send(&init_message).expect("Couldn't send the initialization message to Aggregator Node");

    // Route gRPC invocations to Aggregator Node.
    while let Ok(invocation) = grpc_server_channel.receive() {
        invocation_sender
            .send(&invocation)
            .expect("Couldn't send invocation to worker node");
    }
});
<|endoftext|>


<commit_before>use std::io::Write;
use std::os::unix::process::CommandExt;
use std::process::Command;

const SYSTEMD_RUN: &'static str = "/usr/bin/systemd-run";
const FIREJAIL: &'static str = "/usr/bin/firejail";

const FIREFOX: &'static str = "/usr/bin/firefox";
const FIREFOX_PROFILE: &'static str = "/etc/firejail/firefox.profile";
const FIREFOX_MOZILLA_SFW_PROFILE: &'static str = "default-1473025815439";

const FIREFOX_MEMORY_HIGH: i32 = 4420;
const FIREFOX_MEMORY_MAX: i32 = 5200;

const KEIRA: &'static str = "/home/kalvin/Downloads/.firefox-nightly/firefox";
const KEIRA_PROFILE: &'static str = "/etc/firejail/firefox-nightly.profile";

const TERM: &'static str = "/usr/bin/alacritty";
const TERM_PROFILE: &'static str = "/etc/firejail/x-terminal-emulator.profile";

const THUNDERBIRD: &'static str = "/home/kalvin/Downloads/.thunderbird-beta/thunderbird";
const THUNDERBIRD_PROFILE: &'static str = "/etc/firejail/thunderbird-beta.profile";

const QUODLIBET_FIFO: &'static str = "/home/kalvin/.config/quodlibet/control";

const SWAYMSG: &'static str = "/usr/bin/swaymsg";
const SWAY_EXTERNAL_DISPLAY: &'static str = "HDMI-A-1";

#[derive(Debug)]
struct CgroupedFirejailedCommandOptions<'a> {
    bin_path: &'a str,
    memory_high: Option<i32>,
    memory_max: Option<i32>,
    firejail_profile: Option<&'a str>,
    implicit_extra_args: Option<&'a [&'a str]>,
    argv_remainder: Vec<String>,
}

fn cgrouped_firejail_command(options: CgroupedFirejailedCommandOptions) -> Command {
    let mut command = Command::new(SYSTEMD_RUN);
    command.args(&["--user", "--scope"]);
    command.args(&["-p", "MemorySwapMax=0"]);

    if let Some(memory_high) = options.memory_high {
        let memory_high_owned_string = format!("MemoryHigh={}M", memory_high);
        command.args(&["-p", memory_high_owned_string.as_str()]);
    }
    if let Some(memory_max) = options.memory_max {
        let memory_max_owned_string = format!("MemoryMax={}M", memory_max);
        command.args(&["-p", memory_max_owned_string.as_str()]);
    }

    if let Some(firejail_profile) = options.firejail_profile {
        command.arg(FIREJAIL);
        command.arg("--ignore=seccomp");
        command.arg(format!("--profile={}", firejail_profile));
    }

    command.arg(options.bin_path);

    if let Some(implicit_extra_args) = options.implicit_extra_args {
        command.args(implicit_extra_args);
    }

    command.args(options.argv_remainder);

    command
}

fn simple_firejail_command(target: &str, args: Vec<String>) -> Command {
    let mut command = Command::new(FIREJAIL);

    command.arg(format!("--profile=/etc/firejail/{}.profile", target));
    command.arg(format!("/usr/bin/{}", target));
    command.args(args);

    command
}

fn swaymsg_display_command(args: Vec<String>) -> Command {
    let mut command = Command::new(SWAYMSG);
    command.arg(format!(
        "output {} {}",
        SWAY_EXTERNAL_DISPLAY,
        match args[0].as_str() {
            "e" => "enable",
            "d" => "disable",
            _ => panic!(
                "bad argument for swaymsg ``output'' command: ``{}''",
                args[0].as_str()
            ),
        }
    ));

    command
}

fn quodlibet_command(args: Vec<String>) -> Command {
    let mut file = std::fs::OpenOptions::new()
        .append(true)
        .open(QUODLIBET_FIFO)
        .unwrap();
    file.write_all(match args[0].as_str() {
        "pp" => "play-pause".as_bytes(),
        "sa" => "stop-after 1".as_bytes(),
        _ => panic!(
            "bad argument for quodlibet command: ``{}''",
            args[0].as_str()
        ),
    })
    .unwrap();

    Command::new("/bin/true")
}

fn init_command() -> Command {
    let mut args_less_the_first = std::env::args();
    // Pops the name of this executable.
    args_less_the_first.next();

    // Pops the name of the unner target.
    let target = args_less_the_first.next().unwrap();

    // Copies and collects the remaining argv.
    let args: Vec<String> = args_less_the_first.collect();

    match target.as_str() {
        "d" => return swaymsg_display_command(args),
        "ff" => {
            return cgrouped_firejail_command(CgroupedFirejailedCommandOptions {
                bin_path: FIREFOX,
                memory_high: Some(FIREFOX_MEMORY_HIGH),
                memory_max: Some(FIREFOX_MEMORY_MAX),
                firejail_profile: Some(FIREFOX_PROFILE),
                implicit_extra_args: Some(&["-P", FIREFOX_MOZILLA_SFW_PROFILE]),
                argv_remainder: args,
            });
        }
        "keira" => {
            return cgrouped_firejail_command(CgroupedFirejailedCommandOptions {
                bin_path: KEIRA,
                memory_high: Some(FIREFOX_MEMORY_HIGH),
                memory_max: Some(FIREFOX_MEMORY_MAX),
                firejail_profile: Some(KEIRA_PROFILE),
                implicit_extra_args: Some(&["-P", "nightly"]),
                argv_remainder: args,
            });
        }
        "npv" => return simple_firejail_command("mpv", args),
        "q" => return quodlibet_command(args),
        "read" => {
            let formatted_args: Vec<String> = vec![format!("about:reader?url={}", args[0])];
            return cgrouped_firejail_command(CgroupedFirejailedCommandOptions {
                bin_path: FIREFOX,
                memory_high: Some(FIREFOX_MEMORY_HIGH),
                memory_max: Some(FIREFOX_MEMORY_MAX),
                firejail_profile: Some(FIREFOX_PROFILE),
                implicit_extra_args: Some(&["-P", FIREFOX_MOZILLA_SFW_PROFILE]),
                argv_remainder: formatted_args,
            });
        }
        "t" => {
            return cgrouped_firejail_command(CgroupedFirejailedCommandOptions {
                bin_path: TERM,
                memory_high: None,
                memory_max: None,
                firejail_profile: Some(TERM_PROFILE),
                implicit_extra_args: None,
                argv_remainder: args,
            })
        }
        "tbb" => {
            return cgrouped_firejail_command(CgroupedFirejailedCommandOptions {
                bin_path: THUNDERBIRD,
                memory_high: None,
                memory_max: None,
                firejail_profile: Some(THUNDERBIRD_PROFILE),
                implicit_extra_args: None,
                argv_remainder: args,
            });
        }
        "vlk" => {
            return cgrouped_firejail_command(CgroupedFirejailedCommandOptions {
                bin_path: "/usr/bin/vlc",
                memory_high: None,
                memory_max: None,
                firejail_profile: Some("/etc/firejail/vlc.profile"),
                implicit_extra_args: None,
                argv_remainder: args,
            })
        }
        "e" => return simple_firejail_command("evince", args),
        _ => panic!(format!("no handler for ``{}''", target)),
    }
}

fn main() {
    let mut command = init_command();
    println!("{:#?}", command);
    command.exec();
}
<commit_msg>unner: use panic! formatter<commit_after>use std::io::Write;
use std::os::unix::process::CommandExt;
use std::process::Command;

const SYSTEMD_RUN: &'static str = "/usr/bin/systemd-run";
const FIREJAIL: &'static str = "/usr/bin/firejail";

const FIREFOX: &'static str = "/usr/bin/firefox";
const FIREFOX_PROFILE: &'static str = "/etc/firejail/firefox.profile";
const FIREFOX_MOZILLA_SFW_PROFILE: &'static str = "default-1473025815439";

const FIREFOX_MEMORY_HIGH: i32 = 4420;
const FIREFOX_MEMORY_MAX: i32 = 5200;

const KEIRA: &'static str = "/home/kalvin/Downloads/.firefox-nightly/firefox";
const KEIRA_PROFILE: &'static str = "/etc/firejail/firefox-nightly.profile";

const TERM: &'static str = "/usr/bin/alacritty";
const TERM_PROFILE: &'static str = "/etc/firejail/x-terminal-emulator.profile";

const THUNDERBIRD: &'static str = "/home/kalvin/Downloads/.thunderbird-beta/thunderbird";
const THUNDERBIRD_PROFILE: &'static str = "/etc/firejail/thunderbird-beta.profile";

const QUODLIBET_FIFO: &'static str = "/home/kalvin/.config/quodlibet/control";

const SWAYMSG: &'static str = "/usr/bin/swaymsg";
const SWAY_EXTERNAL_DISPLAY: &'static str = "HDMI-A-1";

#[derive(Debug)]
struct CgroupedFirejailedCommandOptions<'a> {
    bin_path: &'a str,
    memory_high: Option<i32>,
    memory_max: Option<i32>,
    firejail_profile: Option<&'a str>,
    implicit_extra_args: Option<&'a [&'a str]>,
    argv_remainder: Vec<String>,
}

fn cgrouped_firejail_command(options: CgroupedFirejailedCommandOptions) -> Command {
    let mut command = Command::new(SYSTEMD_RUN);
    command.args(&["--user", "--scope"]);
    command.args(&["-p", "MemorySwapMax=0"]);

    if let Some(memory_high) = options.memory_high {
        let memory_high_owned_string = format!("MemoryHigh={}M", memory_high);
        command.args(&["-p", memory_high_owned_string.as_str()]);
    }
    if let Some(memory_max) = options.memory_max {
        let memory_max_owned_string = format!("MemoryMax={}M", memory_max);
        command.args(&["-p", memory_max_owned_string.as_str()]);
    }

    if let Some(firejail_profile) = options.firejail_profile {
        command.arg(FIREJAIL);
        command.arg("--ignore=seccomp");
        command.arg(format!("--profile={}", firejail_profile));
    }

    command.arg(options.bin_path);

    if let Some(implicit_extra_args) = options.implicit_extra_args {
        command.args(implicit_extra_args);
    }

    command.args(options.argv_remainder);

    command
}

fn simple_firejail_command(target: &str, args: Vec<String>) -> Command {
    let mut command = Command::new(FIREJAIL);

    command.arg(format!("--profile=/etc/firejail/{}.profile", target));
    command.arg(format!("/usr/bin/{}", target));
    command.args(args);

    command
}

fn swaymsg_display_command(args: Vec<String>) -> Command {
    let mut command = Command::new(SWAYMSG);
    command.arg(format!(
        "output {} {}",
        SWAY_EXTERNAL_DISPLAY,
        match args[0].as_str() {
            "e" => "enable",
            "d" => "disable",
            _ => panic!(
                "bad argument for swaymsg ``output'' command: ``{}''",
                args[0].as_str()
            ),
        }
    ));

    command
}

fn quodlibet_command(args: Vec<String>) -> Command {
    let mut file = std::fs::OpenOptions::new()
        .append(true)
        .open(QUODLIBET_FIFO)
        .unwrap();
    file.write_all(match args[0].as_str() {
        "pp" => "play-pause".as_bytes(),
        "sa" => "stop-after 1".as_bytes(),
        _ => panic!(
            "bad argument for quodlibet command: ``{}''",
            args[0].as_str()
        ),
    })
    .unwrap();

    Command::new("/bin/true")
}

fn init_command() -> Command {
    let mut args_less_the_first = std::env::args();
    // Pops the name of this executable.
    args_less_the_first.next();

    // Pops the name of the unner target.
    let target = args_less_the_first.next().unwrap();

    // Copies and collects the remaining argv.
    let args: Vec<String> = args_less_the_first.collect();

    match target.as_str() {
        "d" => return swaymsg_display_command(args),
        "ff" => {
            return cgrouped_firejail_command(CgroupedFirejailedCommandOptions {
                bin_path: FIREFOX,
                memory_high: Some(FIREFOX_MEMORY_HIGH),
                memory_max: Some(FIREFOX_MEMORY_MAX),
                firejail_profile: Some(FIREFOX_PROFILE),
                implicit_extra_args: Some(&["-P", FIREFOX_MOZILLA_SFW_PROFILE]),
                argv_remainder: args,
            });
        }
        "keira" => {
            return cgrouped_firejail_command(CgroupedFirejailedCommandOptions {
                bin_path: KEIRA,
                memory_high: Some(FIREFOX_MEMORY_HIGH),
                memory_max: Some(FIREFOX_MEMORY_MAX),
                firejail_profile: Some(KEIRA_PROFILE),
                implicit_extra_args: Some(&["-P", "nightly"]),
                argv_remainder: args,
            });
        }
        "npv" => return simple_firejail_command("mpv", args),
        "q" => return quodlibet_command(args),
        "read" => {
            let formatted_args: Vec<String> = vec![format!("about:reader?url={}", args[0])];
            return cgrouped_firejail_command(CgroupedFirejailedCommandOptions {
                bin_path: FIREFOX,
                memory_high: Some(FIREFOX_MEMORY_HIGH),
                memory_max: Some(FIREFOX_MEMORY_MAX),
                firejail_profile: Some(FIREFOX_PROFILE),
                implicit_extra_args: Some(&["-P", FIREFOX_MOZILLA_SFW_PROFILE]),
                argv_remainder: formatted_args,
            });
        }
        "t" => {
            return cgrouped_firejail_command(CgroupedFirejailedCommandOptions {
                bin_path: TERM,
                memory_high: None,
                memory_max: None,
                firejail_profile: Some(TERM_PROFILE),
                implicit_extra_args: None,
                argv_remainder: args,
            })
        }
        "tbb" => {
            return cgrouped_firejail_command(CgroupedFirejailedCommandOptions {
                bin_path: THUNDERBIRD,
                memory_high: None,
                memory_max: None,
                firejail_profile: Some(THUNDERBIRD_PROFILE),
                implicit_extra_args: None,
                argv_remainder: args,
            });
        }
        "vlk" => {
            return cgrouped_firejail_command(CgroupedFirejailedCommandOptions {
                bin_path: "/usr/bin/vlc",
                memory_high: None,
                memory_max: None,
                firejail_profile: Some("/etc/firejail/vlc.profile"),
                implicit_extra_args: None,
                argv_remainder: args,
            })
        }
        "e" => return simple_firejail_command("evince", args),
        _ => panic!("no handler for ``{}''", target),
    }
}

fn main() {
    let mut command = init_command();
    println!("{:#?}", command);
    command.exec();
}
<|endoftext|>


<commit_before>use crate::ReadableKey;
use crate::{KeySet, StringKey, WriteableKey};
use std::ptr::NonNull;
use KDBError::*;
use LogicalError::*;
use PermanentError::*;
use ResourceError::*;
use ValidationError::*;

#[derive(Debug)]
pub struct KDB {
    ptr: NonNull<elektra_sys::KDB>,
    _marker: std::marker::PhantomData<elektra_sys::KDB>,
}

impl Drop for KDB {
    fn drop(&mut self) {
        let mut err_key = StringKey::new_empty();
        unsafe {
            elektra_sys::kdbClose(self.as_ptr(), err_key.as_ptr());
        }
    }
}

impl KDB {
    /// Opens the session with the Key database.
    pub fn open<'a>() -> Result<Self, KDBError<'a>> {
        let mut key = StringKey::new_empty();
        let kdb_ptr = unsafe { elektra_sys::kdbOpen(key.as_ptr()) };

        if kdb_ptr.is_null() {
            Err(map_kdb_error(&key))
        } else {
            Ok(KDB {
                ptr: NonNull::new(kdb_ptr).unwrap(),
                _marker: std::marker::PhantomData,
            })
        }
    }

    /// Retrieve keys in an atomic and universal way.
    /// Note that the provided keyset is modified and contains the result.
    /// The return value is true, if the keys were successfully retrieved
    /// and false if there were no changes to the keyset.
    pub fn get<'a>(
        &mut self,
        keyset: &mut KeySet,
        key: &mut StringKey<'a>,
    ) -> Result<bool, KDBError<'a>> {
        let ret_val = unsafe { elektra_sys::kdbGet(self.as_ptr(), keyset.as_ptr(), key.as_ptr()) };

        if ret_val == 1 {
            Ok(true)
        } else if ret_val == 0 {
            Ok(false)
        } else {
            Err(map_kdb_error(key))
        }
    }

    /// Set keys in an atomic and universal way.
    /// The return value is true on success,
    /// and false if there were no changes to the KDB.
    /// # Notes
    /// You have to call [`get`](#method.get) with `keyset` first.
    pub fn set<'a>(
        &mut self,
        keyset: &mut KeySet,
        key: &mut StringKey<'a>,
    ) -> Result<bool, KDBError<'a>> {
        let ret_val = unsafe { elektra_sys::kdbSet(self.as_ptr(), keyset.as_ptr(), key.as_ptr()) };

        if ret_val == 1 {
            Ok(true)
        } else if ret_val == 0 {
            Ok(false)
        } else {
            Err(map_kdb_error(key))
        }
    }

    /// This method can be used the given KDB handle meets certain clauses, specified in contract.
    /// The return value is true on success,
    /// and false if clauses of the contract are unmet.
    pub fn ensure<'a>(
        &mut self,
        keyset: &mut KeySet,
        key: &mut StringKey<'a>,
    ) -> Result<bool, KDBError<'a>> {
        let ret_val =
            unsafe { elektra_sys::kdbEnsure(self.as_ptr(), keyset.as_ptr(), key.as_ptr()) };

        if ret_val == 0 {
            Ok(true)
        } else if ret_val == 1 {
            Ok(false)
        } else {
            Err(map_kdb_error(key))
        }
    }

    pub fn as_ptr(&mut self) -> *mut elektra_sys::KDB {
        self.ptr.as_ptr()
    }
}

impl AsRef<elektra_sys::KDB> for KDB {
    fn as_ref(&self) -> &elektra_sys::KDB {
        unsafe { self.ptr.as_ref() }
    }
}

// const ELEKTRA_ERROR_RESOURCE: &str = "C01100";
const ELEKTRA_ERROR_OUT_OF_MEMORY: &str = "C01110";
// const ELEKTRA_ERROR_INSTALLATION: &str = "C01200";
const ELEKTRA_ERROR_INTERNAL: &str = "C01310";
const ELEKTRA_ERROR_INTERFACE: &str = "C01320";
const ELEKTRA_ERROR_PLUGIN_MISBEHAVIOR: &str = "C01330";
const ELEKTRA_ERROR_CONFLICTING_STATE: &str = "C02000";
const ELEKTRA_ERROR_VALIDATION_SYNTACTIC: &str = "C03100";
const ELEKTRA_ERROR_VALIDATION_SEMANTIC: &str = "C03200";

#[derive(Debug)]
pub enum KDBError<'a> {
    Permanent(PermanentError<'a>),
    ConflictingState(KDBErrorWrapper<'a>),
    Validation(ValidationError<'a>),
}

#[derive(Debug)]
pub enum PermanentError<'a> {
    Resource(ResourceError<'a>),
    Logical(LogicalError<'a>),
    Installation(KDBErrorWrapper<'a>),
}

#[derive(Debug)]
pub enum LogicalError<'a> {
    Internal(KDBErrorWrapper<'a>),
    Interface(KDBErrorWrapper<'a>),
    PluginMisbehavior(KDBErrorWrapper<'a>),
}

#[derive(Debug)]
pub enum ResourceError<'a> {
    OutOfMemory(KDBErrorWrapper<'a>),
}

#[derive(Debug)]
pub enum ValidationError<'a> {
    Syntactic(KDBErrorWrapper<'a>),
    Semantic(KDBErrorWrapper<'a>),
}

#[derive(Debug)]
pub struct KDBErrorWrapper<'a> {
    error_key: StringKey<'a>,
}

impl<'a> KDBErrorWrapper<'a> {
    /// Constructs a new KDBErrorWrapper from a StringKey.
    /// Only pass keys where the metakeys error/* are set.
    pub fn new(error_key: StringKey) -> KDBErrorWrapper {
        KDBErrorWrapper { error_key }
    }

    // TODO: For which of these error/* can we be sure that they exist?

    /// Returns the error number.
    pub fn number(&self) -> String {
        self.error_key
            .meta("error/number")
            .unwrap()
            .value()
            .to_owned()
            .to_string()
    }

    /// Returns the error reason.
    pub fn reason(&self) -> String {
        self.error_key
            .meta("error/reason")
            .unwrap()
            .value()
            .to_owned()
            .to_string()
    }

    /// Returns the module where the error occured.
    pub fn module(&self) -> String {
        self.error_key
            .meta("error/module")
            .unwrap()
            .value()
            .to_owned()
            .to_string()
    }

    /// Returns a description of the error.
    pub fn description(&self) -> String {
        self.error_key
            .meta("error/description")
            .unwrap()
            .value()
            .to_owned()
            .to_string()
    }

    /// Returns the source file from where the error information comes.
    pub fn file(&self) -> String {
        self.error_key
            .meta("error/file")
            .unwrap()
            .value()
            .to_owned()
            .to_string()
    }

    /// Returns the the exact line of that source file.
    pub fn line(&self) -> String {
        self.error_key
            .meta("error/line")
            .unwrap()
            .value()
            .to_owned()
            .to_string()
    }
    // TODO: key is not the error_key, but the key that the keysets internal cursor points to, but this is not accessible here
    pub fn to_error_message(&self) -> String {
        format!("Sorry, module {module} issued error {error_number}:\n{description}: Validation of key \"{key}\" with string \"{string}\" failed.",
            module = self.module(),
            error_number = self.number(),
            description = self.description(),
            key = self.error_key.name(),
            string = self.error_key.value())
    }
}

impl<'a> std::fmt::Display for KDBErrorWrapper<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
        write!(f, "{}", self.to_error_message())
    }
}

impl<'a> std::error::Error for KDBErrorWrapper<'a> {}

fn map_kdb_error<'a, 'b>(error_key: &'a StringKey) -> KDBError<'b> {
    let err_num_key_res = error_key.meta("error/number");
    if let Ok(err_num_key) = err_num_key_res {
        let err_wrapper = KDBErrorWrapper::new(error_key.duplicate());

        match err_num_key.value().to_owned().to_string().as_str() {
            ELEKTRA_ERROR_OUT_OF_MEMORY => {
                return Permanent(Resource(OutOfMemory(err_wrapper)));
            }
            ELEKTRA_ERROR_INTERNAL => {
                return Permanent(Logical(Internal(err_wrapper)));
            }
            ELEKTRA_ERROR_INTERFACE => {
                return Permanent(Logical(Interface(err_wrapper)));
            }
            ELEKTRA_ERROR_PLUGIN_MISBEHAVIOR => {
                return Permanent(Logical(PluginMisbehavior(err_wrapper)));
            }
            ELEKTRA_ERROR_CONFLICTING_STATE => {
                return ConflictingState(err_wrapper);
            }
            ELEKTRA_ERROR_VALIDATION_SYNTACTIC => {
                return Validation(Syntactic(err_wrapper));
            }
            ELEKTRA_ERROR_VALIDATION_SEMANTIC => {
                return Validation(Semantic(err_wrapper));
            }
            _ => {
                unreachable!();
            }
        }
    }
    unreachable!()
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::{KeyBuilder, LookupOption};

    const PARENT_KEY: &str = "user/sw/tests/rust/1/";
    const KEY_1_NAME: &str = "user/sw/tests/rust/1/key_name";
    const KEY_2_NAME: &str = "user/sw/tests/rust/1/key_name/2";

    const KEY_1_VALUE: &str = "key_value_1";
    const KEY_2_VALUE: &str = "key_value_2";

    fn create_test_keys<'a>() -> (StringKey<'a>, StringKey<'a>, StringKey<'a>) {
        let parent_key = StringKey::new(PARENT_KEY).unwrap();
        let key1: StringKey = KeyBuilder::new(KEY_1_NAME)
            .unwrap()
            .value(KEY_1_VALUE)
            .build();
        let key2: StringKey = KeyBuilder::new(KEY_2_NAME)
            .unwrap()
            .value(KEY_2_VALUE)
            .build();
        (parent_key, key1, key2)
    }

    #[test]
    fn can_use_kdb() {
        let (mut parent_key, key1, key2) = create_test_keys();
        {
            let mut kdb = KDB::open().unwrap();
            let mut ks = KeySet::with_capacity(10);
            kdb.get(&mut ks, &mut parent_key).unwrap();
            ks.append_key(key1).unwrap();
            ks.append_key(key2).unwrap();
            kdb.set(&mut ks, &mut parent_key).unwrap();
        }
        {
            let mut kdb = KDB::open().unwrap();
            let mut ks = KeySet::with_capacity(2);
            kdb.get(&mut ks, &mut parent_key).unwrap();
            let key1_lookup = ks
                .lookup_by_name(KEY_1_NAME, Default::default())
                .unwrap()
                .duplicate();
            assert_eq!(key1_lookup.value(), KEY_1_VALUE);

            let key2_lookup = ks
                .lookup_by_name(KEY_2_NAME, Default::default())
                .unwrap()
                .duplicate();
            assert_eq!(key2_lookup.value(), KEY_2_VALUE);
        }
        remove_test_keys();
    }

    #[test]
    fn remove_test_keys() {
        let (mut parent_key, _, _) = create_test_keys();
        let mut kdb = KDB::open().unwrap();
        let mut ks = KeySet::with_capacity(10);
        kdb.get(&mut ks, &mut parent_key).unwrap();
        ks.lookup_by_name(KEY_1_NAME, LookupOption::KDB_O_POP)
            .unwrap();
        ks.lookup_by_name(KEY_2_NAME, LookupOption::KDB_O_POP)
            .unwrap();
        kdb.set(&mut ks, &mut parent_key).unwrap();
    }
}
<commit_msg>Rust: Remove test attribute from plain fn<commit_after>use crate::ReadableKey;
use crate::{KeySet, StringKey, WriteableKey};
use std::ptr::NonNull;
use KDBError::*;
use LogicalError::*;
use PermanentError::*;
use ResourceError::*;
use ValidationError::*;

#[derive(Debug)]
pub struct KDB {
    ptr: NonNull<elektra_sys::KDB>,
    _marker: std::marker::PhantomData<elektra_sys::KDB>,
}

impl Drop for KDB {
    fn drop(&mut self) {
        let mut err_key = StringKey::new_empty();
        unsafe {
            elektra_sys::kdbClose(self.as_ptr(), err_key.as_ptr());
        }
    }
}

impl KDB {
    /// Opens the session with the Key database.
    pub fn open<'a>() -> Result<Self, KDBError<'a>> {
        let mut key = StringKey::new_empty();
        let kdb_ptr = unsafe { elektra_sys::kdbOpen(key.as_ptr()) };

        if kdb_ptr.is_null() {
            Err(map_kdb_error(&key))
        } else {
            Ok(KDB {
                ptr: NonNull::new(kdb_ptr).unwrap(),
                _marker: std::marker::PhantomData,
            })
        }
    }

    /// Retrieve keys in an atomic and universal way.
    /// Note that the provided keyset is modified and contains the result.
    /// The return value is true, if the keys were successfully retrieved
    /// and false if there were no changes to the keyset.
    pub fn get<'a>(
        &mut self,
        keyset: &mut KeySet,
        key: &mut StringKey<'a>,
    ) -> Result<bool, KDBError<'a>> {
        let ret_val = unsafe { elektra_sys::kdbGet(self.as_ptr(), keyset.as_ptr(), key.as_ptr()) };

        if ret_val == 1 {
            Ok(true)
        } else if ret_val == 0 {
            Ok(false)
        } else {
            Err(map_kdb_error(key))
        }
    }

    /// Set keys in an atomic and universal way.
    /// The return value is true on success,
    /// and false if there were no changes to the KDB.
    /// # Notes
    /// You have to call [`get`](#method.get) with `keyset` first.
    pub fn set<'a>(
        &mut self,
        keyset: &mut KeySet,
        key: &mut StringKey<'a>,
    ) -> Result<bool, KDBError<'a>> {
        let ret_val = unsafe { elektra_sys::kdbSet(self.as_ptr(), keyset.as_ptr(), key.as_ptr()) };

        if ret_val == 1 {
            Ok(true)
        } else if ret_val == 0 {
            Ok(false)
        } else {
            Err(map_kdb_error(key))
        }
    }

    /// This method can be used the given KDB handle meets certain clauses, specified in contract.
    /// The return value is true on success,
    /// and false if clauses of the contract are unmet.
    pub fn ensure<'a>(
        &mut self,
        keyset: &mut KeySet,
        key: &mut StringKey<'a>,
    ) -> Result<bool, KDBError<'a>> {
        let ret_val =
            unsafe { elektra_sys::kdbEnsure(self.as_ptr(), keyset.as_ptr(), key.as_ptr()) };

        if ret_val == 0 {
            Ok(true)
        } else if ret_val == 1 {
            Ok(false)
        } else {
            Err(map_kdb_error(key))
        }
    }

    pub fn as_ptr(&mut self) -> *mut elektra_sys::KDB {
        self.ptr.as_ptr()
    }
}

impl AsRef<elektra_sys::KDB> for KDB {
    fn as_ref(&self) -> &elektra_sys::KDB {
        unsafe { self.ptr.as_ref() }
    }
}

// const ELEKTRA_ERROR_RESOURCE: &str = "C01100";
const ELEKTRA_ERROR_OUT_OF_MEMORY: &str = "C01110";
// const ELEKTRA_ERROR_INSTALLATION: &str = "C01200";
const ELEKTRA_ERROR_INTERNAL: &str = "C01310";
const ELEKTRA_ERROR_INTERFACE: &str = "C01320";
const ELEKTRA_ERROR_PLUGIN_MISBEHAVIOR: &str = "C01330";
const ELEKTRA_ERROR_CONFLICTING_STATE: &str = "C02000";
const ELEKTRA_ERROR_VALIDATION_SYNTACTIC: &str = "C03100";
const ELEKTRA_ERROR_VALIDATION_SEMANTIC: &str = "C03200";

#[derive(Debug)]
pub enum KDBError<'a> {
    Permanent(PermanentError<'a>),
    ConflictingState(KDBErrorWrapper<'a>),
    Validation(ValidationError<'a>),
}

#[derive(Debug)]
pub enum PermanentError<'a> {
    Resource(ResourceError<'a>),
    Logical(LogicalError<'a>),
    Installation(KDBErrorWrapper<'a>),
}

#[derive(Debug)]
pub enum LogicalError<'a> {
    Internal(KDBErrorWrapper<'a>),
    Interface(KDBErrorWrapper<'a>),
    PluginMisbehavior(KDBErrorWrapper<'a>),
}

#[derive(Debug)]
pub enum ResourceError<'a> {
    OutOfMemory(KDBErrorWrapper<'a>),
}

#[derive(Debug)]
pub enum ValidationError<'a> {
    Syntactic(KDBErrorWrapper<'a>),
    Semantic(KDBErrorWrapper<'a>),
}

#[derive(Debug)]
pub struct KDBErrorWrapper<'a> {
    error_key: StringKey<'a>,
}

impl<'a> KDBErrorWrapper<'a> {
    /// Constructs a new KDBErrorWrapper from a StringKey.
    /// Only pass keys where the metakeys error/* are set.
    pub fn new(error_key: StringKey) -> KDBErrorWrapper {
        KDBErrorWrapper { error_key }
    }

    // TODO: For which of these error/* can we be sure that they exist?

    /// Returns the error number.
    pub fn number(&self) -> String {
        self.error_key
            .meta("error/number")
            .unwrap()
            .value()
            .to_owned()
            .to_string()
    }

    /// Returns the error reason.
    pub fn reason(&self) -> String {
        self.error_key
            .meta("error/reason")
            .unwrap()
            .value()
            .to_owned()
            .to_string()
    }

    /// Returns the module where the error occured.
    pub fn module(&self) -> String {
        self.error_key
            .meta("error/module")
            .unwrap()
            .value()
            .to_owned()
            .to_string()
    }

    /// Returns a description of the error.
    pub fn description(&self) -> String {
        self.error_key
            .meta("error/description")
            .unwrap()
            .value()
            .to_owned()
            .to_string()
    }

    /// Returns the source file from where the error information comes.
    pub fn file(&self) -> String {
        self.error_key
            .meta("error/file")
            .unwrap()
            .value()
            .to_owned()
            .to_string()
    }

    /// Returns the the exact line of that source file.
    pub fn line(&self) -> String {
        self.error_key
            .meta("error/line")
            .unwrap()
            .value()
            .to_owned()
            .to_string()
    }
    // TODO: key is not the error_key, but the key that the keysets internal cursor points to, but this is not accessible here
    pub fn to_error_message(&self) -> String {
        format!("Sorry, module {module} issued error {error_number}:\n{description}: Validation of key \"{key}\" with string \"{string}\" failed.",
            module = self.module(),
            error_number = self.number(),
            description = self.description(),
            key = self.error_key.name(),
            string = self.error_key.value())
    }
}

impl<'a> std::fmt::Display for KDBErrorWrapper<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
        write!(f, "{}", self.to_error_message())
    }
}

impl<'a> std::error::Error for KDBErrorWrapper<'a> {}

fn map_kdb_error<'a, 'b>(error_key: &'a StringKey) -> KDBError<'b> {
    let err_num_key_res = error_key.meta("error/number");
    if let Ok(err_num_key) = err_num_key_res {
        let err_wrapper = KDBErrorWrapper::new(error_key.duplicate());

        match err_num_key.value().to_owned().to_string().as_str() {
            ELEKTRA_ERROR_OUT_OF_MEMORY => {
                return Permanent(Resource(OutOfMemory(err_wrapper)));
            }
            ELEKTRA_ERROR_INTERNAL => {
                return Permanent(Logical(Internal(err_wrapper)));
            }
            ELEKTRA_ERROR_INTERFACE => {
                return Permanent(Logical(Interface(err_wrapper)));
            }
            ELEKTRA_ERROR_PLUGIN_MISBEHAVIOR => {
                return Permanent(Logical(PluginMisbehavior(err_wrapper)));
            }
            ELEKTRA_ERROR_CONFLICTING_STATE => {
                return ConflictingState(err_wrapper);
            }
            ELEKTRA_ERROR_VALIDATION_SYNTACTIC => {
                return Validation(Syntactic(err_wrapper));
            }
            ELEKTRA_ERROR_VALIDATION_SEMANTIC => {
                return Validation(Semantic(err_wrapper));
            }
            _ => {
                unreachable!();
            }
        }
    }
    unreachable!()
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::{KeyBuilder, LookupOption};

    const PARENT_KEY: &str = "user/sw/tests/rust/1/";
    const KEY_1_NAME: &str = "user/sw/tests/rust/1/key_name";
    const KEY_2_NAME: &str = "user/sw/tests/rust/1/key_name/2";

    const KEY_1_VALUE: &str = "key_value_1";
    const KEY_2_VALUE: &str = "key_value_2";

    fn create_test_keys<'a>() -> (StringKey<'a>, StringKey<'a>, StringKey<'a>) {
        let parent_key = StringKey::new(PARENT_KEY).unwrap();
        let key1: StringKey = KeyBuilder::new(KEY_1_NAME)
            .unwrap()
            .value(KEY_1_VALUE)
            .build();
        let key2: StringKey = KeyBuilder::new(KEY_2_NAME)
            .unwrap()
            .value(KEY_2_VALUE)
            .build();
        (parent_key, key1, key2)
    }

    #[test]
    fn can_use_kdb() {
        let (mut parent_key, key1, key2) = create_test_keys();
        {
            let mut kdb = KDB::open().unwrap();
            let mut ks = KeySet::with_capacity(10);
            kdb.get(&mut ks, &mut parent_key).unwrap();
            ks.append_key(key1).unwrap();
            ks.append_key(key2).unwrap();
            kdb.set(&mut ks, &mut parent_key).unwrap();
        }
        {
            let mut kdb = KDB::open().unwrap();
            let mut ks = KeySet::with_capacity(2);
            kdb.get(&mut ks, &mut parent_key).unwrap();
            let key1_lookup = ks
                .lookup_by_name(KEY_1_NAME, Default::default())
                .unwrap()
                .duplicate();
            assert_eq!(key1_lookup.value(), KEY_1_VALUE);

            let key2_lookup = ks
                .lookup_by_name(KEY_2_NAME, Default::default())
                .unwrap()
                .duplicate();
            assert_eq!(key2_lookup.value(), KEY_2_VALUE);
        }
        remove_test_keys();
    }

    fn remove_test_keys() {
        let (mut parent_key, _, _) = create_test_keys();
        let mut kdb = KDB::open().unwrap();
        let mut ks = KeySet::with_capacity(10);
        kdb.get(&mut ks, &mut parent_key).unwrap();
        ks.lookup_by_name(KEY_1_NAME, LookupOption::KDB_O_POP)
            .unwrap();
        ks.lookup_by_name(KEY_2_NAME, LookupOption::KDB_O_POP)
            .unwrap();
        kdb.set(&mut ks, &mut parent_key).unwrap();
    }
}
<|endoftext|>


<commit_before>use std::fmt::Debug;

use toml::Value;

use self::error::HookError;

pub type HookResult<T> = Result<T, HookError>;

pub trait Configureable {
    fn set_config(&mut self, cfg: Value);
}

pub trait Hook : Configureable + Debug {
}

pub mod read {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreReadHook : Hook {
        fn pre_read(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostReadHook : Hook {
        fn post_read<'a>(&self, FileLockEntry<'a>) -> HookResult<FileLockEntry<'a>>;
    }

}

pub mod create {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreCreateHook : Hook {
        fn pre_create(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostCreateHook : Hook {
        fn post_create<'a>(&self, FileLockEntry<'a>) -> HookResult<FileLockEntry<'a>>;
    }

}

pub mod retrieve {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreRetrieveHook : Hook {
        fn pre_retrieve(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostRetrieveHook : Hook {
        fn post_retrieve<'a>(&self, FileLockEntry<'a>) -> HookResult<FileLockEntry<'a>>;
    }
}

pub mod update {
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreUpdateHook : Hook {
        fn pre_update(&self, &FileLockEntry) -> HookResult<()>;
    }

    pub trait PostUpdateHook : Hook {
        fn post_update(&self, &FileLockEntry) -> HookResult<()>;
    }
}

pub mod delete {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreDeleteHook : Hook {
        fn pre_delete(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostDeleteHook : Hook {
        fn post_delete(&self, &StoreId) -> HookResult<()>;
    }
}

pub mod error {
    use std::error::Error;
    use std::fmt::Error as FmtError;
    use std::clone::Clone;
    use std::fmt::{Display, Formatter};
    use std::convert::Into;

    /**
     * Kind of error
     */
    #[derive(Clone, Copy, Debug)]
    pub enum HookErrorKind {
        Pre(PreHookErrorKind),
        Post(PostHookErrorKind)
    }

    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    pub enum PreHookErrorKind {
        // ...
    }

    impl Into<HookErrorKind> for PreHookErrorKind {
        fn into(self) -> HookErrorKind {
            HookErrorKind::Pre(self)
        }
    }

    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    pub enum PostHookErrorKind {
        // ...
    }

    impl Into<HookErrorKind> for PostHookErrorKind {
        fn into(self) -> HookErrorKind {
            HookErrorKind::Post(self)
        }
    }

    fn hook_error_type_as_str(e: &HookErrorKind) -> &'static str {
        match e {
            _ => "",
        }
    }

    impl Display for HookErrorKind {

        fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
            try!(write!(fmt, "{}", hook_error_type_as_str(self)));
            Ok(())
        }

    }

    /**
     * Error type
     */
    #[derive(Debug)]
    pub struct HookError {
        err_type: HookErrorKind,
        cause: Option<Box<Error>>,
    }

    impl HookError {

        /**
         * Build a new HookError from an HookErrorKind, optionally with cause
         */
        pub fn new(errtype: HookErrorKind, cause: Option<Box<Error>>)
            -> HookError
            {
                HookError {
                    err_type: errtype,
                    cause: cause,
                }
            }

        /**
         * Get the error type of this HookError
         */
        pub fn err_type(&self) -> HookErrorKind {
            self.err_type.clone()
        }

    }

    impl Display for HookError {

        fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
            try!(write!(fmt, "[{}]", hook_error_type_as_str(&self.err_type.clone())));
            Ok(())
        }

    }

    impl Error for HookError {

        fn description(&self) -> &str {
            hook_error_type_as_str(&self.err_type.clone())
        }

        fn cause(&self) -> Option<&Error> {
            self.cause.as_ref().map(|e| &**e)
        }

    }

}

<commit_msg>Impl Into<HookError> for error kinds to convert errors in a simple way<commit_after>use std::fmt::Debug;

use toml::Value;

use self::error::HookError;

pub type HookResult<T> = Result<T, HookError>;

pub trait Configureable {
    fn set_config(&mut self, cfg: Value);
}

pub trait Hook : Configureable + Debug {
}

pub mod read {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreReadHook : Hook {
        fn pre_read(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostReadHook : Hook {
        fn post_read<'a>(&self, FileLockEntry<'a>) -> HookResult<FileLockEntry<'a>>;
    }

}

pub mod create {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreCreateHook : Hook {
        fn pre_create(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostCreateHook : Hook {
        fn post_create<'a>(&self, FileLockEntry<'a>) -> HookResult<FileLockEntry<'a>>;
    }

}

pub mod retrieve {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreRetrieveHook : Hook {
        fn pre_retrieve(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostRetrieveHook : Hook {
        fn post_retrieve<'a>(&self, FileLockEntry<'a>) -> HookResult<FileLockEntry<'a>>;
    }
}

pub mod update {
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreUpdateHook : Hook {
        fn pre_update(&self, &FileLockEntry) -> HookResult<()>;
    }

    pub trait PostUpdateHook : Hook {
        fn post_update(&self, &FileLockEntry) -> HookResult<()>;
    }
}

pub mod delete {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreDeleteHook : Hook {
        fn pre_delete(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostDeleteHook : Hook {
        fn post_delete(&self, &StoreId) -> HookResult<()>;
    }
}

pub mod error {
    use std::error::Error;
    use std::fmt::Error as FmtError;
    use std::clone::Clone;
    use std::fmt::{Display, Formatter};
    use std::convert::Into;

    /**
     * Kind of error
     */
    #[derive(Clone, Copy, Debug)]
    pub enum HookErrorKind {
        Pre(PreHookErrorKind),
        Post(PostHookErrorKind)
    }

    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    pub enum PreHookErrorKind {
        // ...
    }

    impl Into<HookErrorKind> for PreHookErrorKind {
        fn into(self) -> HookErrorKind {
            HookErrorKind::Pre(self)
        }
    }

    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    pub enum PostHookErrorKind {
        // ...
    }

    impl Into<HookErrorKind> for PostHookErrorKind {
        fn into(self) -> HookErrorKind {
            HookErrorKind::Post(self)
        }
    }

    pub trait IntoHookError {
        fn into_hookerror(self) -> HookError;
        fn into_hookerror_with_cause(self, cause: Box<Error>) -> HookError;
    }

    impl Into<HookError> for HookErrorKind {

        fn into(self) -> HookError {
            HookError::new(self, None)
        }

    }

    impl Into<HookError> for (HookErrorKind, Box<Error>) {

        fn into(self) -> HookError {
            HookError::new(self.0, Some(self.1))
        }

    }

    impl Into<HookError> for PreHookErrorKind {

        fn into(self) -> HookError {
            HookError::new(HookErrorKind::Pre(self), None)
        }

    }

    impl Into<HookError> for (PreHookErrorKind, Box<Error>) {

        fn into(self) -> HookError {
            HookError::new(HookErrorKind::Pre(self.0), Some(self.1))
        }

    }

    impl Into<HookError> for PostHookErrorKind {

        fn into(self) -> HookError {
            HookError::new(HookErrorKind::Post(self), None)
        }

    }

    impl Into<HookError> for (PostHookErrorKind, Box<Error>) {

        fn into(self) -> HookError {
            HookError::new(HookErrorKind::Post(self.0), Some(self.1))
        }

    }

    fn hook_error_type_as_str(e: &HookErrorKind) -> &'static str {
        match e {
            _ => "",
        }
    }

    impl Display for HookErrorKind {

        fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
            try!(write!(fmt, "{}", hook_error_type_as_str(self)));
            Ok(())
        }

    }

    /**
     * Error type
     */
    #[derive(Debug)]
    pub struct HookError {
        err_type: HookErrorKind,
        cause: Option<Box<Error>>,
    }

    impl HookError {

        /**
         * Build a new HookError from an HookErrorKind, optionally with cause
         */
        pub fn new(errtype: HookErrorKind, cause: Option<Box<Error>>)
            -> HookError
            {
                HookError {
                    err_type: errtype,
                    cause: cause,
                }
            }

        /**
         * Get the error type of this HookError
         */
        pub fn err_type(&self) -> HookErrorKind {
            self.err_type.clone()
        }

    }

    impl Display for HookError {

        fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
            try!(write!(fmt, "[{}]", hook_error_type_as_str(&self.err_type.clone())));
            Ok(())
        }

    }

    impl Error for HookError {

        fn description(&self) -> &str {
            hook_error_type_as_str(&self.err_type.clone())
        }

        fn cause(&self) -> Option<&Error> {
            self.cause.as_ref().map(|e| &**e)
        }

    }

}

<|endoftext|>


<commit_before>//! Configuration module for sans

use std::fs::OpenOptions;
use std::io::{Read, Write};
use toml as serde_toml;

#[derive(Serialize, Deserialize)]
struct ConfigBackend {
    cameras: Vec<String>,
}

pub struct SansConfig {
    path: String,
    backend: ConfigBackend,
}

impl SansConfig {
    pub fn save(&self) {
        OpenOptions::new()
            .create(true)
            .write(true)
            .open(&self.path)
            .expect("Failed to open sans config! Does the directory exist?")
            .write_all(&serde_toml::to_string_pretty(&self.backend)
                .unwrap_or_else(|_| {
                    serde_toml::to_string_pretty(&ConfigBackend {
                        cameras: Vec::new(),
                    }).unwrap()
                })
                .as_bytes())
            .expect("Failed to write sans config. Is your disk full?");
    }
}
<commit_msg>Improving configuration handling<commit_after>//! Configuration module for sans

use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use toml as serde_toml;

#[derive(Serialize, Deserialize)]
struct ConfigBackend {
    cameras: Vec<String>,
}

pub struct SansConfig {
    path: String,
    backend: ConfigBackend,
}

pub enum ConfigError {
    FileNotFount,
    FailedToReadFile,
    CorruptFile,
}

impl SansConfig {
    pub fn save(&self) {
        OpenOptions::new()
            .create(true)
            .write(true)
            .open(&self.path)
            .expect("Failed to open sans config! Does the directory exist?")
            .write_all(&serde_toml::to_string_pretty(&self.backend)
                .unwrap_or_else(|_| {
                    serde_toml::to_string_pretty(&ConfigBackend {
                        cameras: Vec::new(),
                    }).unwrap()
                })
                .as_bytes())
            .expect("Failed to write sans config. Is your disk full?");
    }

    /// Attempt to load the configuration for sans. Will return
    /// None if that was not possible.SansConfig
    ///
    /// In this case sans needs to terminate!
    pub fn load(path: &str) -> Result<Self, ConfigError> {
        use self::ConfigError::{CorruptFile, FailedToReadFile, FileNotFount};

        let mut f = match File::open(path) {
            Ok(f) => f,
            Err(_) => return Err(FileNotFount),
        };

        let mut content = String::new();
        if f.read_to_string(&mut content).is_err() {
            return Err(FailedToReadFile);
        }

        return Ok(SansConfig {
            path: path.into(),
            backend: match serde_toml::from_str(&content) {
                Ok(s) => s,
                Err(_) => return Err(CorruptFile),
            },
        });
    }
}
<|endoftext|>


<commit_before>//! utils used in proc-macro tests

use crate::dylib;
use crate::ProcMacroSrv;
pub use difference::Changeset as __Changeset;
use ra_proc_macro::ListMacrosTask;
use std::str::FromStr;
use test_utils::assert_eq_text;

mod fixtures {
    use cargo_metadata::Message;
    use std::process::Command;

    // Use current project metadata to get the proc-macro dylib path
    pub fn dylib_path(crate_name: &str, version: &str) -> std::path::PathBuf {
        let command = Command::new("cargo")
            .args(&["check", "--message-format", "json"])
            .output()
            .unwrap()
            .stdout;

        for message in Message::parse_stream(command.as_slice()) {
            match message.unwrap() {
                Message::CompilerArtifact(artifact) => {
                    if artifact.target.kind.contains(&"proc-macro".to_string()) {
                        let repr = format!("{} {}", crate_name, version);
                        if artifact.package_id.repr.starts_with(dbg!(&repr)) {
                            return artifact.filenames[0].clone();
                        }
                    }
                }
                _ => (), // Unknown message
            }
        }

        panic!("No proc-macro dylib for {} found!", crate_name);
    }
}

fn parse_string(code: &str) -> Option<crate::rustc_server::TokenStream> {
    Some(crate::rustc_server::TokenStream::from_str(code).unwrap())
}

pub fn assert_expand(
    crate_name: &str,
    macro_name: &str,
    version: &str,
    fixture: &str,
    expect: &str,
) {
    let path = fixtures::dylib_path(crate_name, version);
    let expander = dylib::Expander::new(&path).unwrap();
    let fixture = parse_string(fixture).unwrap();

    let res = expander.expand(macro_name, &fixture.subtree, None).unwrap();
    assert_eq_text!(&format!("{:?}", res), &expect.trim());
}

pub fn list(crate_name: &str, version: &str) -> Vec<String> {
    let path = fixtures::dylib_path(crate_name, version);
    let task = ListMacrosTask { lib: path };
    let mut srv = ProcMacroSrv::default();
    let res = srv.list_macros(&task).unwrap();
    res.macros.into_iter().map(|(name, kind)| format!("{} [{:?}]", name, kind)).collect()
}
<commit_msg>Remove dbg<commit_after>//! utils used in proc-macro tests

use crate::dylib;
use crate::ProcMacroSrv;
pub use difference::Changeset as __Changeset;
use ra_proc_macro::ListMacrosTask;
use std::str::FromStr;
use test_utils::assert_eq_text;

mod fixtures {
    use cargo_metadata::Message;
    use std::process::Command;

    // Use current project metadata to get the proc-macro dylib path
    pub fn dylib_path(crate_name: &str, version: &str) -> std::path::PathBuf {
        let command = Command::new("cargo")
            .args(&["check", "--message-format", "json"])
            .output()
            .unwrap()
            .stdout;

        for message in Message::parse_stream(command.as_slice()) {
            match message.unwrap() {
                Message::CompilerArtifact(artifact) => {
                    if artifact.target.kind.contains(&"proc-macro".to_string()) {
                        let repr = format!("{} {}", crate_name, version);
                        if artifact.package_id.repr.starts_with(&repr) {
                            return artifact.filenames[0].clone();
                        }
                    }
                }
                _ => (), // Unknown message
            }
        }

        panic!("No proc-macro dylib for {} found!", crate_name);
    }
}

fn parse_string(code: &str) -> Option<crate::rustc_server::TokenStream> {
    Some(crate::rustc_server::TokenStream::from_str(code).unwrap())
}

pub fn assert_expand(
    crate_name: &str,
    macro_name: &str,
    version: &str,
    fixture: &str,
    expect: &str,
) {
    let path = fixtures::dylib_path(crate_name, version);
    let expander = dylib::Expander::new(&path).unwrap();
    let fixture = parse_string(fixture).unwrap();

    let res = expander.expand(macro_name, &fixture.subtree, None).unwrap();
    assert_eq_text!(&format!("{:?}", res), &expect.trim());
}

pub fn list(crate_name: &str, version: &str) -> Vec<String> {
    let path = fixtures::dylib_path(crate_name, version);
    let task = ListMacrosTask { lib: path };
    let mut srv = ProcMacroSrv::default();
    let res = srv.list_macros(&task).unwrap();
    res.macros.into_iter().map(|(name, kind)| format!("{} [{:?}]", name, kind)).collect()
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(non_snake_case)]

register_long_diagnostics! {
E0454: r##"
A link name was given with an empty name. Erroneous code example:

```
#[link(name = "")] extern {} // error: #[link(name = "")] given with empty name
```

The rust compiler cannot link to an external library if you don't give it its
name. Example:

```
#[link(name = "some_lib")] extern {} // ok!
```
"##,

E0455: r##"
Linking with `kind=framework` is only supported when targeting OS X,
as frameworks are specific to that operating system.

Erroneous code example:

```compile_fail"
#[link(name = "FooCoreServices",  kind = "framework")] extern {}
// OS used to compile is Linux for example
```

To solve this error you can use conditional compilation:

```
#[cfg_attr(target="macos", link(name = "FooCoreServices", kind = "framework"))]
extern {}
```

See more: https://doc.rust-lang.org/book/conditional-compilation.html
"##,

E0458: r##"
An unknown "kind" was specified for a link attribute. Erroneous code example:

```
#[link(kind = "wonderful_unicorn")] extern {}
// error: unknown kind: `wonderful_unicorn`
```

Please specify a valid "kind" value, from one of the following:
 * static
 * dylib
 * framework
"##,

E0459: r##"
A link was used without a name parameter. Erroneous code example:

```
#[link(kind = "dylib")] extern {}
// error: #[link(...)] specified without `name = "foo"`
```

Please add the name parameter to allow the rust compiler to find the library
you want. Example:

```
#[link(kind = "dylib", name = "some_lib")] extern {} // ok!
```
"##,

E0463: r##"
A plugin/crate was declared but cannot be found. Erroneous code example:

```
#![feature(plugin)]
#![plugin(cookie_monster)] // error: can't find crate for `cookie_monster`
extern crate cake_is_a_lie; // error: can't find crate for `cake_is_a_lie`
```

You need to link your code to the relevant crate in order to be able to use it
(through Cargo or the `-L` option of rustc example). Plugins are crates as
well, and you link to them the same way.
"##,

}

register_diagnostics! {
    E0456, // plugin `..` is not available for triple `..`
    E0457, // plugin `..` only found in rlib format, but must be available...
    E0514, // metadata version mismatch
    E0460, // found possibly newer version of crate `..`
    E0461, // couldn't find crate `..` with expected target triple ..
    E0462, // found staticlib `..` instead of rlib or dylib
    E0464, // multiple matching crates for `..`
    E0465, // multiple .. candidates for `..` found
    E0466, // bad macro import
    E0467, // bad macro reexport
    E0468, // an `extern crate` loading macros must be at the crate root
    E0469, // imported macro not found
    E0470, // reexported macro not found
    E0519, // local crate and dependency have same (crate-name, disambiguator)
    E0523, // two dependencies have same (crate-name, disambiguator) but different SVH
}
<commit_msg>Update block codes' flags<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(non_snake_case)]

register_long_diagnostics! {
E0454: r##"
A link name was given with an empty name. Erroneous code example:

```compile_fail,E0454
#[link(name = "")] extern {} // error: #[link(name = "")] given with empty name
```

The rust compiler cannot link to an external library if you don't give it its
name. Example:

```
#[link(name = "some_lib")] extern {} // ok!
```
"##,

E0455: r##"
Linking with `kind=framework` is only supported when targeting OS X,
as frameworks are specific to that operating system.

Erroneous code example:

```compile_fail,E0455
#[link(name = "FooCoreServices",  kind = "framework")] extern {}
// OS used to compile is Linux for example
```

To solve this error you can use conditional compilation:

```
#[cfg_attr(target="macos", link(name = "FooCoreServices", kind = "framework"))]
extern {}
```

See more: https://doc.rust-lang.org/book/conditional-compilation.html
"##,

E0458: r##"
An unknown "kind" was specified for a link attribute. Erroneous code example:

```compile_fail,E0458
#[link(kind = "wonderful_unicorn")] extern {}
// error: unknown kind: `wonderful_unicorn`
```

Please specify a valid "kind" value, from one of the following:
 * static
 * dylib
 * framework
"##,

E0459: r##"
A link was used without a name parameter. Erroneous code example:

```compile_fail,E0459
#[link(kind = "dylib")] extern {}
// error: #[link(...)] specified without `name = "foo"`
```

Please add the name parameter to allow the rust compiler to find the library
you want. Example:

```
#[link(kind = "dylib", name = "some_lib")] extern {} // ok!
```
"##,

E0463: r##"
A plugin/crate was declared but cannot be found. Erroneous code example:

```compile_fail,E0463
#![feature(plugin)]
#![plugin(cookie_monster)] // error: can't find crate for `cookie_monster`
extern crate cake_is_a_lie; // error: can't find crate for `cake_is_a_lie`
```

You need to link your code to the relevant crate in order to be able to use it
(through Cargo or the `-L` option of rustc example). Plugins are crates as
well, and you link to them the same way.
"##,

}

register_diagnostics! {
    E0456, // plugin `..` is not available for triple `..`
    E0457, // plugin `..` only found in rlib format, but must be available...
    E0514, // metadata version mismatch
    E0460, // found possibly newer version of crate `..`
    E0461, // couldn't find crate `..` with expected target triple ..
    E0462, // found staticlib `..` instead of rlib or dylib
    E0464, // multiple matching crates for `..`
    E0465, // multiple .. candidates for `..` found
    E0466, // bad macro import
    E0467, // bad macro reexport
    E0468, // an `extern crate` loading macros must be at the crate root
    E0469, // imported macro not found
    E0470, // reexported macro not found
    E0519, // local crate and dependency have same (crate-name, disambiguator)
    E0523, // two dependencies have same (crate-name, disambiguator) but different SVH
}
<|endoftext|>