<commit_before>///! Property-based testing strategies
use itertools::interleave;
use proptest::collection::{size_range, vec};
use proptest::prelude::*;
use proptest::strategy::Union;
use stencila_schema::*;

/// The degree of freedom when generating arbitrary nodes.
///
/// Generally, when adding a `proptest` it is wise to start with `Nil`
/// freedom and gradually increase it while fixing issues along the way.
#[allow(dead_code)]
#[derive(Copy, Clone)]
pub enum Freedom {
    Nil,
    Low,
    High,
}

prop_compose! {
    /// Generate an arbitrary inline string
    pub fn string(freedom: Freedom)(
        string in (match freedom {
            Freedom::Nil => r"string",
            Freedom::Low => r"[A-Za-z0-9 ]+",
            Freedom::High => any::<String>(),
        }).prop_filter(
            "Inline strings should not be empty",
            |string| !string.is_empty()
        )
    ) -> InlineContent {
        InlineContent::String(string)
    }
}

prop_compose! {
    /// Generate an arbitrary inline string with no spaces
    pub fn string_no_whitespace(freedom: Freedom)(
        string in match freedom {
            Freedom::Nil => r"string",
            _ => r"[A-Za-z0-9]+",
        }
    ) -> InlineContent {
        InlineContent::String(string)
    }
}

