<commit_before>use std::env;
use std::error::Error;
use std::fs;
use std::io::{self, Read};
use std::iter::Iterator;
use std::path::{Path, PathBuf};

use crate::file::{
    format::ALL_EXTENSIONS, source::FileSourceResult, FileSource, FileStoredFormat, Format,
};

/// Describes a file sourced from a file
#[derive(Clone, Debug)]
pub struct FileSourceFile {
    /// Path of configuration file
    name: PathBuf,
}

impl FileSourceFile {
    pub fn new(name: PathBuf) -> FileSourceFile {
        FileSourceFile { name }
    }

    fn find_file<F>(
        &self,
        format_hint: Option<F>,
    ) -> Result<(PathBuf, Box<dyn Format>), Box<dyn Error + Send + Sync>>
    where
        F: FileStoredFormat + Format + 'static,
    {
        // First check for an _exact_ match
        let mut filename = env::current_dir()?.as_path().join(self.name.clone());
        if filename.is_file() {
            return match format_hint {
                Some(format) => Ok((filename, Box::new(format))),
                None => {
                    for (format, extensions) in ALL_EXTENSIONS.iter() {
                        if extensions.contains(
                            &filename
                                .extension()
                                .unwrap_or_default()
                                .to_string_lossy()
                                .as_ref(),
                        ) {
                            return Ok((filename, Box::new(*format)));
                        }
                    }

                    Err(Box::new(io::Error::new(
                        io::ErrorKind::NotFound,
                        format!(
                            "configuration file \"{}\" is not of a registered file format",
                            filename.to_string_lossy()
                        ),
                    )))
                }
            };
        }

        match format_hint {
            Some(format) => {
                for ext in format.file_extensions() {
                    filename.set_extension(ext);

                    if filename.is_file() {
                        return Ok((filename, Box::new(format)));
                    }
                }
            }

            None => {
                for (format, extensions) in ALL_EXTENSIONS.iter() {
                    for ext in format.extensions() {
                        filename.set_extension(ext);

                        if filename.is_file() {
                            return Ok((filename, Box::new(*format)));
                        }
                    }
                }
            }
        }

        Err(Box::new(io::Error::new(
            io::ErrorKind::NotFound,
            format!(
                "configuration file \"{}\" not found",
                self.name.to_string_lossy()
            ),
        )))
    }
}

impl<F> FileSource<F> for FileSourceFile
where
    F: Format + FileStoredFormat + 'static,
{
    fn resolve(
        &self,
        format_hint: Option<F>,
    ) -> Result<FileSourceResult, Box<dyn Error + Send + Sync>> {
        // Find file
        let (filename, format) = self.find_file(format_hint)?;

        // Attempt to use a relative path for the URI
        let base = env::current_dir()?;
        let uri = match path_relative_from(&filename, &base) {
            Some(value) => value,
            None => filename.clone(),
        };

        // Read contents from file
        let mut file = fs::File::open(filename)?;
        let mut text = String::new();
        file.read_to_string(&mut text)?;

        Ok(FileSourceResult {
            uri: Some(uri.to_string_lossy().into_owned()),
            content: text,
            format,
        })
    }
}

// TODO: This should probably be a crate
// https://github.com/rust-lang/rust/blob/master/src/librustc_trans/back/rpath.rs#L128
fn path_relative_from(path: &Path, base: &Path) -> Option<PathBuf> {
    use std::path::Component;

    if path.is_absolute() != base.is_absolute() {
        if path.is_absolute() {
            Some(PathBuf::from(path))
        } else {
            None
        }
    } else {
        let mut ita = path.components();
        let mut itb = base.components();
        let mut comps: Vec<Component> = vec![];
        loop {
            match (ita.next(), itb.next()) {
                (None, None) => break,
                (Some(a), None) => {
                    comps.push(a);
                    comps.extend(ita.by_ref());
                    break;
                }
                (None, _) => comps.push(Component::ParentDir),
                (Some(a), Some(b)) if comps.is_empty() && a == b => (),
                (Some(a), Some(b)) if b == Component::CurDir => comps.push(a),
                (Some(_), Some(b)) if b == Component::ParentDir => return None,
                (Some(a), Some(_)) => {
                    comps.push(Component::ParentDir);
                    for _ in itb {
                        comps.push(Component::ParentDir);
                    }
                    comps.push(a);
                    comps.extend(ita.by_ref());
                    break;
                }
            }
        }
        Some(comps.iter().map(|c| c.as_os_str()).collect())
    }
}
<commit_msg>feat: allow for current_dir failure<commit_after>use std::env;
use std::error::Error;
use std::fs;
use std::io::{self, Read};
use std::iter::Iterator;
use std::path::{Path, PathBuf};

use crate::file::{
    format::ALL_EXTENSIONS, source::FileSourceResult, FileSource, FileStoredFormat, Format,
};

/// Describes a file sourced from a file
#[derive(Clone, Debug)]
pub struct FileSourceFile {
    /// Path of configuration file
    name: PathBuf,
}

impl FileSourceFile {
    pub fn new(name: PathBuf) -> FileSourceFile {
        FileSourceFile { name }
    }

    fn find_file<F>(
        &self,
        format_hint: Option<F>,
    ) -> Result<(PathBuf, Box<dyn Format>), Box<dyn Error + Send + Sync>>
    where
        F: FileStoredFormat + Format + 'static,
    {
        // First check for an _exact_ match
        let mut filename = env::current_dir()?.as_path().join(self.name.clone());
        if filename.is_file() {
            return match format_hint {
                Some(format) => Ok((filename, Box::new(format))),
                None => {
                    for (format, extensions) in ALL_EXTENSIONS.iter() {
                        if extensions.contains(
                            &filename
                                .extension()
                                .unwrap_or_default()
                                .to_string_lossy()
                                .as_ref(),
                        ) {
                            return Ok((filename, Box::new(*format)));
                        }
                    }

                    Err(Box::new(io::Error::new(
                        io::ErrorKind::NotFound,
                        format!(
                            "configuration file \"{}\" is not of a registered file format",
                            filename.to_string_lossy()
                        ),
                    )))
                }
            };
        }

        match format_hint {
            Some(format) => {
                for ext in format.file_extensions() {
                    filename.set_extension(ext);

                    if filename.is_file() {
                        return Ok((filename, Box::new(format)));
                    }
                }
            }

            None => {
                for (format, extensions) in ALL_EXTENSIONS.iter() {
                    for ext in format.extensions() {
                        filename.set_extension(ext);

                        if filename.is_file() {
                            return Ok((filename, Box::new(*format)));
                        }
                    }
                }
            }
        }

        Err(Box::new(io::Error::new(
            io::ErrorKind::NotFound,
            format!(
                "configuration file \"{}\" not found",
                self.name.to_string_lossy()
            ),
        )))
    }
}

impl<F> FileSource<F> for FileSourceFile
where
    F: Format + FileStoredFormat + 'static,
{
    fn resolve(
        &self,
        format_hint: Option<F>,
    ) -> Result<FileSourceResult, Box<dyn Error + Send + Sync>> {
        // Find file
        let (filename, format) = self.find_file(format_hint)?;

        // Attempt to use a relative path for the URI
        let uri = env::current_dir()
            .ok()
            .and_then(|base| path_relative_from(&filename, &base))
            .unwrap_or_else(|| filename.clone());

        // Read contents from file
        let mut file = fs::File::open(filename)?;
        let mut text = String::new();
        file.read_to_string(&mut text)?;

        Ok(FileSourceResult {
            uri: Some(uri.to_string_lossy().into_owned()),
            content: text,
            format,
        })
    }
}

// TODO: This should probably be a crate
// https://github.com/rust-lang/rust/blob/master/src/librustc_trans/back/rpath.rs#L128
fn path_relative_from(path: &Path, base: &Path) -> Option<PathBuf> {
    use std::path::Component;

    if path.is_absolute() != base.is_absolute() {
        if path.is_absolute() {
            Some(PathBuf::from(path))
        } else {
            None
        }
    } else {
        let mut ita = path.components();
        let mut itb = base.components();
        let mut comps: Vec<Component> = vec![];
        loop {
            match (ita.next(), itb.next()) {
                (None, None) => break,
                (Some(a), None) => {
                    comps.push(a);
                    comps.extend(ita.by_ref());
                    break;
                }
                (None, _) => comps.push(Component::ParentDir),
                (Some(a), Some(b)) if comps.is_empty() && a == b => (),
                (Some(a), Some(b)) if b == Component::CurDir => comps.push(a),
                (Some(_), Some(b)) if b == Component::ParentDir => return None,
                (Some(a), Some(_)) => {
                    comps.push(Component::ParentDir);
                    for _ in itb {
                        comps.push(Component::ParentDir);
                    }
                    comps.push(a);
                    comps.extend(ita.by_ref());
                    break;
                }
            }
        }
        Some(comps.iter().map(|c| c.as_os_str()).collect())
    }
}
<|endoftext|>


<commit_before>pub mod checksum;

<commit_msg>Adds short_name and full_name functions<commit_after>pub mod checksum;

pub fn short_name(filename: Option<&str>, seconds: f64) -> Result<String, &'static str> {
  let multiplier: f64 = 40500000f64;
  let tick: u64 = (seconds * multiplier) as u64;

  match filename {
    Some(name) => {
      if name.len() < 18 {
        Ok(format!("fze1-{}.dat", name))
      } else {
        Err("emblem-filename should be 18 characters or less.")
      }
    },
    None => Ok(format!("fze0200002000{:14X}.dat", tick as u64))
  }
}

