<commit_before>use crate::char;
use crate::fmt::{self, Write};
use crate::mem;

use super::from_utf8_unchecked;
use super::validations::utf8_char_width;

/// Lossy UTF-8 string.
#[unstable(feature = "str_internals", issue = "none")]
pub struct Utf8Lossy {
    bytes: [u8],
}

impl Utf8Lossy {
    #[must_use]
    pub fn from_str(s: &str) -> &Utf8Lossy {
        Utf8Lossy::from_bytes(s.as_bytes())
    }

    #[must_use]
    pub fn from_bytes(bytes: &[u8]) -> &Utf8Lossy {
        // SAFETY: Both use the same memory layout, and UTF-8 correctness isn't required.
        unsafe { mem::transmute(bytes) }
    }

    pub fn chunks(&self) -> Utf8LossyChunksIter<'_> {
        Utf8LossyChunksIter { source: &self.bytes }
    }
}

/// Iterator over lossy UTF-8 string
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[unstable(feature = "str_internals", issue = "none")]
#[allow(missing_debug_implementations)]
pub struct Utf8LossyChunksIter<'a> {
    source: &'a [u8],
}

#[unstable(feature = "str_internals", issue = "none")]
#[derive(PartialEq, Eq, Debug)]
pub struct Utf8LossyChunk<'a> {
    /// Sequence of valid chars.
    /// Can be empty between broken UTF-8 chars.
    pub valid: &'a str,
    /// Single broken char, empty if none.
    /// Empty iff iterator item is last.
    pub broken: &'a [u8],
}

impl<'a> Iterator for Utf8LossyChunksIter<'a> {
    type Item = Utf8LossyChunk<'a>;

    fn next(&mut self) -> Option<Utf8LossyChunk<'a>> {
        if self.source.is_empty() {
            return None;
        }

        const TAG_CONT_U8: u8 = 128;
        fn safe_get(xs: &[u8], i: usize) -> u8 {
            *xs.get(i).unwrap_or(&0)
        }

        let mut i = 0;
        let mut valid_up_to = 0;
        while i < self.source.len() {
            // SAFETY: `i < self.source.len()` per previous line.
            // For some reason the following are both significantly slower:
            // while let Some(&byte) = self.source.get(i) {
            // while let Some(byte) = self.source.get(i).copied() {
            let byte = unsafe { *self.source.get_unchecked(i) };
            i += 1;

            if byte < 128 {
                // This could be a `1 => ...` case in the match below, but for
                // the common case of all-ASCII inputs, we bypass loading the
                // sizeable UTF8_CHAR_WIDTH table into cache.
            } else {
                let w = utf8_char_width(byte);

                match w {
                    2 => {
                        if safe_get(self.source, i) & 192 != TAG_CONT_U8 {
                            break;
                        }
                        i += 1;
                    }
                    3 => {
                        match (byte, safe_get(self.source, i)) {
                            (0xE0, 0xA0..=0xBF) => (),
                            (0xE1..=0xEC, 0x80..=0xBF) => (),
                            (0xED, 0x80..=0x9F) => (),
                            (0xEE..=0xEF, 0x80..=0xBF) => (),
                            _ => break,
                        }
                        i += 1;
                        if safe_get(self.source, i) & 192 != TAG_CONT_U8 {
                            break;
                        }
                        i += 1;
                    }
                    4 => {
                        match (byte, safe_get(self.source, i)) {
                            (0xF0, 0x90..=0xBF) => (),
                            (0xF1..=0xF3, 0x80..=0xBF) => (),
                            (0xF4, 0x80..=0x8F) => (),
                            _ => break,
                        }
                        i += 1;
                        if safe_get(self.source, i) & 192 != TAG_CONT_U8 {
                            break;
                        }
                        i += 1;
                        if safe_get(self.source, i) & 192 != TAG_CONT_U8 {
                            break;
                        }
                        i += 1;
                    }
                    _ => break,
                }
            }

            valid_up_to = i;
        }

        // SAFETY: `i <= self.source.len()` because it only ever increments by 1
        // and the loop is terminated as soon as that goes beyond bounds.
        let (inspected, remaining) = unsafe { self.source.split_at_unchecked(i) };
        self.source = remaining;

        // SAFETY: `valid_up_to <= i` because it is only ever assigned via
        // `valid_up_to = i` and `i` only increases.
        let (valid, broken) = unsafe { inspected.split_at_unchecked(valid_up_to) };

        Some(Utf8LossyChunk {
            // SAFETY: All bytes up to `valid_up_to` are valid UTF-8.
            valid: unsafe { from_utf8_unchecked(valid) },
            broken,
        })
    }
}

impl fmt::Display for Utf8Lossy {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // If we're the empty string then our iterator won't actually yield
        // anything, so perform the formatting manually
        if self.bytes.is_empty() {
            return "".fmt(f);
        }

        for Utf8LossyChunk { valid, broken } in self.chunks() {
            // If we successfully decoded the whole chunk as a valid string then
            // we can return a direct formatting of the string which will also
            // respect various formatting flags if possible.
            if valid.len() == self.bytes.len() {
                assert!(broken.is_empty());
                return valid.fmt(f);
            }

            f.write_str(valid)?;
            if !broken.is_empty() {
                f.write_char(char::REPLACEMENT_CHARACTER)?;
            }
        }
        Ok(())
    }
}

impl fmt::Debug for Utf8Lossy {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_char('"')?;

        for Utf8LossyChunk { valid, broken } in self.chunks() {
            // Valid part.
            // Here we partially parse UTF-8 again which is suboptimal.
            {
                let mut from = 0;
                for (i, c) in valid.char_indices() {
                    let esc = c.escape_debug();
                    // If char needs escaping, flush backlog so far and write, else skip
                    if esc.len() != 1 {
                        f.write_str(&valid[from..i])?;
                        for c in esc {
                            f.write_char(c)?;
                        }
                        from = i + c.len_utf8();
                    }
                }
                f.write_str(&valid[from..])?;
            }

            // Broken parts of string as hex escape.
            for &b in broken {
                write!(f, "\\x{:02x}", b)?;
            }
        }

        f.write_char('"')
    }
}
<commit_msg>Clarify safety comment on using i to index into self.source<commit_after>use crate::char;
use crate::fmt::{self, Write};
use crate::mem;

use super::from_utf8_unchecked;
use super::validations::utf8_char_width;

/// Lossy UTF-8 string.
#[unstable(feature = "str_internals", issue = "none")]
pub struct Utf8Lossy {
    bytes: [u8],
}

impl Utf8Lossy {
    #[must_use]
    pub fn from_str(s: &str) -> &Utf8Lossy {
        Utf8Lossy::from_bytes(s.as_bytes())
    }

    #[must_use]
    pub fn from_bytes(bytes: &[u8]) -> &Utf8Lossy {
        // SAFETY: Both use the same memory layout, and UTF-8 correctness isn't required.
        unsafe { mem::transmute(bytes) }
    }

    pub fn chunks(&self) -> Utf8LossyChunksIter<'_> {
        Utf8LossyChunksIter { source: &self.bytes }
    }
}

/// Iterator over lossy UTF-8 string
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[unstable(feature = "str_internals", issue = "none")]
#[allow(missing_debug_implementations)]
pub struct Utf8LossyChunksIter<'a> {
    source: &'a [u8],
}

#[unstable(feature = "str_internals", issue = "none")]
#[derive(PartialEq, Eq, Debug)]
pub struct Utf8LossyChunk<'a> {
    /// Sequence of valid chars.
    /// Can be empty between broken UTF-8 chars.
    pub valid: &'a str,
    /// Single broken char, empty if none.
    /// Empty iff iterator item is last.
    pub broken: &'a [u8],
}

impl<'a> Iterator for Utf8LossyChunksIter<'a> {
    type Item = Utf8LossyChunk<'a>;

    fn next(&mut self) -> Option<Utf8LossyChunk<'a>> {
        if self.source.is_empty() {
            return None;
        }

        const TAG_CONT_U8: u8 = 128;
        fn safe_get(xs: &[u8], i: usize) -> u8 {
            *xs.get(i).unwrap_or(&0)
        }

        let mut i = 0;
        let mut valid_up_to = 0;
        while i < self.source.len() {
            // SAFETY: `i < self.source.len()` per previous line.
            // For some reason the following are both significantly slower:
            // while let Some(&byte) = self.source.get(i) {
            // while let Some(byte) = self.source.get(i).copied() {
            let byte = unsafe { *self.source.get_unchecked(i) };
            i += 1;

            if byte < 128 {
                // This could be a `1 => ...` case in the match below, but for
                // the common case of all-ASCII inputs, we bypass loading the
                // sizeable UTF8_CHAR_WIDTH table into cache.
            } else {
                let w = utf8_char_width(byte);

                match w {
                    2 => {
                        if safe_get(self.source, i) & 192 != TAG_CONT_U8 {
                            break;
                        }
                        i += 1;
                    }
                    3 => {
                        match (byte, safe_get(self.source, i)) {
                            (0xE0, 0xA0..=0xBF) => (),
                            (0xE1..=0xEC, 0x80..=0xBF) => (),
                            (0xED, 0x80..=0x9F) => (),
                            (0xEE..=0xEF, 0x80..=0xBF) => (),
                            _ => break,
                        }
                        i += 1;
                        if safe_get(self.source, i) & 192 != TAG_CONT_U8 {
                            break;
                        }
                        i += 1;
                    }
                    4 => {
                        match (byte, safe_get(self.source, i)) {
                            (0xF0, 0x90..=0xBF) => (),
                            (0xF1..=0xF3, 0x80..=0xBF) => (),
                            (0xF4, 0x80..=0x8F) => (),
                            _ => break,
                        }
                        i += 1;
                        if safe_get(self.source, i) & 192 != TAG_CONT_U8 {
                            break;
                        }
                        i += 1;
                        if safe_get(self.source, i) & 192 != TAG_CONT_U8 {
                            break;
                        }
                        i += 1;
                    }
                    _ => break,
                }
            }

            valid_up_to = i;
        }

        // SAFETY: `i <= self.source.len()` because it is only ever incremented
        // via `i += 1` and in between every single one of those increments, `i`
        // is compared against `self.source.len()`. That happens either
        // literally by `i < self.source.len()` in the while-loop's condition,
        // or indirectly by `safe_get(self.source, i) & 192 != TAG_CONT_U8`. The
        // loop is terminated as soon as the latest `i += 1` has made `i` no
        // longer less than `self.source.len()`, which means it'll be at most
        // equal to `self.source.len()`.
        let (inspected, remaining) = unsafe { self.source.split_at_unchecked(i) };
        self.source = remaining;

