<commit_before>use color_eyre::eyre::{bail, Result};
use std::{
    collections::{HashMap, HashSet},
    path::{Path, PathBuf},
    process::Command,
};

use crate::Config;

#[derive(Default, Debug)]
pub struct Dependencies {
    /// All paths that must be imported with `-L dependency=`. This is for
    /// finding proc macros run on the host and dependencies for the target.
    pub import_paths: Vec<PathBuf>,
    /// The name as chosen in the `Cargo.toml` and its corresponding rmeta file.
    pub dependencies: Vec<(String, PathBuf)>,
}

/// Compiles dependencies and returns the crate names and corresponding rmeta files.
pub fn build_dependencies(config: &Config) -> Result<Dependencies> {
    let manifest_path = match &config.dependencies_crate_manifest_path {
        Some(path) => path,
        None => return Ok(Default::default()),
    };
    let (program, args, envs): (&Path, &[_], &[_]) = match &config.dependency_builder {
        Some(db) => (&db.program, &db.args, &db.envs),
        None => (Path::new("cargo"), &[], &[]),
    };
    let mut build = Command::new(program);
    build.args(args);
    // HACK: we're using `cargo run` (or `cargo miri run`), because the latter does not
    // support `cargo miri build` yet.
    build.arg("run");

    if let Some(target) = &config.target {
        build.arg(format!("--target={target}"));
    }

    // Reusable closure for setting up the environment both for artifact generation and `cargo_metadata`
    let setup_command = |cmd: &mut Command| {
        cmd.envs(envs.iter().map(|(k, v)| (k, v)));
        cmd.arg("--manifest-path").arg(manifest_path);
    };

    setup_command(&mut build);
    build
        .arg("--target-dir=target/test_dependencies")
        .arg("--message-format=json")
        .arg("-Zunstable-options");

    let output = build.output()?;

    if !output.status.success() {
        let stdout = String::from_utf8(output.stdout)?;
        let stderr = String::from_utf8(output.stderr)?;
        bail!("failed to compile dependencies:\nstderr:\n{stderr}\n\nstdout:{stdout}");
    }

    // Collect all artifacts generated
    let output = output.stdout;
    let output = String::from_utf8(output)?;
    let mut import_paths: HashSet<PathBuf> = HashSet::new();
    let mut artifacts: HashMap<_, _> = output
        .lines()
        .filter_map(|line| {
            let message = serde_json::from_str::<cargo_metadata::Message>(line).ok()?;
            if let cargo_metadata::Message::CompilerArtifact(artifact) = message {
                for filename in &artifact.filenames {
                    import_paths.insert(filename.parent().unwrap().into());
                }
                let filename = artifact
                    .filenames
                    .into_iter()
                    .find(|filename| filename.extension() == Some("rmeta"))?;
                Some((artifact.package_id, filename.into_std_path_buf()))
            } else {
                None
            }
        })
        .collect();

    // Check which crates are mentioned in the crate itself
    let mut metadata = cargo_metadata::MetadataCommand::new().cargo_command();
    setup_command(&mut metadata);
    let output = metadata.output()?;

    if !output.status.success() {
        let stdout = String::from_utf8(output.stdout)?;
        let stderr = String::from_utf8(output.stderr)?;
        bail!("failed to run cargo-metadata:\nstderr:\n{stderr}\n\nstdout:{stdout}");
    }

    let output = output.stdout;
    let output = String::from_utf8(output)?;

    for line in output.lines() {
        if !line.starts_with('{') {
            continue;
        }
        let metadata: cargo_metadata::Metadata = serde_json::from_str(line)?;
        // Only take artifacts that are defined in the Cargo.toml

        // First, find the root artifact
        let root = metadata
            .packages
            .iter()
            .find(|package| {
                package.manifest_path.as_std_path().canonicalize().unwrap()
                    == manifest_path.canonicalize().unwrap()
            })
            .unwrap();

        // Then go over all of its dependencies
        let dependencies = root
            .dependencies
            .iter()
            .map(|package| {
                // Get the id for the package matching the version requirement of the dep
                let id = &metadata
                    .packages
                    .iter()
                    .find(|&dep| dep.name == package.name && package.req.matches(&dep.version))
                    .expect("dependency does not exist")
                    .id;
                // Return the name chosen in `Cargo.toml` and the path to the corresponding artifact
                (
                    package.rename.clone().unwrap_or_else(|| package.name.clone()),
                    artifacts.remove(id).expect("package without artifact"),
                )
            })
            .collect();
        let import_paths = import_paths.into_iter().collect();
        return Ok(Dependencies { dependencies, import_paths });
    }

    bail!("no json found in cargo-metadata output")
}
<commit_msg>we don't need unstable options<commit_after>use color_eyre::eyre::{bail, Result};
use std::{
    collections::{HashMap, HashSet},
    path::{Path, PathBuf},
    process::Command,
};

use crate::Config;

#[derive(Default, Debug)]
pub struct Dependencies {
    /// All paths that must be imported with `-L dependency=`. This is for
    /// finding proc macros run on the host and dependencies for the target.
    pub import_paths: Vec<PathBuf>,
    /// The name as chosen in the `Cargo.toml` and its corresponding rmeta file.
    pub dependencies: Vec<(String, PathBuf)>,
}

/// Compiles dependencies and returns the crate names and corresponding rmeta files.
pub fn build_dependencies(config: &Config) -> Result<Dependencies> {
    let manifest_path = match &config.dependencies_crate_manifest_path {
        Some(path) => path,
        None => return Ok(Default::default()),
    };
    let (program, args, envs): (&Path, &[_], &[_]) = match &config.dependency_builder {
        Some(db) => (&db.program, &db.args, &db.envs),
        None => (Path::new("cargo"), &[], &[]),
    };
    let mut build = Command::new(program);
    build.args(args);
    // HACK: we're using `cargo run` (or `cargo miri run`), because the latter does not
    // support `cargo miri build` yet.
    build.arg("run");

    if let Some(target) = &config.target {
        build.arg(format!("--target={target}"));
    }

    // Reusable closure for setting up the environment both for artifact generation and `cargo_metadata`
    let setup_command = |cmd: &mut Command| {
        cmd.envs(envs.iter().map(|(k, v)| (k, v)));
        cmd.arg("--manifest-path").arg(manifest_path);
    };

    setup_command(&mut build);
    build
        .arg("--target-dir=target/test_dependencies")
        .arg("--message-format=json");

    let output = build.output()?;

    if !output.status.success() {
        let stdout = String::from_utf8(output.stdout)?;
        let stderr = String::from_utf8(output.stderr)?;
        bail!("failed to compile dependencies:\nstderr:\n{stderr}\n\nstdout:{stdout}");
    }

    // Collect all artifacts generated
    let output = output.stdout;
    let output = String::from_utf8(output)?;
    let mut import_paths: HashSet<PathBuf> = HashSet::new();
    let mut artifacts: HashMap<_, _> = output
        .lines()
        .filter_map(|line| {
            let message = serde_json::from_str::<cargo_metadata::Message>(line).ok()?;
            if let cargo_metadata::Message::CompilerArtifact(artifact) = message {
                for filename in &artifact.filenames {
                    import_paths.insert(filename.parent().unwrap().into());
                }
                let filename = artifact
                    .filenames
                    .into_iter()
                    .find(|filename| filename.extension() == Some("rmeta"))?;
                Some((artifact.package_id, filename.into_std_path_buf()))
            } else {
                None
            }
        })
        .collect();

    // Check which crates are mentioned in the crate itself
    let mut metadata = cargo_metadata::MetadataCommand::new().cargo_command();
    setup_command(&mut metadata);
    let output = metadata.output()?;

    if !output.status.success() {
        let stdout = String::from_utf8(output.stdout)?;
        let stderr = String::from_utf8(output.stderr)?;
        bail!("failed to run cargo-metadata:\nstderr:\n{stderr}\n\nstdout:{stdout}");
    }

    let output = output.stdout;
    let output = String::from_utf8(output)?;

    for line in output.lines() {
        if !line.starts_with('{') {
            continue;
        }
        let metadata: cargo_metadata::Metadata = serde_json::from_str(line)?;
        // Only take artifacts that are defined in the Cargo.toml

        // First, find the root artifact
        let root = metadata
            .packages
            .iter()
            .find(|package| {
                package.manifest_path.as_std_path().canonicalize().unwrap()
                    == manifest_path.canonicalize().unwrap()
            })
            .unwrap();

        // Then go over all of its dependencies
        let dependencies = root
            .dependencies
            .iter()
            .map(|package| {
                // Get the id for the package matching the version requirement of the dep
                let id = &metadata
                    .packages
                    .iter()
                    .find(|&dep| dep.name == package.name && package.req.matches(&dep.version))
                    .expect("dependency does not exist")
                    .id;
                // Return the name chosen in `Cargo.toml` and the path to the corresponding artifact
                (
                    package.rename.clone().unwrap_or_else(|| package.name.clone()),
                    artifacts.remove(id).expect("package without artifact"),
                )
            })
            .collect();
        let import_paths = import_paths.into_iter().collect();
        return Ok(Dependencies { dependencies, import_paths });
    }

    bail!("no json found in cargo-metadata output")
}
<|endoftext|>


<commit_before>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

#[macro_use]
mod macros;
mod direct_download;
mod download;
mod entry_point;
mod error;
pub(crate) mod install;
pub(crate) mod machine;
mod park;
mod poll;
mod prepare_local_install;
mod probe;
mod reboot;
mod validation;

#[cfg(test)]
mod tests;

use self::{
    direct_download::DirectDownload, download::Download, entry_point::EntryPoint, error::Error,
    install::Install, park::Park, poll::Poll, prepare_local_install::PrepareLocalInstall,
    probe::Probe, reboot::Reboot, validation::Validation,
};
use crate::{
    firmware::{self, Metadata, Transition},
    http_api,
    runtime_settings::RuntimeSettings,
    settings::Settings,
};
use async_trait::async_trait;
use derive_more::{Display, Error, From};
use slog_scope::{error, info, trace, warn};
use std::path::Path;

pub type Result<T> = std::result::Result<T, TransitionError>;

#[derive(Debug, Display, Error, From)]
pub enum TransitionError {
    #[display("some objects are not ready for use")]
    SomeObjectsAreNotReady,
    #[display("signature not found")]
    SignatureNotFound,
    #[display("channel communication as failed")]
    CommunicationFailed,

    Firmware(crate::firmware::Error),
    Installation(crate::object::Error),
    RuntimeSettings(crate::runtime_settings::Error),
    UpdatePackage(crate::update_package::Error),
    Client(cloud::Error),
    Uncompress(compress_tools::Error),
    SerdeJson(serde_json::error::Error),
    Io(std::io::Error),
    NonUtf8(std::string::FromUtf8Error),
    Process(easy_process::Error),
}

#[async_trait]
trait StateChangeImpl {
    async fn handle(
        self,
        context: &mut machine::Context,
    ) -> Result<(State, machine::StepTransition)>;