pub fn full_name(filename: Option<&str>) -> String {
  format!("8P-GFZE-{}.gci", filename)
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate bitflags;

#[macro_use]
extern crate nom;

#[macro_use]
extern crate downcast_rs;

pub use aspect_ratio::{
    FitMode,
    AlignMode,
    Align,
    AspectRatio,
    ParseAspectRatioError,
    rsvg_aspect_ratio_parse,
    rsvg_aspect_ratio_compute
};

pub use bbox::{
    RsvgBbox,
    rsvg_bbox_init,
    rsvg_bbox_insert,
    rsvg_bbox_clip
};

pub use cnode::{
    rsvg_rust_cnode_new,
    rsvg_rust_cnode_get_impl
};

pub use gradient::{
    gradient_linear_new,
    gradient_radial_new,
    gradient_destroy,
    gradient_add_color_stop,
    gradient_resolve_fallbacks_and_set_pattern
};

pub use length::{
    LengthUnit,
    LengthDir,
    RsvgLength,
    rsvg_length_parse,
    rsvg_length_normalize,
    rsvg_length_hand_normalize,
};

pub use marker::{
    rsvg_node_marker_new,
};

pub use node::{
    rsvg_node_get_type,
    rsvg_node_get_parent,
    rsvg_node_ref,
    rsvg_node_unref,
    rsvg_node_is_same,
    rsvg_node_get_state,
    rsvg_node_add_child,
    rsvg_node_set_atts,
    rsvg_node_draw,
    rsvg_node_foreach_child,
};

pub use path_builder::{
    rsvg_path_builder_new,
    rsvg_path_builder_destroy,
    rsvg_path_builder_move_to,
    rsvg_path_builder_line_to,
    rsvg_path_builder_curve_to,
    rsvg_path_builder_close_path,
    rsvg_path_builder_arc,
    rsvg_path_builder_add_to_cairo_context
};

pub use path_parser::{
    rsvg_path_parser_from_str_into_builder
};

pub use pattern::{
    pattern_new,
    pattern_destroy,
    pattern_resolve_fallbacks_and_set_pattern,
};

pub use shapes::{
    rsvg_node_circle_new,
    rsvg_node_ellipse_new,
    rsvg_node_line_new,
    rsvg_node_path_new,
    rsvg_node_polygon_new,
    rsvg_node_polyline_new,
    rsvg_node_rect_new,
};
pub use viewbox::{
    RsvgViewBox
};

mod aspect_ratio;
mod bbox;
mod cnode;
mod drawing_ctx;
mod error;
mod handle;
mod gradient;
mod length;
mod marker;
mod node;
mod parsers;
mod path_builder;
mod path_parser;
mod pattern;
mod property_bag;
mod state;
mod shapes;
mod util;
mod viewbox;
<commit_msg>lib.rs: Remove exports that don't need to be exported<commit_after>#[macro_use]
extern crate bitflags;

#[macro_use]
extern crate nom;

#[macro_use]
extern crate downcast_rs;

pub use aspect_ratio::{
    rsvg_aspect_ratio_parse,
    rsvg_aspect_ratio_compute
};

pub use bbox::{
    RsvgBbox,
    rsvg_bbox_init,
    rsvg_bbox_insert,
    rsvg_bbox_clip
};

pub use cnode::{
    rsvg_rust_cnode_new,
    rsvg_rust_cnode_get_impl
};

pub use gradient::{
    gradient_linear_new,
    gradient_radial_new,
    gradient_destroy,
    gradient_add_color_stop,
    gradient_resolve_fallbacks_and_set_pattern
};

pub use length::{
    LengthUnit,
    LengthDir,
    RsvgLength,
    rsvg_length_parse,
    rsvg_length_normalize,
    rsvg_length_hand_normalize,
};

pub use marker::{
    rsvg_node_marker_new,
};

pub use node::{
    rsvg_node_get_type,
    rsvg_node_get_parent,
    rsvg_node_ref,
    rsvg_node_unref,
    rsvg_node_is_same,
    rsvg_node_get_state,
    rsvg_node_add_child,
    rsvg_node_set_atts,
    rsvg_node_draw,
    rsvg_node_foreach_child,
};

pub use path_builder::{
    rsvg_path_builder_new,
    rsvg_path_builder_destroy,
    rsvg_path_builder_move_to,
    rsvg_path_builder_line_to,
    rsvg_path_builder_curve_to,
    rsvg_path_builder_close_path,
    rsvg_path_builder_arc,
    rsvg_path_builder_add_to_cairo_context
};

pub use path_parser::{
    rsvg_path_parser_from_str_into_builder
};

pub use pattern::{
    pattern_new,
    pattern_destroy,
    pattern_resolve_fallbacks_and_set_pattern,
};

pub use shapes::{
    rsvg_node_circle_new,
    rsvg_node_ellipse_new,
    rsvg_node_line_new,
    rsvg_node_path_new,
    rsvg_node_polygon_new,
    rsvg_node_polyline_new,
    rsvg_node_rect_new,
};
pub use viewbox::{
    RsvgViewBox
};

mod aspect_ratio;
mod bbox;
mod cnode;
mod drawing_ctx;
mod error;
mod handle;
mod gradient;
mod length;
mod marker;
mod node;
mod parsers;
mod path_builder;
mod path_parser;
mod pattern;
mod property_bag;
mod state;
mod shapes;
mod util;
mod viewbox;
<|endoftext|>


<commit_before>use super::helpers::allocations;
use super::helpers::fixtures::{get_language, get_language_queries_path};
use std::ffi::CString;
use std::{fs, ptr, slice, str};
use tree_sitter_tags::c_lib as c;
use tree_sitter_tags::{TagKind, TagsConfiguration, TagsContext};

const PYTHON_TAG_QUERY: &'static str = r#"
((function_definition
  name: (identifier) @name
  body: (block . (expression_statement (string) @doc))) @function
 (strip! @doc "(^['\"\\s]*)|(['\"\\s]*$)"))
(function_definition
  name: (identifier) @name) @function
((class_definition
  name: (identifier) @name
  body: (block . (expression_statement (string) @doc))) @class
 (strip! @doc "(^['\"\\s]*)|(['\"\\s]*$)"))
(class_definition
  name: (identifier) @name) @class
(call
  function: (identifier) @name) @call
"#;

const JS_TAG_QUERY: &'static str = r#"
((*
    (comment)+ @doc .
    (class_declaration
        name: (identifier) @name) @class)
 (select-adjacent! @doc @class)
 (strip! @doc "(^[/\\*\\s]*)|([/\\*\\s]*$)"))

((*
    (comment)+ @doc .
    (method_definition
        name: (property_identifier) @name) @method)
 (select-adjacent! @doc @method)
 (strip! @doc "(^[/\\*\\s]*)|([/\\*\\s]*$)"))

((*
    (comment)+ @doc .
    (function_declaration
        name: (identifier) @name) @function)
 (select-adjacent! @doc @function)
 (strip! @doc "(^[/\\*\\s]*)|([/\\*\\s]*$)"))

(call_expression function: (identifier) @name) @call
  "#;

const RUBY_TAG_QUERY: &'static str = r#"
(method
    name: (identifier) @name) @method

(method_call
    method: (identifier) @name) @call

((identifier) @name @call
 (is-not? local))
"#;

#[test]
fn test_tags_python() {
    let language = get_language("python");
    let tags_config = TagsConfiguration::new(language, PYTHON_TAG_QUERY, "").unwrap();
    let mut tag_context = TagsContext::new();

    let source = br#"
    class Customer:
        """
        Data about a customer
        """

        def age(self):
            '''
            Get the customer's age
            '''
            compute_age(self.id)
    }
    "#;

    let tags = tag_context
        .generate_tags(&tags_config, source)
        .collect::<Vec<_>>();

    assert_eq!(
        tags.iter()
            .map(|t| (substr(source, &t.name_range), t.kind))
            .collect::<Vec<_>>(),
        &[
            ("Customer", TagKind::Class),
            ("age", TagKind::Function),
            ("compute_age", TagKind::Call),
        ]
    );

    assert_eq!(substr(source, &tags[0].line_range), "    class Customer:");
    assert_eq!(
        substr(source, &tags[1].line_range),
        "        def age(self):"
    );
    assert_eq!(tags[0].docs.as_ref().unwrap(), "Data about a customer");
    assert_eq!(tags[1].docs.as_ref().unwrap(), "Get the customer's age");
}

#[test]
fn test_tags_javascript() {
    let language = get_language("javascript");
    let tags_config = TagsConfiguration::new(language, JS_TAG_QUERY, "").unwrap();
    let source = br#"
    // hi

    // Data about a customer.
    // bla bla bla
    class Customer {
        /*
         * Get the customer's age
         */
        getAge() {
        }
    }

    // ok

    class Agent {

    }
    "#;

    let mut tag_context = TagsContext::new();
    let tags = tag_context
        .generate_tags(&tags_config, source)
        .collect::<Vec<_>>();

    assert_eq!(
        tags.iter()
            .map(|t| (substr(source, &t.name_range), t.kind))
            .collect::<Vec<_>>(),
        &[
            ("getAge", TagKind::Method),
            ("Customer", TagKind::Class),
            ("Agent", TagKind::Class)
        ]
    );
    assert_eq!(tags[0].docs.as_ref().unwrap(), "Get the customer's age");
    assert_eq!(
        tags[1].docs.as_ref().unwrap(),
        "Data about a customer.\nbla bla bla"
    );
    assert_eq!(tags[2].docs, None);
}

#[test]
fn test_tags_ruby() {
    let language = get_language("ruby");
    let locals_query =
        fs::read_to_string(get_language_queries_path("ruby").join("locals.scm")).unwrap();
    let tags_config = TagsConfiguration::new(language, RUBY_TAG_QUERY, &locals_query).unwrap();
    let source = strip_whitespace(
        8,
        "
        b = 1

        def foo()
            c = 1

            # a is a method because it is not in scope
            # b is a method because `b` doesn't capture variables from its containing scope
            bar a, b, c

            [1, 2, 3].each do |a|
                # a is a parameter
                # b is a method
                # c is a variable, because the block captures variables from its containing scope.
                baz a, b, c
            end
        end",
    );

    let mut tag_context = TagsContext::new();
    let tags = tag_context
        .generate_tags(&tags_config, source.as_bytes())
        .collect::<Vec<_>>();

    assert_eq!(
        tags.iter()
            .map(|t| (
                substr(source.as_bytes(), &t.name_range),
                t.kind,
                (t.span.start.row, t.span.start.column),
            ))
            .collect::<Vec<_>>(),
        &[
            ("foo", TagKind::Method, (2, 0)),
            ("bar", TagKind::Call, (7, 4)),
            ("a", TagKind::Call, (7, 8)),
            ("b", TagKind::Call, (7, 11)),
            ("each", TagKind::Call, (9, 14)),
            ("baz", TagKind::Call, (13, 8)),
            ("b", TagKind::Call, (13, 15),),
        ]
    );
}

#[test]
fn test_tags_via_c_api() {
    allocations::record(|| {
        let tagger = c::ts_tagger_new();
        let buffer = c::ts_tags_buffer_new();
        let scope_name = "source.js";
        let language = get_language("javascript");

        let source_code = strip_whitespace(
            12,
            "
            var a = 1;

            // one
            // two
            // three
            function b() {
            }

            // four
            // five
            class C extends D {

            }

            b(a);",
        );

        let c_scope_name = CString::new(scope_name).unwrap();
        let result = c::ts_tagger_add_language(
            tagger,
            c_scope_name.as_ptr(),
            language,
            JS_TAG_QUERY.as_ptr(),
            ptr::null(),
            JS_TAG_QUERY.len() as u32,
            0,
        );
        assert_eq!(result, c::TSTagsError::Ok);

        let result = c::ts_tagger_tag(
            tagger,
            c_scope_name.as_ptr(),
            source_code.as_ptr(),
            source_code.len() as u32,
            buffer,
            ptr::null(),
        );
        assert_eq!(result, c::TSTagsError::Ok);
        let tags = unsafe {
            slice::from_raw_parts(
                c::ts_tags_buffer_tags(buffer),
                c::ts_tags_buffer_tags_len(buffer) as usize,
            )
        };
        let docs = str::from_utf8(unsafe {
            slice::from_raw_parts(
                c::ts_tags_buffer_docs(buffer) as *const u8,
                c::ts_tags_buffer_docs_len(buffer) as usize,
            )
        })
        .unwrap();

        assert_eq!(
            tags.iter()
                .map(|tag| (
                    tag.kind,
                    &source_code[tag.name_start_byte as usize..tag.name_end_byte as usize],
                    &source_code[tag.line_start_byte as usize..tag.line_end_byte as usize],
                    &docs[tag.docs_start_byte as usize..tag.docs_end_byte as usize],
                ))
                .collect::<Vec<_>>(),
            &[
                (
                    c::TSTagKind::Function,
                    "b",
                    "function b() {",
                    "one\ntwo\nthree"
                ),
                (
                    c::TSTagKind::Class,
                    "C",
                    "class C extends D {",
                    "four\nfive"
                ),
                (c::TSTagKind::Call, "b", "b(a);", "")
            ]
        );

        c::ts_tags_buffer_delete(buffer);
        c::ts_tagger_delete(tagger);
    });
}

fn substr<'a>(source: &'a [u8], range: &std::ops::Range<usize>) -> &'a str {
    std::str::from_utf8(&source[range.clone()]).unwrap()
}