        // SAFETY: `valid_up_to <= i` because it is only ever assigned via
        // `valid_up_to = i` and `i` only increases.
        let (valid, broken) = unsafe { inspected.split_at_unchecked(valid_up_to) };

        Some(Utf8LossyChunk {
            // SAFETY: All bytes up to `valid_up_to` are valid UTF-8.
            valid: unsafe { from_utf8_unchecked(valid) },
            broken,
        })
    }
}

impl fmt::Display for Utf8Lossy {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // If we're the empty string then our iterator won't actually yield
        // anything, so perform the formatting manually
        if self.bytes.is_empty() {
            return "".fmt(f);
        }

        for Utf8LossyChunk { valid, broken } in self.chunks() {
            // If we successfully decoded the whole chunk as a valid string then
            // we can return a direct formatting of the string which will also
            // respect various formatting flags if possible.
            if valid.len() == self.bytes.len() {
                assert!(broken.is_empty());
                return valid.fmt(f);
            }

            f.write_str(valid)?;
            if !broken.is_empty() {
                f.write_char(char::REPLACEMENT_CHARACTER)?;
            }
        }
        Ok(())
    }
}

impl fmt::Debug for Utf8Lossy {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_char('"')?;

        for Utf8LossyChunk { valid, broken } in self.chunks() {
            // Valid part.
            // Here we partially parse UTF-8 again which is suboptimal.
            {
                let mut from = 0;
                for (i, c) in valid.char_indices() {
                    let esc = c.escape_debug();
                    // If char needs escaping, flush backlog so far and write, else skip
                    if esc.len() != 1 {
                        f.write_str(&valid[from..i])?;
                        for c in esc {
                            f.write_char(c)?;
                        }
                        from = i + c.len_utf8();
                    }
                }
                f.write_str(&valid[from..])?;
            }

            // Broken parts of string as hex escape.
            for &b in broken {
                write!(f, "\\x{:02x}", b)?;
            }
        }

        f.write_char('"')
    }
}
<|endoftext|>


<commit_before>mod builtin_functions;
mod compiler;
mod interpreter;
mod language_server;

use crate::compiler::ast_to_hir::CompileVecAstsToHir;
use crate::compiler::cst_to_ast::LowerCstToAst;
use crate::compiler::string_to_cst::StringToCst;
use crate::interpreter::fiber::FiberStatus;
use crate::interpreter::*;
use language_server::CandyLanguageServer;
use log;
use lspower::{LspService, Server};
use simplelog::{
    ColorChoice, CombinedLogger, Config, LevelFilter, TermLogger, TerminalMode, WriteLogger,
};
use std::{fs::File, path::PathBuf};
use structopt::StructOpt;

#[derive(StructOpt, Debug)]
#[structopt(name = "candy", about = "The 🍭 Candy CLI.")]
enum CandyOptions {
    Run(CandyRunOptions),
    Lsp,
}

#[derive(StructOpt, Debug)]
struct CandyRunOptions {
    #[structopt(long)]
    print_cst: bool,

    #[structopt(long)]
    print_ast: bool,

    #[structopt(long)]
    print_hir: bool,

    #[structopt(long)]
    no_run: bool,

    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[tokio::main]
async fn main() {
    match CandyOptions::from_args() {
        CandyOptions::Run(options) => run(options),
        CandyOptions::Lsp => lsp().await,
    }
}

fn run(options: CandyRunOptions) {
    init_logger(TerminalMode::Mixed);
    log::debug!("Running test.candy.\n");

    let test_code = std::fs::read_to_string(options.file).expect("File test.candy not found.");

    log::info!("Parsing string to CST…");
    let cst = test_code.parse_cst();
    if options.print_cst {
        log::info!("CST: {:?}", cst);
    }

    log::info!("Lowering CST to AST…");
    let (asts, errors) = cst.into_ast();
    if options.print_ast {
        log::info!("AST: {:?}", asts);
    }
    if !errors.is_empty() {
        log::error!("Errors occurred while lowering CST to AST:\n{:?}", errors);
        return;
    }

    log::info!("Compiling AST to HIR…");
    let lambda = asts.compile_to_hir();
    if options.print_hir {
        log::info!("HIR: {:?}", lambda);
    }

    if !options.no_run {
        log::info!("Executing code…");
        let mut fiber = fiber::Fiber::new(lambda);
        fiber.run();
        match fiber.status() {
            FiberStatus::Running => log::info!("Fiber is still running."),
            FiberStatus::Done(value) => log::info!("Fiber is done: {:?}", value),
            FiberStatus::Panicked(value) => log::error!("Fiber panicked: {:?}", value),
        }
    }
}

async fn lsp() {
    init_logger(TerminalMode::Stderr);
    log::info!("Starting language server…");
    let (service, messages) = LspService::new(|client| CandyLanguageServer::from_client(client));
    Server::new(tokio::io::stdin(), tokio::io::stdout())
        .interleave(messages)
        .serve(service)
        .await;
}

fn init_logger(terminal_mode: TerminalMode) {
    TermLogger::init(
        LevelFilter::Trace,
        Config::default(),
        terminal_mode,
        ColorChoice::Auto,
    )
    .unwrap();
}
<commit_msg>fix(compiler): print actual Candy file name<commit_after>mod builtin_functions;
mod compiler;
mod interpreter;
mod language_server;

use crate::compiler::ast_to_hir::CompileVecAstsToHir;
use crate::compiler::cst_to_ast::LowerCstToAst;
use crate::compiler::string_to_cst::StringToCst;
use crate::interpreter::fiber::FiberStatus;
use crate::interpreter::*;
use language_server::CandyLanguageServer;
use log;
use lspower::{LspService, Server};
use simplelog::{
    ColorChoice, CombinedLogger, Config, LevelFilter, TermLogger, TerminalMode, WriteLogger,
};
use std::{fs::File, path::PathBuf};
use structopt::StructOpt;

#[derive(StructOpt, Debug)]
#[structopt(name = "candy", about = "The 🍭 Candy CLI.")]
enum CandyOptions {
    Run(CandyRunOptions),
    Lsp,
}

#[derive(StructOpt, Debug)]
struct CandyRunOptions {
    #[structopt(long)]
    print_cst: bool,

    #[structopt(long)]
    print_ast: bool,

    #[structopt(long)]
    print_hir: bool,

    #[structopt(long)]
    no_run: bool,

    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[tokio::main]
async fn main() {
    match CandyOptions::from_args() {
        CandyOptions::Run(options) => run(options),
        CandyOptions::Lsp => lsp().await,
    }
}

fn run(options: CandyRunOptions) {
    init_logger(TerminalMode::Mixed);
    log::debug!("Running `{}`.\n", options.file.to_string_lossy());

    let test_code = std::fs::read_to_string(options.file.clone())
        .unwrap_or_else(|_| panic!("File `{}` not found.", options.file.to_string_lossy()));

    log::info!("Parsing string to CST…");
    let cst = test_code.parse_cst();
    if options.print_cst {
        log::info!("CST: {:?}", cst);
    }

    log::info!("Lowering CST to AST…");
    let (asts, errors) = cst.into_ast();
    if options.print_ast {
        log::info!("AST: {:?}", asts);
    }
    if !errors.is_empty() {
        log::error!("Errors occurred while lowering CST to AST:\n{:?}", errors);
        return;
    }

    log::info!("Compiling AST to HIR…");
    let lambda = asts.compile_to_hir();
    if options.print_hir {
        log::info!("HIR: {:?}", lambda);
    }

    if !options.no_run {
        log::info!("Executing code…");
        let mut fiber = fiber::Fiber::new(lambda);
        fiber.run();
        match fiber.status() {
            FiberStatus::Running => log::info!("Fiber is still running."),
            FiberStatus::Done(value) => log::info!("Fiber is done: {:?}", value),
            FiberStatus::Panicked(value) => log::error!("Fiber panicked: {:?}", value),
        }
    }
}

async fn lsp() {
    init_logger(TerminalMode::Stderr);
    log::info!("Starting language server…");
    let (service, messages) = LspService::new(|client| CandyLanguageServer::from_client(client));
    Server::new(tokio::io::stdin(), tokio::io::stdout())
        .interleave(messages)
        .serve(service)
        .await;
}

fn init_logger(terminal_mode: TerminalMode) {
    TermLogger::init(
        LevelFilter::Trace,
        Config::default(),
        terminal_mode,
        ColorChoice::Auto,
    )
    .unwrap();
}
<|endoftext|>


<commit_before>//! # Tracer
//!
//! The OpenTelemetry library achieves in-process context propagation of
//! `Span`s by way of the `Tracer`.
//!
//! The `Tracer` is responsible for tracking the currently active `Span`,
//! and exposes methods for creating and activating new `Spans`.
//!
//! Docs: <https://github.com/open-telemetry/opentelemetry-specification/blob/v1.3.0/specification/trace/api.md#tracer>
use crate::trace::SpanLimits;
use crate::{
    trace::{
        provider::{TracerProvider, TracerProviderInner},
        span::{Span, SpanData},
        Config, EvictedHashMap, EvictedQueue,
    },
    InstrumentationLibrary,
};
use opentelemetry_api::trace::{
    Link, OrderMap, SamplingDecision, SamplingResult, SpanBuilder, SpanContext, SpanId, SpanKind,
    TraceContextExt, TraceFlags, TraceId, TraceState,
};
use opentelemetry_api::{Context, Key, KeyValue, Value};
use std::fmt;
use std::sync::Weak;

/// `Tracer` implementation to create and manage spans
#[derive(Clone)]
pub struct Tracer {
    instrumentation_lib: InstrumentationLibrary,
    provider: Weak<TracerProviderInner>,
}

impl fmt::Debug for Tracer {
    /// Formats the `Tracer` using the given formatter.
    /// Omitting `provider` here is necessary to avoid cycles.
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Tracer")
            .field("name", &self.instrumentation_lib.name)
            .field("version", &self.instrumentation_lib.version)
            .finish()
    }
}

impl Tracer {
    /// Create a new tracer (used internally by `TracerProvider`s).
    pub(crate) fn new(
        instrumentation_lib: InstrumentationLibrary,
        provider: Weak<TracerProviderInner>,
    ) -> Self {
        Tracer {
            instrumentation_lib,
            provider,
        }
    }

    /// TracerProvider associated with this tracer.
    pub fn provider(&self) -> Option<TracerProvider> {
        self.provider.upgrade().map(TracerProvider::new)
    }

    /// Instrumentation library information of this tracer.
    pub fn instrumentation_library(&self) -> &InstrumentationLibrary {
        &self.instrumentation_lib
    }