    fn name(&self) -> &'static str;

    /// All states downloading large files should overwrite this to return true.
    /// That way, a external request to abort download can be heeded.
    fn is_handling_download(&self) -> bool {
        false
    }

    /// A preemptive state is a state whose transition can be yield
    /// to handle a user's request. Any preemptive state should overwrite
    /// this method to return true.
    fn is_preemptive_state(&self) -> bool {
        false
    }
}

#[async_trait]
trait CallbackReporter: Sized + StateChangeImpl {
    async fn handle_with_callback(
        self,
        context: &mut machine::Context,
    ) -> Result<(State, machine::StepTransition)> {
        let transition =
            firmware::state_change_callback(&context.settings.firmware.metadata, self.name())?;

        match transition {
            Transition::Continue => Ok(self.handle(context).await?),
            Transition::Cancel => {
                info!(
                    "cancelling transition to '{}' due to state change callback request",
                    self.name()
                );
                Ok((State::EntryPoint(EntryPoint {}), machine::StepTransition::Immediate))
            }
        }
    }
}

#[async_trait]
trait ProgressReporter: CallbackReporter {
    fn package_uid(&self) -> String;
    fn report_enter_state_name(&self) -> &'static str;
    fn report_leave_state_name(&self) -> &'static str;

    async fn handle_and_report_progress(
        self,
        context: &mut machine::Context,
    ) -> Result<(State, machine::StepTransition)> {
        let server = context.server_address().to_owned();
        let firmware = &context.firmware.clone();
        let package_uid = &self.package_uid();
        let enter_state = self.report_enter_state_name();
        let leave_state = self.report_leave_state_name();
        let api = crate::CloudClient::new(&server);

        let report = |state, previous_state, error_message, current_log| {
            api.report(
                state,
                firmware.as_cloud_metadata(),
                package_uid,
                previous_state,
                error_message,
                current_log,
            )
        };

        if let Err(e) = report(enter_state, None, None, None).await {
            warn!("report failed: {}", e);
        }
        match self.handle(context).await {
            Ok((state, trans)) => {
                if let Err(e) = report(leave_state, None, None, None).await {
                    warn!("report failed: {}", e);
                };
                Ok((state, trans))
            }
            Err(e) => {
                if let Err(e) = report(
                    "error",
                    Some(enter_state),
                    Some(e.to_string()),
                    Some(crate::logger::get_memory_log()),
                )
                .await
                {
                    warn!("report failed: {}", e);
                }
                Err(e)
            }
        }
    }

    async fn handle_with_callback_and_report_progress(
        self,
        context: &mut machine::Context,
    ) -> Result<(State, machine::StepTransition)> {
        let transition =
            firmware::state_change_callback(&context.settings.firmware.metadata, self.name())?;

        match transition {
            Transition::Continue => Ok(self.handle_and_report_progress(context).await?),
            Transition::Cancel => {
                info!(
                    "cancelling transition to '{}' due to state change callback request",
                    self.name()
                );
                Ok((State::EntryPoint(EntryPoint {}), machine::StepTransition::Immediate))
            }
        }
    }
}

#[derive(Debug)]
enum State {
    Park(Park),
    EntryPoint(EntryPoint),
    Poll(Poll),
    Probe(Probe),
    Validation(Validation),
    Download(Download),
    Install(Install),
    Reboot(Reboot),
    DirectDownload(DirectDownload),
    PrepareLocalInstall(PrepareLocalInstall),
    Error(Error),
}

fn handle_startup_callbacks(
    settings: &Settings,
    runtime_settings: &mut RuntimeSettings,
) -> crate::Result<()> {
    if let Some(expected_set) = runtime_settings.update.upgrade_to_installation {
        info!("booting from a recent installation");
        if expected_set == firmware::installation_set::active()?.0 {
            match firmware::validate_callback(&settings.firmware.metadata)? {
                Transition::Cancel => {
                    warn!("validate callback has failed");
                    firmware::installation_set::swap_active()?;
                    warn!("swapped active installation set and running rollback");
                    firmware::rollback_callback(&settings.firmware.metadata)?;
                    runtime_settings.reset_installation_settings()?;
                    easy_process::run("reboot")?;
                }
                Transition::Continue => firmware::installation_set::validate()?,
            }
        }
        runtime_settings.reset_installation_settings()?;
    }
    Ok(())
}

#[async_trait]
impl StateChangeImpl for State {
    async fn handle(self, st: &mut machine::Context) -> Result<(State, machine::StepTransition)> {
        trace!("starting to handle: {}", self.name());
        self.move_to_next_state(st).await
    }

    fn name(&self) -> &'static str {
        self.inner_state().name()
    }

    fn is_handling_download(&self) -> bool {
        self.inner_state().is_handling_download()
    }

    fn is_preemptive_state(&self) -> bool {
        self.inner_state().is_preemptive_state()
    }
}

impl State {
    fn new() -> Self {
        State::EntryPoint(EntryPoint {})
    }

    async fn move_to_next_state(
        self,
        context: &mut machine::Context,
    ) -> Result<(Self, machine::StepTransition)> {
        match self {
            State::Park(s) => s.handle(context).await,
            State::EntryPoint(s) => s.handle(context).await,
            State::Poll(s) => s.handle(context).await,
            State::Probe(s) => s.handle_with_callback(context).await,
            State::Validation(s) => s.handle(context).await,
            State::DirectDownload(s) => s.handle(context).await,
            State::PrepareLocalInstall(s) => s.handle(context).await,
            State::Error(s) => s.handle_with_callback(context).await,
            State::Download(s) => s.handle_with_callback_and_report_progress(context).await,
            State::Install(s) => s.handle_with_callback_and_report_progress(context).await,
            State::Reboot(s) => s.handle_with_callback_and_report_progress(context).await,
        }
    }

    fn inner_state(&self) -> &dyn StateChangeImpl {
        match self {
            State::Error(s) => s,
            State::Park(s) => s,
            State::EntryPoint(s) => s,
            State::Poll(s) => s,
            State::Probe(s) => s,
            State::Validation(s) => s,
            State::DirectDownload(s) => s,
            State::PrepareLocalInstall(s) => s,
            State::Download(s) => s,
            State::Install(s) => s,
            State::Reboot(s) => s,
        }
    }
}

/// Runs the state machine up to completion handling all processing
/// states without extra manual work.
///
/// It supports following states, and transitions, as shown in the
/// below diagram:
///
/// ```text
///             .-------------------.
///             |                   v
/// Park <- EntryPoint -> Poll -> Probe -> Download -> Install -> Reboot
///             ^          ^        '          '          '
///             '          '        '          '          '
///             '          `--------'          '          '
///             `-------------------'          '          '
///             `------------------------------'          '
///             `-----------------------------------------'
/// ```
///
/// # Example
/// ```no_run
/// # extern crate updatehub;
/// # use std::path::PathBuf;
/// # async fn run() -> Result<(), updatehub::Error> {
/// use updatehub;
/// use std::path::PathBuf;
///
/// updatehub::logger::init(slog::Level::Info);
/// updatehub::run(&PathBuf::from("/etc/updatehub.conf")).await?;
/// # Ok(())
/// # }
/// ```
pub async fn run(settings: &Path) -> crate::Result<()> {
    crate::logger::start_memory_logging();
    let settings = Settings::load(settings)?;
    let listen_socket = settings.network.listen_socket.clone();
    let mut runtime_settings = RuntimeSettings::load(&settings.storage.runtime_settings)?;
    if !settings.storage.read_only {
        runtime_settings.enable_persistency();
    }
    let firmware = Metadata::from_path(&settings.firmware.metadata)?;

    if let Err(e) = handle_startup_callbacks(&settings, &mut runtime_settings) {
        error!("Failed to handle startup callbacks: {}", e);
    }

    let machine = machine::StateMachine::new(State::new(), settings, runtime_settings, firmware);
    let addr = machine.address();
    async_std::task::spawn(machine.start());

    http_api::Api::server(addr).listen(listen_socket).await?;

    info!("Server has gracefully stopped");
    Ok(())
}
<commit_msg>updatehub: Fix single thread execution<commit_after>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

#[macro_use]
mod macros;
mod direct_download;
mod download;
mod entry_point;
mod error;
pub(crate) mod install;
pub(crate) mod machine;
mod park;
mod poll;
mod prepare_local_install;
mod probe;
mod reboot;
mod validation;

#[cfg(test)]
mod tests;

use self::{
    direct_download::DirectDownload, download::Download, entry_point::EntryPoint, error::Error,
    install::Install, park::Park, poll::Poll, prepare_local_install::PrepareLocalInstall,
    probe::Probe, reboot::Reboot, validation::Validation,
};
use crate::{
    firmware::{self, Metadata, Transition},
    http_api,
    runtime_settings::RuntimeSettings,
    settings::Settings,
};
use async_trait::async_trait;
use derive_more::{Display, Error, From};
use slog_scope::{error, info, trace, warn};
use std::path::Path;

pub type Result<T> = std::result::Result<T, TransitionError>;

#[derive(Debug, Display, Error, From)]
pub enum TransitionError {
    #[display("some objects are not ready for use")]
    SomeObjectsAreNotReady,
    #[display("signature not found")]
    SignatureNotFound,
    #[display("channel communication as failed")]
    CommunicationFailed,

    Firmware(crate::firmware::Error),
    Installation(crate::object::Error),
    RuntimeSettings(crate::runtime_settings::Error),
    UpdatePackage(crate::update_package::Error),
    Client(cloud::Error),
    Uncompress(compress_tools::Error),
    SerdeJson(serde_json::error::Error),
    Io(std::io::Error),
    NonUtf8(std::string::FromUtf8Error),
    Process(easy_process::Error),
}

#[async_trait]
trait StateChangeImpl {
    async fn handle(
        self,
        context: &mut machine::Context,
    ) -> Result<(State, machine::StepTransition)>;

    fn name(&self) -> &'static str;

    /// All states downloading large files should overwrite this to return true.
    /// That way, a external request to abort download can be heeded.
    fn is_handling_download(&self) -> bool {
        false
    }

    /// A preemptive state is a state whose transition can be yield
    /// to handle a user's request. Any preemptive state should overwrite
    /// this method to return true.
    fn is_preemptive_state(&self) -> bool {
        false
    }
}

#[async_trait]
trait CallbackReporter: Sized + StateChangeImpl {
    async fn handle_with_callback(
        self,
        context: &mut machine::Context,
    ) -> Result<(State, machine::StepTransition)> {
        let transition =
            firmware::state_change_callback(&context.settings.firmware.metadata, self.name())?;

        match transition {
            Transition::Continue => Ok(self.handle(context).await?),
            Transition::Cancel => {
                info!(
                    "cancelling transition to '{}' due to state change callback request",
                    self.name()
                );
                Ok((State::EntryPoint(EntryPoint {}), machine::StepTransition::Immediate))
            }
        }
    }
}