fn strip_whitespace(indent: usize, s: &str) -> String {
    s.lines()
        .skip(1)
        .map(|line| &line[line.len().min(indent)..])
        .collect::<Vec<_>>()
        .join("\n")
}
<commit_msg>Fix tag order in JS tags test<commit_after>use super::helpers::allocations;
use super::helpers::fixtures::{get_language, get_language_queries_path};
use std::ffi::CString;
use std::{fs, ptr, slice, str};
use tree_sitter_tags::c_lib as c;
use tree_sitter_tags::{TagKind, TagsConfiguration, TagsContext};

const PYTHON_TAG_QUERY: &'static str = r#"
((function_definition
  name: (identifier) @name
  body: (block . (expression_statement (string) @doc))) @function
 (strip! @doc "(^['\"\\s]*)|(['\"\\s]*$)"))
(function_definition
  name: (identifier) @name) @function
((class_definition
  name: (identifier) @name
  body: (block . (expression_statement (string) @doc))) @class
 (strip! @doc "(^['\"\\s]*)|(['\"\\s]*$)"))
(class_definition
  name: (identifier) @name) @class
(call
  function: (identifier) @name) @call
"#;

const JS_TAG_QUERY: &'static str = r#"
((*
    (comment)+ @doc .
    (class_declaration
        name: (identifier) @name) @class)
 (select-adjacent! @doc @class)
 (strip! @doc "(^[/\\*\\s]*)|([/\\*\\s]*$)"))

((*
    (comment)+ @doc .
    (method_definition
        name: (property_identifier) @name) @method)
 (select-adjacent! @doc @method)
 (strip! @doc "(^[/\\*\\s]*)|([/\\*\\s]*$)"))

((*
    (comment)+ @doc .
    (function_declaration
        name: (identifier) @name) @function)
 (select-adjacent! @doc @function)
 (strip! @doc "(^[/\\*\\s]*)|([/\\*\\s]*$)"))

(call_expression function: (identifier) @name) @call
  "#;

const RUBY_TAG_QUERY: &'static str = r#"
(method
    name: (identifier) @name) @method

(method_call
    method: (identifier) @name) @call

((identifier) @name @call
 (is-not? local))
"#;

#[test]
fn test_tags_python() {
    let language = get_language("python");
    let tags_config = TagsConfiguration::new(language, PYTHON_TAG_QUERY, "").unwrap();
    let mut tag_context = TagsContext::new();

    let source = br#"
    class Customer:
        """
        Data about a customer
        """

        def age(self):
            '''
            Get the customer's age
            '''
            compute_age(self.id)
    }
    "#;

    let tags = tag_context
        .generate_tags(&tags_config, source)
        .collect::<Vec<_>>();

    assert_eq!(
        tags.iter()
            .map(|t| (substr(source, &t.name_range), t.kind))
            .collect::<Vec<_>>(),
        &[
            ("Customer", TagKind::Class),
            ("age", TagKind::Function),
            ("compute_age", TagKind::Call),
        ]
    );

    assert_eq!(substr(source, &tags[0].line_range), "    class Customer:");
    assert_eq!(
        substr(source, &tags[1].line_range),
        "        def age(self):"
    );
    assert_eq!(tags[0].docs.as_ref().unwrap(), "Data about a customer");
    assert_eq!(tags[1].docs.as_ref().unwrap(), "Get the customer's age");
}

#[test]
fn test_tags_javascript() {
    let language = get_language("javascript");
    let tags_config = TagsConfiguration::new(language, JS_TAG_QUERY, "").unwrap();
    let source = br#"
    // hi

    // Data about a customer.
    // bla bla bla
    class Customer {
        /*
         * Get the customer's age
         */
        getAge() {
        }
    }

    // ok

    class Agent {

    }
    "#;

    let mut tag_context = TagsContext::new();
    let tags = tag_context
        .generate_tags(&tags_config, source)
        .collect::<Vec<_>>();

    assert_eq!(
        tags.iter()
            .map(|t| (substr(source, &t.name_range), t.kind))
            .collect::<Vec<_>>(),
        &[
            ("Customer", TagKind::Class),
            ("getAge", TagKind::Method),
            ("Agent", TagKind::Class)
        ]
    );
    assert_eq!(
        tags[0].docs.as_ref().unwrap(),
        "Data about a customer.\nbla bla bla"
    );
    assert_eq!(tags[1].docs.as_ref().unwrap(), "Get the customer's age");
    assert_eq!(tags[2].docs, None);
}

#[test]
fn test_tags_ruby() {
    let language = get_language("ruby");
    let locals_query =
        fs::read_to_string(get_language_queries_path("ruby").join("locals.scm")).unwrap();
    let tags_config = TagsConfiguration::new(language, RUBY_TAG_QUERY, &locals_query).unwrap();
    let source = strip_whitespace(
        8,
        "
        b = 1

        def foo()
            c = 1

            # a is a method because it is not in scope
            # b is a method because `b` doesn't capture variables from its containing scope
            bar a, b, c

            [1, 2, 3].each do |a|
                # a is a parameter
                # b is a method
                # c is a variable, because the block captures variables from its containing scope.
                baz a, b, c
            end
        end",
    );

    let mut tag_context = TagsContext::new();
    let tags = tag_context
        .generate_tags(&tags_config, source.as_bytes())
        .collect::<Vec<_>>();

    assert_eq!(
        tags.iter()
            .map(|t| (
                substr(source.as_bytes(), &t.name_range),
                t.kind,
                (t.span.start.row, t.span.start.column),
            ))
            .collect::<Vec<_>>(),
        &[
            ("foo", TagKind::Method, (2, 0)),
            ("bar", TagKind::Call, (7, 4)),
            ("a", TagKind::Call, (7, 8)),
            ("b", TagKind::Call, (7, 11)),
            ("each", TagKind::Call, (9, 14)),
            ("baz", TagKind::Call, (13, 8)),
            ("b", TagKind::Call, (13, 15),),
        ]
    );
}

#[test]
fn test_tags_via_c_api() {
    allocations::record(|| {
        let tagger = c::ts_tagger_new();
        let buffer = c::ts_tags_buffer_new();
        let scope_name = "source.js";
        let language = get_language("javascript");

        let source_code = strip_whitespace(
            12,
            "
            var a = 1;

            // one
            // two
            // three
            function b() {
            }

            // four
            // five
            class C extends D {

            }

            b(a);",
        );

        let c_scope_name = CString::new(scope_name).unwrap();
        let result = c::ts_tagger_add_language(
            tagger,
            c_scope_name.as_ptr(),
            language,
            JS_TAG_QUERY.as_ptr(),
            ptr::null(),
            JS_TAG_QUERY.len() as u32,
            0,
        );
        assert_eq!(result, c::TSTagsError::Ok);

        let result = c::ts_tagger_tag(
            tagger,
            c_scope_name.as_ptr(),
            source_code.as_ptr(),
            source_code.len() as u32,
            buffer,
            ptr::null(),
        );
        assert_eq!(result, c::TSTagsError::Ok);
        let tags = unsafe {
            slice::from_raw_parts(
                c::ts_tags_buffer_tags(buffer),
                c::ts_tags_buffer_tags_len(buffer) as usize,
            )
        };
        let docs = str::from_utf8(unsafe {
            slice::from_raw_parts(
                c::ts_tags_buffer_docs(buffer) as *const u8,
                c::ts_tags_buffer_docs_len(buffer) as usize,
            )
        })
        .unwrap();

        assert_eq!(
            tags.iter()
                .map(|tag| (
                    tag.kind,
                    &source_code[tag.name_start_byte as usize..tag.name_end_byte as usize],
                    &source_code[tag.line_start_byte as usize..tag.line_end_byte as usize],
                    &docs[tag.docs_start_byte as usize..tag.docs_end_byte as usize],
                ))
                .collect::<Vec<_>>(),
            &[
                (
                    c::TSTagKind::Function,
                    "b",
                    "function b() {",
                    "one\ntwo\nthree"
                ),
                (
                    c::TSTagKind::Class,
                    "C",
                    "class C extends D {",
                    "four\nfive"
                ),
                (c::TSTagKind::Call, "b", "b(a);", "")
            ]
        );

        c::ts_tags_buffer_delete(buffer);
        c::ts_tagger_delete(tagger);
    });
}

fn substr<'a>(source: &'a [u8], range: &std::ops::Range<usize>) -> &'a str {
    std::str::from_utf8(&source[range.clone()]).unwrap()
}

fn strip_whitespace(indent: usize, s: &str) -> String {
    s.lines()
        .skip(1)
        .map(|line| &line[line.len().min(indent)..])
        .collect::<Vec<_>>()
        .join("\n")
}
<|endoftext|>


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

//! Low-level code to work with the service catalog.

use hyper::{Get, Url};

use super::super::ApiError;
use super::super::session::AuthenticatedClient;
use super::protocol;

/// Type alias for the catalog.
pub type Catalog = Vec<protocol::CatalogRecord>;

/// Fetch the service catalog from a given auth URL.
pub fn get_service_catalog(auth_url: &Url, client: &AuthenticatedClient)
        -> Result<Catalog, ApiError> {
    let url = format!("{}/v3/auth/catalog", auth_url.to_string());
    debug!("Requesting a service catalog from {}", url);

    let resp = try!(client.request(Get, &url).send());
    let body = try!(protocol::CatalogRoot::from_reader(resp));
    Ok(body.catalog)
}

/// Find an endpoint in the service catalog.
pub fn find_endpoint<'a>(catalog: &'a Catalog, service_type: &str,
                         endpoint_interface: &str, region: Option<&str>)
        -> Result<&'a protocol::Endpoint, ApiError> {
    let svc = match catalog.iter().find(|x| &x.service_type == service_type) {
        Some(s) => s,
        None => return Err(ApiError::EndpointNotFound)
    };

    let maybe_endp: Option<&protocol::Endpoint>;
    if let Some(rgn) = region {
        maybe_endp = svc.endpoints.iter().find(
            |x| &x.interface == endpoint_interface && &x.region == rgn);
    } else {
        maybe_endp = svc.endpoints.iter().find(
            |x| &x.interface == endpoint_interface);
    }

    match maybe_endp {
        Some(e) => Ok(e),
        None => Err(ApiError::EndpointNotFound)
    }
}
<commit_msg>Add missing unit tests for catalog::find_endpoint<commit_after>// Copyright 2017 Dmitry Tantsur <divius.inside@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Low-level code to work with the service catalog.

use hyper::{Get, Url};

use super::super::ApiError;
use super::super::session::AuthenticatedClient;
use super::protocol;

/// Type alias for the catalog.
pub type Catalog = Vec<protocol::CatalogRecord>;

/// Fetch the service catalog from a given auth URL.
pub fn get_service_catalog(auth_url: &Url, client: &AuthenticatedClient)
        -> Result<Catalog, ApiError> {
    let url = format!("{}/v3/auth/catalog", auth_url.to_string());
    debug!("Requesting a service catalog from {}", url);

    let resp = try!(client.request(Get, &url).send());
    let body = try!(protocol::CatalogRoot::from_reader(resp));
    Ok(body.catalog)
}

/// Find an endpoint in the service catalog.
pub fn find_endpoint<'a>(catalog: &'a Catalog, service_type: &str,
                         endpoint_interface: &str, region: Option<&str>)
        -> Result<&'a protocol::Endpoint, ApiError> {
    let svc = match catalog.iter().find(|x| &x.service_type == service_type) {
        Some(s) => s,
        None => return Err(ApiError::EndpointNotFound)
    };

    let maybe_endp: Option<&protocol::Endpoint>;
    if let Some(rgn) = region {
        maybe_endp = svc.endpoints.iter().find(
            |x| &x.interface == endpoint_interface && &x.region == rgn);
    } else {
        maybe_endp = svc.endpoints.iter().find(
            |x| &x.interface == endpoint_interface);
    }

    match maybe_endp {
        Some(e) => Ok(e),
        None => Err(ApiError::EndpointNotFound)
    }
}