    /// Make a sampling decision using the provided sampler for the span and context.
    #[allow(clippy::too_many_arguments)]
    fn make_sampling_decision(
        &self,
        parent_cx: &Context,
        trace_id: TraceId,
        name: &str,
        span_kind: &SpanKind,
        attributes: &OrderMap<Key, Value>,
        links: &[Link],
        config: &Config,
        instrumentation_library: &InstrumentationLibrary,
    ) -> Option<(TraceFlags, Vec<KeyValue>, TraceState)> {
        let sampling_result = config.sampler.should_sample(
            Some(parent_cx),
            trace_id,
            name,
            span_kind,
            attributes,
            links,
            instrumentation_library,
        );

        self.process_sampling_result(sampling_result, parent_cx)
    }

    fn process_sampling_result(
        &self,
        sampling_result: SamplingResult,
        parent_cx: &Context,
    ) -> Option<(TraceFlags, Vec<KeyValue>, TraceState)> {
        match sampling_result {
            SamplingResult {
                decision: SamplingDecision::Drop,
                ..
            } => None,
            SamplingResult {
                decision: SamplingDecision::RecordOnly,
                attributes,
                trace_state,
            } => {
                let trace_flags = parent_cx.span().span_context().trace_flags();
                Some((trace_flags.with_sampled(false), attributes, trace_state))
            }
            SamplingResult {
                decision: SamplingDecision::RecordAndSample,
                attributes,
                trace_state,
            } => {
                let trace_flags = parent_cx.span().span_context().trace_flags();
                Some((trace_flags.with_sampled(true), attributes, trace_state))
            }
        }
    }
}

impl opentelemetry_api::trace::Tracer for Tracer {
    /// This implementation of `Tracer` produces `sdk::Span` instances.
    type Span = Span;

    /// Starts a span from a `SpanBuilder`.
    ///
    /// Each span has zero or one parent spans and zero or more child spans, which
    /// represent causally related operations. A tree of related spans comprises a
    /// trace. A span is said to be a _root span_ if it does not have a parent. Each
    /// trace includes a single root span, which is the shared ancestor of all other
    /// spans in the trace.
    fn build_with_context(&self, mut builder: SpanBuilder, parent_cx: &Context) -> Self::Span {
        let provider = self.provider();
        if provider.is_none() {
            return Span::new(
                SpanContext::empty_context(),
                None,
                self.clone(),
                SpanLimits::default(),
            );
        }

        let provider = provider.unwrap();
        let config = provider.config();
        let span_limits = config.span_limits;
        let span_id = builder
            .span_id
            .take()
            .unwrap_or_else(|| config.id_generator.new_span_id());
        let span_kind = builder.span_kind.take().unwrap_or(SpanKind::Internal);
        let mut attribute_options = builder.attributes.take().unwrap_or_default();
        let mut link_options = builder.links.take();
        let mut no_parent = true;
        let mut remote_parent = false;
        let mut parent_span_id = SpanId::INVALID;
        let mut parent_trace_flags = TraceFlags::default();
        let trace_id;

        let parent_span = if parent_cx.has_active_span() {
            Some(parent_cx.span())
        } else {
            None
        };

        // Build context for sampling decision
        if let Some(sc) = parent_span.as_ref().map(|parent| parent.span_context()) {
            no_parent = false;
            remote_parent = sc.is_remote();
            parent_span_id = sc.span_id();
            parent_trace_flags = sc.trace_flags();
            trace_id = sc.trace_id();
        } else {
            trace_id = builder
                .trace_id
                .unwrap_or_else(|| config.id_generator.new_trace_id());
        };

        // There are 3 paths for sampling.
        //
        // * Sampling has occurred elsewhere and is already stored in the builder
        // * There is no parent or a remote parent, in which case make decision now
        // * There is a local parent, in which case defer to the parent's decision
        let sampling_decision = if let Some(sampling_result) = builder.sampling_result.take() {
            self.process_sampling_result(sampling_result, parent_cx)
        } else if no_parent || remote_parent {
            self.make_sampling_decision(
                parent_cx,
                trace_id,
                &builder.name,
                &span_kind,
                &attribute_options,
                link_options.as_deref().unwrap_or(&[]),
                provider.config(),
                &self.instrumentation_lib,
            )
        } else {
            // has parent that is local: use parent if sampled, or don't record.
            parent_span
                .filter(|span| span.span_context().is_sampled())
                .map(|span| {
                    (
                        parent_trace_flags,
                        Vec::new(),
                        span.span_context().trace_state().clone(),
                    )
                })
        };

        let SpanBuilder {
            name,
            start_time,
            end_time,
            events,
            status,
            ..
        } = builder;

        // Build optional inner context, `None` if not recording.
        let mut span = if let Some((flags, extra_attrs, trace_state)) = sampling_decision {
            for extra_attr in extra_attrs {
                attribute_options.insert(extra_attr.key, extra_attr.value);
            }
            let mut attributes =
                EvictedHashMap::new(span_limits.max_attributes_per_span, attribute_options.len());
            for (key, value) in attribute_options {
                attributes.insert(KeyValue::new(key, value));
            }
            let mut links = EvictedQueue::new(span_limits.max_links_per_span);
            if let Some(link_options) = &mut link_options {
                let link_attributes_limit = span_limits.max_attributes_per_link as usize;
                for link in link_options.iter_mut() {
                    let dropped_attributes_count =
                        link.attributes.len().saturating_sub(link_attributes_limit);
                    link.attributes.truncate(link_attributes_limit);
                    link.dropped_attributes_count = dropped_attributes_count as u32;
                }
                links.append_vec(link_options);
            }
            let start_time = start_time.unwrap_or_else(opentelemetry_api::time::now);
            let end_time = end_time.unwrap_or(start_time);
            let mut events_queue = EvictedQueue::new(span_limits.max_events_per_span);
            if let Some(mut events) = events {
                let event_attributes_limit = span_limits.max_attributes_per_event as usize;
                for event in events.iter_mut() {
                    let dropped_attributes_count = event
                        .attributes
                        .len()
                        .saturating_sub(event_attributes_limit);
                    event.attributes.truncate(event_attributes_limit);
                    event.dropped_attributes_count = dropped_attributes_count as u32;
                }
                events_queue.append_vec(&mut events);
            }

            let span_context = SpanContext::new(trace_id, span_id, flags, false, trace_state);
            Span::new(
                span_context,
                Some(SpanData {
                    parent_span_id,
                    span_kind,
                    name,
                    start_time,
                    end_time,
                    attributes,
                    events: events_queue,
                    links,
                    status,
                }),
                self.clone(),
                span_limits,
            )
        } else {
            let span_context = SpanContext::new(
                trace_id,
                span_id,
                TraceFlags::default(),
                false,
                Default::default(),
            );
            Span::new(span_context, None, self.clone(), span_limits)
        };

        // Call `on_start` for all processors
        for processor in provider.span_processors() {
            processor.on_start(&mut span, parent_cx)
        }

        span
    }
}

#[cfg(all(test, feature = "testing", feature = "trace"))]
mod tests {
    use crate::{
        testing::trace::TestSpan,
        trace::{Config, Sampler, ShouldSample},
        InstrumentationLibrary,
    };
    use opentelemetry_api::{
        trace::{
            Link, OrderMap, SamplingDecision, SamplingResult, Span, SpanContext, SpanId, SpanKind,
            TraceContextExt, TraceFlags, TraceId, TraceState, Tracer, TracerProvider,
        },
        Context, Key, Value,
    };

    #[derive(Clone, Debug)]
    struct TestSampler {}

    impl ShouldSample for TestSampler {
        fn should_sample(
            &self,
            parent_context: Option<&Context>,
            _trace_id: TraceId,
            _name: &str,
            _span_kind: &SpanKind,
            _attributes: &OrderMap<Key, Value>,
            _links: &[Link],
            _instrumentation_library: &InstrumentationLibrary,
        ) -> SamplingResult {
            let trace_state = parent_context
                .unwrap()
                .span()
                .span_context()
                .trace_state()
                .clone();
            SamplingResult {
                decision: SamplingDecision::RecordAndSample,
                attributes: Vec::new(),
                trace_state: trace_state.insert("foo", "notbar").unwrap(),
            }
        }
    }

    #[test]
    fn allow_sampler_to_change_trace_state() {
        // Setup
        let sampler = TestSampler {};
        let config = Config::default().with_sampler(sampler);
        let tracer_provider = crate::trace::TracerProvider::builder()
            .with_config(config)
            .build();
        let tracer = tracer_provider.tracer("test");
        let trace_state = TraceState::from_key_value(vec![("foo", "bar")]).unwrap();

        let parent_context = Context::new().with_span(TestSpan(SpanContext::new(
            TraceId::from_u128(128),
            SpanId::from_u64(64),
            TraceFlags::SAMPLED,
            true,
            trace_state,
        )));

        // Test sampler should change trace state
        let span = tracer.start_with_context("foo", &parent_context);
        let span_context = span.span_context();
        let expected = span_context.trace_state();
        assert_eq!(expected.get("foo"), Some("notbar"))
    }

    #[test]
    fn drop_parent_based_children() {
        let sampler = Sampler::ParentBased(Box::new(Sampler::AlwaysOn));
        let config = Config::default().with_sampler(sampler);
        let tracer_provider = crate::trace::TracerProvider::builder()
            .with_config(config)
            .build();

        let context = Context::current_with_span(TestSpan(SpanContext::empty_context()));
        let tracer = tracer_provider.tracer("test");
        let span = tracer.start_with_context("must_not_be_sampled", &context);

        assert!(!span.span_context().is_sampled());
    }