#[async_trait]
trait ProgressReporter: CallbackReporter {
    fn package_uid(&self) -> String;
    fn report_enter_state_name(&self) -> &'static str;
    fn report_leave_state_name(&self) -> &'static str;

    async fn handle_and_report_progress(
        self,
        context: &mut machine::Context,
    ) -> Result<(State, machine::StepTransition)> {
        let server = context.server_address().to_owned();
        let firmware = &context.firmware.clone();
        let package_uid = &self.package_uid();
        let enter_state = self.report_enter_state_name();
        let leave_state = self.report_leave_state_name();
        let api = crate::CloudClient::new(&server);

        let report = |state, previous_state, error_message, current_log| {
            api.report(
                state,
                firmware.as_cloud_metadata(),
                package_uid,
                previous_state,
                error_message,
                current_log,
            )
        };

        if let Err(e) = report(enter_state, None, None, None).await {
            warn!("report failed: {}", e);
        }
        match self.handle(context).await {
            Ok((state, trans)) => {
                if let Err(e) = report(leave_state, None, None, None).await {
                    warn!("report failed: {}", e);
                };
                Ok((state, trans))
            }
            Err(e) => {
                if let Err(e) = report(
                    "error",
                    Some(enter_state),
                    Some(e.to_string()),
                    Some(crate::logger::get_memory_log()),
                )
                .await
                {
                    warn!("report failed: {}", e);
                }
                Err(e)
            }
        }
    }

    async fn handle_with_callback_and_report_progress(
        self,
        context: &mut machine::Context,
    ) -> Result<(State, machine::StepTransition)> {
        let transition =
            firmware::state_change_callback(&context.settings.firmware.metadata, self.name())?;

        match transition {
            Transition::Continue => Ok(self.handle_and_report_progress(context).await?),
            Transition::Cancel => {
                info!(
                    "cancelling transition to '{}' due to state change callback request",
                    self.name()
                );
                Ok((State::EntryPoint(EntryPoint {}), machine::StepTransition::Immediate))
            }
        }
    }
}

#[derive(Debug)]
enum State {
    Park(Park),
    EntryPoint(EntryPoint),
    Poll(Poll),
    Probe(Probe),
    Validation(Validation),
    Download(Download),
    Install(Install),
    Reboot(Reboot),
    DirectDownload(DirectDownload),
    PrepareLocalInstall(PrepareLocalInstall),
    Error(Error),
}

fn handle_startup_callbacks(
    settings: &Settings,
    runtime_settings: &mut RuntimeSettings,
) -> crate::Result<()> {
    if let Some(expected_set) = runtime_settings.update.upgrade_to_installation {
        info!("booting from a recent installation");
        if expected_set == firmware::installation_set::active()?.0 {
            match firmware::validate_callback(&settings.firmware.metadata)? {
                Transition::Cancel => {
                    warn!("validate callback has failed");
                    firmware::installation_set::swap_active()?;
                    warn!("swapped active installation set and running rollback");
                    firmware::rollback_callback(&settings.firmware.metadata)?;
                    runtime_settings.reset_installation_settings()?;
                    easy_process::run("reboot")?;
                }
                Transition::Continue => firmware::installation_set::validate()?,
            }
        }
        runtime_settings.reset_installation_settings()?;
    }
    Ok(())
}

#[async_trait]
impl StateChangeImpl for State {
    async fn handle(self, st: &mut machine::Context) -> Result<(State, machine::StepTransition)> {
        trace!("starting to handle: {}", self.name());
        self.move_to_next_state(st).await
    }

    fn name(&self) -> &'static str {
        self.inner_state().name()
    }

    fn is_handling_download(&self) -> bool {
        self.inner_state().is_handling_download()
    }

    fn is_preemptive_state(&self) -> bool {
        self.inner_state().is_preemptive_state()
    }
}

impl State {
    fn new() -> Self {
        State::EntryPoint(EntryPoint {})
    }

    async fn move_to_next_state(
        self,
        context: &mut machine::Context,
    ) -> Result<(Self, machine::StepTransition)> {
        match self {
            State::Park(s) => s.handle(context).await,
            State::EntryPoint(s) => s.handle(context).await,
            State::Poll(s) => s.handle(context).await,
            State::Probe(s) => s.handle_with_callback(context).await,
            State::Validation(s) => s.handle(context).await,
            State::DirectDownload(s) => s.handle(context).await,
            State::PrepareLocalInstall(s) => s.handle(context).await,
            State::Error(s) => s.handle_with_callback(context).await,
            State::Download(s) => s.handle_with_callback_and_report_progress(context).await,
            State::Install(s) => s.handle_with_callback_and_report_progress(context).await,
            State::Reboot(s) => s.handle_with_callback_and_report_progress(context).await,
        }
    }

    fn inner_state(&self) -> &dyn StateChangeImpl {
        match self {
            State::Error(s) => s,
            State::Park(s) => s,
            State::EntryPoint(s) => s,
            State::Poll(s) => s,
            State::Probe(s) => s,
            State::Validation(s) => s,
            State::DirectDownload(s) => s,
            State::PrepareLocalInstall(s) => s,
            State::Download(s) => s,
            State::Install(s) => s,
            State::Reboot(s) => s,
        }
    }
}

/// Runs the state machine up to completion handling all processing
/// states without extra manual work.
///
/// It supports following states, and transitions, as shown in the
/// below diagram:
///
/// ```text
///             .-------------------.
///             |                   v
/// Park <- EntryPoint -> Poll -> Probe -> Download -> Install -> Reboot
///             ^          ^        '          '          '
///             '          '        '          '          '
///             '          `--------'          '          '
///             `-------------------'          '          '
///             `------------------------------'          '
///             `-----------------------------------------'
/// ```
///
/// # Example
/// ```no_run
/// # extern crate updatehub;
/// # use std::path::PathBuf;
/// # async fn run() -> Result<(), updatehub::Error> {
/// use updatehub;
/// use std::path::PathBuf;
///
/// updatehub::logger::init(slog::Level::Info);
/// updatehub::run(&PathBuf::from("/etc/updatehub.conf")).await?;
/// # Ok(())
/// # }
/// ```
pub async fn run(settings: &Path) -> crate::Result<()> {
    crate::logger::start_memory_logging();
    let settings = Settings::load(settings)?;
    let listen_socket = settings.network.listen_socket.clone();
    let mut runtime_settings = RuntimeSettings::load(&settings.storage.runtime_settings)?;
    if !settings.storage.read_only {
        runtime_settings.enable_persistency();
    }
    let firmware = Metadata::from_path(&settings.firmware.metadata)?;

    if let Err(e) = handle_startup_callbacks(&settings, &mut runtime_settings) {
        error!("Failed to handle startup callbacks: {}", e);
    }

    let machine = machine::StateMachine::new(State::new(), settings, runtime_settings, firmware);
    let addr = machine.address();
    // Use a local spawn since spawned file write operations can get stucked on
    // single threaded envirements:
    // https://github.com/async-rs/async-std/issues/973
    async_std::task::spawn_local(machine.start());

    http_api::Api::server(addr).listen(listen_socket).await?;

    info!("Server has gracefully stopped");
    Ok(())
}
<|endoftext|>


<commit_before>pub mod prometheus;

use std::io::prelude::*;
use chrono::*;
use result;
use timing_analyzer;

pub trait Renderer {
    fn render(&mut self, result: result::RequestLogAnalyzerResult) -> ();
}

pub struct TerminalRenderer {}

impl TerminalRenderer {
    pub fn new() -> TerminalRenderer {
        TerminalRenderer {}
    }
}

impl Renderer for TerminalRenderer {
    fn render(&mut self, result: result::RequestLogAnalyzerResult) -> () {
        println!("count:\t{}", result.count);
        match result.timing {
            Some(timing) => {
                println!("time.avg:\t{}", timing.avg);
                println!("time.min:\t{}", timing.min);
                println!("time.median:\t{}", timing.median);
                println!("time.90percent:\t{}", timing.percentile90);
                println!("time.max:\t{}", timing.max);
            }
            None => warn!("No matching log lines in file."),
        }
    }
}

pub struct GraphiteRenderer<'a> {
    time: DateTime<UTC>,
    prefix: Option<String>,
    stream: &'a mut Write,
}

impl<'a> GraphiteRenderer<'a> {
    pub fn new(time: DateTime<UTC>,
               prefix: Option<String>,
               stream: &'a mut Write)
               -> GraphiteRenderer<'a> {
        GraphiteRenderer {
            time: time,
            prefix: prefix,
            stream: stream,
        }
    }
}

impl<'a> Renderer for GraphiteRenderer<'a> {
    fn render(&mut self, result: result::RequestLogAnalyzerResult) -> () {
        let prefix_text: String;
        let prefix_separator: &str;

        match self.prefix {
            Some(ref p) => {
                prefix_text = p.clone();
                prefix_separator = ".";
            }
            None => {
                prefix_text = String::from("");
                prefix_separator = "";
            }
        };

        let mut write = |text: String| {
            let _ = self.stream.write(format!("{}{}{} {}\n",
                                              prefix_text,
                                              prefix_separator,
                                              text,
                                              self.time.timestamp())
                .as_bytes());
        };

        write(format!("requests.count {}", result.count));

        match result.timing {
            Some(timing) => {
                write(format!("requests.time.max {}", timing.max));
                write(format!("requests.time.min {}", timing.min));
                write(format!("requests.time.avg {}", timing.avg));
                write(format!("requests.time.median {}", timing.median));
                write(format!("requests.time.90percent {}", timing.percentile90));
            }
            None => warn!("No matching log lines in file."),
        }
    }
}

#[cfg(test)]
mod tests {
    use std::io;
    use std::io::prelude::*;
    use std::str;
    use chrono::*;
    use timing_analyzer;
    use super::*;

    struct MockTcpStream {
        write_calls: Vec<String>,
    }

    impl Write for MockTcpStream {
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
            self.write_calls.push(str::from_utf8(buf).unwrap().to_string());
            Ok(1)
        }