#[cfg(test)]
pub mod test {
    use super::super::super::ApiError;
    use super::super::protocol::{CatalogRecord, Endpoint};
    use super::{Catalog, find_endpoint};

    fn demo_service1() -> CatalogRecord {
        CatalogRecord {
            id: String::from("1"),
            service_type: String::from("identity"),
            name: String::from("keystone"),
            endpoints: vec![
                Endpoint {
                    id: String::from("e1"),
                    interface: String::from("public"),
                    region: String::from("RegionOne"),
                    url: String::from("https://host.one/identity")
                },
                Endpoint {
                    id: String::from("e2"),
                    interface: String::from("internal"),
                    region: String::from("RegionOne"),
                    url: String::from("http://192.168.22.1/identity")
                },
                Endpoint {
                    id: String::from("e3"),
                    interface: String::from("public"),
                    region: String::from("RegionTwo"),
                    url: String::from("https://host.two:5000")
                }
            ]
        }
    }

    fn demo_service2() -> CatalogRecord {
        CatalogRecord {
            id: String::from("2"),
            service_type: String::from("baremetal"),
            name: String::from("ironic"),
            endpoints: vec![
                Endpoint {
                    id: String::from("e4"),
                    interface: String::from("public"),
                    region: String::from("RegionOne"),
                    url: String::from("https://host.one/baremetal")
                },
                Endpoint {
                    id: String::from("e5"),
                    interface: String::from("public"),
                    region: String::from("RegionTwo"),
                    url: String::from("https://host.two:6385")
                }
            ]
        }
    }

    pub fn demo_catalog() -> Catalog {
        vec![demo_service1(), demo_service2()]
    }

    #[test]
    fn test_find_endpoint() {
        let cat = demo_catalog();

        let e1 = find_endpoint(&cat, "identity", "public", None).unwrap();
        assert_eq!(&e1.id, "e1");
        assert_eq!(&e1.url, "https://host.one/identity");

        let e2 = find_endpoint(&cat, "identity", "internal", None).unwrap();
        assert_eq!(&e2.id, "e2");
        assert_eq!(&e2.url, "http://192.168.22.1/identity");

        let e3 = find_endpoint(&cat, "baremetal", "public", None).unwrap();
        assert_eq!(&e3.id, "e4");
        assert_eq!(&e3.url, "https://host.one/baremetal");
    }

    #[test]
    fn test_find_endpoint_with_region() {
        let cat = demo_catalog();

        let e1 = find_endpoint(&cat, "identity", "public",
                               Some("RegionTwo")).unwrap();
        assert_eq!(&e1.id, "e3");
        assert_eq!(&e1.url, "https://host.two:5000");

        let e2 = find_endpoint(&cat, "identity", "internal",
                               Some("RegionOne")).unwrap();
        assert_eq!(&e2.id, "e2");
        assert_eq!(&e2.url, "http://192.168.22.1/identity");

        let e3 = find_endpoint(&cat, "baremetal", "public",
                               Some("RegionTwo")).unwrap();
        assert_eq!(&e3.id, "e5");
        assert_eq!(&e3.url, "https://host.two:6385");
    }

    fn assert_not_found(result: Result<&Endpoint, ApiError>) {
        match result.err().unwrap() {
            ApiError::EndpointNotFound => (),
            other => panic!("Unexpected error {}", other)
        }
    }

    #[test]
    fn test_find_endpoint_not_found() {
        let cat = demo_catalog();

        assert_not_found(find_endpoint(&cat, "foobar", "public", None));
        assert_not_found(find_endpoint(&cat, "identity", "public",
                                       Some("RegionFoo")));
        assert_not_found(find_endpoint(&cat, "baremetal", "internal", None));
        assert_not_found(find_endpoint(&cat, "identity", "internal",
                                       Some("RegionTwo")));
    }
}
<|endoftext|>


<commit_before>use libc::c_ulonglong;
use llvm_sys::core;
use llvm_sys::prelude::{
  LLVMValueRef,
  LLVMContextRef
};
use types::LLVMTy;

#[derive(Copy, Clone)]
pub struct Value(pub LLVMValueRef);

/// A type that can be represented as a constant in LLVM IR.
pub trait ToValue {
	/// Compile this value into a constant in the context given.
  fn to_value(self, ctx: LLVMContextRef) -> Value;
}

macro_rules! int_to_value (
  ($ty:ty) => (
    impl ToValue for $ty {
      fn to_value(self, ctx: LLVMContextRef) -> Value
      {
        Value(unsafe { core::LLVMConstInt(Self::get_ty(ctx).as_ptr(), self as c_ulonglong, 0) })
      }
    }
  );
);

int_to_value!{i8}
int_to_value!{u8}
int_to_value!{i16}
int_to_value!{u16}
int_to_value!{i32}
int_to_value!{u32}
int_to_value!{i64}
int_to_value!{u64}
int_to_value!{usize}
int_to_value!{isize}

impl ToValue for f32 {
  fn to_value(self, ctx: LLVMContextRef) -> Value
  {
    Value(unsafe{core::LLVMConstReal(Self::get_ty(ctx).as_ptr(), self as f64)})
  }
}

impl ToValue for f64 {
  fn to_value(self, ctx: LLVMContextRef) -> Value
  {
    Value(unsafe{core::LLVMConstReal(Self::get_ty(ctx).as_ptr(), self)})
  }
}<commit_msg>Fixed some comments.<commit_after>use libc::c_ulonglong;
use llvm_sys::core;
use llvm_sys::prelude::{
  LLVMValueRef,
  LLVMContextRef
};
use types::LLVMTy;

#[derive(Copy, Clone)]
pub struct Value(pub LLVMValueRef);

pub trait ToValue {
	/// Transform this value into a constant in the context given.
  fn to_value(self, ctx: LLVMContextRef) -> Value;
}

macro_rules! int_to_value (
  ($ty:ty) => (
    impl ToValue for $ty {
      fn to_value(self, ctx: LLVMContextRef) -> Value
      {
        Value(unsafe { core::LLVMConstInt(Self::get_ty(ctx).as_ptr(), self as c_ulonglong, 0) })
      }
    }
  );
);

int_to_value!{i8}
int_to_value!{u8}
int_to_value!{i16}
int_to_value!{u16}
int_to_value!{i32}
int_to_value!{u32}
int_to_value!{i64}
int_to_value!{u64}
int_to_value!{usize}
int_to_value!{isize}

impl ToValue for f32 {
  fn to_value(self, ctx: LLVMContextRef) -> Value
  {
    Value(unsafe{core::LLVMConstReal(Self::get_ty(ctx).as_ptr(), self as f64)})
  }
}

impl ToValue for f64 {
  fn to_value(self, ctx: LLVMContextRef) -> Value
  {
    Value(unsafe{core::LLVMConstReal(Self::get_ty(ctx).as_ptr(), self)})
  }
}<|endoftext|>


<commit_before>// Copyright 2016 Walter Kuppens.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use byteorder::{LittleEndian, ReadBytesExt};
use nes::cpu::CPU;
use nes::memory::Memory;
use nes::opcode::Opcode::*;
use nes::opcode::Opcode;
use std::io::Cursor;

/// All 6502 instructions are a maximum size of 3 bytes. The first byte is the
/// opcode which is determines the action of the instruction. The following 2
/// bytes are the arguments and are present depending on the opcode.
#[derive(Debug)]
pub struct Instruction(pub u8, pub u8, pub u8);

impl Instruction {
    /// Parses an instruction from memory at the address of the passed program
    /// counter. Some instructions when parsed by the original 6502 will read
    /// arguments from the wrong addresses (e.g indirect JMP), so those bugs are
    /// emulated accurately here.
    pub fn parse(pc: usize, memory: &mut Memory) -> Instruction {
        use nes::opcode::decode_opcode;
        use nes::opcode::opcode_len;

        let raw_opcode = memory.read_u8(pc);
        let opcode = decode_opcode(raw_opcode);
        let len = opcode_len(&opcode);

        match len {
            1 => Instruction(raw_opcode, 0, 0),
            2 => Instruction(raw_opcode, memory.read_u8(pc + 1), 0),
            3 => Instruction(raw_opcode, memory.read_u8(pc + 1),
                             memory.read_u8(pc + 2)),
            _ => { panic!("Invalid instruction length returned") }
        }
    }

    /// Disassembles the instruction into human readable assembly.
    pub fn disassemble(&self, cpu: &CPU, memory: &mut Memory) -> String {
        let opcode = self.opcode();
        match opcode {
            JMPAbs   => format!("JMP ${:02X}{:02X}", self.2, self.1),
            JMPInd   => format!("JMP (${:02X}{:02X})", self.2, self.1),
            JSRAbs   => format!("JSR ${:02X}{:02X}", self.2, self.1),
            LDXImm   => format!("LDX #${:02X}", self.1),
            LDXZero  => format!("LDX ${:02X}", self.1),
            LDXZeroY => format!("LDX ${:02X},Y", self.1),
            LDXAbs   => format!("LDX ${:02X}${:02X}", self.2, self.1),
            LDXAbsY  => format!("LDX ${:02X}${:02X},Y", self.2, self.1),
            NOPImp   => format!("NOP"),
            SECImp   => format!("SEC"),
            STXZero  => format!("STX ${:02X} = {:02X}", self.1, cpu.x),
            STXZeroY => format!("STX ${:02X},Y = {:02X}", self.1, cpu.x),
            STXAbs   => format!("STX ${:02X}${:02X} = {:02X}", self.2, self.1, cpu.x),
            _ => { panic!("Unimplemented opcode found: {:?}", opcode); }
        }
    }

    /// Logs a human-readable representation of the instruction along with the
    /// CPU state in an easy to parse format.
    ///
    /// TODO: Return a string for the test suite so CPU correctness can be
    /// checked. Also it may be more appropriate to move this function into the
    /// CPU.
    pub fn log(&self, cpu: &CPU, memory: &mut Memory) {
        use nes::opcode::opcode_len;
        let opcode = self.opcode();
        let len = opcode_len(&opcode) as u16;

        // Get human readable hex of the instruction bytes. A pattern match is
        // used as bytes that do not exist in an instruction should not be
        // displayed (rather than displaying the default struct value 0).
        let instr_str = match len {
            1 => format!("{:02X}      ", self.0),
            2 => format!("{:02X} {:02X}   ", self.0, self.1),
            3 => format!("{:02X} {:02X} {:02X}", self.0, self.1, self.2),
            _ => { panic!("Invalid instruction length given"); }
        };

        // Disassemble the instruction to a human readable format for the log.
        let disassembled = self.disassemble(cpu, memory);

        // Prints the CPU state and disassembled instruction in a nice parsable
        // format. In the future this output will be used for automatically
        // testing the CPU's accuracy.
        //
        // NOTE: CYC is not cycles like the name sugests, but PPU dots. The PPU
        // can output 3 dots every CPU cycle on NTSC (PAL outputs an extra dot
        // every fifth CPU cycle).
        println!("{:04X}  {}  {:30}  A:{:02X} X:{:02X} Y:{:02X} P:{:02X} SP:{:02X} CYC:{:3}",
                 cpu.pc, instr_str, disassembled, cpu.a, cpu.x, cpu.y, cpu.p,
                 cpu.sp, 0);
    }