    #[test]
    fn uses_current_context_for_builders_if_unset() {
        let sampler = Sampler::ParentBased(Box::new(Sampler::AlwaysOn));
        let config = Config::default().with_sampler(sampler);
        let tracer_provider = crate::trace::TracerProvider::builder()
            .with_config(config)
            .build();
        let tracer = tracer_provider.tracer("test");

        let _attached = Context::current_with_span(TestSpan(SpanContext::empty_context())).attach();
        let span = tracer.span_builder("must_not_be_sampled").start(&tracer);
        assert!(!span.span_context().is_sampled());

        let _attached = Context::current()
            .with_remote_span_context(SpanContext::new(
                TraceId::from_u128(1),
                SpanId::from_u64(1),
                TraceFlags::default(),
                true,
                Default::default(),
            ))
            .attach();
        let span = tracer.span_builder("must_not_be_sampled").start(&tracer);

        assert!(!span.span_context().is_sampled());
    }
}
<commit_msg>Perform sampling as explained in the specification. (#839)<commit_after>//! # Tracer
//!
//! The OpenTelemetry library achieves in-process context propagation of
//! `Span`s by way of the `Tracer`.
//!
//! The `Tracer` is responsible for tracking the currently active `Span`,
//! and exposes methods for creating and activating new `Spans`.
//!
//! Docs: <https://github.com/open-telemetry/opentelemetry-specification/blob/v1.3.0/specification/trace/api.md#tracer>
use crate::trace::SpanLimits;
use crate::{
    trace::{
        provider::{TracerProvider, TracerProviderInner},
        span::{Span, SpanData},
        Config, EvictedHashMap, EvictedQueue,
    },
    InstrumentationLibrary,
};
use opentelemetry_api::trace::{
    Link, OrderMap, SamplingDecision, SamplingResult, SpanBuilder, SpanContext, SpanId, SpanKind,
    TraceContextExt, TraceFlags, TraceId, TraceState,
};
use opentelemetry_api::{Context, Key, KeyValue, Value};
use std::fmt;
use std::sync::Weak;

/// `Tracer` implementation to create and manage spans
#[derive(Clone)]
pub struct Tracer {
    instrumentation_lib: InstrumentationLibrary,
    provider: Weak<TracerProviderInner>,
}

impl fmt::Debug for Tracer {
    /// Formats the `Tracer` using the given formatter.
    /// Omitting `provider` here is necessary to avoid cycles.
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Tracer")
            .field("name", &self.instrumentation_lib.name)
            .field("version", &self.instrumentation_lib.version)
            .finish()
    }
}

impl Tracer {
    /// Create a new tracer (used internally by `TracerProvider`s).
    pub(crate) fn new(
        instrumentation_lib: InstrumentationLibrary,
        provider: Weak<TracerProviderInner>,
    ) -> Self {
        Tracer {
            instrumentation_lib,
            provider,
        }
    }

    /// TracerProvider associated with this tracer.
    pub fn provider(&self) -> Option<TracerProvider> {
        self.provider.upgrade().map(TracerProvider::new)
    }

    /// Instrumentation library information of this tracer.
    pub fn instrumentation_library(&self) -> &InstrumentationLibrary {
        &self.instrumentation_lib
    }

    /// Make a sampling decision using the provided sampler for the span and context.
    #[allow(clippy::too_many_arguments)]
    fn make_sampling_decision(
        &self,
        parent_cx: &Context,
        trace_id: TraceId,
        name: &str,
        span_kind: &SpanKind,
        attributes: &OrderMap<Key, Value>,
        links: &[Link],
        config: &Config,
        instrumentation_library: &InstrumentationLibrary,
    ) -> Option<(TraceFlags, Vec<KeyValue>, TraceState)> {
        let sampling_result = config.sampler.should_sample(
            Some(parent_cx),
            trace_id,
            name,
            span_kind,
            attributes,
            links,
            instrumentation_library,
        );

        self.process_sampling_result(sampling_result, parent_cx)
    }

    fn process_sampling_result(
        &self,
        sampling_result: SamplingResult,
        parent_cx: &Context,
    ) -> Option<(TraceFlags, Vec<KeyValue>, TraceState)> {
        match sampling_result {
            SamplingResult {
                decision: SamplingDecision::Drop,
                ..
            } => None,
            SamplingResult {
                decision: SamplingDecision::RecordOnly,
                attributes,
                trace_state,
            } => {
                let trace_flags = parent_cx.span().span_context().trace_flags();
                Some((trace_flags.with_sampled(false), attributes, trace_state))
            }
            SamplingResult {
                decision: SamplingDecision::RecordAndSample,
                attributes,
                trace_state,
            } => {
                let trace_flags = parent_cx.span().span_context().trace_flags();
                Some((trace_flags.with_sampled(true), attributes, trace_state))
            }
        }
    }
}

impl opentelemetry_api::trace::Tracer for Tracer {
    /// This implementation of `Tracer` produces `sdk::Span` instances.
    type Span = Span;

    /// Starts a span from a `SpanBuilder`.
    ///
    /// Each span has zero or one parent spans and zero or more child spans, which
    /// represent causally related operations. A tree of related spans comprises a
    /// trace. A span is said to be a _root span_ if it does not have a parent. Each
    /// trace includes a single root span, which is the shared ancestor of all other
    /// spans in the trace.
    fn build_with_context(&self, mut builder: SpanBuilder, parent_cx: &Context) -> Self::Span {
        let provider = self.provider();
        if provider.is_none() {
            return Span::new(
                SpanContext::empty_context(),
                None,
                self.clone(),
                SpanLimits::default(),
            );
        }

        let provider = provider.unwrap();
        let config = provider.config();
        let span_limits = config.span_limits;
        let span_id = builder
            .span_id
            .take()
            .unwrap_or_else(|| config.id_generator.new_span_id());
        let span_kind = builder.span_kind.take().unwrap_or(SpanKind::Internal);
        let mut attribute_options = builder.attributes.take().unwrap_or_default();
        let mut link_options = builder.links.take();
        let mut parent_span_id = SpanId::INVALID;
        let trace_id;

        let parent_span = if parent_cx.has_active_span() {
            Some(parent_cx.span())
        } else {
            None
        };

        // Build context for sampling decision
        if let Some(sc) = parent_span.as_ref().map(|parent| parent.span_context()) {
            parent_span_id = sc.span_id();
            trace_id = sc.trace_id();
        } else {
            trace_id = builder
                .trace_id
                .unwrap_or_else(|| config.id_generator.new_trace_id());
        };

        // In order to accomodate use cases like `tracing-opentelemetry` we there is the ability
        // to use pre-sampling. Otherwise, the standard method of sampling is followed.
        let sampling_decision = if let Some(sampling_result) = builder.sampling_result.take() {
            self.process_sampling_result(sampling_result, parent_cx)
        } else {
            self.make_sampling_decision(
                parent_cx,
                trace_id,
                &builder.name,
                &span_kind,
                &attribute_options,
                link_options.as_deref().unwrap_or(&[]),
                provider.config(),
                &self.instrumentation_lib,
            )
        };

        let SpanBuilder {
            name,
            start_time,
            end_time,
            events,
            status,
            ..
        } = builder;

        // Build optional inner context, `None` if not recording.
        let mut span = if let Some((flags, extra_attrs, trace_state)) = sampling_decision {
            for extra_attr in extra_attrs {
                attribute_options.insert(extra_attr.key, extra_attr.value);
            }
            let mut attributes =
                EvictedHashMap::new(span_limits.max_attributes_per_span, attribute_options.len());
            for (key, value) in attribute_options {
                attributes.insert(KeyValue::new(key, value));
            }
            let mut links = EvictedQueue::new(span_limits.max_links_per_span);
            if let Some(link_options) = &mut link_options {
                let link_attributes_limit = span_limits.max_attributes_per_link as usize;
                for link in link_options.iter_mut() {
                    let dropped_attributes_count =
                        link.attributes.len().saturating_sub(link_attributes_limit);
                    link.attributes.truncate(link_attributes_limit);
                    link.dropped_attributes_count = dropped_attributes_count as u32;
                }
                links.append_vec(link_options);
            }
            let start_time = start_time.unwrap_or_else(opentelemetry_api::time::now);
            let end_time = end_time.unwrap_or(start_time);
            let mut events_queue = EvictedQueue::new(span_limits.max_events_per_span);
            if let Some(mut events) = events {
                let event_attributes_limit = span_limits.max_attributes_per_event as usize;
                for event in events.iter_mut() {
                    let dropped_attributes_count = event
                        .attributes
                        .len()
                        .saturating_sub(event_attributes_limit);
                    event.attributes.truncate(event_attributes_limit);
                    event.dropped_attributes_count = dropped_attributes_count as u32;
                }
                events_queue.append_vec(&mut events);
            }

            let span_context = SpanContext::new(trace_id, span_id, flags, false, trace_state);
            Span::new(
                span_context,
                Some(SpanData {
                    parent_span_id,
                    span_kind,
                    name,
                    start_time,
                    end_time,
                    attributes,
                    events: events_queue,
                    links,
                    status,
                }),
                self.clone(),
                span_limits,
            )
        } else {
            let span_context = SpanContext::new(
                trace_id,
                span_id,
                TraceFlags::default(),
                false,
                Default::default(),
            );
            Span::new(span_context, None, self.clone(), span_limits)
        };

        // Call `on_start` for all processors
        for processor in provider.span_processors() {
            processor.on_start(&mut span, parent_cx)
        }

        span
    }
}

#[cfg(all(test, feature = "testing", feature = "trace"))]
mod tests {
    use crate::{
        testing::trace::TestSpan,
        trace::{Config, Sampler, ShouldSample},
        InstrumentationLibrary,
    };
    use opentelemetry_api::{
        trace::{
            Link, OrderMap, SamplingDecision, SamplingResult, Span, SpanContext, SpanId, SpanKind,
            TraceContextExt, TraceFlags, TraceId, TraceState, Tracer, TracerProvider,
        },
        Context, Key, Value,
    };

    #[derive(Clone, Debug)]
    struct TestSampler {}

    impl ShouldSample for TestSampler {
        fn should_sample(
            &self,
            parent_context: Option<&Context>,
            _trace_id: TraceId,
            _name: &str,
            _span_kind: &SpanKind,
            _attributes: &OrderMap<Key, Value>,
            _links: &[Link],
            _instrumentation_library: &InstrumentationLibrary,
        ) -> SamplingResult {
            let trace_state = parent_context
                .unwrap()
                .span()
                .span_context()
                .trace_state()
                .clone();
            SamplingResult {
                decision: SamplingDecision::RecordAndSample,
                attributes: Vec::new(),
                trace_state: trace_state.insert("foo", "notbar").unwrap(),
            }
        }
    }

    #[test]
    fn allow_sampler_to_change_trace_state() {
        // Setup
        let sampler = TestSampler {};
        let config = Config::default().with_sampler(sampler);
        let tracer_provider = crate::trace::TracerProvider::builder()
            .with_config(config)
            .build();
        let tracer = tracer_provider.tracer("test");
        let trace_state = TraceState::from_key_value(vec![("foo", "bar")]).unwrap();

        let parent_context = Context::new().with_span(TestSpan(SpanContext::new(
            TraceId::from_u128(128),
            SpanId::from_u64(64),
            TraceFlags::SAMPLED,
            true,
            trace_state,
        )));

        // Test sampler should change trace state
        let span = tracer.start_with_context("foo", &parent_context);
        let span_context = span.span_context();
        let expected = span_context.trace_state();
        assert_eq!(expected.get("foo"), Some("notbar"))
    }