        fn flush(&mut self) -> io::Result<()> {
            Ok(())
        }
    }

    fn get_result_fixture() -> result::RequestLogAnalyzerResult {
        result::RequestLogAnalyzerResult {
            count: 3,
            timing: Some(timing_analyzer::RequestLogAnalyzerResult {
                max: 100,
                min: 1,
                avg: 37,
                median: 10,
                percentile90: 100,
            }),
            error: None,
        }
    }

    fn get_time_fixture() -> DateTime<UTC> {
        let time: DateTime<UTC> = DateTime::parse_from_str("22/Sep/2016:22:41:59 +0200",
                                                           "%d/%b/%Y:%H:%M:%S %z")
            .unwrap()
            .with_timezone(&UTC);

        time
    }

    #[test]
    fn test_terminal_renderer() {
        let mut renderer = TerminalRenderer::new();

        let result = get_result_fixture();

        renderer.render(result);
    }

    #[test]
    fn test_render_graphite() {
        let mut mock_tcp_stream = MockTcpStream { write_calls: vec![] };

        {
            let mut renderer =
                GraphiteRenderer::new(get_time_fixture(), None, &mut mock_tcp_stream);
            renderer.render(get_result_fixture());
        }

        assert_eq!(&mock_tcp_stream.write_calls[0],
                   "requests.count 3 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[1],
                   "requests.time.max 100 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[2],
                   "requests.time.min 1 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[3],
                   "requests.time.avg 37 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[4],
                   "requests.time.median 10 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[5],
                   "requests.time.90percent 100 1474576919\n");
    }

    #[test]
    fn test_render_graphite_with_prefix() {
        let mut mock_tcp_stream = MockTcpStream { write_calls: vec![] };

        {
            let mut renderer = GraphiteRenderer::new(get_time_fixture(),
                                                     Some(String::from("my_prefix")),
                                                     &mut mock_tcp_stream);
            renderer.render(get_result_fixture());
        }

        assert_eq!(&mock_tcp_stream.write_calls[0],
                   "my_prefix.requests.count 3 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[1],
                   "my_prefix.requests.time.max 100 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[2],
                   "my_prefix.requests.time.min 1 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[3],
                   "my_prefix.requests.time.avg 37 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[4],
                   "my_prefix.requests.time.median 10 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[5],
                   "my_prefix.requests.time.90percent 100 1474576919\n");
    }
}
<commit_msg>Render error rates for terminal<commit_after>pub mod prometheus;

use std::io::prelude::*;
use chrono::*;
use result;
use timing_analyzer;

pub trait Renderer {
    fn render(&mut self, result: result::RequestLogAnalyzerResult) -> ();
}

pub struct TerminalRenderer {}

impl TerminalRenderer {
    pub fn new() -> TerminalRenderer {
        TerminalRenderer {}
    }
}

impl Renderer for TerminalRenderer {
    fn render(&mut self, result: result::RequestLogAnalyzerResult) -> () {
        println!("count:\t{}", result.count);
        match result.timing {
            Some(timing) => {
                println!("time.avg:\t{}", timing.avg);
                println!("time.min:\t{}", timing.min);
                println!("time.median:\t{}", timing.median);
                println!("time.90percent:\t{}", timing.percentile90);
                println!("time.max:\t{}", timing.max);
            }
            None => warn!("No matching log lines for timing results."),
        }

        match result.error {
            Some(error) => {
                println!("error.client_error_4xx_rate:\t{}", error.client_error_4xx);
                println!("error.server_error_5xx_rate:\t{}", error.server_error_5xx);
            }
            None => warn!("No matching log lines for error rate results."),
        }
    }
}

pub struct GraphiteRenderer<'a> {
    time: DateTime<UTC>,
    prefix: Option<String>,
    stream: &'a mut Write,
}

impl<'a> GraphiteRenderer<'a> {
    pub fn new(time: DateTime<UTC>,
               prefix: Option<String>,
               stream: &'a mut Write)
               -> GraphiteRenderer<'a> {
        GraphiteRenderer {
            time: time,
            prefix: prefix,
            stream: stream,
        }
    }
}

impl<'a> Renderer for GraphiteRenderer<'a> {
    fn render(&mut self, result: result::RequestLogAnalyzerResult) -> () {
        let prefix_text: String;
        let prefix_separator: &str;

        match self.prefix {
            Some(ref p) => {
                prefix_text = p.clone();
                prefix_separator = ".";
            }
            None => {
                prefix_text = String::from("");
                prefix_separator = "";
            }
        };

        let mut write = |text: String| {
            let _ = self.stream.write(format!("{}{}{} {}\n",
                                              prefix_text,
                                              prefix_separator,
                                              text,
                                              self.time.timestamp())
                .as_bytes());
        };

        write(format!("requests.count {}", result.count));

        match result.timing {
            Some(timing) => {
                write(format!("requests.time.max {}", timing.max));
                write(format!("requests.time.min {}", timing.min));
                write(format!("requests.time.avg {}", timing.avg));
                write(format!("requests.time.median {}", timing.median));
                write(format!("requests.time.90percent {}", timing.percentile90));
            }
            None => warn!("No matching log lines in file."),
        }
    }
}

#[cfg(test)]
mod tests {
    use std::io;
    use std::io::prelude::*;
    use std::str;
    use chrono::*;
    use timing_analyzer;
    use error_analyzer;
    use super::*;

    struct MockTcpStream {
        write_calls: Vec<String>,
    }

    impl Write for MockTcpStream {
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
            self.write_calls.push(str::from_utf8(buf).unwrap().to_string());
            Ok(1)
        }

        fn flush(&mut self) -> io::Result<()> {
            Ok(())
        }
    }

    fn get_result_fixture() -> result::RequestLogAnalyzerResult {
        result::RequestLogAnalyzerResult {
            count: 3,
            timing: Some(timing_analyzer::RequestLogAnalyzerResult {
                max: 100,
                min: 1,
                avg: 37,
                median: 10,
                percentile90: 100,
            }),
            error: Some(error_analyzer::ErrorRatesResult {
                client_error_4xx: 0.1,
                server_error_5xx: 0.2,
            }),
        }
    }

    fn get_time_fixture() -> DateTime<UTC> {
        let time: DateTime<UTC> = DateTime::parse_from_str("22/Sep/2016:22:41:59 +0200",
                                                           "%d/%b/%Y:%H:%M:%S %z")
            .unwrap()
            .with_timezone(&UTC);

        time
    }

    #[test]
    fn test_terminal_renderer() {
        let mut renderer = TerminalRenderer::new();

        let result = get_result_fixture();

        renderer.render(result);
    }

    #[test]
    fn test_render_graphite() {
        let mut mock_tcp_stream = MockTcpStream { write_calls: vec![] };

        {
            let mut renderer =
                GraphiteRenderer::new(get_time_fixture(), None, &mut mock_tcp_stream);
            renderer.render(get_result_fixture());
        }

        assert_eq!(&mock_tcp_stream.write_calls[0],
                   "requests.count 3 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[1],
                   "requests.time.max 100 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[2],
                   "requests.time.min 1 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[3],
                   "requests.time.avg 37 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[4],
                   "requests.time.median 10 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[5],
                   "requests.time.90percent 100 1474576919\n");
    }

    #[test]
    fn test_render_graphite_with_prefix() {
        let mut mock_tcp_stream = MockTcpStream { write_calls: vec![] };

        {
            let mut renderer = GraphiteRenderer::new(get_time_fixture(),
                                                     Some(String::from("my_prefix")),
                                                     &mut mock_tcp_stream);
            renderer.render(get_result_fixture());
        }

        assert_eq!(&mock_tcp_stream.write_calls[0],
                   "my_prefix.requests.count 3 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[1],
                   "my_prefix.requests.time.max 100 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[2],
                   "my_prefix.requests.time.min 1 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[3],
                   "my_prefix.requests.time.avg 37 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[4],
                   "my_prefix.requests.time.median 10 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[5],
                   "my_prefix.requests.time.90percent 100 1474576919\n");
    }
}
<|endoftext|>


<commit_before>use tokio::sync::mpsc;

// SQlite worker thread
use futures::executor::block_on_stream;

use rusqlite as rs;
use tokio::sync::oneshot;
use std::collections::HashSet;

use crate::schema_sample;

// TODO: should be able to replace all of these stuff maybe by some trait or some other tricks
// so that we can have a extensible query where i define the query/process/result and then send it
// to the query engine and get the result back in a good format
#[derive(Debug)]
pub enum RunQuery {
    TopNDenormalized {
        karma_col: KarmaCol,
        karma_typ: KarmaTyp,
        limit: u32,
        ord: OrdQuery
    },
    RankingDenormalized {
        karma_col: KarmaCol,
        karma_typ: KarmaTyp,
        user: String, // TODO: make it support a Set of user
    },
    Count(KarmaCol),
    Partial {
        karma_col: KarmaCol,
        users: HashSet<String>,
    }
}

#[derive(Debug)]
pub enum OrdQuery { Asc, Desc }

#[derive(Debug)]
pub enum KarmaCol { Given, Recieved }

#[derive(Debug)]
pub enum KarmaTyp { Total, Side }

#[derive(Debug)]
pub enum ResQuery {
    Insert(usize),
    Query(String),
}