    /// Execute the instruction with a routine that corresponds with it's
    /// opcode. All routines for every instruction in the 6502 instruction set
    /// are present here.
    #[inline(always)]
    pub fn execute(&self, cpu: &mut CPU, memory: &mut Memory) {
        use nes::opcode::opcode_len;

        let opcode = self.opcode();
        let len = opcode_len(&opcode) as u16;

        // Execute the internal logic of the instruction based on it's opcode.
        match opcode {
            JMPAbs => {
                cpu.pc = self.absolute() as u16;
                cpu.cycles += 3;
            },
            JMPInd => {
                // A special version of indirect addressing is implemented here
                // due to a bug in the indirect JMP operation.
                // https://github.com/Reshurum/nes-rs/issues/3
                let arg = self.arg_u16() as usize;
                cpu.pc = memory.read_u16_wrapped_msb(arg);
                cpu.cycles += 5;
            },
            JSRAbs => {
                let addr = self.absolute() as u16;
                memory.stack_push_u16(cpu, addr - 1);
                cpu.pc = addr;
                cpu.cycles += 6;
            },
            LDXImm => {
                cpu.x = self.immediate();
                if cpu.x == 0 {
                    cpu.set_zero_flag();
                }
                if is_negative(cpu.x) {
                    cpu.set_negative_flag();
                }
                cpu.cycles += 2;
                cpu.pc += len;
            },
            LDXZero => {
                cpu.x = memory.read_u8(self.zero_page());
                if cpu.x == 0 {
                    cpu.set_zero_flag();
                }
                if is_negative(cpu.x) {
                    cpu.set_negative_flag();
                }
                cpu.cycles += 3;
                cpu.pc += len;
            },
            LDXZeroY => {
                cpu.x = memory.read_u8(self.zero_page_y(cpu));
                if cpu.x == 0 {
                    cpu.set_zero_flag();
                }
                if is_negative(cpu.x) {
                    cpu.set_negative_flag();
                }
                cpu.cycles += 4;
                cpu.pc += len;
            },
            LDXAbs => {
                cpu.x = memory.read_u8(self.absolute());
                if cpu.x == 0 {
                    cpu.set_zero_flag();
                }
                if is_negative(cpu.x) {
                    cpu.set_negative_flag();
                }
                cpu.cycles += 4;
                cpu.pc += len;
            },
            LDXAbsY => {
                let (addr, page_cross) = self.absolute_y(cpu);
                if page_cross != PageCross::Same {
                    cpu.cycles += 1;
                }
                cpu.x = memory.read_u8(addr);
                if cpu.x == 0 {
                    cpu.set_zero_flag();
                }
                if is_negative(cpu.x) {
                    cpu.set_negative_flag();
                }
                cpu.cycles += 4;
                cpu.pc += len;
            },
            NOPImp => {
                // This is the most difficult instruction to implement.
                cpu.cycles += 2;
                cpu.pc += len;
            },
            SECImp => {
                cpu.set_carry_flag();
                cpu.cycles += 2;
                cpu.pc += len;
            },
            STXZero => {
                memory.write_u8(self.zero_page(), cpu.x);
                cpu.cycles += 3;
                cpu.pc += len;
            },
            STXZeroY => {
                memory.write_u8(self.zero_page_y(cpu), cpu.x);
                cpu.cycles += 4;
                cpu.pc += len;
            },
            STXAbs => {
                memory.write_u8(self.absolute(), cpu.x);
                cpu.cycles += 4;
                cpu.pc += len;
            },
            _ => { panic!("Unimplemented opcode found: {:?}", opcode); }
        };
    }

    /// Obtain the opcode of the instruction.
    #[inline(always)]
    fn opcode(&self) -> Opcode {
        use nes::opcode::decode_opcode;
        decode_opcode(self.0)
    }

    /// Read the instruction argument as an 8-bit value.
    #[inline(always)]
    fn arg_u8(&self) -> u8 {
        self.1
    }

    /// Read the instruction argument as a 16-bit value.
    #[inline(always)]
    fn arg_u16(&self) -> u16 {
        let mut reader = Cursor::new(vec![self.1, self.2]);
        reader.read_u16::<LittleEndian>().unwrap()
    }

    /// Dereferences a zero page address in the instruction.
    #[inline(always)]
    fn dereference_u8(&self, memory: &mut Memory) -> u8 {
        memory.read_u8(self.arg_u8() as usize)
    }

    /// Dereferences a memory address in the instruction.
    #[inline(always)]
    fn dereference_u16(&self, memory: &mut Memory) -> u8 {
        memory.read_u8(self.arg_u16() as usize)
    }

    // Addressing mode utility functions here. These are used to simplify the
    // development of instructions and a good explanation of addressing modes
    // can be found at https://skilldrick.github.io/easy6502/#addressing.

    /// Accumulator addressing simply gets values from the accumulator register
    /// rather than from the instruction.
    #[inline(always)]
    fn accumulator(&self, cpu: &CPU) -> u8 {
        cpu.a
    }

    /// Directly return the argument. Immediate addressing simply stores the
    /// value in the argument unlike other addressing modes which typically use
    /// this space for memory addresses.
    #[inline(always)]
    fn immediate(&self) -> u8 {
        self.arg_u8()
    }

    /// Returns an address from the instruction arguments that's between
    /// $00-$FF. This is used for zero page addressing which is typically faster
    /// than it's counterpart absolute addressing.
    #[inline(always)]
    fn zero_page(&self) -> usize {
        self.arg_u8() as usize
    }

    /// Returns a zero page address stored in the instruction with the X
    /// register added to it.
    #[inline(always)]
    fn zero_page_x(&self, cpu: &CPU) -> usize {
        self.arg_u8().wrapping_add(cpu.x) as usize
    }

    /// Returns a zero page address stored in the instruction with the Y
    /// register added to it.
    #[inline(always)]
    fn zero_page_y(&self, cpu: &CPU) -> usize {
        self.arg_u8().wrapping_add(cpu.y) as usize
    }

    /// Returns a signed variation of the 8-bit argument. Relative addressing is
    /// used for branch operations and uses a signed integer containing an
    /// offset of bytes of where to place the program counter.
    #[inline(always)]
    fn relative(&self) -> i8 {
        self.arg_u8() as i8
    }

    /// Returns an address from the instruction argument unaltered.
    #[inline(always)]
    fn absolute(&self) -> usize {
        self.arg_u16() as usize
    }

    /// Returns an address from the instruction argument with the value in the X
    /// register added to it.
    #[inline(always)]
    fn absolute_x(&self, cpu: &CPU) -> (usize, PageCross) {
        let base_addr = self.arg_u16();
        let addr = base_addr.wrapping_add(cpu.x as u16) as usize;
        let page_cross = page_cross(base_addr as usize, addr);
        (addr, page_cross)
    }

    /// Returns an address from the instruction argument with the value in the Y
    /// register added to it.
    #[inline(always)]
    fn absolute_y(&self, cpu: &CPU) -> (usize, PageCross) {
        let base_addr = self.arg_u16();
        let addr = base_addr.wrapping_add(cpu.y as u16) as usize;
        let page_cross = page_cross(base_addr as usize, addr);
        (addr, page_cross)
    }

    /// Indirect addressing uses an absolute address to lookup another address.
    #[inline(always)]
    fn indirect(&self, memory: &mut Memory) -> usize {
        let arg = self.arg_u16() as usize;
        memory.read_u16(arg) as usize
    }

    /// Calculates a memory address using by adding X to the 8-bit value in the
    /// instruction, THEN use that address to find ANOTHER address, then return
    /// THAT address.
    #[inline(always)]
    fn indirect_x(&self, cpu: &CPU, memory: &mut Memory) -> usize {
        let addr = self.arg_u8().wrapping_add(cpu.x) as usize;
        memory.read_u16(addr) as usize
    }

    /// Sane version of indirect_x that gets the zero page address in the
    /// instruction, adds Y to it, then returns the resulting address.
    #[inline(always)]
    fn indirect_y(&self, cpu: &CPU, memory: &mut Memory) -> usize {
        let arg = self.arg_u8() as usize;
        memory.read_u16(arg).wrapping_add(cpu.y as u16) as usize
    }
}

#[derive(PartialEq)]
enum PageCross {
    Same,
    Backwards,
    Forwards,
}

// Additional utility functions used often in instruction logic.

/// Checks if an unsigned number would be negative if it was signed. This is
/// done by checking if the 7th bit is set.
#[inline(always)]
fn is_negative(arg: u8) -> bool {
    let negative_bitmask = 0b10000000;
    arg & negative_bitmask == negative_bitmask
}

/// Returns the page index of the given address. Each memory page for the
/// 6502 is 256 (FF) bytes in size and is relevant because some instructions
/// need extra cycles to use addresses in different pages.
#[inline(always)]
fn page(addr: usize) -> u8 {
    (addr as u16 >> 8) as u8
}

/// Determine if there was a page cross between the addresses and what
/// direction was crossed. Most instructions don't care which direction the
/// page cross was in so those instructions will check for either forwards
/// or backwards.
#[inline(always)]
fn page_cross(addr1: usize, addr2: usize) -> PageCross {
    let page1 = page(addr1);
    let page2 = page(addr2);

    if page1 > page2 {
        PageCross::Backwards
    } else if page1 < page2 {
        PageCross::Forwards
    } else {
        PageCross::Same
    }
}
<commit_msg>Implemented BCS<commit_after>// Copyright 2016 Walter Kuppens.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use byteorder::{LittleEndian, ReadBytesExt};
use nes::cpu::CPU;
use nes::memory::Memory;
use nes::opcode::Opcode::*;
use nes::opcode::Opcode;
use std::io::Cursor;

/// All 6502 instructions are a maximum size of 3 bytes. The first byte is the
/// opcode which is determines the action of the instruction. The following 2
/// bytes are the arguments and are present depending on the opcode.
#[derive(Debug)]
pub struct Instruction(pub u8, pub u8, pub u8);

impl Instruction {
    /// Parses an instruction from memory at the address of the passed program
    /// counter. Some instructions when parsed by the original 6502 will read
    /// arguments from the wrong addresses (e.g indirect JMP), so those bugs are
    /// emulated accurately here.
    pub fn parse(pc: usize, memory: &mut Memory) -> Instruction {
        use nes::opcode::decode_opcode;
        use nes::opcode::opcode_len;

        let raw_opcode = memory.read_u8(pc);
        let opcode = decode_opcode(raw_opcode);
        let len = opcode_len(&opcode);

        match len {
            1 => Instruction(raw_opcode, 0, 0),
            2 => Instruction(raw_opcode, memory.read_u8(pc + 1), 0),
            3 => Instruction(raw_opcode, memory.read_u8(pc + 1),
                             memory.read_u8(pc + 2)),
            _ => { panic!("Invalid instruction length returned") }
        }
    }