    #[test]
    fn drop_parent_based_children() {
        let sampler = Sampler::ParentBased(Box::new(Sampler::AlwaysOn));
        let config = Config::default().with_sampler(sampler);
        let tracer_provider = crate::trace::TracerProvider::builder()
            .with_config(config)
            .build();

        let context = Context::current_with_span(TestSpan(SpanContext::empty_context()));
        let tracer = tracer_provider.tracer("test");
        let span = tracer.start_with_context("must_not_be_sampled", &context);

        assert!(!span.span_context().is_sampled());
    }

    #[test]
    fn uses_current_context_for_builders_if_unset() {
        let sampler = Sampler::ParentBased(Box::new(Sampler::AlwaysOn));
        let config = Config::default().with_sampler(sampler);
        let tracer_provider = crate::trace::TracerProvider::builder()
            .with_config(config)
            .build();
        let tracer = tracer_provider.tracer("test");

        let _attached = Context::current_with_span(TestSpan(SpanContext::empty_context())).attach();
        let span = tracer.span_builder("must_not_be_sampled").start(&tracer);
        assert!(!span.span_context().is_sampled());

        let _attached = Context::current()
            .with_remote_span_context(SpanContext::new(
                TraceId::from_u128(1),
                SpanId::from_u64(1),
                TraceFlags::default(),
                true,
                Default::default(),
            ))
            .attach();
        let span = tracer.span_builder("must_not_be_sampled").start(&tracer);

        assert!(!span.span_context().is_sampled());
    }
}
<|endoftext|>


<commit_before>//! A LinkedList that lets you insert elements from the front or the end of the list.
//! TODO: Add the ability to insert in-between a Node.
//! Lots of thanks to reem and Sharp for help with this from the #rust channel

use std::mem;

/// A Node is the basic unit in a LinkedList which stores the payload of type T.
#[derive(Debug,PartialEq)]
struct Node<T> {
    /// The payload of type T that is wrapped aorund a Node.
    pub payload: T,
    /// An Option of a boxed Node<T> that would point to the next Node<T>.
    next: Option<Box<Node<T>>>,
    /// An Option of a boxed Node<T> that would point to the previous Node<T>.
    prev: Option<Box<Node<T>>>,
}

impl<T> Node<T> {

    pub fn new(payload: T) -> Node<T> {
        //! Creates a instance of a `Node` with `next` and `prev` point to a Option::Node.
        Node::<T> {
            payload: payload,
            next: None,
            prev: None,
        }
    }

    pub fn payload(&self) -> Option<&T> {
        //! Access to a payload of Node.
        Some(&self.payload)
    }

    pub fn insert_after(&mut self, mut new: Box<Node<T>>) -> Option<Box<Node<T>>> {
        //! Takes a new boxed Node<T> and swaps it with any existing Node after the current one.
        //! We replace any Option::None or Option::Some with a new Option<Box<Node<T>>>
        mem::swap(&mut new.next, &mut self.next);
        mem::replace(&mut self.next, Some(new))
    }

    pub fn insert_before(&mut self, mut new: Box<Node<T>>) -> Option<Box<Node<T>>> {
        //! Takes a new boxed Node<T> and swaps it with any existing Node after the current one.
        //! We replace any Option::None or Option::Some with a new Option<Box<Node<T>>>
        mem::swap(&mut new.prev, &mut self.prev);
        mem::replace(&mut self.prev, Some(new))
    }

    pub fn remove_after(&mut self) -> Option<Box<Node<T>>> {
        //! Removes the Node after the current node.
        match self.next.take() {
            Some(mut next)  => {
                mem::swap(&mut self.next, &mut next.next);
                Some(next)
            },
            None => None,
        }
    }

    pub fn remove_before(&mut self) -> Option<Box<Node<T>>> {
        //! Removes the Node before the current node.
        match self.prev.take() {
            Some(mut prev)  => {
                mem::swap(&mut self.prev, &mut prev.prev);
                Some(prev)
            },
            None => None,
        }
    }

    pub fn next(&mut self) -> Option<&mut T> {
        //! Goes to the next Node and returns an Option to it.
        let Node {
            ref mut payload,
            ref mut prev,
            ref mut next,
        } = *self;

        match *next {
            Some(ref mut next) => {
                mem::swap(&mut next.payload, payload);

                let mut next = &mut **next;     // Cannot completely understand this bit yet.
                mem::swap(&mut next.next, &mut next.prev);

                mem::swap(&mut next.prev, prev);
            },
            None => return None,
        }
        mem::swap(prev, next);
        Some(payload)
    }

    pub fn prev(&mut self) -> Option<&mut T> {
        let Node {
            ref mut payload,
            ref mut prev,
            ref mut next,
        } = *self;

        match *prev {
            Some(ref mut prev) => {
                mem::swap(&mut prev.payload, payload);

                let mut prev = &mut **prev;     // Still don't understand this bit.
                mem::swap(&mut prev.prev, &mut prev.next);

                mem::swap(&mut prev.next, next);
            },
            None => return None,
        }
        mem::swap(prev, next);
        Some(payload)
    }
}

/// A LinkedList is the struct that you would need to use to create a list of elements.
/// It contains a pointer to the first and last node in the list represented as `head and `tail`
/// respectively.
pub struct LinkedList<T> {
    /// First element in the list.
    head: Option<Box<Node<T>>>,
    /// Last element in the list.
    tail: Option<Box<Node<T>>>,
    /// Number of nodes in the list.
    size: usize,
}

impl<T> LinkedList<T> {
    pub fn new() -> LinkedList<T> {
        //! Create a new LinkedList to contain a payloads with type T.
        LinkedList::<T> {
            head: None,
            tail: None,
            size: 0,
        }
    }

    pub fn push_back(&mut self, mut new: T) {
        let new_node = Box::new(Node::new(new));
        if self.size == 0 {
            // Then `head` and `tail` need to point to the same node.
            // But can I do that in rust? Have one point as mutable and the other as immutable?!

            // FIXME Create a reference to `head` and `tail` from new_node

            // FIXME Remove placeholder linked Node for tail
            match self.tail {
                Some(ref mut tail_node) => {
                    tail_node.insert_before(new_node);
                },
                None => {
                    mem::swap(&mut self.tail, &mut Some(new_node));
                },
            }
        } else {
            match self.tail {
                Some(ref mut tail_node) => {
                    tail_node.insert_before(new_node);
                    // FIXME Replace tail_node with new_node reference
                },
                None => {
                    mem::swap(&mut self.tail, &mut Some(new_node));
                },
            }
        }
        self.size += 1;
    }
}

#[test]
fn node_new() {
    let expected = Node {
        payload: "test",
        next: None,
        prev: None,
    };

    let node: Node<&str> = Node::new("test");

    assert_eq!(expected.payload,  node.payload);
    assert_eq!(expected.next,     node.next);
    assert_eq!(expected.prev,     node.prev);
}

#[test]
fn node_payload() {
    let node: Node<&str> = Node::new("test");
    assert_eq!(&"test", node.payload().unwrap());
}

#[test]
fn node_insert_after() {
    let mut node: Node<&str> = Node::new("one");
    node.insert_after(Box::new(Node::new("two")));
    assert_eq!("two", node.next.unwrap().payload);
}

#[test]
fn node_insert_before() {
    let mut node: Node<&str> = Node::new("two");
    node.insert_before(Box::new(Node::new("one")));
    assert_eq!("one", node.prev.unwrap().payload);
}

#[test]
fn node_remove_next() {
    let mut node: Node<&str> = Node::new("one");
    node.insert_after(Box::new(Node::new("two")));
    node.remove_after();
}

#[test]
fn node_remove_prev() {
    let mut node: Node<&str> = Node::new("one");
    node.insert_before(Box::new(Node::new("two")));
    node.remove_before();
}

#[test]
fn node_next() {
    let mut node: Node<&str> = Node::new("one");
    node.insert_after(Box::new(Node::new("two")));
    assert_eq!("two", *node.next().unwrap());
}

#[test]
fn node_prev() {
    let mut node: Node<&str> = Node::new("two");
    node.insert_before(Box::new(Node::new("one")));
    assert_eq!("one", *node.prev().unwrap());
}

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

    //#[test]
    fn test_push_back() {
        let mut list: LinkedList<String> = LinkedList::new();
        list.push_back("one".to_string());
        assert_eq!("one".to_string(), list.tail.unwrap().payload);
    }

    #[test]
    fn test_push_back_two() {
        let mut list: LinkedList<String> = LinkedList::new();
        list.push_back("one".to_string());
        list.push_back("two".to_string());
        //assert_eq!("one".to_string(), list.tail.unwrap().payload);
        assert_eq!("one".to_string(), list.tail.unwrap().prev.unwrap().payload);
    }

}
<commit_msg>[Github-Todos] Inject issue numbers<commit_after>//! A LinkedList that lets you insert elements from the front or the end of the list.
//! TODO: Add the ability to insert in-between a Node.
//! Lots of thanks to reem and Sharp for help with this from the #rust channel

use std::mem;

/// A Node is the basic unit in a LinkedList which stores the payload of type T.
#[derive(Debug,PartialEq)]
struct Node<T> {
    /// The payload of type T that is wrapped aorund a Node.
    pub payload: T,
    /// An Option of a boxed Node<T> that would point to the next Node<T>.
    next: Option<Box<Node<T>>>,
    /// An Option of a boxed Node<T> that would point to the previous Node<T>.
    prev: Option<Box<Node<T>>>,
}

impl<T> Node<T> {

    pub fn new(payload: T) -> Node<T> {
        //! Creates a instance of a `Node` with `next` and `prev` point to a Option::Node.
        Node::<T> {
            payload: payload,
            next: None,
            prev: None,
        }
    }

    pub fn payload(&self) -> Option<&T> {
        //! Access to a payload of Node.
        Some(&self.payload)
    }

    pub fn insert_after(&mut self, mut new: Box<Node<T>>) -> Option<Box<Node<T>>> {
        //! Takes a new boxed Node<T> and swaps it with any existing Node after the current one.
        //! We replace any Option::None or Option::Some with a new Option<Box<Node<T>>>
        mem::swap(&mut new.next, &mut self.next);
        mem::replace(&mut self.next, Some(new))
    }

    pub fn insert_before(&mut self, mut new: Box<Node<T>>) -> Option<Box<Node<T>>> {
        //! Takes a new boxed Node<T> and swaps it with any existing Node after the current one.
        //! We replace any Option::None or Option::Some with a new Option<Box<Node<T>>>
        mem::swap(&mut new.prev, &mut self.prev);
        mem::replace(&mut self.prev, Some(new))
    }