pub fn process_queries(
    sql_rx: mpsc::Receiver<(RunQuery, Option<oneshot::Sender<ResQuery>>)>
) -> rs::Result<()> {
    let mut block_sql_rx = block_on_stream(sql_rx);

    //let filename = "db.sqlite";
    //let conn = rs::Connection::open_with_flags(
    //    Path::new(filename),
    //    rs::OpenFlags::SQLITE_OPEN_READ_WRITE
    //).expect(&format!("Connection error: {}", filename));
    let conn = rs::Connection::open_in_memory().expect("Failure");

    // Test data/build tables
    schema_sample::setup_table(&conn)?;
    schema_sample::setup_data(&conn)?;

    // Listen for inbound query
    while let Some((query, res_tx)) = block_sql_rx.next() {
        match query {
            RunQuery::TopNDenormalized{karma_col, karma_typ, limit, ord} => {
                println!(
                    "Sql Worker - TopNDenormalized - karma_col: {:?}, karma_typ: {:?}, limit: {:?}, ord: {:?}",
                    karma_col, karma_typ, limit, ord
                );

                let table = match karma_col {
                    KarmaCol::Given    => "karma_given_count",
                    KarmaCol::Recieved => "karma_received_count",
                };

                let t_col = match karma_typ {
                    KarmaTyp::Total => "up - down",
                    KarmaTyp::Side  => "side",
                };

                let q_ord = match ord {
                    OrdQuery::Asc  => "ASC",
                    OrdQuery::Desc => "DESC",
                };

                let mut stmt = conn.prepare(&format!(
                        "SELECT name, {t_col} as total FROM {table} ORDER BY total {q_ord} LIMIT {limit}",
                        t_col=t_col,
                        table=table,
                        q_ord=q_ord,
                        limit=limit
                )).unwrap();
                let mut rows = stmt.query(rs::NO_PARAMS).unwrap();

                if let Ok(Some(row)) = rows.next() {
                    let name: String = row.get(0).unwrap();
                    let count: i32 = row.get(1).unwrap();

                    println!("Sql Worker - TopNDenormalized - Name: {:?}, Count: {:?}", name, count);
                } else {
                    println!("Sql Worker - TopNDenormalized - Error");
                }
            },
            RunQuery::RankingDenormalized{karma_col, karma_typ, user} => {
                println!("Sql Worker - RankingDenormalized - karma_col: {:?}, karma_typ: {:?}, user: {:?}", karma_col, karma_typ, user);

                let table = match karma_col {
                    KarmaCol::Given    => "karma_given_count",
                    KarmaCol::Recieved => "karma_received_count",
                };

                let t_col1 = match karma_typ {
                    KarmaTyp::Total => "up - down",
                    KarmaTyp::Side  => "side",
                };

                let t_col2 = match karma_typ {
                    KarmaTyp::Total => "kcol2.up - kcol2.down",
                    KarmaTyp::Side  => "kcol2.side",
                };

                let mut stmt = conn.prepare(&format!(
                    "SELECT CASE WHEN (
                        EXISTS (SELECT TRUE FROM {table} WHERE name = ?1)
                    ) THEN (
                        SELECT (COUNT(name) + 1) FROM {table} WHERE (
                            {t_col1}
                        ) > (
                            SELECT ({t_col2}) FROM {table} AS kcol2 WHERE kcol2.name = ?2
                        )
                    ) ELSE NULL END",
                    table=table, t_col1=t_col1, t_col2=t_col2
                )).unwrap();
                let mut rows = stmt.query(rs::params![user, user]).unwrap();

                if let Ok(Some(row)) = rows.next() {
                    let count: i32 = row.get(0).unwrap();

                    println!("Sql Worker - RankingDenormalized - Count: {:?}", count);
                } else {
                    println!("Sql Worker - RankingDenormalized - Error");
                }
            },
            RunQuery::Count(karma_col) => {
                println!("Sql Worker - Count - karma_col: {:?}", karma_col);

                let table = match karma_col {
                    KarmaCol::Given => "karma_given_count",
                    KarmaCol::Recieved => "karma_received_count",
                };

                let mut stmt = conn.prepare(&format!(
                    "SELECT COUNT(name) FROM {table}",
                    table=table
                )).unwrap();
                let mut rows = stmt.query(rs::NO_PARAMS).unwrap();

                if let Ok(Some(row)) = rows.next() {
                    let count: u32 = row.get(0).unwrap();

                    println!("Sql Worker - Count - Tally: {:?}", count);
                } else {
                    println!("Sql Worker - Count - Error");
                }
            },
            RunQuery::Partial{karma_col, users} => {
                println!("Sql Worker - Partial - karma_col: {:?}, users: {:?}", karma_col, users);

                let table = match karma_col {
                    KarmaCol::Given    => "karma_given_count",
                    KarmaCol::Recieved => "karma_received_count",
                };

                // Hack to insert enough parameterizers into the query
                let p_user = {
                    let mut p_user: String = "?1".to_string();
                    for i in 2..(users.len() + 1) {
                        p_user.push_str(&format!(", ?{}", i));
                    }
                    p_user
                };

                // Params
                let param: Vec<&String> = users.iter().collect();

                let mut stmt = conn.prepare(&format!(
                    "SELECT name, up, down, side FROM {table} WHERE name in ({p_user}) ORDER BY name DESC",
                    table=table, p_user=p_user
                )).unwrap();
                let mut rows = stmt.query(&param).unwrap();

                // A bit more additional work than usual
                let mut ret: Vec<(String, i32, i32, i32)> = vec![];
                let mut has: HashSet<String> = HashSet::new();

                while let Ok(Some(row)) = rows.next() {
                    let name: String = row.get(0).unwrap();
                    let up: i32 = row.get(1).unwrap();
                    let down: i32 = row.get(2).unwrap();
                    let side: i32 = row.get(3).unwrap();

                    ret.push((name.clone(), up, down, side));
                    has.insert(name);
                }

                // Evaulate if there's missing ones and add if so
                for n in users.difference(&has) {
                    ret.push((n.to_string(), 0, 0, 0));
                }

                // Print out for records
                println!("Sql Worker - Partial - Dump: {:?}", ret);
            },
        }
    }

    Ok(())
}
<commit_msg>database replies now<commit_after>use tokio::sync::mpsc;

// SQlite worker thread
use futures::executor::block_on_stream;

use rusqlite as rs;
use tokio::sync::oneshot;
use std::collections::HashSet;

use crate::schema_sample;

// TODO: should be able to replace all of these stuff maybe by some trait or some other tricks
// so that we can have a extensible query where i define the query/process/result and then send it
// to the query engine and get the result back in a good format
#[derive(Debug)]
pub enum RunQuery {
    TopNDenormalized {
        karma_col: KarmaCol,
        karma_typ: KarmaTyp,
        limit: u32,
        ord: OrdQuery
    },
    RankingDenormalized {
        karma_col: KarmaCol,
        karma_typ: KarmaTyp,
        user: String, // TODO: make it support a Set of user
    },
    Count(KarmaCol),
    Partial {
        karma_col: KarmaCol,
        users: HashSet<String>,
    }
}

#[derive(Debug)]
pub enum OrdQuery { Asc, Desc }

#[derive(Debug)]
pub enum KarmaCol { Given, Recieved }

#[derive(Debug)]
pub enum KarmaTyp { Total, Side }

#[derive(Debug)]
pub enum ResQuery {
    // RankingDenormalized
    // Count
    Count(u32),
    // TopNDenormalized
    TopN(Vec<(String, i32)>),
    // Partial
    Partial(Vec<(String, i32, i32, i32)>),
}

fn maybe_send(
    res_tx: Option<oneshot::Sender<ResQuery>>,
    res: ResQuery,
) -> Result<(), String> {
    match res_tx {
        Some(tx) => tx.send(res).map_err(|_| "Cant send ResQuery".to_string()),
        None     => Ok(()),
    }
}