    /// Disassembles the instruction into human readable assembly.
    pub fn disassemble(&self, cpu: &CPU, memory: &mut Memory) -> String {
        use nes::opcode::opcode_len;

        let opcode = self.opcode();
        let len = opcode_len(&opcode);

        match opcode {
            BCSRel   => format!("BCS ${:04X}", cpu.pc + self.1 as u16 + len as u16),
            JMPAbs   => format!("JMP ${:02X}{:02X}", self.2, self.1),
            JMPInd   => format!("JMP (${:02X}{:02X})", self.2, self.1),
            JSRAbs   => format!("JSR ${:02X}{:02X}", self.2, self.1),
            LDXImm   => format!("LDX #${:02X}", self.1),
            LDXZero  => format!("LDX ${:02X}", self.1),
            LDXZeroY => format!("LDX ${:02X},Y", self.1),
            LDXAbs   => format!("LDX ${:02X}${:02X}", self.2, self.1),
            LDXAbsY  => format!("LDX ${:02X}${:02X},Y", self.2, self.1),
            NOPImp   => format!("NOP"),
            SECImp   => format!("SEC"),
            STXZero  => format!("STX ${:02X} = {:02X}", self.1, cpu.x),
            STXZeroY => format!("STX ${:02X},Y = {:02X}", self.1, cpu.x),
            STXAbs   => format!("STX ${:02X}${:02X} = {:02X}", self.2, self.1, cpu.x),
            _ => { panic!("Unimplemented opcode found: {:?}", opcode); }
        }
    }

    /// Logs a human-readable representation of the instruction along with the
    /// CPU state in an easy to parse format.
    ///
    /// TODO: Return a string for the test suite so CPU correctness can be
    /// checked. Also it may be more appropriate to move this function into the
    /// CPU.
    pub fn log(&self, cpu: &CPU, memory: &mut Memory) {
        use nes::opcode::opcode_len;
        let opcode = self.opcode();
        let len = opcode_len(&opcode) as u16;

        // Get human readable hex of the instruction bytes. A pattern match is
        // used as bytes that do not exist in an instruction should not be
        // displayed (rather than displaying the default struct value 0).
        let instr_str = match len {
            1 => format!("{:02X}      ", self.0),
            2 => format!("{:02X} {:02X}   ", self.0, self.1),
            3 => format!("{:02X} {:02X} {:02X}", self.0, self.1, self.2),
            _ => { panic!("Invalid instruction length given"); }
        };

        // Disassemble the instruction to a human readable format for the log.
        let disassembled = self.disassemble(cpu, memory);

        // Prints the CPU state and disassembled instruction in a nice parsable
        // format. In the future this output will be used for automatically
        // testing the CPU's accuracy.
        //
        // NOTE: CYC is not cycles like the name sugests, but PPU dots. The PPU
        // can output 3 dots every CPU cycle on NTSC (PAL outputs an extra dot
        // every fifth CPU cycle).
        println!("{:04X}  {}  {:30}  A:{:02X} X:{:02X} Y:{:02X} P:{:02X} SP:{:02X} CYC:{:3}",
                 cpu.pc, instr_str, disassembled, cpu.a, cpu.x, cpu.y, cpu.p,
                 cpu.sp, 0);
    }

    /// Execute the instruction with a routine that corresponds with it's
    /// opcode. All routines for every instruction in the 6502 instruction set
    /// are present here.
    #[inline(always)]
    pub fn execute(&self, cpu: &mut CPU, memory: &mut Memory) {
        use nes::opcode::opcode_len;

        let opcode = self.opcode();
        let len = opcode_len(&opcode) as u16;

        // Execute the internal logic of the instruction based on it's opcode.
        match opcode {
            BCSRel => {
                if cpu.carry_flag_set() {
                    let old_pc = cpu.pc as usize;
                    cpu.pc = cpu.pc.wrapping_add(self.relative() as u16) + len;
                    cpu.cycles += 1;
                    if page_cross(old_pc, cpu.pc as usize) != PageCross::Same {
                        cpu.cycles += 2;
                    }
                }
                cpu.cycles += 2;
            },
            JMPAbs => {
                cpu.pc = self.absolute() as u16;
                cpu.cycles += 3;
            },
            JMPInd => {
                // A special version of indirect addressing is implemented here
                // due to a bug in the indirect JMP operation.
                // https://github.com/Reshurum/nes-rs/issues/3
                let arg = self.arg_u16() as usize;
                cpu.pc = memory.read_u16_wrapped_msb(arg);
                cpu.cycles += 5;
            },
            JSRAbs => {
                let addr = self.absolute() as u16;
                memory.stack_push_u16(cpu, addr - 1);
                cpu.pc = addr;
                cpu.cycles += 6;
            },
            LDXImm => {
                cpu.x = self.immediate();
                if cpu.x == 0 {
                    cpu.set_zero_flag();
                }
                if is_negative(cpu.x) {
                    cpu.set_negative_flag();
                }
                cpu.cycles += 2;
                cpu.pc += len;
            },
            LDXZero => {
                cpu.x = memory.read_u8(self.zero_page());
                if cpu.x == 0 {
                    cpu.set_zero_flag();
                }
                if is_negative(cpu.x) {
                    cpu.set_negative_flag();
                }
                cpu.cycles += 3;
                cpu.pc += len;
            },
            LDXZeroY => {
                cpu.x = memory.read_u8(self.zero_page_y(cpu));
                if cpu.x == 0 {
                    cpu.set_zero_flag();
                }
                if is_negative(cpu.x) {
                    cpu.set_negative_flag();
                }
                cpu.cycles += 4;
                cpu.pc += len;
            },
            LDXAbs => {
                cpu.x = memory.read_u8(self.absolute());
                if cpu.x == 0 {
                    cpu.set_zero_flag();
                }
                if is_negative(cpu.x) {
                    cpu.set_negative_flag();
                }
                cpu.cycles += 4;
                cpu.pc += len;
            },
            LDXAbsY => {
                let (addr, page_cross) = self.absolute_y(cpu);
                if page_cross != PageCross::Same {
                    cpu.cycles += 1;
                }
                cpu.x = memory.read_u8(addr);
                if cpu.x == 0 {
                    cpu.set_zero_flag();
                }
                if is_negative(cpu.x) {
                    cpu.set_negative_flag();
                }
                cpu.cycles += 4;
                cpu.pc += len;
            },
            NOPImp => {
                // This is the most difficult instruction to implement.
                cpu.cycles += 2;
                cpu.pc += len;
            },
            SECImp => {
                cpu.set_carry_flag();
                cpu.cycles += 2;
                cpu.pc += len;
            },
            STXZero => {
                memory.write_u8(self.zero_page(), cpu.x);
                cpu.cycles += 3;
                cpu.pc += len;
            },
            STXZeroY => {
                memory.write_u8(self.zero_page_y(cpu), cpu.x);
                cpu.cycles += 4;
                cpu.pc += len;
            },
            STXAbs => {
                memory.write_u8(self.absolute(), cpu.x);
                cpu.cycles += 4;
                cpu.pc += len;
            },
            _ => { panic!("Unimplemented opcode found: {:?}", opcode); }
        };
    }

    /// Obtain the opcode of the instruction.
    #[inline(always)]
    fn opcode(&self) -> Opcode {
        use nes::opcode::decode_opcode;
        decode_opcode(self.0)
    }

    /// Read the instruction argument as an 8-bit value.
    #[inline(always)]
    fn arg_u8(&self) -> u8 {
        self.1
    }

    /// Read the instruction argument as a 16-bit value.
    #[inline(always)]
    fn arg_u16(&self) -> u16 {
        let mut reader = Cursor::new(vec![self.1, self.2]);
        reader.read_u16::<LittleEndian>().unwrap()
    }

    /// Dereferences a zero page address in the instruction.
    #[inline(always)]
    fn dereference_u8(&self, memory: &mut Memory) -> u8 {
        memory.read_u8(self.arg_u8() as usize)
    }

    /// Dereferences a memory address in the instruction.
    #[inline(always)]
    fn dereference_u16(&self, memory: &mut Memory) -> u8 {
        memory.read_u8(self.arg_u16() as usize)
    }

    // Addressing mode utility functions here. These are used to simplify the
    // development of instructions and a good explanation of addressing modes
    // can be found at https://skilldrick.github.io/easy6502/#addressing.

    /// Accumulator addressing simply gets values from the accumulator register
    /// rather than from the instruction.
    #[inline(always)]
    fn accumulator(&self, cpu: &CPU) -> u8 {
        cpu.a
    }

    /// Directly return the argument. Immediate addressing simply stores the
    /// value in the argument unlike other addressing modes which typically use
    /// this space for memory addresses.
    #[inline(always)]
    fn immediate(&self) -> u8 {
        self.arg_u8()
    }

    /// Returns an address from the instruction arguments that's between
    /// $00-$FF. This is used for zero page addressing which is typically faster
    /// than it's counterpart absolute addressing.
    #[inline(always)]
    fn zero_page(&self) -> usize {
        self.arg_u8() as usize
    }

    /// Returns a zero page address stored in the instruction with the X
    /// register added to it.
    #[inline(always)]
    fn zero_page_x(&self, cpu: &CPU) -> usize {
        self.arg_u8().wrapping_add(cpu.x) as usize
    }

    /// Returns a zero page address stored in the instruction with the Y
    /// register added to it.
    #[inline(always)]
    fn zero_page_y(&self, cpu: &CPU) -> usize {
        self.arg_u8().wrapping_add(cpu.y) as usize
    }

    /// Returns a signed variation of the 8-bit argument. Relative addressing is
    /// used for branch operations and uses a signed integer containing an
    /// offset of bytes of where to place the program counter.
    #[inline(always)]
    fn relative(&self) -> i8 {
        self.arg_u8() as i8
    }

    /// Returns an address from the instruction argument unaltered.
    #[inline(always)]
    fn absolute(&self) -> usize {
        self.arg_u16() as usize
    }

    /// Returns an address from the instruction argument with the value in the X
    /// register added to it.
    #[inline(always)]
    fn absolute_x(&self, cpu: &CPU) -> (usize, PageCross) {
        let base_addr = self.arg_u16();
        let addr = base_addr.wrapping_add(cpu.x as u16) as usize;
        let page_cross = page_cross(base_addr as usize, addr);
        (addr, page_cross)
    }

    /// Returns an address from the instruction argument with the value in the Y
    /// register added to it.
    #[inline(always)]
    fn absolute_y(&self, cpu: &CPU) -> (usize, PageCross) {
        let base_addr = self.arg_u16();
        let addr = base_addr.wrapping_add(cpu.y as u16) as usize;
        let page_cross = page_cross(base_addr as usize, addr);
        (addr, page_cross)
    }

    /// Indirect addressing uses an absolute address to lookup another address.
    #[inline(always)]
    fn indirect(&self, memory: &mut Memory) -> usize {
        let arg = self.arg_u16() as usize;
        memory.read_u16(arg) as usize
    }

    /// Calculates a memory address using by adding X to the 8-bit value in the
    /// instruction, THEN use that address to find ANOTHER address, then return
    /// THAT address.
    #[inline(always)]
    fn indirect_x(&self, cpu: &CPU, memory: &mut Memory) -> usize {
        let addr = self.arg_u8().wrapping_add(cpu.x) as usize;
        memory.read_u16(addr) as usize
    }

    /// Sane version of indirect_x that gets the zero page address in the
    /// instruction, adds Y to it, then returns the resulting address.
    #[inline(always)]
    fn indirect_y(&self, cpu: &CPU, memory: &mut Memory) -> usize {
        let arg = self.arg_u8() as usize;
        memory.read_u16(arg).wrapping_add(cpu.y as u16) as usize
    }
}

#[derive(PartialEq)]
enum PageCross {
    Same,
    Backwards,
    Forwards,
}

// Additional utility functions used often in instruction logic.

/// Checks if an unsigned number would be negative if it was signed. This is
/// done by checking if the 7th bit is set.
#[inline(always)]
fn is_negative(arg: u8) -> bool {
    let negative_bitmask = 0b10000000;
    arg & negative_bitmask == negative_bitmask
}