    pub fn remove_after(&mut self) -> Option<Box<Node<T>>> {
        //! Removes the Node after the current node.
        match self.next.take() {
            Some(mut next)  => {
                mem::swap(&mut self.next, &mut next.next);
                Some(next)
            },
            None => None,
        }
    }

    pub fn remove_before(&mut self) -> Option<Box<Node<T>>> {
        //! Removes the Node before the current node.
        match self.prev.take() {
            Some(mut prev)  => {
                mem::swap(&mut self.prev, &mut prev.prev);
                Some(prev)
            },
            None => None,
        }
    }

    pub fn next(&mut self) -> Option<&mut T> {
        //! Goes to the next Node and returns an Option to it.
        let Node {
            ref mut payload,
            ref mut prev,
            ref mut next,
        } = *self;

        match *next {
            Some(ref mut next) => {
                mem::swap(&mut next.payload, payload);

                let mut next = &mut **next;     // Cannot completely understand this bit yet.
                mem::swap(&mut next.next, &mut next.prev);

                mem::swap(&mut next.prev, prev);
            },
            None => return None,
        }
        mem::swap(prev, next);
        Some(payload)
    }

    pub fn prev(&mut self) -> Option<&mut T> {
        let Node {
            ref mut payload,
            ref mut prev,
            ref mut next,
        } = *self;

        match *prev {
            Some(ref mut prev) => {
                mem::swap(&mut prev.payload, payload);

                let mut prev = &mut **prev;     // Still don't understand this bit.
                mem::swap(&mut prev.prev, &mut prev.next);

                mem::swap(&mut prev.next, next);
            },
            None => return None,
        }
        mem::swap(prev, next);
        Some(payload)
    }
}

/// A LinkedList is the struct that you would need to use to create a list of elements.
/// It contains a pointer to the first and last node in the list represented as `head and `tail`
/// respectively.
pub struct LinkedList<T> {
    /// First element in the list.
    head: Option<Box<Node<T>>>,
    /// Last element in the list.
    tail: Option<Box<Node<T>>>,
    /// Number of nodes in the list.
    size: usize,
}

impl<T> LinkedList<T> {
    pub fn new() -> LinkedList<T> {
        //! Create a new LinkedList to contain a payloads with type T.
        LinkedList::<T> {
            head: None,
            tail: None,
            size: 0,
        }
    }

    pub fn push_back(&mut self, mut new: T) {
        let new_node = Box::new(Node::new(new));
        if self.size == 0 {
            // Then `head` and `tail` need to point to the same node.
            // But can I do that in rust? Have one point as mutable and the other as immutable?!

            // FIXME Create a reference to `head` and `tail` from new_node

            // FIXME Remove placeholder linked Node for tail
            match self.tail {
                Some(ref mut tail_node) => {
                    tail_node.insert_before(new_node);
                },
                None => {
                    mem::swap(&mut self.tail, &mut Some(new_node));
                },
            }
        } else {
            match self.tail {
                Some(ref mut tail_node) => {
                    tail_node.insert_before(new_node);
                    // FIXME #9 Replace tail_node with new_node reference
                },
                None => {
                    mem::swap(&mut self.tail, &mut Some(new_node));
                },
            }
        }
        self.size += 1;
    }
}

#[test]
fn node_new() {
    let expected = Node {
        payload: "test",
        next: None,
        prev: None,
    };

    let node: Node<&str> = Node::new("test");

    assert_eq!(expected.payload,  node.payload);
    assert_eq!(expected.next,     node.next);
    assert_eq!(expected.prev,     node.prev);
}

#[test]
fn node_payload() {
    let node: Node<&str> = Node::new("test");
    assert_eq!(&"test", node.payload().unwrap());
}

#[test]
fn node_insert_after() {
    let mut node: Node<&str> = Node::new("one");
    node.insert_after(Box::new(Node::new("two")));
    assert_eq!("two", node.next.unwrap().payload);
}

#[test]
fn node_insert_before() {
    let mut node: Node<&str> = Node::new("two");
    node.insert_before(Box::new(Node::new("one")));
    assert_eq!("one", node.prev.unwrap().payload);
}

#[test]
fn node_remove_next() {
    let mut node: Node<&str> = Node::new("one");
    node.insert_after(Box::new(Node::new("two")));
    node.remove_after();
}

#[test]
fn node_remove_prev() {
    let mut node: Node<&str> = Node::new("one");
    node.insert_before(Box::new(Node::new("two")));
    node.remove_before();
}

#[test]
fn node_next() {
    let mut node: Node<&str> = Node::new("one");
    node.insert_after(Box::new(Node::new("two")));
    assert_eq!("two", *node.next().unwrap());
}

#[test]
fn node_prev() {
    let mut node: Node<&str> = Node::new("two");
    node.insert_before(Box::new(Node::new("one")));
    assert_eq!("one", *node.prev().unwrap());
}

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

    //#[test]
    fn test_push_back() {
        let mut list: LinkedList<String> = LinkedList::new();
        list.push_back("one".to_string());
        assert_eq!("one".to_string(), list.tail.unwrap().payload);
    }

    #[test]
    fn test_push_back_two() {
        let mut list: LinkedList<String> = LinkedList::new();
        list.push_back("one".to_string());
        list.push_back("two".to_string());
        //assert_eq!("one".to_string(), list.tail.unwrap().payload);
        assert_eq!("one".to_string(), list.tail.unwrap().prev.unwrap().payload);
    }

}
<|endoftext|>


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

#[macro_use]
extern crate log;

#[cfg(target_os = "windows")]
mod vslogger;

#[cfg(not(target_os = "windows"))]
use env_logger;
use simpleservo::{self, gl_glue, ServoGlue, SERVO};
use simpleservo::{Coordinates, EventLoopWaker, HostTrait, InitOptions, VRInitOptions};
use std::ffi::{CStr, CString};
use std::mem;
use std::os::raw::{c_char, c_void};

fn call<F>(f: F)
where
    F: Fn(&mut ServoGlue) -> Result<(), &'static str>,
{
    SERVO.with(|s| {
        if let Err(error) = match s.borrow_mut().as_mut() {
            Some(ref mut s) => (f)(s),
            None => Err("Servo not available in this thread"),
        } {
            // FIXME: All C calls should have a have generic Result-like
            // return type. For now, we just panic instead of notifying
            // the embedder.
            panic!(error);
        }
    });
}

/// Callback used by Servo internals
#[repr(C)]
pub struct CHostCallbacks {
    pub flush: extern "C" fn(),
    pub make_current: extern "C" fn(),
    pub on_load_started: extern "C" fn(),
    pub on_load_ended: extern "C" fn(),
    pub on_title_changed: extern "C" fn(title: *const c_char),
    pub on_allow_navigation: extern "C" fn(url: *const c_char) -> bool,
    pub on_url_changed: extern "C" fn(url: *const c_char),
    pub on_history_changed: extern "C" fn(can_go_back: bool, can_go_forward: bool),
    pub on_animating_changed: extern "C" fn(animating: bool),
    pub on_shutdown_complete: extern "C" fn(),
    pub on_ime_state_changed: extern "C" fn(show: bool),
    pub get_clipboard_contents: extern "C" fn() -> *const c_char,
    pub set_clipboard_contents: extern "C" fn(contents: *const c_char),
}

/// Servo options
#[repr(C)]
pub struct CInitOptions {
    pub args: *const c_char,
    pub url: *const c_char,
    pub width: i32,
    pub height: i32,
    pub density: f32,
    pub vr_pointer: *mut c_void,
    pub enable_subpixel_text_antialiasing: bool,
}

/// The returned string is not freed. This will leak.
#[no_mangle]
pub extern "C" fn servo_version() -> *const c_char {
    let v = simpleservo::servo_version();
    let text = CString::new(v).expect("Can't create string");
    let ptr = text.as_ptr();
    mem::forget(text);
    ptr
}

#[cfg(target_os = "windows")]
fn init_logger() {
    use log::LevelFilter;
    use std::sync::Once;
    use vslogger::VSLogger;

    static LOGGER: VSLogger = VSLogger;
    static LOGGER_INIT: Once = Once::new();
    LOGGER_INIT.call_once(|| {
        log::set_logger(&LOGGER)
            .map(|_| log::set_max_level(LevelFilter::Debug))
            .unwrap();
    });
}

#[cfg(not(target_os = "windows"))]
fn init_logger() {
    crate::env_logger::init();
}

unsafe fn init(
    opts: CInitOptions,
    gl: gl_glue::ServoGl,
    gl_context: Option<*const c_void>,
    display: Option<*const c_void>,
    wakeup: extern "C" fn(),
    callbacks: CHostCallbacks,
) {
    init_logger();

    let args = if !opts.args.is_null() {
        let args = CStr::from_ptr(opts.args);
        args.to_str()
            .unwrap_or("")
            .split(' ')
            .map(|s| s.to_owned())
            .collect()
    } else {
        vec![]
    };

    let url = CStr::from_ptr(opts.url);
    let url = url.to_str().map(|s| s.to_string()).ok();

    let coordinates = Coordinates::new(0, 0, opts.width, opts.height, opts.width, opts.height);

    let opts = InitOptions {
        args,
        url,
        coordinates,
        density: opts.density,
        vr_init: if opts.vr_pointer.is_null() {
            VRInitOptions::None
        } else {
            VRInitOptions::VRExternal(opts.vr_pointer)
        },
        enable_subpixel_text_antialiasing: opts.enable_subpixel_text_antialiasing,
        gl_context_pointer: gl_context,
        native_display_pointer: display,
    };

    let wakeup = Box::new(WakeupCallback::new(wakeup));
    let callbacks = Box::new(HostCallbacks::new(callbacks));

    simpleservo::init(opts, gl, wakeup, callbacks).unwrap();
}

#[cfg(target_os = "windows")]
#[no_mangle]
pub extern "C" fn init_with_egl(
    opts: CInitOptions,
    wakeup: extern "C" fn(),
    callbacks: CHostCallbacks,
) {
    init_logger();
    let gl = gl_glue::egl::init().unwrap();
    unsafe {
        init(
            opts,
            gl.gl_wrapper,
            Some(gl.gl_context),
            Some(gl.display),
            wakeup,
            callbacks,
        )
    }
}

#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
#[no_mangle]
pub extern "C" fn init_with_gl(
    opts: CInitOptions,
    wakeup: extern "C" fn(),
    callbacks: CHostCallbacks,
) {
    init_logger();
    let gl = gl_glue::gl::init().unwrap();
    unsafe { init(opts, gl, None, None, wakeup, callbacks) }
}