pub fn process_queries(
    sql_rx: mpsc::Receiver<(RunQuery, Option<oneshot::Sender<ResQuery>>)>
) -> Result<(), Box<dyn std::error::Error>> {
    let mut block_sql_rx = block_on_stream(sql_rx);

    //let filename = "db.sqlite";
    //let conn = rs::Connection::open_with_flags(
    //    Path::new(filename),
    //    rs::OpenFlags::SQLITE_OPEN_READ_WRITE
    //).expect(&format!("Connection error: {}", filename));
    let conn = rs::Connection::open_in_memory().expect("Failure");

    // Test data/build tables
    schema_sample::setup_table(&conn)?;
    schema_sample::setup_data(&conn)?;

    // Listen for inbound query
    while let Some((query, res_tx)) = block_sql_rx.next() {
        match query {
            RunQuery::TopNDenormalized{karma_col, karma_typ, limit, ord} => {
                println!(
                    "Sql Worker - TopNDenormalized - karma_col: {:?}, karma_typ: {:?}, limit: {:?}, ord: {:?}",
                    karma_col, karma_typ, limit, ord
                );

                let table = match karma_col {
                    KarmaCol::Given    => "karma_given_count",
                    KarmaCol::Recieved => "karma_received_count",
                };

                let t_col = match karma_typ {
                    KarmaTyp::Total => "up - down",
                    KarmaTyp::Side  => "side",
                };

                let q_ord = match ord {
                    OrdQuery::Asc  => "ASC",
                    OrdQuery::Desc => "DESC",
                };

                let mut stmt = conn.prepare(&format!(
                        "SELECT name, {t_col} as total FROM {table} ORDER BY total {q_ord} LIMIT {limit}",
                        t_col=t_col,
                        table=table,
                        q_ord=q_ord,
                        limit=limit
                )).unwrap();
                let mut rows = stmt.query(rs::NO_PARAMS).unwrap();

                let mut ret: Vec<(String, i32)> = vec![];

                while let Ok(Some(row)) = rows.next() {
                    let name: String = row.get(0).unwrap();
                    let count: i32 = row.get(1).unwrap();

                    ret.push((name.clone(), count));
                }

                // Print out for records
                println!("Sql Worker - TopNDenormalized - Dump: {:?}", &ret);
                maybe_send(res_tx, ResQuery::TopN(ret))?;
            },
            RunQuery::RankingDenormalized{karma_col, karma_typ, user} => {
                println!("Sql Worker - RankingDenormalized - karma_col: {:?}, karma_typ: {:?}, user: {:?}", karma_col, karma_typ, user);

                let table = match karma_col {
                    KarmaCol::Given    => "karma_given_count",
                    KarmaCol::Recieved => "karma_received_count",
                };

                let t_col1 = match karma_typ {
                    KarmaTyp::Total => "up - down",
                    KarmaTyp::Side  => "side",
                };

                let t_col2 = match karma_typ {
                    KarmaTyp::Total => "kcol2.up - kcol2.down",
                    KarmaTyp::Side  => "kcol2.side",
                };

                let mut stmt = conn.prepare(&format!(
                    "SELECT CASE WHEN (
                        EXISTS (SELECT TRUE FROM {table} WHERE name = ?1)
                    ) THEN (
                        SELECT (COUNT(name) + 1) FROM {table} WHERE (
                            {t_col1}
                        ) > (
                            SELECT ({t_col2}) FROM {table} AS kcol2 WHERE kcol2.name = ?2
                        )
                    ) ELSE NULL END",
                    table=table, t_col1=t_col1, t_col2=t_col2
                )).unwrap();
                let mut rows = stmt.query(rs::params![user, user]).unwrap();

                if let Ok(Some(row)) = rows.next() {
                    let count: u32 = row.get(0).unwrap();

                    maybe_send(res_tx, ResQuery::Count(count))?;
                    println!("Sql Worker - RankingDenormalized - Count: {:?}", count);
                } else {
                    println!("Sql Worker - RankingDenormalized - Error");
                }
            },
            RunQuery::Count(karma_col) => {
                println!("Sql Worker - Count - karma_col: {:?}", karma_col);

                let table = match karma_col {
                    KarmaCol::Given => "karma_given_count",
                    KarmaCol::Recieved => "karma_received_count",
                };

                let mut stmt = conn.prepare(&format!(
                    "SELECT COUNT(name) FROM {table}",
                    table=table
                )).unwrap();
                let mut rows = stmt.query(rs::NO_PARAMS).unwrap();

                if let Ok(Some(row)) = rows.next() {
                    let count: u32 = row.get(0).unwrap();

                    maybe_send(res_tx, ResQuery::Count(count))?;
                    println!("Sql Worker - Count - Tally: {:?}", count);
                } else {
                    println!("Sql Worker - Count - Error");
                }
            },
            RunQuery::Partial{karma_col, users} => {
                println!("Sql Worker - Partial - karma_col: {:?}, users: {:?}", karma_col, users);

                let table = match karma_col {
                    KarmaCol::Given    => "karma_given_count",
                    KarmaCol::Recieved => "karma_received_count",
                };

                // Hack to insert enough parameterizers into the query
                let p_user = {
                    let mut p_user: String = "?1".to_string();
                    for i in 2..(users.len() + 1) {
                        p_user.push_str(&format!(", ?{}", i));
                    }
                    p_user
                };

                // Params
                let param: Vec<&String> = users.iter().collect();

                let mut stmt = conn.prepare(&format!(
                    "SELECT name, up, down, side FROM {table} WHERE name in ({p_user}) ORDER BY name DESC",
                    table=table, p_user=p_user
                )).unwrap();
                let mut rows = stmt.query(&param).unwrap();

                // A bit more additional work than usual
                let mut ret: Vec<(String, i32, i32, i32)> = vec![];
                let mut has: HashSet<String> = HashSet::new();

                while let Ok(Some(row)) = rows.next() {
                    let name: String = row.get(0).unwrap();
                    let up: i32 = row.get(1).unwrap();
                    let down: i32 = row.get(2).unwrap();
                    let side: i32 = row.get(3).unwrap();

                    ret.push((name.clone(), up, down, side));
                    has.insert(name);
                }

                // Evaulate if there's missing ones and add if so
                for n in users.difference(&has) {
                    ret.push((n.to_string(), 0, 0, 0));
                }

                // Print out for records
                println!("Sql Worker - Partial - Dump: {:?}", &ret);
                maybe_send(res_tx, ResQuery::Partial(ret))?;
            },
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before><commit_msg>Implement BigChuncks<commit_after><|endoftext|>


<commit_before>//! This module contains all the operations related to the Ardite Schema
//! Definition, a format which is used to imperitevly define the data interface
//! used with Ardite services.

pub mod definition;
pub mod schema;
pub mod serde;

pub use schema::definition::Definition;
pub use schema::schema::Schema;
pub use schema::serde::from_file;
<commit_msg>refactor: don't make pub what doesn't have to be<commit_after>//! This module contains all the operations related to the Ardite Schema
//! Definition, a format which is used to imperitevly define the data interface
//! used with Ardite services.

mod definition;
mod schema;
mod serde;

pub use schema::definition::Definition;
pub use schema::schema::Schema;
pub use schema::serde::from_file;
<|endoftext|>


<commit_before>//! Compress and decompress Zstd streams.
//!
//! This module provide a `Read`/`Write` interface
//! to zstd streams of arbitrary length.
//!
//! They are compatible with the `zstd` command-line tool.

use std::io;

mod decoder;
mod encoder;

pub use self::decoder::Decoder;
pub use self::encoder::{AutoFinishEncoder, Encoder};

/// Decompress from the given source as if using a `Decoder`.
///
/// The input data must be in the zstd frame format.
pub fn decode_all<R: io::Read>(source: R) -> io::Result<Vec<u8>> {
    let mut result = Vec::new();
    copy_decode(source, &mut result)?;
    Ok(result)
}

/// Decompress from the given source as if using a `Decoder`.
///
/// Decompressed data will be appended to `destination`.
pub fn copy_decode<R, W>(source: R, mut destination: W) -> io::Result<()>
where
    R: io::Read,
    W: io::Write,
{
    let mut decoder = Decoder::new(source)?;
    io::copy(&mut decoder, &mut destination)?;
    Ok(())
}

/// Compress all data from the given source as if using an `Encoder`.
///
/// Result will be in the zstd frame format.
///
/// A level of `0` uses zstd's default (currently `3`).
pub fn encode_all<R: io::Read>(source: R, level: i32) -> io::Result<Vec<u8>> {
    let mut result = Vec::<u8>::new();
    copy_encode(source, &mut result, level)?;
    Ok(result)
}

/// Compress all data from the given source as if using an `Encoder`.
///
/// Compressed data will be appended to `destination`.
///
/// A level of `0` uses zstd's default (currently `3`).
pub fn copy_encode<R, W>(
    mut source: R,
    destination: W,
    level: i32,
) -> io::Result<()>
where
    R: io::Read,
    W: io::Write,
{
    let mut encoder = Encoder::new(destination, level)?;
    io::copy(&mut source, &mut encoder)?;
    encoder.finish()?;
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::{copy_encode, decode_all, encode_all};
    use super::{Decoder, Encoder};

    use partial_io::{PartialOp, PartialWrite};

    use std::io;
    use std::iter;

    #[test]
    fn test_end_of_frame() {
        use std::io::{Read, Write};

        let mut enc = Encoder::new(Vec::new(), 1).unwrap();
        enc.write_all(b"foo").unwrap();
        let mut compressed = enc.finish().unwrap();

        // Add footer/whatever to underlying storage.
        compressed.push(0);

        // Drain zstd stream until end-of-frame.
        let mut dec = Decoder::new(&compressed[..]).unwrap().single_frame();
        let mut buf = Vec::new();
        dec.read_to_end(&mut buf).unwrap();
        assert_eq!(&buf, b"foo");
    }

    #[test]
    fn test_concatenated_frames() {
        let mut buffer = Vec::new();
        copy_encode(&b"foo"[..], &mut buffer, 1).unwrap();
        copy_encode(&b"bar"[..], &mut buffer, 2).unwrap();
        copy_encode(&b"baz"[..], &mut buffer, 3).unwrap();

        assert_eq!(&decode_all(&buffer[..]).unwrap(), b"foobarbaz");
    }

    #[test]
    fn test_flush() {
        use std::io::Write;

        let buf = Vec::new();
        let mut z = Encoder::new(buf, 19).unwrap();

        z.write_all(b"hello").unwrap();

        z.flush().unwrap(); // Might corrupt stream
        let buf = z.finish().unwrap();

        let s = decode_all(&buf[..]).unwrap();
        let s = ::std::str::from_utf8(&s).unwrap();
        assert_eq!(s, "hello");
    }

    #[test]
    fn test_try_finish() {
        use std::io::Write;
        let mut z = setup_try_finish();

        z.get_mut().set_ops(iter::repeat(PartialOp::Unlimited));

        // flush() should continue to work even though write() doesn't.
        z.flush().unwrap();

        let buf = match z.try_finish() {
            Ok(buf) => buf.into_inner(),
            Err((_z, e)) => panic!("try_finish failed with {:?}", e),
        };

        // Make sure the multiple try_finish calls didn't screw up the internal
        // buffer and continued to produce valid compressed data.
        assert_eq!(&decode_all(&buf[..]).unwrap(), b"hello");
    }

    #[test]
    #[should_panic]
    fn test_write_after_try_finish() {
        use std::io::Write;
        let mut z = setup_try_finish();
        z.write_all(b"hello world").unwrap();
    }

    fn setup_try_finish() -> Encoder<PartialWrite<Vec<u8>>> {
        use std::io::Write;

        let buf =
            PartialWrite::new(Vec::new(), iter::repeat(PartialOp::Unlimited));
        let mut z = Encoder::new(buf, 19).unwrap();

        z.write_all(b"hello").unwrap();

        z.get_mut()
            .set_ops(iter::repeat(PartialOp::Err(io::ErrorKind::WouldBlock)));

        let (z, err) = z.try_finish().unwrap_err();
        assert_eq!(err.kind(), io::ErrorKind::WouldBlock);

        z
    }

    #[test]
    fn test_failing_write() {
        use std::io::Write;

        let buf = PartialWrite::new(
            Vec::new(),
            iter::repeat(PartialOp::Err(io::ErrorKind::WouldBlock)),
        );
        let mut z = Encoder::new(buf, 1).unwrap();

        // Fill in enough data to make sure the buffer gets written out.
        let input = "b".repeat(128 * 1024).into_bytes();
        // This should work even though the inner writer rejects writes.
        assert_eq!(z.write(&input).unwrap(), 128 * 1024);

        // The next write would fail (the buffer still has some data in it).
        assert_eq!(
            z.write(b"abc").unwrap_err().kind(),
            io::ErrorKind::WouldBlock
        );

        z.get_mut().set_ops(iter::repeat(PartialOp::Unlimited));

        // This shouldn't have led to any corruption.
        let buf = z.finish().unwrap().into_inner();
        assert_eq!(&decode_all(&buf[..]).unwrap(), &input);
    }

    #[test]
    fn test_invalid_frame() {
        use std::io::Read;

        // I really hope this data is invalid...
        let data = &[1u8, 2u8, 3u8, 4u8, 5u8];
        let mut dec = Decoder::new(&data[..]).unwrap();
        assert_eq!(
            dec.read_to_end(&mut Vec::new()).err().map(|e| e.kind()),
            Some(io::ErrorKind::Other)
        );
    }

    #[test]
    fn test_incomplete_frame() {
        use std::io::{Read, Write};

        let mut enc = Encoder::new(Vec::new(), 1).unwrap();
        enc.write_all(b"This is a regular string").unwrap();
        let mut compressed = enc.finish().unwrap();

        let half_size = compressed.len() - 2;
        compressed.truncate(half_size);

        let mut dec = Decoder::new(&compressed[..]).unwrap();
        assert_eq!(
            dec.read_to_end(&mut Vec::new()).err().map(|e| e.kind()),
            Some(io::ErrorKind::UnexpectedEof)
        );
    }

    #[test]
    fn test_cli_compatibility() {
        let input = include_bytes!("../../assets/example.txt.zst");

        let output = decode_all(&input[..]).unwrap();

        let expected = include_bytes!("../../assets/example.txt");

        assert_eq!(&output[..], &expected[..]);
    }

    #[test]
    fn test_legacy() {
        use std::fs;
        use std::io::Read;

        let mut target = Vec::new();

        // Read the content from that file
        fs::File::open("assets/example.txt")
            .unwrap()
            .read_to_end(&mut target)
            .unwrap();

        for version in &[5, 6, 7, 8] {
            let filename = format!("assets/example.txt.v{}.zst", version);
            let file = fs::File::open(filename).unwrap();
            let mut decoder = Decoder::new(file).unwrap();

            let mut buffer = Vec::new();
            decoder.read_to_end(&mut buffer).unwrap();

            assert!(
                target == buffer,
                "Error decompressing legacy version {}",
                version
            );
        }
    }

    // Check that compressing+decompressing some data gives back the original
    fn test_full_cycle(input: &[u8], level: i32) {
        ::test_cycle_unwrap(
            input,
            |data| encode_all(data, level),
            |data| decode_all(data),
        );
    }

    #[test]
    fn test_ll_source() {
        // Where could I find some long text?...
        let data = include_bytes!("../../zstd-safe/zstd-sys/src/bindings.rs");
        // Test a few compression levels.
        // TODO: check them all?
        for level in 1..5 {
            test_full_cycle(data, level);
        }
    }
}
<commit_msg>Do not test legacy when the feature is turned off<commit_after>//! Compress and decompress Zstd streams.
//!
//! This module provide a `Read`/`Write` interface
//! to zstd streams of arbitrary length.
//!
//! They are compatible with the `zstd` command-line tool.

use std::io;

mod decoder;
mod encoder;

pub use self::decoder::Decoder;
pub use self::encoder::{AutoFinishEncoder, Encoder};

/// Decompress from the given source as if using a `Decoder`.
///
/// The input data must be in the zstd frame format.
pub fn decode_all<R: io::Read>(source: R) -> io::Result<Vec<u8>> {
    let mut result = Vec::new();
    copy_decode(source, &mut result)?;
    Ok(result)
}

/// Decompress from the given source as if using a `Decoder`.
///
/// Decompressed data will be appended to `destination`.
pub fn copy_decode<R, W>(source: R, mut destination: W) -> io::Result<()>
where
    R: io::Read,
    W: io::Write,
{
    let mut decoder = Decoder::new(source)?;
    io::copy(&mut decoder, &mut destination)?;
    Ok(())
}

/// Compress all data from the given source as if using an `Encoder`.
///
/// Result will be in the zstd frame format.
///
/// A level of `0` uses zstd's default (currently `3`).
pub fn encode_all<R: io::Read>(source: R, level: i32) -> io::Result<Vec<u8>> {
    let mut result = Vec::<u8>::new();
    copy_encode(source, &mut result, level)?;
    Ok(result)
}

/// Compress all data from the given source as if using an `Encoder`.
///
/// Compressed data will be appended to `destination`.
///
/// A level of `0` uses zstd's default (currently `3`).
pub fn copy_encode<R, W>(
    mut source: R,
    destination: W,
    level: i32,
) -> io::Result<()>
where
    R: io::Read,
    W: io::Write,
{
    let mut encoder = Encoder::new(destination, level)?;
    io::copy(&mut source, &mut encoder)?;
    encoder.finish()?;
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::{copy_encode, decode_all, encode_all};
    use super::{Decoder, Encoder};

    use partial_io::{PartialOp, PartialWrite};

    use std::io;
    use std::iter;

    #[test]
    fn test_end_of_frame() {
        use std::io::{Read, Write};

        let mut enc = Encoder::new(Vec::new(), 1).unwrap();
        enc.write_all(b"foo").unwrap();
        let mut compressed = enc.finish().unwrap();

        // Add footer/whatever to underlying storage.
        compressed.push(0);

        // Drain zstd stream until end-of-frame.
        let mut dec = Decoder::new(&compressed[..]).unwrap().single_frame();
        let mut buf = Vec::new();
        dec.read_to_end(&mut buf).unwrap();
        assert_eq!(&buf, b"foo", "Error decoding a single frame.");
    }

    #[test]
    fn test_concatenated_frames() {
        let mut buffer = Vec::new();
        copy_encode(&b"foo"[..], &mut buffer, 1).unwrap();
        copy_encode(&b"bar"[..], &mut buffer, 2).unwrap();
        copy_encode(&b"baz"[..], &mut buffer, 3).unwrap();

        assert_eq!(
            &decode_all(&buffer[..]).unwrap(),
            b"foobarbaz",
            "Error decoding concatenated frames."
        );
    }

    #[test]
    fn test_flush() {
        use std::io::Write;

        let buf = Vec::new();
        let mut z = Encoder::new(buf, 19).unwrap();

        z.write_all(b"hello").unwrap();

        z.flush().unwrap(); // Might corrupt stream
        let buf = z.finish().unwrap();

        let s = decode_all(&buf[..]).unwrap();
        assert_eq!(s, b"hello", "Error decoding after flush.");
    }

    #[test]
    fn test_try_finish() {
        use std::io::Write;
        let mut z = setup_try_finish();

        z.get_mut().set_ops(iter::repeat(PartialOp::Unlimited));

        // flush() should continue to work even though write() doesn't.
        z.flush().unwrap();

        let buf = match z.try_finish() {
            Ok(buf) => buf.into_inner(),
            Err((_z, e)) => panic!("try_finish failed with {:?}", e),
        };

        // Make sure the multiple try_finish calls didn't screw up the internal
        // buffer and continued to produce valid compressed data.
        assert_eq!(&decode_all(&buf[..]).unwrap(), b"hello", "Error decoding");
    }

    #[test]
    #[should_panic]
    fn test_write_after_try_finish() {
        use std::io::Write;
        let mut z = setup_try_finish();
        z.write_all(b"hello world").unwrap();
    }

    fn setup_try_finish() -> Encoder<PartialWrite<Vec<u8>>> {
        use std::io::Write;

        let buf =
            PartialWrite::new(Vec::new(), iter::repeat(PartialOp::Unlimited));
        let mut z = Encoder::new(buf, 19).unwrap();

        z.write_all(b"hello").unwrap();

        z.get_mut()
            .set_ops(iter::repeat(PartialOp::Err(io::ErrorKind::WouldBlock)));

        let (z, err) = z.try_finish().unwrap_err();
        assert_eq!(
            err.kind(),
            io::ErrorKind::WouldBlock,
            "expected WouldBlock error"
        );

        z
    }

    #[test]
    fn test_failing_write() {
        use std::io::Write;

        let buf = PartialWrite::new(
            Vec::new(),
            iter::repeat(PartialOp::Err(io::ErrorKind::WouldBlock)),
        );
        let mut z = Encoder::new(buf, 1).unwrap();

        // Fill in enough data to make sure the buffer gets written out.
        let input = vec![b'b'; 128 * 1024];
        // This should work even though the inner writer rejects writes.
        assert_eq!(
            z.write(&input).unwrap(),
            128 * 1024,
            "did not write all input buffer"
        );

        // The next write would fail (the buffer still has some data in it).
        assert_eq!(
            z.write(b"abc").unwrap_err().kind(),
            io::ErrorKind::WouldBlock,
            "expected WouldBlock error"
        );

        z.get_mut().set_ops(iter::repeat(PartialOp::Unlimited));

        // This shouldn't have led to any corruption.
        let buf = z.finish().unwrap().into_inner();
        assert_eq!(
            &decode_all(&buf[..]).unwrap(),
            &input,
            "WouldBlock errors should not corrupt stream"
        );
    }

    #[test]
    fn test_invalid_frame() {
        use std::io::Read;

        // I really hope this data is invalid...
        let data = &[1u8, 2u8, 3u8, 4u8, 5u8];
        let mut dec = Decoder::new(&data[..]).unwrap();
        assert_eq!(
            dec.read_to_end(&mut Vec::new()).err().map(|e| e.kind()),
            Some(io::ErrorKind::Other),
            "did not encounter expected 'invalid frame' error"
        );
    }

    #[test]
    fn test_incomplete_frame() {
        use std::io::{Read, Write};

        let mut enc = Encoder::new(Vec::new(), 1).unwrap();
        enc.write_all(b"This is a regular string").unwrap();
        let mut compressed = enc.finish().unwrap();

        let half_size = compressed.len() - 2;
        compressed.truncate(half_size);

        let mut dec = Decoder::new(&compressed[..]).unwrap();
        assert_eq!(
            dec.read_to_end(&mut Vec::new()).err().map(|e| e.kind()),
            Some(io::ErrorKind::UnexpectedEof),
            "did not encounter expected EOF error"
        );
    }

    #[test]
    fn test_cli_compatibility() {
        let input = include_bytes!("../../assets/example.txt.zst");

        let output = decode_all(&input[..]).unwrap();

        let expected = include_bytes!("../../assets/example.txt");

        assert_eq!(
            &output[..],
            &expected[..],
            "error decoding cli-compressed data"
        );
    }

    #[cfg(feature = "legacy")]
    #[test]
    fn test_legacy() {
        use std::fs;
        use std::io::Read;

        // Read the content from that file
        let expected = include_bytes!("../../assets/example.txt");

        for version in &[5, 6, 7, 8] {
            let filename = format!("assets/example.txt.v{}.zst", version);
            let file = fs::File::open(filename).unwrap();
            let mut decoder = Decoder::new(file).unwrap();

            let mut buffer = Vec::new();
            decoder.read_to_end(&mut buffer).unwrap();

            assert_eq!(
                &expected[..],
                &buffer[..],
                "error decompressing legacy version {}",
                version
            );
        }
    }

    // Check that compressing+decompressing some data gives back the original
    fn test_full_cycle(input: &[u8], level: i32) {
        ::test_cycle_unwrap(
            input,
            |data| encode_all(data, level),
            |data| decode_all(data),
        );
    }

    #[test]
    fn test_ll_source() {
        // Where could I find some long text?...
        let data = include_bytes!("../../zstd-safe/zstd-sys/src/bindings.rs");
        // Test a few compression levels.
        // TODO: check them all?
        for level in 1..5 {
            test_full_cycle(data, level);
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt::Debug;
use std::hash::Hash;
use std::rc::Rc;

#[macro_use]
use adapton_syntax::* ;
use adapton_sigs::* ;

pub trait ListT<A:Adapton,Hd> {
    type List ;

    fn nil  (&mut A) -> Self::List ;
    fn cons (&mut A, Hd, Rc<Self::List>) -> Self::List ;

    // requisite "adaptonic" constructors: `name` and `art`:
    fn name (&mut A, A::Name, Rc<Self::List>) -> Self::List ;
    fn art  (&mut A, Art<Self::List,A::Loc>) -> Self::List ;

    fn elim<Res,Nil,Cons,Name> (self:&Self, &mut A, &Self::List, Nil, Cons, Name) -> Res
        where Nil:FnOnce(&mut A) -> Res
        ,    Cons:FnOnce(&mut A, &Hd, &Rc<Self::List>) -> Res
        ,    Name:FnOnce(&mut A, &A::Name, &Rc<Self::List>) -> Res ;

    fn fold<Res,Cons> (self:&Self, &mut A, &Self::List, Res, Cons) -> Res
        where Cons:Fn(&mut A, Res, &Hd) -> Res ;

    // TODO: Add derived operations (max, min, sum, etc.)
}

// Questions:
//  - Should `Name`s always be passed by reference?
//  - Do these Fn argss for fold need to be passed in `Rc<Box<_>>`s ?

pub trait TreeT<A:Adapton,Leaf,Bin:Hash> {
    type Tree ;

    fn nil  (&mut A) -> Self::Tree ;
    fn leaf (&mut A, Leaf) -> Self::Tree ;
    fn bin  (&mut A, Bin, Rc<Self::Tree>, Rc<Self::Tree>) -> Self::Tree ;

    // requisite "adaptonic" constructors: `name` and `art`:
    fn name (&mut A, A::Name, Rc<Self::Tree>, Rc<Self::Tree>) -> Self::Tree ;
    fn art  (&mut A, Art<Self::Tree,A::Loc>) -> Self::Tree ;

    fn fold<Res,LeafC,BinC> (&mut A, Self::Tree, Res, LeafC, BinC) -> Res
        where LeafC:Fn(&mut A, Res, Leaf) -> Res
        ,      BinC:Fn(&mut A, Res, Bin ) -> Res ;

    fn fold2<Arg,Res,LeafC,BinC> (&mut A, Self::Tree, Arg, LeafC, BinC) -> Res
        where LeafC:Fn(&mut A, Arg, Leaf) -> Res
        ,      BinC:Fn(&mut A, Arg, Bin, Res, Res ) -> Res ;
}

#[derive(Debug,PartialEq,Eq,Hash)]
enum List<A:Adapton,Hd> {
    Nil,
    Cons(Hd,Rc<List<A,Hd>>),
    Name(A::Name,Rc<List<A,Hd>>),
    Art(Art<List<A,Hd>, A::Loc>),
}

impl<A:Adapton+Debug+PartialEq+Eq+Hash,Hd:Debug+PartialEq+Eq+Hash> ListT<A,Hd> for List<A,Hd> {
    type List = List<A,Hd>;

    fn nil  (_:&mut A)                                 -> Self::List { List::Nil }
    fn cons (_:&mut A, hd:Hd, tl:Rc<Self::List>)       -> Self::List { List::Cons(hd,tl) }
    fn name (_:&mut A, nm:A::Name, tl:Rc<Self::List>)  -> Self::List { List::Name(nm, tl) }
    fn art  (_:&mut A, art:Art<List<A,Hd>,A::Loc>)     -> Self::List { List::Art(art) }

    fn elim<Res,Nil,Cons,Name> (self:&Self, st:&mut A, list:&Self::List, nilf:Nil, consf:Cons, namef:Name) -> Res
        where Nil:FnOnce(&mut A) -> Res
        ,    Cons:FnOnce(&mut A, &Hd, &Rc<Self::List>) -> Res
        ,    Name:FnOnce(&mut A, &A::Name, &Rc<Self::List>) -> Res
    {
        match *list {
            List::Nil => nilf(st),
            List::Cons(ref hd, ref tl) => consf(st, hd, tl),
            List::Name(ref nm, ref tl) => namef(st, nm, tl),
            List::Art(ref art) => {
                let list = st.force(art);
                self.elim(st, &*list, nilf, consf, namef)
            }
        }
    }
    fn fold<Res,Cons> (self:&Self, st:&mut A, list:&Self::List, res:Res, consf:Cons) -> Res
        where Cons:Fn(&mut A, Res, &Hd) -> Res
    {
        match *list {
            List::Nil => res,
            List::Cons(ref hd, ref tl) => {
                let res = consf(st, res, hd) ;
                self.fold(st, &*tl, res, consf)
            },
            List::Name(_, ref tl) => self.fold(st, &*tl, res, consf),
            List::Art(ref art) => {
                let list = st.force(art) ;
                self.fold(st, &*list, res, consf)
            }
        }
    }
}

pub fn tree_of_list_rec <A:Adapton, X:Hash+Clone, T:TreeT<A,X,X>, L:ListT<A,X>>
    (l:&L, st:&mut A, list:&Rc<L::List>, left_tree:&Rc<T::Tree>)
     -> (Rc<T::Tree>, Rc<L::List>)
{
    l.elim (
        st, &list,
        /* Nil */  |st| ( Rc::new(T::nil(st)), Rc::new(L::nil(st)) ),
        /* Cons */ |st, hd, rest| {
            if my_hash( hd ) == 0 {
                let nil = Rc::new(T::nil(st)) ;
                let (right_tree, rest) = tree_of_list_rec::<A,X,T,L> ( l, st, rest, &nil ) ;
                let tree = Rc::new(T::bin ( st, hd.clone(), left_tree.clone(), right_tree )) ;
                tree_of_list_rec::<A,X,T,L> ( l, st, &rest, &tree )
            }
            else {
                let rest = Rc::new(L::cons(st, hd.clone(), rest.clone())) ;
                (left_tree.clone(), rest)
            }},
        /* Name */ |st, nm, rest| {
            if my_hash( nm ) == 0 {
                let nil = Rc::new(T::nil(st)) ;
                let (right_tree, rest) = tree_of_list_rec::<A,X,T,L> ( l, st, rest, &nil ) ;
                // TODO: Place left_ and right_ trees into articulations, named by name.
                // TODO: Memoize the recursive calls to tree_of_list_rec.
                let tree = Rc::new(T::name( st, nm.clone(), left_tree.clone(), right_tree )) ;
                tree_of_list_rec::<A,X,T,L> ( l, st, &rest, &tree )
            }
            else {
                let rest = Rc::new(L::name(st, nm.clone(), rest.clone())) ;
                (left_tree.clone(), rest)
            }}
        )
}
<commit_msg>exploiting hashes to build the binary tree, a la, Pugh & Teitelbaum 1989<commit_after>use std::fmt::Debug;
use std::hash::Hash;
use std::rc::Rc;

#[macro_use]
use adapton_syntax::* ;
use adapton_sigs::* ;

pub trait ListT<A:Adapton,Hd> {
    type List ;

    fn nil  (&mut A) -> Self::List ;
    fn cons (&mut A, Hd, Rc<Self::List>) -> Self::List ;

    // requisite "adaptonic" constructors: `name` and `art`:
    fn name (&mut A, A::Name, Rc<Self::List>) -> Self::List ;
    fn art  (&mut A, Art<Self::List,A::Loc>) -> Self::List ;

    fn elim<Res,Nil,Cons,Name> (self:&Self, &mut A, &Self::List, Nil, Cons, Name) -> Res
        where Nil:FnOnce(&mut A) -> Res
        ,    Cons:FnOnce(&mut A, &Hd, &Rc<Self::List>) -> Res
        ,    Name:FnOnce(&mut A, &A::Name, &Rc<Self::List>) -> Res ;

    fn fold<Res,Cons> (self:&Self, &mut A, &Self::List, Res, Cons) -> Res
        where Cons:Fn(&mut A, Res, &Hd) -> Res ;

    // TODO: Add derived operations (max, min, sum, etc.)
}

// Questions:
//  - Should `Name`s always be passed by reference?
//  - Do these Fn argss for fold need to be passed in `Rc<Box<_>>`s ?

pub trait TreeT<A:Adapton,Leaf,Bin:Hash> {
    type Tree ;

    fn nil  (&mut A) -> Self::Tree ;
    fn leaf (&mut A, Leaf) -> Self::Tree ;
    fn bin  (&mut A, Bin, Rc<Self::Tree>, Rc<Self::Tree>) -> Self::Tree ;

    // requisite "adaptonic" constructors: `name` and `art`:
    fn name (&mut A, A::Name, Rc<Self::Tree>, Rc<Self::Tree>) -> Self::Tree ;
    fn art  (&mut A, Art<Self::Tree,A::Loc>) -> Self::Tree ;

    fn fold<Res,LeafC,BinC> (&mut A, Self::Tree, Res, LeafC, BinC) -> Res
        where LeafC:Fn(&mut A, Res, Leaf) -> Res
        ,      BinC:Fn(&mut A, Res, Bin ) -> Res ;

    fn fold2<Arg,Res,LeafC,BinC> (&mut A, Self::Tree, Arg, LeafC, BinC) -> Res
        where LeafC:Fn(&mut A, Arg, Leaf) -> Res
        ,      BinC:Fn(&mut A, Arg, Bin, Res, Res ) -> Res ;
}

#[derive(Debug,PartialEq,Eq,Hash)]
enum List<A:Adapton,Hd> {
    Nil,
    Cons(Hd,Rc<List<A,Hd>>),
    Name(A::Name,Rc<List<A,Hd>>),
    Art(Art<List<A,Hd>, A::Loc>),
}

impl<A:Adapton+Debug+PartialEq+Eq+Hash,Hd:Debug+PartialEq+Eq+Hash> ListT<A,Hd> for List<A,Hd> {
    type List = List<A,Hd>;

    fn nil  (_:&mut A)                                 -> Self::List { List::Nil }
    fn cons (_:&mut A, hd:Hd, tl:Rc<Self::List>)       -> Self::List { List::Cons(hd,tl) }
    fn name (_:&mut A, nm:A::Name, tl:Rc<Self::List>)  -> Self::List { List::Name(nm, tl) }
    fn art  (_:&mut A, art:Art<List<A,Hd>,A::Loc>)     -> Self::List { List::Art(art) }

    fn elim<Res,Nil,Cons,Name> (self:&Self, st:&mut A, list:&Self::List, nilf:Nil, consf:Cons, namef:Name) -> Res
        where Nil:FnOnce(&mut A) -> Res
        ,    Cons:FnOnce(&mut A, &Hd, &Rc<Self::List>) -> Res
        ,    Name:FnOnce(&mut A, &A::Name, &Rc<Self::List>) -> Res
    {
        match *list {
            List::Nil => nilf(st),
            List::Cons(ref hd, ref tl) => consf(st, hd, tl),
            List::Name(ref nm, ref tl) => namef(st, nm, tl),
            List::Art(ref art) => {
                let list = st.force(art);
                self.elim(st, &*list, nilf, consf, namef)
            }
        }
    }

    fn fold<Res,Cons> (self:&Self, st:&mut A, list:&Self::List, res:Res, consf:Cons) -> Res
        where Cons:Fn(&mut A, Res, &Hd) -> Res
    {
        match *list {
            List::Nil => res,
            List::Cons(ref hd, ref tl) => {
                let res = consf(st, res, hd) ;
                self.fold(st, &*tl, res, consf)
            },
            List::Name(_, ref tl) => self.fold(st, &*tl, res, consf),
            List::Art(ref art) => {
                let list = st.force(art) ;
                self.fold(st, &*list, res, consf)
            }
        }
    }
}

pub fn tree_of_list_rec <A:Adapton, X:Hash+Clone, T:TreeT<A,X,X>, L:ListT<A,X>>
    (l:&L, st:&mut A, list:&Rc<L::List>, left_tree:&Rc<T::Tree>, left_tree_lev:u32, parent_lev:u32)
     -> (Rc<T::Tree>, Rc<L::List>)
{
    l.elim (
        st, &list,
        /* Nil */  |st| ( Rc::new(T::nil(st)), Rc::new(L::nil(st)) ),
        /* Cons */ |st, hd, rest| {
            let lev_hd = (1 + (my_hash(hd).leading_zeros())) as u32 ;
            if left_tree_lev <= lev_hd && lev_hd <= parent_lev {
                let nil = Rc::new(T::nil(st)) ;
                let (right_tree, rest) = tree_of_list_rec::<A,X,T,L> ( l, st, rest, &nil, 0 as u32, lev_hd ) ;
                let tree = Rc::new(T::bin ( st, hd.clone(), left_tree.clone(), right_tree )) ;
                tree_of_list_rec::<A,X,T,L> ( l, st, &rest, &tree, lev_hd, parent_lev )
            }
            else {
                let rest = Rc::new(L::cons(st, hd.clone(), rest.clone())) ;
                (left_tree.clone(), rest)
            }},
        /* Name */ |st, nm, rest| {
            let lev_nm = (1 + 64 + (my_hash(nm).leading_zeros())) as u32 ;
            if left_tree_lev <= lev_nm && lev_nm <= parent_lev {
                let nil = Rc::new(T::nil(st)) ;
                let (right_tree, rest) = tree_of_list_rec::<A,X,T,L> ( l, st, rest, &nil, 0 as u32, lev_nm ) ;
                // TODO: Place left_ and right_ trees into articulations, named by name.
                // TODO: Memoize the recursive calls to tree_of_list_rec.
                let tree = Rc::new(T::name( st, nm.clone(), left_tree.clone(), right_tree )) ;
                tree_of_list_rec::<A,X,T,L> ( l, st, &rest, &tree, lev_nm, parent_lev )
            }
            else {
                let rest = Rc::new(L::name(st, nm.clone(), rest.clone())) ;
                (left_tree.clone(), rest)
            }}
        )
}
<|endoftext|>