/// Returns the page index of the given address. Each memory page for the
/// 6502 is 256 (FF) bytes in size and is relevant because some instructions
/// need extra cycles to use addresses in different pages.
#[inline(always)]
fn page(addr: usize) -> u8 {
    (addr as u16 >> 8) as u8
}

/// Determine if there was a page cross between the addresses and what
/// direction was crossed. Most instructions don't care which direction the
/// page cross was in so those instructions will check for either forwards
/// or backwards.
#[inline(always)]
fn page_cross(addr1: usize, addr2: usize) -> PageCross {
    let page1 = page(addr1);
    let page2 = page(addr2);

    if page1 > page2 {
        PageCross::Backwards
    } else if page1 < page2 {
        PageCross::Forwards
    } else {
        PageCross::Same
    }
}
<|endoftext|>


<commit_before>use crate::matching::{MatchResult, match_request};

use pact_matching::models::{Pact, Request, OptionalBody, HttpPart};
use pact_matching::models::matchingrules::*;
use pact_matching::models::generators::*;
use pact_matching::models::parse_query_string;

use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use log::{error, warn, info, debug};
use hyper::{Body, Response, Server, Error};
use hyper::http::response::{Builder as ResponseBuilder};
use hyper::http::header::{HeaderName, HeaderValue};
use hyper::service::service_fn;
use hyper::service::make_service_fn;
use serde_json::json;
use maplit::*;
use hyper::server::conn::AddrIncoming;
use rustls::ServerConfig;

#[derive(Debug, Clone)]
enum InteractionError {
    RequestHeaderEncodingError,
    RequestBodyError,
    ResponseHeaderEncodingError,
    ResponseBodyError
}

fn extract_path(uri: &hyper::Uri) -> String {
    uri.path_and_query()
        .map(|path_and_query| path_and_query.path())
        .unwrap_or("/")
        .into()
}

fn extract_query_string(uri: &hyper::Uri) -> Option<HashMap<String, Vec<String>>> {
    uri.path_and_query()
        .and_then(|path_and_query| path_and_query.query())
        .and_then(|query| parse_query_string(&query.into()))
}

fn extract_headers(headers: &hyper::HeaderMap) -> Result<Option<HashMap<String, Vec<String>>>, InteractionError> {
  if !headers.is_empty() {
    let result: Result<HashMap<String, Vec<String>>, InteractionError> = headers.keys()
      .map(|name| -> Result<(String, Vec<String>), InteractionError> {
        let values = headers.get_all(name);
        let parsed_vals: Vec<Result<String, InteractionError>> = values.iter()
          .map(|val| val.to_str()
            .map(|v| v.to_string())
            .map_err(|err| {
              warn!("Failed to parse HTTP header value: {}", err);
              InteractionError::RequestHeaderEncodingError
            })
          ).collect();
        if parsed_vals.iter().find(|val| val.is_err()).is_some() {
          Err(InteractionError::RequestHeaderEncodingError)
        } else {
          Ok((name.as_str().into(), parsed_vals.iter().cloned()
            .map(|val| val.unwrap_or_default())
            .flat_map(|val| val.split(",").map(|v| v.to_string()).collect::<Vec<String>>())
            .map(|val| val.trim().to_string())
            .collect()))
        }
      })
      .collect();

    result.map(|map| Some(map))
  } else {
    Ok(None)
  }
}

fn extract_body(bytes: bytes::Bytes) -> OptionalBody {
    if bytes.len() > 0 {
        OptionalBody::Present(bytes.to_vec())
    } else {
        OptionalBody::Empty
    }
}

async fn hyper_request_to_pact_request(req: hyper::Request<Body>) -> Result<Request, InteractionError> {
    let method = req.method().to_string();
    let path = extract_path(req.uri());
    let query = extract_query_string(req.uri());
    let headers = extract_headers(req.headers())?;

    let body_bytes = hyper::body::to_bytes(req.into_body())
        .await
        .map_err(|_| InteractionError::RequestBodyError)?;

    Ok(Request {
        method,
        path,
        query,
        headers,
        body: extract_body(body_bytes),
        matching_rules: MatchingRules::default(),
        generators: Generators::default()
    })
}

fn set_hyper_headers(builder: &mut ResponseBuilder, headers: &Option<HashMap<String, Vec<String>>>) -> Result<(), InteractionError> {
    let hyper_headers = builder.headers_mut().unwrap();
    match headers {
        Some(header_map) => {
            for (k, v) in header_map {
                for val in v {
                    // FIXME?: Headers are not sent in "raw" mode.
                    // Names are converted to lower case and values are parsed.
                    hyper_headers.append(
                        HeaderName::from_bytes(k.as_bytes())
                            .map_err(|err| {
                                error!("Invalid header name '{}' ({})", k, err);
                                InteractionError::ResponseHeaderEncodingError
                            })?,
                        val.parse::<HeaderValue>()
                            .map_err(|err| {
                                error!("Invalid header value '{}': '{}' ({})", k, val, err);
                                InteractionError::ResponseHeaderEncodingError
                            })?
                    );
                }
            }
        },
        _ => {}
    }
    Ok(())
}

fn error_body(request: &Request, error: &String) -> String {
    let body = json!({ "error" : format!("{} : {:?}", error, request) });
    body.to_string()
}

fn match_result_to_hyper_response(request: &Request, match_result: MatchResult) -> Result<Response<Body>, InteractionError> {
    match match_result {
        MatchResult::RequestMatch(ref interaction) => {
            let response = pact_matching::generate_response(&interaction.response, &hashmap!{});
            info!("Request matched, sending response {}", response);
            if interaction.response.has_text_body() {
                debug!("     body: '{}'", interaction.response.body.str_value());
            }

            let mut builder = Response::builder()
                .status(response.status)
                .header(hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN, "*");

            set_hyper_headers(&mut builder, &response.headers)?;

            builder.body(match response.body {
                OptionalBody::Present(ref s) => Body::from(s.clone()),
                _ => Body::empty()
            })
                .map_err(|_| InteractionError::ResponseBodyError)
        },
        _ => {
            Response::builder()
                .status(500)
                .header(hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN, "*")
                .header(hyper::header::CONTENT_TYPE, "application/json; charset=utf-8")
                .header("X-Pact", match_result.match_key())
                .body(Body::from(error_body(&request, &match_result.match_key())))
                .map_err(|_| InteractionError::ResponseBodyError)
        }
    }
}

async fn handle_request(
    req: hyper::Request<Body>,
    pact: Arc<Pact>,
    matches: Arc<Mutex<Vec<MatchResult>>>
) -> Result<Response<Body>, InteractionError> {
    debug!("Creating pact request from hyper request");

    let pact_request = hyper_request_to_pact_request(req).await?;
    info!("Received request {}", pact_request);
    if pact_request.has_text_body() {
      debug!("     body: '{}'", pact_request.body.str_value());
    }

    let match_result = match_request(&pact_request, &pact.interactions);

    matches.lock().unwrap().push(match_result.clone());

    match_result_to_hyper_response(&pact_request, match_result)
}

// TODO: Should instead use some form of X-Pact headers
fn handle_mock_request_error(result: Result<Response<Body>, InteractionError>) -> Result<Response<Body>, Error> {
    match result {
        Ok(response) => Ok(response),
        Err(error) => {
            let response = match error {
                InteractionError::RequestHeaderEncodingError => Response::builder()
                    .status(400)
                    .body(Body::from("Found an invalid header encoding")),
                InteractionError::RequestBodyError => Response::builder()
                    .status(500)
                    .body(Body::from("Could not process request body")),
                InteractionError::ResponseBodyError => Response::builder()
                    .status(500)
                    .body(Body::from("Could not process response body")),
                InteractionError::ResponseHeaderEncodingError => Response::builder()
                    .status(500)
                    .body(Body::from("Could not set response header"))
            };
            Ok(response.unwrap())
        }
    }
}

// Create and bind the server, but do not start it.
// Returns a future that drives the server.
// The reason that the function itself is still async (even if it performs
// no async operations) is that it needs a tokio context to be able to call try_bind.
pub(crate) async fn create_and_bind(
    pact: Pact,
    addr: std::net::SocketAddr,
    shutdown: impl std::future::Future<Output = ()>,
    matches: Arc<Mutex<Vec<MatchResult>>>,
) -> Result<(impl std::future::Future<Output = ()>, std::net::SocketAddr), hyper::Error> {
    let pact = Arc::new(pact);

    let server = Server::try_bind(&addr)?
        .serve(make_service_fn(move |_| {
            let pact = pact.clone();
            let matches = matches.clone();

            async {
                Ok::<_, hyper::Error>(
                    service_fn(move |req| {
                        let pact = pact.clone();
                        let matches = matches.clone();

                        async {
                            handle_mock_request_error(
                                handle_request(req, pact, matches).await
                            )
                        }
                    })
                )
            }
        }));

    let socket_addr = server.local_addr();

    Ok((
        // This is the future that drives the server:
        async {
            let _ = server
                .with_graceful_shutdown(shutdown)
                .await;
        },
        socket_addr
    ))
}

pub(crate) async fn create_and_bind_tls(
  pact: Pact,
  addr: std::net::SocketAddr,
  shutdown: impl std::future::Future<Output = ()>,
  matches: Arc<Mutex<Vec<MatchResult>>>,
  tls: &ServerConfig
) -> Result<(impl std::future::Future<Output = ()>, std::net::SocketAddr), hyper::Error> {
  let pact = Arc::new(pact);

  let incoming = AddrIncoming::bind(&addr)?;
  let socket_addr = incoming.local_addr();
  let server = Server::builder(crate::tls::TlsAcceptor::new(tls.clone(), incoming))
    .serve(make_service_fn(move |_| {
      let pact = pact.clone();
      let matches = matches.clone();

      async {
        Ok::<_, hyper::Error>(
          service_fn(move |req| {
            let pact = pact.clone();
            let matches = matches.clone();

            async {
              handle_mock_request_error(
                handle_request(req, pact, matches).await
              )
            }
          })
        )
      }
    }));

  Ok((
    // This is the future that drives the server:
    async {
      let _ = server
        .with_graceful_shutdown(shutdown)
        .await;
    },
    socket_addr
  ))
}

#[cfg(test)]
mod tests {
  use super::*;
  use hyper::HeaderMap;
  use hyper::header::{ACCEPT, USER_AGENT, CONTENT_TYPE};
  use expectest::prelude::*;
  use expectest::expect;

  #[tokio::test]
    async fn can_fetch_results_on_current_thread() {
        let (shutdown_tx, shutdown_rx) = futures::channel::oneshot::channel();
        let matches = Arc::new(Mutex::new(vec![]));

        let (future, _) = create_and_bind(
            Pact::default(),
            ([0, 0, 0, 0], 0 as u16).into(),
            async {
                shutdown_rx.await.ok();
            },
            matches.clone()
        ).await.unwrap();

        let join_handle = tokio::task::spawn(future);

        shutdown_tx.send(()).unwrap();

        // Server has shut down, now flush the server future from runtime
        join_handle.await.unwrap();

        // 0 matches have been produced
        let all_matches = matches.lock().unwrap().clone();
        assert_eq!(all_matches, vec![]);
    }

  #[test]
  fn handle_hyper_headers_with_multiple_values() {
    let mut headers = HeaderMap::new();
    headers.append(ACCEPT, "application/xml, application/json".parse().unwrap());
    headers.append(USER_AGENT, "test".parse().unwrap());
    headers.append(USER_AGENT, "test2".parse().unwrap());
    headers.append(CONTENT_TYPE, "text/plain".parse().unwrap());
    let result = extract_headers(&headers);
    expect!(result).to(be_ok().value(Some(hashmap! {
      "accept".to_string() => vec!["application/xml".to_string(), "application/json".to_string()],
      "user-agent".to_string() => vec!["test".to_string(), "test2".to_string()],
      "content-type".to_string() => vec!["text/plain".to_string()]
    })));
  }
}
<commit_msg>chore: add debug log entry when request does not match<commit_after>use crate::matching::{MatchResult, match_request};