#[no_mangle]
pub extern "C" fn deinit() {
    debug!("deinit");
    simpleservo::deinit();
}

#[no_mangle]
pub extern "C" fn request_shutdown() {
    debug!("request_shutdown");
    call(|s| s.request_shutdown());
}

#[no_mangle]
pub extern "C" fn set_batch_mode(batch: bool) {
    debug!("set_batch_mode");
    call(|s| s.set_batch_mode(batch));
}

#[no_mangle]
pub extern "C" fn resize(width: i32, height: i32) {
    debug!("resize {}/{}", width, height);
    call(|s| {
        let coordinates = Coordinates::new(0, 0, width, height, width, height);
        s.resize(coordinates)
    });
}

#[no_mangle]
pub extern "C" fn perform_updates() {
    debug!("perform_updates");
    call(|s| s.perform_updates());
}

#[no_mangle]
pub extern "C" fn load_uri(url: *const c_char) {
    debug!("load_url");
    let url = unsafe { CStr::from_ptr(url) };
    let url = url.to_str().expect("Can't read string");
    call(|s| s.load_uri(url));
}

#[no_mangle]
pub extern "C" fn reload() {
    debug!("reload");
    call(|s| s.reload());
}

#[no_mangle]
pub extern "C" fn stop() {
    debug!("stop");
    call(|s| s.stop());
}

#[no_mangle]
pub extern "C" fn refresh() {
    debug!("refresh");
    call(|s| s.refresh());
}

#[no_mangle]
pub extern "C" fn go_back() {
    debug!("go_back");
    call(|s| s.go_back());
}

#[no_mangle]
pub extern "C" fn go_forward() {
    debug!("go_forward");
    call(|s| s.go_forward());
}

#[no_mangle]
pub extern "C" fn scroll_start(dx: i32, dy: i32, x: i32, y: i32) {
    debug!("scroll_start");
    call(|s| s.scroll_start(dx as f32, dy as f32, x, y));
}

#[no_mangle]
pub extern "C" fn scroll_end(dx: i32, dy: i32, x: i32, y: i32) {
    debug!("scroll_end");
    call(|s| s.scroll_end(dx as f32, dy as f32, x, y));
}

#[no_mangle]
pub extern "C" fn scroll(dx: i32, dy: i32, x: i32, y: i32) {
    debug!("scroll");
    call(|s| s.scroll(dx as f32, dy as f32, x, y));
}

#[no_mangle]
pub extern "C" fn touch_down(x: f32, y: f32, pointer_id: i32) {
    debug!("touch down");
    call(|s| s.touch_down(x, y, pointer_id));
}

#[no_mangle]
pub extern "C" fn touch_up(x: f32, y: f32, pointer_id: i32) {
    debug!("touch up");
    call(|s| s.touch_up(x, y, pointer_id));
}

#[no_mangle]
pub extern "C" fn touch_move(x: f32, y: f32, pointer_id: i32) {
    debug!("touch move");
    call(|s| s.touch_move(x, y, pointer_id));
}

#[no_mangle]
pub extern "C" fn touch_cancel(x: f32, y: f32, pointer_id: i32) {
    debug!("touch cancel");
    call(|s| s.touch_cancel(x, y, pointer_id));
}

#[no_mangle]
pub extern "C" fn pinchzoom_start(factor: f32, x: i32, y: i32) {
    debug!("pinchzoom_start");
    call(|s| s.pinchzoom_start(factor, x as u32, y as u32));
}

#[no_mangle]
pub extern "C" fn pinchzoom(factor: f32, x: i32, y: i32) {
    debug!("pinchzoom");
    call(|s| s.pinchzoom(factor, x as u32, y as u32));
}

#[no_mangle]
pub extern "C" fn pinchzoom_end(factor: f32, x: i32, y: i32) {
    debug!("pinchzoom_end");
    call(|s| s.pinchzoom_end(factor, x as u32, y as u32));
}

#[no_mangle]
pub extern "C" fn click(x: i32, y: i32) {
    debug!("click");
    call(|s| s.click(x as f32, y as f32));
}

pub struct WakeupCallback(extern "C" fn());

impl WakeupCallback {
    fn new(callback: extern "C" fn()) -> WakeupCallback {
        WakeupCallback(callback)
    }
}

impl EventLoopWaker for WakeupCallback {
    fn clone(&self) -> Box<dyn EventLoopWaker + Send> {
        Box::new(WakeupCallback(self.0))
    }
    fn wake(&self) {
        (self.0)();
    }
}

struct HostCallbacks(CHostCallbacks);

impl HostCallbacks {
    fn new(callback: CHostCallbacks) -> HostCallbacks {
        HostCallbacks(callback)
    }
}

impl HostTrait for HostCallbacks {
    fn flush(&self) {
        debug!("flush");
        (self.0.flush)();
    }

    fn make_current(&self) {
        debug!("make_current");
        (self.0.make_current)();
    }

    fn on_load_started(&self) {
        debug!("on_load_ended");
        (self.0.on_load_started)();
    }

    fn on_load_ended(&self) {
        debug!("on_load_ended");
        (self.0.on_load_ended)();
    }

    fn on_title_changed(&self, title: String) {
        debug!("on_title_changed");
        let title = CString::new(title).expect("Can't create string");
        let title_ptr = title.as_ptr();
        mem::forget(title);
        (self.0.on_title_changed)(title_ptr);
    }

    fn on_allow_navigation(&self, url: String) -> bool {
        debug!("on_allow_navigation");
        let url = CString::new(url).expect("Can't create string");
        let url_ptr = url.as_ptr();
        mem::forget(url);
        (self.0.on_allow_navigation)(url_ptr)
    }

    fn on_url_changed(&self, url: String) {
        debug!("on_url_changed");
        let url = CString::new(url).expect("Can't create string");
        let url_ptr = url.as_ptr();
        mem::forget(url);
        (self.0.on_url_changed)(url_ptr);
    }

    fn on_history_changed(&self, can_go_back: bool, can_go_forward: bool) {
        debug!("on_history_changed");
        (self.0.on_history_changed)(can_go_back, can_go_forward);
    }

    fn on_animating_changed(&self, animating: bool) {
        debug!("on_animating_changed");
        (self.0.on_animating_changed)(animating);
    }

    fn on_shutdown_complete(&self) {
        debug!("on_shutdown_complete");
        (self.0.on_shutdown_complete)();
    }

    fn on_ime_state_changed(&self, show: bool) {
        debug!("on_ime_state_changed");
        (self.0.on_ime_state_changed)(show);
    }

    fn get_clipboard_contents(&self) -> Option<String> {
        debug!("get_clipboard_contents");
        let raw_contents = (self.0.get_clipboard_contents)();
        if raw_contents.is_null() {
            return None;
        }
        let c_str = unsafe { CStr::from_ptr(raw_contents) };
        let contents_str = c_str.to_str().expect("Can't create str");
        Some(contents_str.to_owned())
    }

    fn set_clipboard_contents(&self, contents: String) {
        debug!("set_clipboard_contents");
        let contents = CString::new(contents).expect("Can't create string");
        let contents_ptr = contents.as_ptr();
        mem::forget(contents);
        (self.0.set_clipboard_contents)(contents_ptr);
    }
}
<commit_msg>Implemented fn catch_any_panic<> to catch any panic from "free" extern "C" functions returning bool. Modified "free" extern "C" functions to now be falliable by returning bool value.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

#[macro_use]
extern crate log;

#[cfg(target_os = "windows")]
mod vslogger;

#[cfg(not(target_os = "windows"))]
use env_logger;
use simpleservo::{self, gl_glue, ServoGlue, SERVO};
use simpleservo::{Coordinates, EventLoopWaker, HostTrait, InitOptions, VRInitOptions};
use std::ffi::{CStr, CString};
use std::mem;
use std::os::raw::{c_char, c_void};
use std::panic::{self, AssertUnwindSafe, UnwindSafe};

/// Catch any panic function used by extern "C" functions.
fn catch_any_panic<F: FnOnce() + UnwindSafe>(function: F) -> bool {
    panic::catch_unwind(function).is_ok()
}

fn call<F>(f: F)
where
    F: Fn(&mut ServoGlue) -> Result<(), &'static str>,
{
    SERVO.with(|s| {
        if let Err(error) = match s.borrow_mut().as_mut() {
            Some(ref mut s) => (f)(s),
            None => Err("Servo not available in this thread"),
        } {
            // FIXME: All C calls should have a have generic Result-like
            // return type. For now, we just panic instead of notifying
            // the embedder.
            panic!(error);
        }
    });
}

/// Callback used by Servo internals
#[repr(C)]
pub struct CHostCallbacks {
    pub flush: extern "C" fn(),
    pub make_current: extern "C" fn(),
    pub on_load_started: extern "C" fn(),
    pub on_load_ended: extern "C" fn(),
    pub on_title_changed: extern "C" fn(title: *const c_char),
    pub on_allow_navigation: extern "C" fn(url: *const c_char) -> bool,
    pub on_url_changed: extern "C" fn(url: *const c_char),
    pub on_history_changed: extern "C" fn(can_go_back: bool, can_go_forward: bool),
    pub on_animating_changed: extern "C" fn(animating: bool),
    pub on_shutdown_complete: extern "C" fn(),
    pub on_ime_state_changed: extern "C" fn(show: bool),
    pub get_clipboard_contents: extern "C" fn() -> *const c_char,
    pub set_clipboard_contents: extern "C" fn(contents: *const c_char),
}

/// Servo options
#[repr(C)]
pub struct CInitOptions {
    pub args: *const c_char,
    pub url: *const c_char,
    pub width: i32,
    pub height: i32,
    pub density: f32,
    pub vr_pointer: *mut c_void,
    pub enable_subpixel_text_antialiasing: bool,
}

/// The returned string is not freed. This will leak.
#[no_mangle]
pub extern "C" fn servo_version() -> *const c_char {
    let result = panic::catch_unwind(AssertUnwindSafe(|| {
        let v = simpleservo::servo_version();
        let text = CString::new(v).expect("Can't create string");
        let ptr: *const c_char = text.as_ptr();
        mem::forget(text);
        ptr
    }));

    match result {
        Ok(ptr) => ptr,
        Err(_) => std::ptr::null(),
    }
}

#[cfg(target_os = "windows")]
fn init_logger() {
    use log::LevelFilter;
    use std::sync::Once;
    use vslogger::VSLogger;

    static LOGGER: VSLogger = VSLogger;
    static LOGGER_INIT: Once = Once::new();
    LOGGER_INIT.call_once(|| {
        log::set_logger(&LOGGER)
            .map(|_| log::set_max_level(LevelFilter::Debug))
            .unwrap();
    });
}