prop_compose! {
    /// Generate an arbitrary audio object
    /// Use audio file extensions because Markdown decoding uses that to determine
    /// to decode to a `AudioObject`.
    pub fn audio_object_simple(freedom: Freedom)(
        content_url in match freedom {
            Freedom::Nil => r"url\.mp3",
            Freedom::Low => r"[A-Za-z0-9-_]+\.(flac|mp3|ogg)",
            Freedom::High => r"\PC*\.(flac|mp3|ogg)",
        }
    ) -> InlineContent {
        InlineContent::AudioObject(AudioObjectSimple{
            content_url,
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate an arbitrary image object
    /// Use image file extensions because Markdown decoding uses that to determine
    /// to decode to a `ImageObject`.
    pub fn image_object_simple(freedom: Freedom)(
        content_url in match freedom {
            Freedom::Nil => r"url\.png",
            Freedom::Low => r"[A-Za-z0-9-_]\.(gif|jpg|jpeg|png)",
            Freedom::High => r"\PC*\.(gif|jpg|jpeg|png)",
        }
    ) -> InlineContent {
        InlineContent::ImageObject(ImageObjectSimple{
            content_url,
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate an arbitrary video object
    /// Use video file extensions because Markdown decoding uses that to determine
    /// to decode to a `VideoObject`.
    pub fn video_object_simple(freedom: Freedom)(
        content_url in match freedom {
            Freedom::Nil => r"url\.mp4",
            Freedom::Low => r"[A-Za-z0-9-_]\.(3gp|mp4|ogv|webm)",
            Freedom::High => r"\PC*\.(3gp|mp4|ogv|webm)",
        }
    ) -> InlineContent {
        InlineContent::VideoObject(VideoObjectSimple{
            content_url,
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a code fragment node with arbitrary text and programming language
    pub fn code_fragment(freedom: Freedom)(
        text in match freedom {
            Freedom::Nil => r"text",
            Freedom::Low => r"[A-Za-z0-9-_ ]+",
            Freedom::High => any::<String>()
        },
        programming_language in match freedom {
            Freedom::Nil => "",
            Freedom::Low => r"[A-Za-z0-9-]+",
            Freedom::High => any::<String>()
        }
    ) -> InlineContent {
        let programming_language = if programming_language.is_empty() {
            None
        } else {
            Some(Box::new(programming_language))
        };
        InlineContent::CodeFragment(CodeFragment{
            text,
            programming_language,
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a delete node with arbitrary content
    pub fn delete(freedom: Freedom)(
        content in string_no_whitespace(freedom)
    ) -> InlineContent {
        InlineContent::Delete(Delete{
            content:vec![content],
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a emphasis node with arbitrary content
    pub fn emphasis(freedom: Freedom)(
        content in string_no_whitespace(freedom)
    ) -> InlineContent {
        InlineContent::Emphasis(Emphasis{
            content:vec![content],
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a link with arbitrary target and content
    pub fn link(freedom: Freedom)(
        target in match freedom {
            Freedom::Nil => r"target",
            Freedom::Low => r"[A-Za-z0-9-]*",
            Freedom::High => any::<String>()
        },
        content in string(freedom)
    ) -> InlineContent {
        InlineContent::Link(Link{
            target,
            content:vec![content],
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a nontextual annotation node with arbitrary content
    pub fn nontextual_annotation(freedom: Freedom)(
        content in string(freedom)
    ) -> InlineContent {
        InlineContent::NontextualAnnotation(NontextualAnnotation{
            content:vec![content],
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a strong node with arbitrary content
    pub fn strong(freedom: Freedom)(
        content in string_no_whitespace(freedom)
    ) -> InlineContent {
        InlineContent::Strong(Strong{
            content:vec![content],
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a subscript node with arbitrary content
    pub fn subscript(freedom: Freedom)(
        content in string_no_whitespace(freedom)
    ) -> InlineContent {
        InlineContent::Subscript(Subscript{
            content:vec![content],
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a superscript node with arbitrary content
    pub fn superscript(freedom: Freedom)(
        content in string_no_whitespace(freedom)
    ) -> InlineContent {
        InlineContent::Superscript(Superscript{
            content:vec![content],
            ..Default::default()
        })
    }
}

/// Generate one of the inline content node types excluding strings (which
/// we usually want to be interleaved between them).
pub fn inline_content(freedom: Freedom) -> impl Strategy<Value = InlineContent> {
    Union::new(vec![
        audio_object_simple(freedom).boxed(),
        image_object_simple(freedom).boxed(),
        video_object_simple(freedom).boxed(),
        code_fragment(freedom).boxed(),
        delete(freedom).boxed(),
        emphasis(freedom).boxed(),
        link(freedom).boxed(),
        nontextual_annotation(freedom).boxed(),
        strong(freedom).boxed(),
        subscript(freedom).boxed(),
        superscript(freedom).boxed(),
    ])
}

prop_compose! {
    /// Generate a vector of inline content of arbitrary length and content
    /// but always having strings interspersed by other inline content (to separate them
    /// so that they do not get decoded as a single string).
    ///
    /// Restrictions:
    ///   - Always starts and ends with a string.
    ///   - Ensures that nodes such as `Strong` and `Emphasis` are surrounded by spaces (for Markdown).
    ///   - No leading or trailing blank strings (for Markdown).
    pub fn vec_inline_content(freedom: Freedom)(length in 1usize..10)(
        strings in vec(string(freedom), size_range(length + 1)),
        others in vec(inline_content(freedom), size_range(length))
    ) -> Vec<InlineContent> {
        let mut content: Vec<InlineContent> = interleave(strings, others).collect();
        for index in 0..content.len() {
            let spaces = match content[index] {
                InlineContent::Emphasis(..) | InlineContent::Strong(..) | InlineContent::Delete(..) => {
                   true
                },
                _ => false
            };

            if spaces {
                if let InlineContent::String(string) = &mut content[index - 1] {
                    *string = [string.as_str(), " "].concat();
                }
                if let InlineContent::String(string) = &mut content[index + 1] {
                    *string = [" ", string.as_str()].concat();
                }
            }

            if index == 0 {
                if let InlineContent::String(string) = &mut content[index] {
                    if string.trim().is_empty() {
                        *string = "Unblanked".to_string();
                    }
                }
            }
            if index == content.len() - 1 {
                if let InlineContent::String(string) = &mut content[index] {
                    if string.trim().is_empty() {
                        *string = ".".to_string();
                    }
                }
            }
        }
        content
    }
}

prop_compose! {
    /// Generate a code block node with arbitrary text and programming language
    pub fn code_block(freedom: Freedom)(
        text in match freedom {
            Freedom::Nil => r"text",
            Freedom::Low => r"[A-Za-z0-9-_ \t\n]*",
            Freedom::High => any::<String>()
        },
        programming_language in match freedom {
            Freedom::Nil => "",
            Freedom::Low => r"[A-Za-z0-9-]*",
            Freedom::High => any::<String>()
        }
    ) -> BlockContent {
        let programming_language = if programming_language.is_empty() {
            None
        } else {
            Some(Box::new(programming_language))
        };
        BlockContent::CodeBlock(CodeBlock{
            text,
            programming_language,
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a heading with arbitrary content and depth
    pub fn heading(freedom: Freedom)(
        depth in 1..6,
        content in vec_inline_content(freedom)
    ) -> BlockContent {
        BlockContent::Heading(Heading{
            depth: Some(depth as u8),
            content,
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a paragraph with arbitrary content
    pub fn paragraph(freedom: Freedom)(
        content in vec_inline_content(freedom)
    ) -> BlockContent {
        BlockContent::Paragraph(Paragraph{
            content,
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a list with arbitrary items and order
    pub fn list(freedom: Freedom)(
        order in prop_oneof![Just(ListOrder::Ascending), Just(ListOrder::Unordered)],
        items in vec(list_item(freedom), 1..5)
    ) -> BlockContent {
        BlockContent::List(List{
            order: Some(order),
            items,
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a list item with arbitrary inline content.
    /// Unable to use block_content strategy here because that causes infinite recursion.
    /// Instead, currently, allow for an single paragraph
    // TODO: allow for alternative numbers of paragraphs
    pub fn list_item(freedom: Freedom)(
        content in vec_inline_content(freedom)
    ) -> ListItem {
        let content = Some(
            ListItemContent::VecBlockContent(vec![
                BlockContent::Paragraph(
                    Paragraph{
                        content,
                        ..Default::default()
                    }
                )
            ])
        );

        ListItem{
            content,
            ..Default::default()
        }
    }
}

/// Generate a thematic break
pub fn thematic_break() -> impl Strategy<Value = BlockContent> {
    Just(BlockContent::ThematicBreak(ThematicBreak::default()))
}

/// Generate one of the block content node types
pub fn block_content(freedom: Freedom) -> impl Strategy<Value = BlockContent> {
    Union::new(vec![
        code_block(freedom).boxed(),
        heading(freedom).boxed(),
        list(freedom).boxed(),
        paragraph(freedom).boxed(),
        thematic_break().boxed(),
    ])
}

prop_compose! {
    /// Generate a vector of block content of arbitrary length and content
    ///
    /// Restrictions:
    ///  - Does not start with a thematic break (unrealistic, and in Markdown can
    ///    be confused with YAML frontmatter)
    pub fn vec_block_content(freedom: Freedom)(length in 1usize..10)(
        blocks in vec(block_content(freedom), size_range(length)).prop_filter(
            "Vector of block content should not start with thematic break",
            |blocks| !matches!(blocks[0], BlockContent::ThematicBreak(..))
        )
    ) -> Vec<BlockContent> {
        blocks
    }
}

prop_compose! {
    /// Generate an article with arbitrary content (and in the future, other properties)
    pub fn article(freedom: Freedom)(
        content in vec_block_content(freedom)
    ) -> Node {
        Node::Article(Article{content: Some(content), ..Default::default()})
    }
}

/// Generate an arbitrary node
pub fn node(freedom: Freedom) -> impl Strategy<Value = Node> {
    Union::new(vec![article(freedom).boxed()])
}
<commit_msg>test(Markdown): Prevent adjacent lists with same ordering<commit_after>///! Property-based testing strategies
use itertools::interleave;
use proptest::collection::{size_range, vec};
use proptest::prelude::*;
use proptest::strategy::Union;
use stencila_schema::*;

/// The degree of freedom when generating arbitrary nodes.
///
/// Generally, when adding a `proptest` it is wise to start with `Nil`
/// freedom and gradually increase it while fixing issues along the way.
#[allow(dead_code)]
#[derive(Copy, Clone)]
pub enum Freedom {
    Nil,
    Low,
    High,
}

prop_compose! {
    /// Generate an arbitrary inline string
    pub fn string(freedom: Freedom)(
        string in (match freedom {
            Freedom::Nil => r"string",
            Freedom::Low => r"[A-Za-z0-9 ]+",
            Freedom::High => any::<String>(),
        }).prop_filter(
            "Inline strings should not be empty",
            |string| !string.is_empty()
        )
    ) -> InlineContent {
        InlineContent::String(string)
    }
}

prop_compose! {
    /// Generate an arbitrary inline string with no spaces
    pub fn string_no_whitespace(freedom: Freedom)(
        string in match freedom {
            Freedom::Nil => r"string",
            _ => r"[A-Za-z0-9]+",
        }
    ) -> InlineContent {
        InlineContent::String(string)
    }
}

prop_compose! {
    /// Generate an arbitrary audio object
    /// Use audio file extensions because Markdown decoding uses that to determine
    /// to decode to a `AudioObject`.
    pub fn audio_object_simple(freedom: Freedom)(
        content_url in match freedom {
            Freedom::Nil => r"url\.mp3",
            Freedom::Low => r"[A-Za-z0-9-_]+\.(flac|mp3|ogg)",
            Freedom::High => r"\PC*\.(flac|mp3|ogg)",
        }
    ) -> InlineContent {
        InlineContent::AudioObject(AudioObjectSimple{
            content_url,
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate an arbitrary image object
    /// Use image file extensions because Markdown decoding uses that to determine
    /// to decode to a `ImageObject`.
    pub fn image_object_simple(freedom: Freedom)(
        content_url in match freedom {
            Freedom::Nil => r"url\.png",
            Freedom::Low => r"[A-Za-z0-9-_]\.(gif|jpg|jpeg|png)",
            Freedom::High => r"\PC*\.(gif|jpg|jpeg|png)",
        }
    ) -> InlineContent {
        InlineContent::ImageObject(ImageObjectSimple{
            content_url,
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate an arbitrary video object
    /// Use video file extensions because Markdown decoding uses that to determine
    /// to decode to a `VideoObject`.
    pub fn video_object_simple(freedom: Freedom)(
        content_url in match freedom {
            Freedom::Nil => r"url\.mp4",
            Freedom::Low => r"[A-Za-z0-9-_]\.(3gp|mp4|ogv|webm)",
            Freedom::High => r"\PC*\.(3gp|mp4|ogv|webm)",
        }
    ) -> InlineContent {
        InlineContent::VideoObject(VideoObjectSimple{
            content_url,
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a code fragment node with arbitrary text and programming language
    pub fn code_fragment(freedom: Freedom)(
        text in match freedom {
            Freedom::Nil => r"text",
            Freedom::Low => r"[A-Za-z0-9-_ ]+",
            Freedom::High => any::<String>()
        },
        programming_language in match freedom {
            Freedom::Nil => "",
            Freedom::Low => r"[A-Za-z0-9-]+",
            Freedom::High => any::<String>()
        }
    ) -> InlineContent {
        let programming_language = if programming_language.is_empty() {
            None
        } else {
            Some(Box::new(programming_language))
        };
        InlineContent::CodeFragment(CodeFragment{
            text,
            programming_language,
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a delete node with arbitrary content
    pub fn delete(freedom: Freedom)(
        content in string_no_whitespace(freedom)
    ) -> InlineContent {
        InlineContent::Delete(Delete{
            content:vec![content],
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a emphasis node with arbitrary content
    pub fn emphasis(freedom: Freedom)(
        content in string_no_whitespace(freedom)
    ) -> InlineContent {
        InlineContent::Emphasis(Emphasis{
            content:vec![content],
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a link with arbitrary target and content
    pub fn link(freedom: Freedom)(
        target in match freedom {
            Freedom::Nil => r"target",
            Freedom::Low => r"[A-Za-z0-9-]*",
            Freedom::High => any::<String>()
        },
        content in string(freedom)
    ) -> InlineContent {
        InlineContent::Link(Link{
            target,
            content:vec![content],
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a nontextual annotation node with arbitrary content
    pub fn nontextual_annotation(freedom: Freedom)(
        content in string(freedom)
    ) -> InlineContent {
        InlineContent::NontextualAnnotation(NontextualAnnotation{
            content:vec![content],
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a strong node with arbitrary content
    pub fn strong(freedom: Freedom)(
        content in string_no_whitespace(freedom)
    ) -> InlineContent {
        InlineContent::Strong(Strong{
            content:vec![content],
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a subscript node with arbitrary content
    pub fn subscript(freedom: Freedom)(
        content in string_no_whitespace(freedom)
    ) -> InlineContent {
        InlineContent::Subscript(Subscript{
            content:vec![content],
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a superscript node with arbitrary content
    pub fn superscript(freedom: Freedom)(
        content in string_no_whitespace(freedom)
    ) -> InlineContent {
        InlineContent::Superscript(Superscript{
            content:vec![content],
            ..Default::default()
        })
    }
}

/// Generate one of the inline content node types excluding strings (which
/// we usually want to be interleaved between them).
pub fn inline_content(freedom: Freedom) -> impl Strategy<Value = InlineContent> {
    Union::new(vec![
        audio_object_simple(freedom).boxed(),
        image_object_simple(freedom).boxed(),
        video_object_simple(freedom).boxed(),
        code_fragment(freedom).boxed(),
        delete(freedom).boxed(),
        emphasis(freedom).boxed(),
        link(freedom).boxed(),
        nontextual_annotation(freedom).boxed(),
        strong(freedom).boxed(),
        subscript(freedom).boxed(),
        superscript(freedom).boxed(),
    ])
}

prop_compose! {
    /// Generate a vector of inline content of arbitrary length and content
    /// but always having strings interspersed by other inline content (to separate them
    /// so that they do not get decoded as a single string).
    ///
    /// Restrictions:
    ///   - Always starts and ends with a string.
    ///   - Ensures that nodes such as `Strong` and `Emphasis` are surrounded by spaces (for Markdown).
    ///   - No leading or trailing blank strings (for Markdown).
    pub fn vec_inline_content(freedom: Freedom)(length in 1usize..10)(
        strings in vec(string(freedom), size_range(length + 1)),
        others in vec(inline_content(freedom), size_range(length))
    ) -> Vec<InlineContent> {
        let mut content: Vec<InlineContent> = interleave(strings, others).collect();
        for index in 0..content.len() {
            let spaces = match content[index] {
                InlineContent::Emphasis(..) | InlineContent::Strong(..) | InlineContent::Delete(..) => {
                   true
                },
                _ => false
            };

            if spaces {
                if let InlineContent::String(string) = &mut content[index - 1] {
                    *string = [string.as_str(), " "].concat();
                }
                if let InlineContent::String(string) = &mut content[index + 1] {
                    *string = [" ", string.as_str()].concat();
                }
            }

            if index == 0 {
                if let InlineContent::String(string) = &mut content[index] {
                    if string.trim().is_empty() {
                        *string = "Unblanked".to_string();
                    }
                }
            }
            if index == content.len() - 1 {
                if let InlineContent::String(string) = &mut content[index] {
                    if string.trim().is_empty() {
                        *string = ".".to_string();
                    }
                }
            }
        }
        content
    }
}

prop_compose! {
    /// Generate a code block node with arbitrary text and programming language
    pub fn code_block(freedom: Freedom)(
        text in match freedom {
            Freedom::Nil => r"text",
            Freedom::Low => r"[A-Za-z0-9-_ \t\n]*",
            Freedom::High => any::<String>()
        },
        programming_language in match freedom {
            Freedom::Nil => "",
            Freedom::Low => r"[A-Za-z0-9-]*",
            Freedom::High => any::<String>()
        }
    ) -> BlockContent {
        let programming_language = if programming_language.is_empty() {
            None
        } else {
            Some(Box::new(programming_language))
        };
        BlockContent::CodeBlock(CodeBlock{
            text,
            programming_language,
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a heading with arbitrary content and depth
    pub fn heading(freedom: Freedom)(
        depth in 1..6,
        content in vec_inline_content(freedom)
    ) -> BlockContent {
        BlockContent::Heading(Heading{
            depth: Some(depth as u8),
            content,
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a paragraph with arbitrary content
    pub fn paragraph(freedom: Freedom)(
        content in vec_inline_content(freedom)
    ) -> BlockContent {
        BlockContent::Paragraph(Paragraph{
            content,
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a list with arbitrary items and order
    pub fn list(freedom: Freedom)(
        order in prop_oneof![Just(ListOrder::Ascending), Just(ListOrder::Unordered)],
        items in vec(list_item(freedom), 1..5)
    ) -> BlockContent {
        BlockContent::List(List{
            order: Some(order),
            items,
            ..Default::default()
        })
    }
}

prop_compose! {
    /// Generate a list item with arbitrary inline content.
    /// Unable to use block_content strategy here because that causes infinite recursion.
    /// Instead, currently, allow for an single paragraph
    // TODO: allow for alternative numbers of paragraphs
    pub fn list_item(freedom: Freedom)(
        content in vec_inline_content(freedom)
    ) -> ListItem {
        let content = Some(
            ListItemContent::VecBlockContent(vec![
                BlockContent::Paragraph(
                    Paragraph{
                        content,
                        ..Default::default()
                    }
                )
            ])
        );

        ListItem{
            content,
            ..Default::default()
        }
    }
}

/// Generate a thematic break
pub fn thematic_break() -> impl Strategy<Value = BlockContent> {
    Just(BlockContent::ThematicBreak(ThematicBreak::default()))
}

/// Generate one of the block content node types
pub fn block_content(freedom: Freedom) -> impl Strategy<Value = BlockContent> {
    Union::new(vec![
        code_block(freedom).boxed(),
        heading(freedom).boxed(),
        list(freedom).boxed(),
        paragraph(freedom).boxed(),
        thematic_break().boxed(),
    ])
}

prop_compose! {
    /// Generate a vector of block content of arbitrary length and content
    ///
    /// Restrictions:
    ///  - Does not start with a thematic break (unrealistic, and in Markdown can
    ///    be confused with YAML frontmatter)
    ///  - List of same ordering can not be adjacent to each other (in Markdown they
    ///    get decoded as the same list)
    pub fn vec_block_content(freedom: Freedom)(length in 1usize..10)(
        blocks in vec(block_content(freedom), size_range(length)).prop_filter(
            "Vector of block content should not start with thematic break",
            |blocks| {
                for index in 0..blocks.len() {
                    if index == 0 && matches!(blocks[0], BlockContent::ThematicBreak(..)) {
                        return false
                    } else if index > 0 {
                        if let (BlockContent::List(prev), BlockContent::List(curr)) = (&blocks[index-1], &blocks[index]) {
                            match (&prev.order, &curr.order) {
                                (None, None) |
                                (Some(ListOrder::Ascending), Some(ListOrder::Ascending)) |
                                (Some(ListOrder::Unordered), Some(ListOrder::Unordered)) => {
                                    return false
                                },
                                _ => ()
                            }
                        }
                    }
                }
                true
            }
        )
    ) -> Vec<BlockContent> {
        blocks
    }
}

prop_compose! {
    /// Generate an article with arbitrary content (and in the future, other properties)
    pub fn article(freedom: Freedom)(
        content in vec_block_content(freedom)
    ) -> Node {
        Node::Article(Article{content: Some(content), ..Default::default()})
    }
}

/// Generate an arbitrary node
pub fn node(freedom: Freedom) -> impl Strategy<Value = Node> {
    Union::new(vec![article(freedom).boxed()])
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::io::{Write, BufWriter};
use std::fs::File;
use std::path::{Path, PathBuf};

use ops::{Context, Unit};
use util::{CargoResult, internal};
use util::paths;
use ops::cargo_rustc::fingerprint;

fn render_filename<P: AsRef<Path>>(path: P, basedir: Option<&str>) -> CargoResult<String> {
    let path = path.as_ref();
    let relpath = match basedir {
        None => path,
        Some(base) => match path.strip_prefix(base) {
            Ok(relpath) => relpath,
            _ => path,
        }
    };
    relpath.to_str().ok_or_else(|| internal("path not utf-8")).map(|f| f.replace(" ", "\\ "))
}

fn add_deps_for_unit<'a, 'b>(
    deps: &mut HashSet<PathBuf>,
    context: &mut Context<'a, 'b>,
    unit: &Unit<'a>,
    visited: &mut HashSet<Unit<'a>>,
)
    -> CargoResult<()>
{
    if !visited.insert(*unit) {
        return Ok(());
    }

    // units representing the execution of a build script don't actually
    // generate a dep info file, so we just keep on going below
    if !unit.profile.run_custom_build {
        // Add dependencies from rustc dep-info output (stored in fingerprint directory)
        let dep_info_loc = fingerprint::dep_info_loc(context, unit);
        if let Some(paths) = fingerprint::parse_dep_info(unit.pkg, &dep_info_loc)? {
            for path in paths {
                deps.insert(path);
            }
        } else {
            debug!("can't find dep_info for {:?} {:?}",
                unit.pkg.package_id(), unit.profile);
            return Err(internal("dep_info missing"));
        }
    }

    // Add rerun-if-changed dependencies
    let key = (unit.pkg.package_id().clone(), unit.kind);
    if let Some(output) = context.build_state.outputs.lock().unwrap().get(&key) {
        for path in &output.rerun_if_changed {
            deps.insert(path.into());
        }
    }

    // Recursively traverse all transitive dependencies
    for dep_unit in &context.dep_targets(unit)? {
        let source_id = dep_unit.pkg.package_id().source_id();
        if source_id.is_path() {
            add_deps_for_unit(deps, context, dep_unit, visited)?;
        }
    }
    Ok(())
}

pub fn output_depinfo<'a, 'b>(context: &mut Context<'a, 'b>, unit: &Unit<'a>) -> CargoResult<()> {
    let mut deps = HashSet::new();
    let mut visited = HashSet::new();
    let success = add_deps_for_unit(&mut deps, context, unit, &mut visited).is_ok();
    let basedir = None; // TODO
    for &(_, ref link_dst, _) in context.target_filenames(unit)?.iter() {
        if let Some(ref link_dst) = *link_dst {
            let output_path = link_dst.with_extension("d");
            if success {
                let mut outfile = BufWriter::new(File::create(output_path)?);
                let target_fn = render_filename(link_dst, basedir)?;
                write!(outfile, "{}:", target_fn)?;
                for dep in &deps {
                    write!(outfile, " {}", render_filename(dep, basedir)?)?;
                }
                writeln!(outfile, "")?;

            // dep-info generation failed, so delete output file. This will
            // usually cause the build system to always rerun the build
            // rule, which is correct if inefficient.
            } else if output_path.exists() {
                paths::remove_file(output_path)?;
            }
        }
    }
    Ok(())
}
<commit_msg>Optionally write dependencies targets relative<commit_after>use std::collections::HashSet;
use std::io::{Write, BufWriter};
use std::fs::File;
use std::path::{Path, PathBuf};

use ops::{Context, Unit};
use util::{CargoResult, internal};
use util::paths;
use ops::cargo_rustc::fingerprint;

fn render_filename<P: AsRef<Path>>(path: P, basedir: Option<&str>) -> CargoResult<String> {
    let path = path.as_ref();
    let relpath = match basedir {
        None => path,
        Some(base) => match path.strip_prefix(base) {
            Ok(relpath) => relpath,
            _ => path,
        }
    };
    relpath.to_str().ok_or_else(|| internal("path not utf-8")).map(|f| f.replace(" ", "\\ "))
}

fn add_deps_for_unit<'a, 'b>(
    deps: &mut HashSet<PathBuf>,
    context: &mut Context<'a, 'b>,
    unit: &Unit<'a>,
    visited: &mut HashSet<Unit<'a>>,
)
    -> CargoResult<()>
{
    if !visited.insert(*unit) {
        return Ok(());
    }

    // units representing the execution of a build script don't actually
    // generate a dep info file, so we just keep on going below
    if !unit.profile.run_custom_build {
        // Add dependencies from rustc dep-info output (stored in fingerprint directory)
        let dep_info_loc = fingerprint::dep_info_loc(context, unit);
        if let Some(paths) = fingerprint::parse_dep_info(unit.pkg, &dep_info_loc)? {
            for path in paths {
                deps.insert(path);
            }
        } else {
            debug!("can't find dep_info for {:?} {:?}",
                unit.pkg.package_id(), unit.profile);
            return Err(internal("dep_info missing"));
        }
    }

    // Add rerun-if-changed dependencies
    let key = (unit.pkg.package_id().clone(), unit.kind);
    if let Some(output) = context.build_state.outputs.lock().unwrap().get(&key) {
        for path in &output.rerun_if_changed {
            deps.insert(path.into());
        }
    }

    // Recursively traverse all transitive dependencies
    for dep_unit in &context.dep_targets(unit)? {
        let source_id = dep_unit.pkg.package_id().source_id();
        if source_id.is_path() {
            add_deps_for_unit(deps, context, dep_unit, visited)?;
        }
    }
    Ok(())
}

pub fn output_depinfo<'a, 'b>(context: &mut Context<'a, 'b>, unit: &Unit<'a>) -> CargoResult<()> {
    let mut deps = HashSet::new();
    let mut visited = HashSet::new();
    let success = add_deps_for_unit(&mut deps, context, unit, &mut visited).is_ok();
    let basedir_config = context.config.get_string("dep-info-basedir");
    let basedir_config_ok = basedir_config.is_ok();
    let basedir_val = basedir_config.unwrap_or(None).map(|s| s.val);
    let basedir_string = basedir_val.unwrap_or("".to_string());
    let basedir = if basedir_config_ok {
        Some(basedir_string.as_str())
    } else {
        None
    };
    for &(_, ref link_dst, _) in context.target_filenames(unit)?.iter() {
        if let Some(ref link_dst) = *link_dst {
            let output_path = link_dst.with_extension("d");
            if success {
                let mut outfile = BufWriter::new(File::create(output_path)?);
                let target_fn = render_filename(link_dst, basedir)?;
                write!(outfile, "{}:", target_fn)?;
                for dep in &deps {
                    write!(outfile, " {}", render_filename(dep, basedir)?)?;
                }
                writeln!(outfile, "")?;

            // dep-info generation failed, so delete output file. This will
            // usually cause the build system to always rerun the build
            // rule, which is correct if inefficient.
            } else if output_path.exists() {
                paths::remove_file(output_path)?;
            }
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before>//!
//! Module for dealing with angles in astronomical calculations.
//!
//! Authors: Ryan Leach
//!
//! Copyright: Ryan Leach, 2017
//!
//! License: [BSD 3-clause](https://opensource.org/licenses/BSD-3-Clause)
//!
//! Most internal calculations will be done using the RadianAngle type, since
//! most computer functions work with radians. The other types are mainly there
//! for ease of use and formatting with input and output.
//!
//! TODO
//!  - [ ] Factor out common functions, there is a lot of repeated code in From traits
//!  - [ ] Use constructors in from and force wrapping?
//!  - [ ] Do not allow negative HMS
//!  - [ ] Unittests for both positive and negative angles.
//!  - [ ] Implement `Display` for each of the structs.
//!  - [ ] Documentation comments
use std::convert::From;

use super::super::error::*;

#[derive(Debug, Clone, Copy)]
pub struct RadianAngle {
    radians: f64,
}

#[derive(Debug, Clone, Copy)]
pub struct DegreeAngle {
    degrees: f64,
}

#[derive(Debug, Clone, Copy)]
pub struct DMSAngle {
    degrees: i32,
    minutes: i32,
    seconds: f64,
}

#[derive(Debug, Clone, Copy)]
pub struct HMSAngle {
    hours: i32,
    minutes: i32,
    seconds: f64,
}

/// Common interface for all angle types.
pub trait AngleTrait
    : From<RadianAngle> + From<DegreeAngle> + From<DMSAngle> + From<HMSAngle> {
    // TODO
    // Getters for each format.
}

impl AngleTrait for RadianAngle {}
impl AngleTrait for DegreeAngle {}
impl AngleTrait for DMSAngle {}
impl AngleTrait for HMSAngle {}

impl RadianAngle {
    /// Create a new angle using radians.
    pub fn new(radians: f64)-> AstroResult<RadianAngle>{
        if radians.is_nan() {
            Err(AstroAlgorithmsError::EncounteredNaN)
        } else if radians.is_infinite() {
            Err(AstroAlgorithmsError::EncounteredInf)
        } else {
            Ok(RadianAngle{radians: radians })
        }
    }
}

impl DegreeAngle {
    /// Create a new angle using degrees.
    pub fn new(degrees: f64)-> AstroResult<DegreeAngle>{
        if degrees.is_nan() {
            Err(AstroAlgorithmsError::EncounteredNaN)
        } else if degrees.is_infinite() {
            Err(AstroAlgorithmsError::EncounteredInf)
        } else {
            Ok(DegreeAngle{degrees: degrees })
        }
    }
}

impl DMSAngle {
    /// Create a new angle using degrees, minutes, seconds.
    pub fn new(degrees: i32, mut minutes: i32, mut seconds: f64)-> AstroResult<DMSAngle>{
        if degrees.is_negative() {
            if minutes.is_positive() {
                minutes *= -1;
            }
            if seconds.is_sign_positive() {
                seconds *= -1.0;
            }
        } else {
            if minutes.is_negative() {
                minutes *= -1;
            }
            if seconds.is_sign_negative() {
                seconds *= -1.0;
            }
        }
        if seconds.is_nan() {
            Err(AstroAlgorithmsError::EncounteredNaN)
        } else if seconds.is_infinite() {
            Err(AstroAlgorithmsError::EncounteredInf)
        } else {
            Ok(DMSAngle{degrees: degrees, minutes: minutes, seconds: seconds })
        }
    }
}

impl HMSAngle {
    /// Create a new angle using hours, minutes, seconds.
    pub fn new(hours: i32, mut minutes: i32, mut seconds: f64)-> AstroResult<HMSAngle>{
        if seconds.is_nan() {
            Err(AstroAlgorithmsError::EncounteredNaN)
        } else if seconds.is_infinite() {
            Err(AstroAlgorithmsError::EncounteredInf)
        } else if hours.is_negative() || minutes.is_negative() || seconds.is_sign_negative() {
            Err(AstroAlgorithmsError::EncounteredInappropriateNegativeValue)
        } else {
            Ok(HMSAngle{hours: hours, minutes: minutes, seconds: seconds })
        }
    }
}

impl From<DegreeAngle> for RadianAngle {
    fn from(degrees: DegreeAngle) -> Self {
        RadianAngle { radians: degrees.degrees.to_radians() }
    }
}
impl From<DMSAngle> for RadianAngle {
    fn from(dms: DMSAngle) -> Self {
        let degrees = dms.degrees as f64 + (60 * dms.minutes) as f64 + 3600.0 * dms.seconds;
        RadianAngle { radians: degrees.to_radians() }
    }
}
impl From<HMSAngle> for RadianAngle {
    fn from(hms: HMSAngle) -> Self {
        let degrees = (hms.hours * 15) as f64 + (60 * hms.minutes) as f64 + 3600.0 * hms.seconds;
        RadianAngle { radians: degrees.to_radians() }
    }
}

impl From<RadianAngle> for DegreeAngle {
    fn from(radians: RadianAngle) -> Self {
        DegreeAngle { degrees: radians.radians.to_degrees() }
    }
}
impl From<DMSAngle> for DegreeAngle {
    fn from(dms: DMSAngle) -> Self {
        let degrees = dms.degrees as f64 + (60 * dms.minutes) as f64 + 3600.0 * dms.seconds;
        DegreeAngle { degrees: degrees }
    }
}
impl From<HMSAngle> for DegreeAngle {
    fn from(hms: HMSAngle) -> Self {
        let degrees = (hms.hours * 15) as f64 + (60 * hms.minutes) as f64 + 3600.0 * hms.seconds;
        DegreeAngle { degrees: degrees }
    }
}

impl From<RadianAngle> for DMSAngle {
    fn from(radians: RadianAngle) -> Self {
        let decimal_degrees = radians.radians.to_degrees();
        let degrees = decimal_degrees.trunc();
        let mut remainder = decimal_degrees - degrees;
        let minutes = (remainder * 60.0).trunc();
        remainder = remainder - 60.0 * minutes;
        let seconds = remainder * 3600.0;

        DMSAngle {
            degrees: degrees as i32,
            minutes: minutes.abs() as i32,
            seconds: seconds.abs(),
        }
    }
}
impl From<DegreeAngle> for DMSAngle {
    fn from(decimal_degrees: DegreeAngle) -> Self {
        let degrees = decimal_degrees.degrees.trunc();
        let mut remainder = decimal_degrees.degrees - degrees;
        let minutes = (remainder * 60.0).trunc();
        remainder = remainder - 60.0 * minutes;
        let seconds = remainder * 3600.0;

        DMSAngle {
            degrees: degrees as i32,
            minutes: minutes.abs() as i32,
            seconds: seconds.abs(),
        }
    }
}
impl From<HMSAngle> for DMSAngle {
    fn from(hms: HMSAngle) -> Self {
        let decimal_degrees = (hms.hours * 15) as f64 + (60 * hms.minutes) as f64 +
                              3600.0 * hms.seconds;
        let degrees = decimal_degrees.trunc();
        let mut remainder = decimal_degrees - degrees;
        let minutes = (remainder * 60.0).trunc();
        remainder = remainder - 60.0 * minutes;
        let seconds = remainder * 3600.0;

        DMSAngle {
            degrees: degrees as i32,
            minutes: minutes.abs() as i32,
            seconds: seconds.abs(),
        }
    }
}

impl From<RadianAngle> for HMSAngle {
    fn from(radians: RadianAngle) -> Self {
        let decimal_degrees = radians.radians.to_degrees();
        let hours = (decimal_degrees / 15.0).trunc();
        let mut remainder = decimal_degrees - (hours * 15.0);
        let minutes = (remainder * 60.0).trunc();
        remainder = remainder - 60.0 * minutes;
        let seconds = remainder * 3600.0;

        HMSAngle {
            hours: hours as i32,
            minutes: minutes.abs() as i32,
            seconds: seconds.abs(),
        }
    }
}
impl From<DegreeAngle> for HMSAngle {
    fn from(decimal_degrees: DegreeAngle) -> Self {
        let hours = (decimal_degrees.degrees / 15.0).trunc();
        let mut remainder = decimal_degrees.degrees - (hours * 15.0);
        let minutes = (remainder * 60.0).trunc();
        remainder = remainder - 60.0 * minutes;
        let seconds = remainder * 3600.0;

        HMSAngle {
            hours: hours as i32,
            minutes: minutes.abs() as i32,
            seconds: seconds.abs(),
        }
    }
}
impl From<DMSAngle> for HMSAngle {
    fn from(dms: DMSAngle) -> Self {
        let decimal_degrees = dms.degrees as f64 + (dms.minutes * 60) as f64 + 3600.0 * dms.seconds;
        let hours = (decimal_degrees / 15.0).trunc();
        let mut remainder = decimal_degrees - (hours * 15.0);
        let minutes = (remainder * 60.0).trunc();
        remainder = remainder - 60.0 * minutes;
        let seconds = remainder * 3600.0;

        HMSAngle {
            hours: hours as i32,
            minutes: minutes.abs() as i32,
            seconds: seconds.abs(),
        }
    }
}

#[cfg(test)]
mod angles_tests {

}
<commit_msg>More progress.<commit_after>//!
//! Module for dealing with angles in astronomical calculations.
//!
//! Authors: Ryan Leach
//!
//! Copyright: Ryan Leach, 2017
//!
//! License: [BSD 3-clause](https://opensource.org/licenses/BSD-3-Clause)
//!
//! Most internal calculations will be done using the RadianAngle type, since
//! most computer functions work with radians. The other types are mainly there
//! for ease of use and formatting with input and output.
//!
//! TODO
//!  - [ ] Unittests for both positive and negative angles.
use std::convert::From;
use std::fmt;

use super::super::error::*;

/// Represent an angle in radians.
#[derive(Debug, Clone, Copy)]
pub struct RadianAngle {
    radians: f64,
}

/// Represent an angle in decimal degrees.
#[derive(Debug, Clone, Copy)]
pub struct DegreeAngle {
    degrees: f64,
}

/// Represent an angle as degrees, minutes, and decimal seconds.
#[derive(Debug, Clone, Copy)]
pub struct DMSAngle {
    degrees: i32,
    minutes: i32,
    seconds: f64,
}

/// Represent an angle in hours, minutes, and seconds.
///
/// This type is forced to be between 0h and 24 hours.
#[derive(Debug, Clone, Copy)]
pub struct HMSAngle {
    hours: i32,
    minutes: i32,
    seconds: f64,
}

/// Common interface for all angle types.
pub trait AngleTrait
    : From<RadianAngle> + From<DegreeAngle> + From<DMSAngle> + From<HMSAngle> + fmt::Display {
    // TODO
    // Getters for each format.
}

impl AngleTrait for RadianAngle {}
impl AngleTrait for DegreeAngle {}
impl AngleTrait for DMSAngle {}
impl AngleTrait for HMSAngle {}

impl RadianAngle {
    /// Create a new angle using radians.
    pub fn new(radians: f64)-> AstroResult<RadianAngle>{
        if radians.is_nan() {
            Err(AstroAlgorithmsError::EncounteredNaN)
        } else if radians.is_infinite() {
            Err(AstroAlgorithmsError::EncounteredInf)
        } else {
            Ok(RadianAngle{radians: radians })
        }
    }
}

impl DegreeAngle {
    /// Create a new angle using degrees.
    pub fn new(degrees: f64)-> AstroResult<DegreeAngle>{
        if degrees.is_nan() {
            Err(AstroAlgorithmsError::EncounteredNaN)
        } else if degrees.is_infinite() {
            Err(AstroAlgorithmsError::EncounteredInf)
        } else {
            Ok(DegreeAngle{degrees: degrees })
        }
    }
}

impl DMSAngle {
    /// Create a new angle using degrees, minutes, seconds.
    pub fn new(degrees: i32, mut minutes: i32, mut seconds: f64)-> AstroResult<DMSAngle>{
        if degrees.is_negative() {
            if minutes.is_positive() {
                minutes *= -1;
            }
            if seconds.is_sign_positive() {
                seconds *= -1.0;
            }
        } else {
            if minutes.is_negative() {
                minutes *= -1;
            }
            if seconds.is_sign_negative() {
                seconds *= -1.0;
            }
        }
        if seconds.is_nan() {
            Err(AstroAlgorithmsError::EncounteredNaN)
        } else if seconds.is_infinite() {
            Err(AstroAlgorithmsError::EncounteredInf)
        } else {
            Ok(DMSAngle{degrees: degrees, minutes: minutes, seconds: seconds })
        }
    }
}

impl HMSAngle {
    /// Create a new angle using hours, minutes, seconds.
    pub fn new(hours: i32, minutes: i32, seconds: f64)-> AstroResult<HMSAngle>{
        if seconds.is_nan() {
            Err(AstroAlgorithmsError::EncounteredNaN)
        } else if seconds.is_infinite() {
            Err(AstroAlgorithmsError::EncounteredInf)
        } else if hours.is_negative() || minutes.is_negative() || seconds.is_sign_negative() {
            Err(AstroAlgorithmsError::EncounteredInappropriateNegativeValue)
        } else if hours > 23 {
            Err(AstroAlgorithmsError::InvalidAngle("Hour limited to range [0,24)".to_owned()))
        } else {
            Ok(HMSAngle{hours: hours, minutes: minutes, seconds: seconds })
        }
    }
}

impl fmt::Display for RadianAngle {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} radians", self.radians)
    }
}

impl fmt::Display for DegreeAngle {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}\u{00B0}", self.degrees)
    }
}

impl fmt::Display for DMSAngle {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}\u{00B0} {}\' {}\"", self.degrees, self.minutes, self.seconds)
    }
}

impl fmt::Display for HMSAngle {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}h {}m {}s", self.hours, self.minutes, self.seconds)
    }
}

impl From<DegreeAngle> for RadianAngle {
    fn from(degrees: DegreeAngle) -> Self {
        RadianAngle { radians: degrees.degrees.to_radians() }
    }
}
impl From<DMSAngle> for RadianAngle {
    fn from(dms: DMSAngle) -> Self {
        let degrees = dms.degrees as f64 + (60 * dms.minutes) as f64 + 3600.0 * dms.seconds;
        RadianAngle { radians: degrees.to_radians() }
    }
}
impl From<HMSAngle> for RadianAngle {
    fn from(hms: HMSAngle) -> Self {
        let degrees = (hms.hours * 15) as f64 + (60 * hms.minutes) as f64 + 3600.0 * hms.seconds;
        RadianAngle { radians: degrees.to_radians() }
    }
}

impl From<RadianAngle> for DegreeAngle {
    fn from(radians: RadianAngle) -> Self {
        DegreeAngle { degrees: radians.radians.to_degrees() }
    }
}
impl From<DMSAngle> for DegreeAngle {
    fn from(dms: DMSAngle) -> Self {
        let degrees = dms.degrees as f64 + (60 * dms.minutes) as f64 + 3600.0 * dms.seconds;
        DegreeAngle { degrees: degrees }
    }
}
impl From<HMSAngle> for DegreeAngle {
    fn from(hms: HMSAngle) -> Self {
        let degrees = (hms.hours * 15) as f64 + (60 * hms.minutes) as f64 + 3600.0 * hms.seconds;
        DegreeAngle { degrees: degrees }
    }
}

impl From<RadianAngle> for DMSAngle {
    fn from(radians: RadianAngle) -> Self {
        let decimal_degrees = radians.radians.to_degrees();
        let degrees = decimal_degrees.trunc();
        let mut remainder = decimal_degrees - degrees;
        let minutes = (remainder * 60.0).trunc();
        remainder = remainder - 60.0 * minutes;
        let seconds = remainder * 3600.0;

        DMSAngle {
            degrees: degrees as i32,
            minutes: minutes.abs() as i32,
            seconds: seconds.abs(),
        }
    }
}
impl From<DegreeAngle> for DMSAngle {
    fn from(decimal_degrees: DegreeAngle) -> Self {
        let degrees = decimal_degrees.degrees.trunc();
        let mut remainder = decimal_degrees.degrees - degrees;
        let minutes = (remainder * 60.0).trunc();
        remainder = remainder - 60.0 * minutes;
        let seconds = remainder * 3600.0;

        DMSAngle {
            degrees: degrees as i32,
            minutes: minutes.abs() as i32,
            seconds: seconds.abs(),
        }
    }
}
impl From<HMSAngle> for DMSAngle {
    fn from(hms: HMSAngle) -> Self {
        let decimal_degrees = (hms.hours * 15) as f64 + (60 * hms.minutes) as f64 +
                              3600.0 * hms.seconds;
        let degrees = decimal_degrees.trunc();
        let mut remainder = decimal_degrees - degrees;
        let minutes = (remainder * 60.0).trunc();
        remainder = remainder - 60.0 * minutes;
        let seconds = remainder * 3600.0;

        DMSAngle {
            degrees: degrees as i32,
            minutes: minutes.abs() as i32,
            seconds: seconds.abs(),
        }
    }
}

impl From<RadianAngle> for HMSAngle {
    fn from(radians: RadianAngle) -> Self {
        let mut decimal_degrees = radians.radians.to_degrees();

        // Force it to be between 0 and 360 degrees
        decimal_degrees = map_to_branch(decimal_degrees, 0.0, 360.0);

        let hours = (decimal_degrees / 15.0).trunc();
        let mut remainder = decimal_degrees - (hours * 15.0);
        let minutes = (remainder * 60.0).trunc();
        remainder = remainder - 60.0 * minutes;
        let seconds = remainder * 3600.0;

        HMSAngle {
            hours: hours as i32,
            minutes: minutes as i32,
            seconds: seconds,
        }
    }
}
impl From<DegreeAngle> for HMSAngle {
    fn from(degrees: DegreeAngle) -> Self {
        let decimal_degrees = map_to_branch(degrees.degrees, 0.0, 360.0);
        let hours = (decimal_degrees / 15.0).trunc();
        let mut remainder = decimal_degrees- (hours * 15.0);
        let minutes = (remainder * 60.0).trunc();
        remainder = remainder - 60.0 * minutes;
        let seconds = remainder * 3600.0;

        HMSAngle {
            hours: hours as i32,
            minutes: minutes.abs() as i32,
            seconds: seconds.abs(),
        }
    }
}
impl From<DMSAngle> for HMSAngle {
    fn from(dms: DMSAngle) -> Self {
        let mut decimal_degrees = dms.degrees as f64 + (dms.minutes * 60) as f64 + 3600.0 * dms.seconds;
        decimal_degrees = map_to_branch(decimal_degrees, 0.0, 360.0);
        let hours = (decimal_degrees / 15.0).trunc();
        let mut remainder = decimal_degrees - (hours * 15.0);
        let minutes = (remainder * 60.0).trunc();
        remainder = remainder - 60.0 * minutes;
        let seconds = remainder * 3600.0;

        HMSAngle {
            hours: hours as i32,
            minutes: minutes.abs() as i32,
            seconds: seconds.abs(),
        }
    }
}

fn map_to_branch(val: f64, min: f64, max: f64) -> f64 {
    let range = max - min;

    if val < min {
        let factor = ((val - min) / range).floor();
        val - factor * range
    } else if val > max {
        let factor = ((val - max) / range).ceil();
        val - factor * range
    } else {
        val
    }
}

// test approximate equality, only used in unit tests.
#[cfg(test)]
fn approx_eq(left: f64, right: f64, tol: f64) -> bool {
    (left - right).abs() < tol
}

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

    #[test]
    fn test_map_to_branch()
    {
        assert!(approx_eq(map_to_branch(-200.0, -180.0, 180.0), 160.0, 1.0e-12));
        assert!(approx_eq(map_to_branch(-200.0, 0.0, 360.0), 160.0, 1.0e-12));
        assert!(approx_eq(map_to_branch(200.0, -180.0, 180.0), -160.0, 1.0e-12));
        assert!(approx_eq(map_to_branch(200.0, 0.0, 360.0), 200.0, 1.0e-12));

        assert!(approx_eq(map_to_branch(-500.0, -180.0, 180.0), -140.0, 1.0e-12));
        assert!(approx_eq(map_to_branch(-500.0, 0.0, 360.0), 220.0, 1.0e-12));
        assert!(approx_eq(map_to_branch(500.0, -180.0, 180.0), 140.0, 1.0e-12));
        assert!(approx_eq(map_to_branch(500.0, 0.0, 360.0), 140.0, 1.0e-12));
    }
}
<|endoftext|>


<commit_before><commit_msg>fixed server including already sent arena updates<commit_after><|endoftext|>


<commit_before>#[macro_use]

use self::streamql::statement;
pub use self::streamql::ParseError;
use super::row_builder::RowBuilder;

mod integration_tests;

peg_file! streamql("streamql.rustpeg");

pub fn parse_statement(query: &str) -> Result<Statement, ParseError> {
    statement(query)
}

#[derive(Debug)]
pub enum Statement {
    // Stream & Keys
    Insert(String, RowBuilder),
    DefineStream,
    DropStream,
    UseDatabase,
    Select,
    Subscribe,
}

#[cfg(test)]
mod test {
    use super::parse_statement;
    use super::Statement;
    use super::streamql::*;

    #[test]
    fn test_basic_insert() {

        let result = parse_statement("INSERT INTO test set k=1;").unwrap();

        if let Statement::Insert(stream, builder) = result {
            assert_eq!(stream, String::from("test"));
        } else {
            panic!("")
        }
        let x = "INSERT INTO test set k=1, v=2;";
        let result = parse_statement(x).unwrap();
    }

    #[test]
    fn test_int_parsing() {
        let tmp = int_value("42").unwrap();
    }

    #[test]
    fn test_basic_string_parsing() {
        string("hello this is a test").unwrap();
        string(r#"hello this is a \"test\""#).unwrap();
    }
}

<commit_msg>removed unneeded code<commit_after>use self::streamql::statement;
pub use self::streamql::ParseError;
use super::row_builder::RowBuilder;

mod integration_tests;

peg_file! streamql("streamql.rustpeg");

pub fn parse_statement(query: &str) -> Result<Statement, ParseError> {
    statement(query)
}

#[derive(Debug)]
pub enum Statement {
    // Stream & Keys
    Insert(String, RowBuilder),
    DefineStream,
    DropStream,
    UseDatabase,
    Select,
    Subscribe,
}

#[cfg(test)]
mod test {
    use super::parse_statement;
    use super::Statement;
    use super::streamql::*;

    #[test]
    fn test_basic_insert() {

        let result = parse_statement("INSERT INTO test set k=1;").unwrap();

        if let Statement::Insert(stream, builder) = result {
            assert_eq!(stream, String::from("test"));
        } else {
            panic!("")
        }
        let x = "INSERT INTO test set k=1, v=2;";
        let result = parse_statement(x).unwrap();
    }

    #[test]
    fn test_int_parsing() {
        let tmp = int_value("42").unwrap();
    }

    #[test]
    fn test_basic_string_parsing() {
        string("hello this is a test").unwrap();
        string(r#"hello this is a \"test\""#).unwrap();
    }
}

<|endoftext|>


<commit_before>use ecs::state::{State, Commit};
use ecs::module::ComponentType;
use std::any::Any;
use std::collections::HashMap;
use daggy::{self, Dag, Walker};
use std::sync::atomic::{AtomicUsize, Ordering};
use parking_lot::Mutex;
use rayon;
use context::Context;

pub trait Model {
    fn from_state(state: &State, commit: Commit) -> Self;

    fn writes() -> &'static [ComponentType];
    fn reads() -> &'static [ComponentType];
}

pub trait Processor: Send + Any {
    type Model: Model;

    fn writes(&self) -> &'static [ComponentType] {
        Self::Model::writes()
    }
    fn reads(&self) -> &'static [ComponentType] {
        Self::Model::reads()
    }
}

pub trait AnyProcessor: Processor {}

impl<T: ?Sized + Processor> AnyProcessor for T {}

type Index = u32;
type NodeIndex = daggy::NodeIndex<Index>;

#[derive(Debug)]
enum LinkType {
    Read,
    Write,
}

struct Slot<P: ?Sized + AnyProcessor> {
    processor: Mutex<Option<Box<P>>>,
    dependencies_counter: AtomicUsize,
    dependencies_count: usize,
}

impl<P: ?Sized + AnyProcessor> Slot<P> {
    fn new(processor: Box<P>) -> Self {
        Slot {
            processor: Mutex::new(Some(processor)),
            dependencies_counter: AtomicUsize::new(0),
            dependencies_count: 0,
        }
    }

    #[inline]
    fn set_dependencies_count(&mut self, count: usize) {
        self.dependencies_count = count;
    }

    #[inline]
    fn acknowledge_dependency_resolved(&self) -> bool {
        let old = self.dependencies_counter.fetch_add(1, Ordering::SeqCst);

        if (old + 1) == self.dependencies_count {
            self.dependencies_counter.store(0, Ordering::SeqCst);

            true
        } else {
            false
        }
    }
}

pub struct ExecutionGraphBuilder<P: ?Sized + AnyProcessor> {
    execution_dag: Dag<Slot<P>, LinkType, Index>,
    writes: HashMap<ComponentType, NodeIndex>,
    reads: HashMap<ComponentType, Vec<NodeIndex>>,
    heads: Vec<NodeIndex>,
}

impl<P: ?Sized + AnyProcessor> ExecutionGraphBuilder<P> {
    pub fn new() -> Self {
        ExecutionGraphBuilder {
            execution_dag: Dag::new(),
            writes: HashMap::new(),
            reads: HashMap::new(),
            heads: Vec::new(),
        }
    }

    pub fn register(mut self, processor: Box<P>) -> Self {
        let writes = processor.writes();
        let reads = processor.reads();

        let node = self.execution_dag.add_node(Slot::new(processor));

        let read_dependencies = self.add_read_dependencies(node, reads);
        let write_dependencies = self.add_write_dependencies(node, writes);

        if read_dependencies == 0 && write_dependencies == 0 {
            self.heads.push(node);
        } else {
            self.execution_dag[node].set_dependencies_count(read_dependencies + write_dependencies);
        }

        self.register_reads(node, reads);

        self
    }

    fn add_write_dependencies(&mut self,
                              processor_node: NodeIndex,
                              writes: &[ComponentType])
                              -> usize {
        use std::collections::hash_map::Entry;

        let mut dependencies_count = 0;

        for &write in writes {
            match self.writes.entry(write) {
                Entry::Occupied(mut old_writer) => {
                    dependencies_count += 1;

                    self.execution_dag
                        .add_edge(*old_writer.get(), processor_node, LinkType::Write)
                        .expect("cyclic dependency");

                    old_writer.insert(processor_node);
                }
                Entry::Vacant(vacant_entry) => {
                    vacant_entry.insert(processor_node);
                }
            }

            let read_nodes = self.reads.entry(write).or_insert(Vec::new());
            for &read in &*read_nodes {
                dependencies_count += 1;
                self.execution_dag
                    .add_edge(read, processor_node, LinkType::Write)
                    .expect("cyclic dependency");
            }
            read_nodes.clear();
        }

        dependencies_count
    }

    fn add_read_dependencies(&mut self,
                             processor_node: NodeIndex,
                             reads: &[ComponentType])
                             -> usize {

        let mut dependencies_count = 0;

        for read in reads {
            if let Some(&writer) = self.writes.get(read) {
                dependencies_count += 1;
                self.execution_dag
                    .add_edge(writer, processor_node, LinkType::Read)
                    .expect("cyclic dependency");
            }
        }

        dependencies_count
    }

    fn register_reads(&mut self, processor_node: NodeIndex, reads: &[ComponentType]) {
        for &read in reads {
            let read_nodes = self.reads.entry(read).or_insert(Vec::new());
            read_nodes.push(processor_node);
        }
    }

    pub fn build(self) -> Scheduler<P> {
        Scheduler {
            heads: self.heads,
            execution_dag: self.execution_dag,
        }
    }
}

pub struct Scheduler<P: ?Sized + AnyProcessor> {
    heads: Vec<NodeIndex>,
    execution_dag: Dag<Slot<P>, LinkType, Index>,
}

impl<P: ?Sized + AnyProcessor> Scheduler<P> {
    pub fn par_for_each_mut<F>(&self, state: &State, commit: Commit, cx: &Context, f: F)
        where F: Fn(&State, Commit, &Context, &mut P) + Sync + Send
    {
        let f = &f;
        rayon::scope(|scope| {
            for &head in &self.heads {
                scope.spawn(move |scope| {
                    self.run_process_mut(scope, head, state, commit, cx, f)
                });
            }
        });
    }

    fn run_process_mut<'b: 'scope, 'scope, F>(&'b self,
                                              scope: &rayon::Scope<'scope>,
                                              node: NodeIndex,
                                              state: &'b State,
                                              commit: Commit<'b>,
                                              cx: &'b Context,
                                              f: &'b F)
        where F: Fn(&State, Commit, &Context, &mut P) + Sync + Send
    {
        let mut process = self.take_process(node);
        f(state, commit, cx, &mut *process);
        self.put_process(node, process);

        let mut children_walker = self.execution_dag.children(node);
        while let Some((_, child)) = children_walker.next(&self.execution_dag) {
            let child_slot = &self.execution_dag[child];

            if child_slot.acknowledge_dependency_resolved() {
                scope.spawn(move |scope| {
                    self.run_process_mut(scope, child, state, commit, cx, f)
                });
            }
        }
    }

    #[inline]
    fn take_process(&self, node: NodeIndex) -> Box<P> {
        let mut slot_opt = self.execution_dag[node].processor.lock();
        slot_opt.take().unwrap()
    }

    #[inline]
    fn put_process(&self, node: NodeIndex, process: Box<P>) {
        let mut slot_opt = self.execution_dag[node].processor.lock();
        *slot_opt = Some(process);
    }
}
<commit_msg>Add a alias for component types array<commit_after>use ecs::state::{State, Commit};
use ecs::module::ComponentType;
use std::any::Any;
use std::collections::HashMap;
use daggy::{self, Dag, Walker};
use std::sync::atomic::{AtomicUsize, Ordering};
use parking_lot::Mutex;
use rayon;
use context::Context;

pub type ComponentTypes = [ComponentType];

pub trait Model {
    fn from_state(state: &State, commit: Commit) -> Self;

    fn writes() -> &'static ComponentTypes;
    fn reads() -> &'static ComponentTypes;
}

pub trait Processor: Send + Any {
    type Model: Model;

    fn writes(&self) -> &'static ComponentTypes {
        Self::Model::writes()
    }

    fn reads(&self) -> &'static ComponentTypes {
        Self::Model::reads()
    }
}

pub trait AnyProcessor: Processor {}

impl<T: ?Sized + Processor> AnyProcessor for T {}

type Index = u32;
type NodeIndex = daggy::NodeIndex<Index>;

#[derive(Debug)]
enum LinkType {
    Read,
    Write,
}

struct Slot<P: ?Sized + AnyProcessor> {
    processor: Mutex<Option<Box<P>>>,
    dependencies_counter: AtomicUsize,
    dependencies_count: usize,
}

impl<P: ?Sized + AnyProcessor> Slot<P> {
    fn new(processor: Box<P>) -> Self {
        Slot {
            processor: Mutex::new(Some(processor)),
            dependencies_counter: AtomicUsize::new(0),
            dependencies_count: 0,
        }
    }

    #[inline]
    fn set_dependencies_count(&mut self, count: usize) {
        self.dependencies_count = count;
    }

    #[inline]
    fn acknowledge_dependency_resolved(&self) -> bool {
        let old = self.dependencies_counter.fetch_add(1, Ordering::SeqCst);

        if (old + 1) == self.dependencies_count {
            self.dependencies_counter.store(0, Ordering::SeqCst);

            true
        } else {
            false
        }
    }
}

pub struct ExecutionGraphBuilder<P: ?Sized + AnyProcessor> {
    execution_dag: Dag<Slot<P>, LinkType, Index>,
    writes: HashMap<ComponentType, NodeIndex>,
    reads: HashMap<ComponentType, Vec<NodeIndex>>,
    heads: Vec<NodeIndex>,
}

impl<P: ?Sized + AnyProcessor> ExecutionGraphBuilder<P> {
    pub fn new() -> Self {
        ExecutionGraphBuilder {
            execution_dag: Dag::new(),
            writes: HashMap::new(),
            reads: HashMap::new(),
            heads: Vec::new(),
        }
    }

    pub fn register(mut self, processor: Box<P>) -> Self {
        let writes = processor.writes();
        let reads = processor.reads();

        let node = self.execution_dag.add_node(Slot::new(processor));

        let read_dependencies = self.add_read_dependencies(node, reads);
        let write_dependencies = self.add_write_dependencies(node, writes);

        if read_dependencies == 0 && write_dependencies == 0 {
            self.heads.push(node);
        } else {
            self.execution_dag[node].set_dependencies_count(read_dependencies + write_dependencies);
        }

        self.register_reads(node, reads);

        self
    }

    fn add_write_dependencies(&mut self,
                              processor_node: NodeIndex,
                              writes: &[ComponentType])
                              -> usize {
        use std::collections::hash_map::Entry;

        let mut dependencies_count = 0;

        for &write in writes {
            match self.writes.entry(write) {
                Entry::Occupied(mut old_writer) => {
                    dependencies_count += 1;

                    self.execution_dag
                        .add_edge(*old_writer.get(), processor_node, LinkType::Write)
                        .expect("cyclic dependency");

                    old_writer.insert(processor_node);
                }
                Entry::Vacant(vacant_entry) => {
                    vacant_entry.insert(processor_node);
                }
            }

            let read_nodes = self.reads.entry(write).or_insert(Vec::new());
            for &read in &*read_nodes {
                dependencies_count += 1;
                self.execution_dag
                    .add_edge(read, processor_node, LinkType::Write)
                    .expect("cyclic dependency");
            }
            read_nodes.clear();
        }

        dependencies_count
    }

    fn add_read_dependencies(&mut self,
                             processor_node: NodeIndex,
                             reads: &[ComponentType])
                             -> usize {

        let mut dependencies_count = 0;

        for read in reads {
            if let Some(&writer) = self.writes.get(read) {
                dependencies_count += 1;
                self.execution_dag
                    .add_edge(writer, processor_node, LinkType::Read)
                    .expect("cyclic dependency");
            }
        }

        dependencies_count
    }

    fn register_reads(&mut self, processor_node: NodeIndex, reads: &[ComponentType]) {
        for &read in reads {
            let read_nodes = self.reads.entry(read).or_insert(Vec::new());
            read_nodes.push(processor_node);
        }
    }

    pub fn build(self) -> Scheduler<P> {
        Scheduler {
            heads: self.heads,
            execution_dag: self.execution_dag,
        }
    }
}

pub struct Scheduler<P: ?Sized + AnyProcessor> {
    heads: Vec<NodeIndex>,
    execution_dag: Dag<Slot<P>, LinkType, Index>,
}

impl<P: ?Sized + AnyProcessor> Scheduler<P> {
    pub fn par_for_each_mut<F>(&self, state: &State, commit: Commit, cx: &Context, f: F)
        where F: Fn(&State, Commit, &Context, &mut P) + Sync + Send
    {
        let f = &f;
        rayon::scope(|scope| {
            for &head in &self.heads {
                scope.spawn(move |scope| {
                    self.run_process_mut(scope, head, state, commit, cx, f)
                });
            }
        });
    }

    fn run_process_mut<'b: 'scope, 'scope, F>(&'b self,
                                              scope: &rayon::Scope<'scope>,
                                              node: NodeIndex,
                                              state: &'b State,
                                              commit: Commit<'b>,
                                              cx: &'b Context,
                                              f: &'b F)
        where F: Fn(&State, Commit, &Context, &mut P) + Sync + Send
    {
        let mut process = self.take_process(node);
        f(state, commit, cx, &mut *process);
        self.put_process(node, process);

        let mut children_walker = self.execution_dag.children(node);
        while let Some((_, child)) = children_walker.next(&self.execution_dag) {
            let child_slot = &self.execution_dag[child];

            if child_slot.acknowledge_dependency_resolved() {
                scope.spawn(move |scope| {
                    self.run_process_mut(scope, child, state, commit, cx, f)
                });
            }
        }
    }

    #[inline]
    fn take_process(&self, node: NodeIndex) -> Box<P> {
        let mut slot_opt = self.execution_dag[node].processor.lock();
        slot_opt.take().unwrap()
    }

    #[inline]
    fn put_process(&self, node: NodeIndex, process: Box<P>) {
        let mut slot_opt = self.execution_dag[node].processor.lock();
        *slot_opt = Some(process);
    }
}
<|endoftext|>