use pact_matching::models::{Pact, Request, OptionalBody, HttpPart};
use pact_matching::models::matchingrules::*;
use pact_matching::models::generators::*;
use pact_matching::models::parse_query_string;

use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use log::{error, warn, info, debug};
use hyper::{Body, Response, Server, Error};
use hyper::http::response::{Builder as ResponseBuilder};
use hyper::http::header::{HeaderName, HeaderValue};
use hyper::service::service_fn;
use hyper::service::make_service_fn;
use serde_json::json;
use maplit::*;
use hyper::server::conn::AddrIncoming;
use rustls::ServerConfig;

#[derive(Debug, Clone)]
enum InteractionError {
    RequestHeaderEncodingError,
    RequestBodyError,
    ResponseHeaderEncodingError,
    ResponseBodyError
}

fn extract_path(uri: &hyper::Uri) -> String {
    uri.path_and_query()
        .map(|path_and_query| path_and_query.path())
        .unwrap_or("/")
        .into()
}

fn extract_query_string(uri: &hyper::Uri) -> Option<HashMap<String, Vec<String>>> {
    uri.path_and_query()
        .and_then(|path_and_query| path_and_query.query())
        .and_then(|query| parse_query_string(&query.into()))
}

fn extract_headers(headers: &hyper::HeaderMap) -> Result<Option<HashMap<String, Vec<String>>>, InteractionError> {
  if !headers.is_empty() {
    let result: Result<HashMap<String, Vec<String>>, InteractionError> = headers.keys()
      .map(|name| -> Result<(String, Vec<String>), InteractionError> {
        let values = headers.get_all(name);
        let parsed_vals: Vec<Result<String, InteractionError>> = values.iter()
          .map(|val| val.to_str()
            .map(|v| v.to_string())
            .map_err(|err| {
              warn!("Failed to parse HTTP header value: {}", err);
              InteractionError::RequestHeaderEncodingError
            })
          ).collect();
        if parsed_vals.iter().find(|val| val.is_err()).is_some() {
          Err(InteractionError::RequestHeaderEncodingError)
        } else {
          Ok((name.as_str().into(), parsed_vals.iter().cloned()
            .map(|val| val.unwrap_or_default())
            .flat_map(|val| val.split(",").map(|v| v.to_string()).collect::<Vec<String>>())
            .map(|val| val.trim().to_string())
            .collect()))
        }
      })
      .collect();

    result.map(|map| Some(map))
  } else {
    Ok(None)
  }
}

fn extract_body(bytes: bytes::Bytes) -> OptionalBody {
    if bytes.len() > 0 {
        OptionalBody::Present(bytes.to_vec())
    } else {
        OptionalBody::Empty
    }
}

async fn hyper_request_to_pact_request(req: hyper::Request<Body>) -> Result<Request, InteractionError> {
    let method = req.method().to_string();
    let path = extract_path(req.uri());
    let query = extract_query_string(req.uri());
    let headers = extract_headers(req.headers())?;

    let body_bytes = hyper::body::to_bytes(req.into_body())
        .await
        .map_err(|_| InteractionError::RequestBodyError)?;

    Ok(Request {
        method,
        path,
        query,
        headers,
        body: extract_body(body_bytes),
        matching_rules: MatchingRules::default(),
        generators: Generators::default()
    })
}

fn set_hyper_headers(builder: &mut ResponseBuilder, headers: &Option<HashMap<String, Vec<String>>>) -> Result<(), InteractionError> {
    let hyper_headers = builder.headers_mut().unwrap();
    match headers {
        Some(header_map) => {
            for (k, v) in header_map {
                for val in v {
                    // FIXME?: Headers are not sent in "raw" mode.
                    // Names are converted to lower case and values are parsed.
                    hyper_headers.append(
                        HeaderName::from_bytes(k.as_bytes())
                            .map_err(|err| {
                                error!("Invalid header name '{}' ({})", k, err);
                                InteractionError::ResponseHeaderEncodingError
                            })?,
                        val.parse::<HeaderValue>()
                            .map_err(|err| {
                                error!("Invalid header value '{}': '{}' ({})", k, val, err);
                                InteractionError::ResponseHeaderEncodingError
                            })?
                    );
                }
            }
        },
        _ => {}
    }
    Ok(())
}

fn error_body(request: &Request, error: &String) -> String {
    let body = json!({ "error" : format!("{} : {:?}", error, request) });
    body.to_string()
}

fn match_result_to_hyper_response(request: &Request, match_result: MatchResult) -> Result<Response<Body>, InteractionError> {
    match match_result {
        MatchResult::RequestMatch(ref interaction) => {
            let response = pact_matching::generate_response(&interaction.response, &hashmap!{});
            info!("Request matched, sending response {}", response);
            if interaction.response.has_text_body() {
                debug!("     body: '{}'", interaction.response.body.str_value());
            }

            let mut builder = Response::builder()
                .status(response.status)
                .header(hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN, "*");

            set_hyper_headers(&mut builder, &response.headers)?;

            builder.body(match response.body {
                OptionalBody::Present(ref s) => Body::from(s.clone()),
                _ => Body::empty()
            })
                .map_err(|_| InteractionError::ResponseBodyError)
        },
        _ => {
            debug!("Request did not match: {:?}", match_result);
            Response::builder()
                .status(500)
                .header(hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN, "*")
                .header(hyper::header::CONTENT_TYPE, "application/json; charset=utf-8")
                .header("X-Pact", match_result.match_key())
                .body(Body::from(error_body(&request, &match_result.match_key())))
                .map_err(|_| InteractionError::ResponseBodyError)
        }
    }
}

async fn handle_request(
    req: hyper::Request<Body>,
    pact: Arc<Pact>,
    matches: Arc<Mutex<Vec<MatchResult>>>
) -> Result<Response<Body>, InteractionError> {
    debug!("Creating pact request from hyper request");

    let pact_request = hyper_request_to_pact_request(req).await?;
    info!("Received request {}", pact_request);
    if pact_request.has_text_body() {
      debug!("     body: '{}'", pact_request.body.str_value());
    }

    let match_result = match_request(&pact_request, &pact.interactions);

    matches.lock().unwrap().push(match_result.clone());

    match_result_to_hyper_response(&pact_request, match_result)
}

// TODO: Should instead use some form of X-Pact headers
fn handle_mock_request_error(result: Result<Response<Body>, InteractionError>) -> Result<Response<Body>, Error> {
    match result {
        Ok(response) => Ok(response),
        Err(error) => {
            let response = match error {
                InteractionError::RequestHeaderEncodingError => Response::builder()
                    .status(400)
                    .body(Body::from("Found an invalid header encoding")),
                InteractionError::RequestBodyError => Response::builder()
                    .status(500)
                    .body(Body::from("Could not process request body")),
                InteractionError::ResponseBodyError => Response::builder()
                    .status(500)
                    .body(Body::from("Could not process response body")),
                InteractionError::ResponseHeaderEncodingError => Response::builder()
                    .status(500)
                    .body(Body::from("Could not set response header"))
            };
            Ok(response.unwrap())
        }
    }
}

// Create and bind the server, but do not start it.
// Returns a future that drives the server.
// The reason that the function itself is still async (even if it performs
// no async operations) is that it needs a tokio context to be able to call try_bind.
pub(crate) async fn create_and_bind(
    pact: Pact,
    addr: std::net::SocketAddr,
    shutdown: impl std::future::Future<Output = ()>,
    matches: Arc<Mutex<Vec<MatchResult>>>,
) -> Result<(impl std::future::Future<Output = ()>, std::net::SocketAddr), hyper::Error> {
    let pact = Arc::new(pact);

    let server = Server::try_bind(&addr)?
        .serve(make_service_fn(move |_| {
            let pact = pact.clone();
            let matches = matches.clone();

            async {
                Ok::<_, hyper::Error>(
                    service_fn(move |req| {
                        let pact = pact.clone();
                        let matches = matches.clone();

                        async {
                            handle_mock_request_error(
                                handle_request(req, pact, matches).await
                            )
                        }
                    })
                )
            }
        }));

    let socket_addr = server.local_addr();

    Ok((
        // This is the future that drives the server:
        async {
            let _ = server
                .with_graceful_shutdown(shutdown)
                .await;
        },
        socket_addr
    ))
}

pub(crate) async fn create_and_bind_tls(
  pact: Pact,
  addr: std::net::SocketAddr,
  shutdown: impl std::future::Future<Output = ()>,
  matches: Arc<Mutex<Vec<MatchResult>>>,
  tls: &ServerConfig
) -> Result<(impl std::future::Future<Output = ()>, std::net::SocketAddr), hyper::Error> {
  let pact = Arc::new(pact);

  let incoming = AddrIncoming::bind(&addr)?;
  let socket_addr = incoming.local_addr();
  let server = Server::builder(crate::tls::TlsAcceptor::new(tls.clone(), incoming))
    .serve(make_service_fn(move |_| {
      let pact = pact.clone();
      let matches = matches.clone();

      async {
        Ok::<_, hyper::Error>(
          service_fn(move |req| {
            let pact = pact.clone();
            let matches = matches.clone();

            async {
              handle_mock_request_error(
                handle_request(req, pact, matches).await
              )
            }
          })
        )
      }
    }));

  Ok((
    // This is the future that drives the server:
    async {
      let _ = server
        .with_graceful_shutdown(shutdown)
        .await;
    },
    socket_addr
  ))
}

#[cfg(test)]
mod tests {
  use super::*;
  use hyper::HeaderMap;
  use hyper::header::{ACCEPT, USER_AGENT, CONTENT_TYPE};
  use expectest::prelude::*;
  use expectest::expect;

  #[tokio::test]
    async fn can_fetch_results_on_current_thread() {
        let (shutdown_tx, shutdown_rx) = futures::channel::oneshot::channel();
        let matches = Arc::new(Mutex::new(vec![]));

        let (future, _) = create_and_bind(
            Pact::default(),
            ([0, 0, 0, 0], 0 as u16).into(),
            async {
                shutdown_rx.await.ok();
            },
            matches.clone()
        ).await.unwrap();

        let join_handle = tokio::task::spawn(future);

        shutdown_tx.send(()).unwrap();

        // Server has shut down, now flush the server future from runtime
        join_handle.await.unwrap();

        // 0 matches have been produced
        let all_matches = matches.lock().unwrap().clone();
        assert_eq!(all_matches, vec![]);
    }

  #[test]
  fn handle_hyper_headers_with_multiple_values() {
    let mut headers = HeaderMap::new();
    headers.append(ACCEPT, "application/xml, application/json".parse().unwrap());
    headers.append(USER_AGENT, "test".parse().unwrap());
    headers.append(USER_AGENT, "test2".parse().unwrap());
    headers.append(CONTENT_TYPE, "text/plain".parse().unwrap());
    let result = extract_headers(&headers);
    expect!(result).to(be_ok().value(Some(hashmap! {
      "accept".to_string() => vec!["application/xml".to_string(), "application/json".to_string()],
      "user-agent".to_string() => vec!["test".to_string(), "test2".to_string()],
      "content-type".to_string() => vec!["text/plain".to_string()]
    })));
  }
}
<|endoftext|>