#[cfg(not(target_os = "windows"))]
fn init_logger() {
    crate::env_logger::init();
}

unsafe fn init(
    opts: CInitOptions,
    gl: gl_glue::ServoGl,
    gl_context: Option<*const c_void>,
    display: Option<*const c_void>,
    wakeup: extern "C" fn(),
    callbacks: CHostCallbacks,
) {
    init_logger();

    let args = if !opts.args.is_null() {
        let args = CStr::from_ptr(opts.args);
        args.to_str()
            .unwrap_or("")
            .split(' ')
            .map(|s| s.to_owned())
            .collect()
    } else {
        vec![]
    };

    let url = CStr::from_ptr(opts.url);
    let url = url.to_str().map(|s| s.to_string()).ok();

    let coordinates = Coordinates::new(0, 0, opts.width, opts.height, opts.width, opts.height);

    let opts = InitOptions {
        args,
        url,
        coordinates,
        density: opts.density,
        vr_init: if opts.vr_pointer.is_null() {
            VRInitOptions::None
        } else {
            VRInitOptions::VRExternal(opts.vr_pointer)
        },
        enable_subpixel_text_antialiasing: opts.enable_subpixel_text_antialiasing,
        gl_context_pointer: gl_context,
        native_display_pointer: display,
    };

    let wakeup = Box::new(WakeupCallback::new(wakeup));
    let callbacks = Box::new(HostCallbacks::new(callbacks));

    simpleservo::init(opts, gl, wakeup, callbacks).unwrap();
}

#[cfg(target_os = "windows")]
#[no_mangle]
pub extern "C" fn init_with_egl(
    opts: CInitOptions,
    wakeup: extern "C" fn(),
    callbacks: CHostCallbacks,
) -> bool {
    catch_any_panic(|| {
        init_logger();
        let gl = gl_glue::egl::init().unwrap();
        unsafe {
            init(
                opts,
                gl.gl_wrapper,
                Some(gl.gl_context),
                Some(gl.display),
                wakeup,
                callbacks,
            )
        }
    })
}

#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
#[no_mangle]
pub extern "C" fn init_with_gl(
    opts: CInitOptions,
    wakeup: extern "C" fn(),
    callbacks: CHostCallbacks,
) -> bool {
    catch_any_panic(|| {
        init_logger();
        let gl = gl_glue::gl::init().unwrap();
        unsafe { init(opts, gl, None, None, wakeup, callbacks) }
    })
}

#[no_mangle]
pub extern "C" fn deinit() {
    debug!("deinit");
    simpleservo::deinit();
}

#[no_mangle]
pub extern "C" fn request_shutdown() {
    debug!("request_shutdown");
    call(|s| s.request_shutdown());
}

#[no_mangle]
pub extern "C" fn set_batch_mode(batch: bool) {
    debug!("set_batch_mode");
    call(|s| s.set_batch_mode(batch));
}

#[no_mangle]
pub extern "C" fn resize(width: i32, height: i32) {
    debug!("resize {}/{}", width, height);
    call(|s| {
        let coordinates = Coordinates::new(0, 0, width, height, width, height);
        s.resize(coordinates)
    });
}

#[no_mangle]
pub extern "C" fn perform_updates() -> bool {
    catch_any_panic(|| {
        debug!("perform_updates");
        call(|s| s.perform_updates());
    })
}

#[no_mangle]
pub extern "C" fn load_uri(url: *const c_char) -> bool {
    catch_any_panic(|| {
        debug!("load_url");
        let url = unsafe { CStr::from_ptr(url) };
        let url = url.to_str().expect("Can't read string");
        call(|s| s.load_uri(url));
    })
}

#[no_mangle]
pub extern "C" fn reload() -> bool {
    catch_any_panic(|| {
        debug!("reload");
        call(|s| s.reload());
    })
}

#[no_mangle]
pub extern "C" fn stop() -> bool {
    catch_any_panic(|| {
        debug!("stop");
        call(|s| s.stop());
    })
}

#[no_mangle]
pub extern "C" fn refresh() -> bool {
    catch_any_panic(|| {
        debug!("refresh");
        call(|s| s.refresh());
    })
}

#[no_mangle]
pub extern "C" fn go_back() -> bool {
    catch_any_panic(|| {
        debug!("go_back");
        call(|s| s.go_back());
    })
}

#[no_mangle]
pub extern "C" fn go_forward() -> bool {
    catch_any_panic(|| {
        debug!("go_forward");
        call(|s| s.go_forward());
    })
}

#[no_mangle]
pub extern "C" fn scroll_start(dx: i32, dy: i32, x: i32, y: i32) -> bool {
    catch_any_panic(|| {
        debug!("scroll_start");
        call(|s| s.scroll_start(dx as f32, dy as f32, x, y));
    })
}

#[no_mangle]
pub extern "C" fn scroll_end(dx: i32, dy: i32, x: i32, y: i32) -> bool {
    catch_any_panic(|| {
        debug!("scroll_end");
        call(|s| s.scroll_end(dx as f32, dy as f32, x, y));
    })
}

#[no_mangle]
pub extern "C" fn scroll(dx: i32, dy: i32, x: i32, y: i32) -> bool {
    catch_any_panic(|| {
        debug!("scroll");
        call(|s| s.scroll(dx as f32, dy as f32, x, y));
    })
}

#[no_mangle]
pub extern "C" fn touch_down(x: f32, y: f32, pointer_id: i32) -> bool {
    catch_any_panic(|| {
        debug!("touch down");
        call(|s| s.touch_down(x, y, pointer_id));
    })
}

#[no_mangle]
pub extern "C" fn touch_up(x: f32, y: f32, pointer_id: i32) -> bool {
    catch_any_panic(|| {
        debug!("touch up");
        call(|s| s.touch_up(x, y, pointer_id));
    })
}

#[no_mangle]
pub extern "C" fn touch_move(x: f32, y: f32, pointer_id: i32) -> bool {
    catch_any_panic(|| {
        debug!("touch move");
        call(|s| s.touch_move(x, y, pointer_id));
    })
}

#[no_mangle]
pub extern "C" fn touch_cancel(x: f32, y: f32, pointer_id: i32) -> bool {
    catch_any_panic(|| {
        debug!("touch cancel");
        call(|s| s.touch_cancel(x, y, pointer_id));
    })
}

#[no_mangle]
pub extern "C" fn pinchzoom_start(factor: f32, x: i32, y: i32) -> bool {
    catch_any_panic(|| {
        debug!("pinchzoom_start");
        call(|s| s.pinchzoom_start(factor, x as u32, y as u32));
    })
}

#[no_mangle]
pub extern "C" fn pinchzoom(factor: f32, x: i32, y: i32) -> bool {
    catch_any_panic(|| {
        debug!("pinchzoom");
        call(|s| s.pinchzoom(factor, x as u32, y as u32));
    })
}

#[no_mangle]
pub extern "C" fn pinchzoom_end(factor: f32, x: i32, y: i32) -> bool {
    catch_any_panic(|| {
        debug!("pinchzoom_end");
        call(|s| s.pinchzoom_end(factor, x as u32, y as u32));
    })
}

#[no_mangle]
pub extern "C" fn click(x: i32, y: i32) -> bool {
    catch_any_panic(|| {
        debug!("click");
        call(|s| s.click(x as f32, y as f32));
    })
}

pub struct WakeupCallback(extern "C" fn());

impl WakeupCallback {
    fn new(callback: extern "C" fn()) -> WakeupCallback {
        WakeupCallback(callback)
    }
}

impl EventLoopWaker for WakeupCallback {
    fn clone(&self) -> Box<dyn EventLoopWaker + Send> {
        Box::new(WakeupCallback(self.0))
    }
    fn wake(&self) {
        (self.0)();
    }
}

struct HostCallbacks(CHostCallbacks);

impl HostCallbacks {
    fn new(callback: CHostCallbacks) -> HostCallbacks {
        HostCallbacks(callback)
    }
}

impl HostTrait for HostCallbacks {
    fn flush(&self) {
        debug!("flush");
        (self.0.flush)();
    }

    fn make_current(&self) {
        debug!("make_current");
        (self.0.make_current)();
    }

    fn on_load_started(&self) {
        debug!("on_load_ended");
        (self.0.on_load_started)();
    }

    fn on_load_ended(&self) {
        debug!("on_load_ended");
        (self.0.on_load_ended)();
    }

    fn on_title_changed(&self, title: String) {
        debug!("on_title_changed");
        let title = CString::new(title).expect("Can't create string");
        let title_ptr = title.as_ptr();
        mem::forget(title);
        (self.0.on_title_changed)(title_ptr);
    }

    fn on_allow_navigation(&self, url: String) -> bool {
        debug!("on_allow_navigation");
        let url = CString::new(url).expect("Can't create string");
        let url_ptr = url.as_ptr();
        mem::forget(url);
        (self.0.on_allow_navigation)(url_ptr)
    }

    fn on_url_changed(&self, url: String) {
        debug!("on_url_changed");
        let url = CString::new(url).expect("Can't create string");
        let url_ptr = url.as_ptr();
        mem::forget(url);
        (self.0.on_url_changed)(url_ptr);
    }

    fn on_history_changed(&self, can_go_back: bool, can_go_forward: bool) {
        debug!("on_history_changed");
        (self.0.on_history_changed)(can_go_back, can_go_forward);
    }

    fn on_animating_changed(&self, animating: bool) {
        debug!("on_animating_changed");
        (self.0.on_animating_changed)(animating);
    }

    fn on_shutdown_complete(&self) {
        debug!("on_shutdown_complete");
        (self.0.on_shutdown_complete)();
    }

    fn on_ime_state_changed(&self, show: bool) {
        debug!("on_ime_state_changed");
        (self.0.on_ime_state_changed)(show);
    }

    fn get_clipboard_contents(&self) -> Option<String> {
        debug!("get_clipboard_contents");
        let raw_contents = (self.0.get_clipboard_contents)();
        if raw_contents.is_null() {
            return None;
        }
        let c_str = unsafe { CStr::from_ptr(raw_contents) };
        let contents_str = c_str.to_str().expect("Can't create str");
        Some(contents_str.to_owned())
    }

    fn set_clipboard_contents(&self, contents: String) {
        debug!("set_clipboard_contents");
        let contents = CString::new(contents).expect("Can't create string");
        let contents_ptr = contents.as_ptr();
        mem::forget(contents);
        (self.0.set_clipboard_contents)(contents_ptr);
    }
}
<|endoftext|>