nanoc-rust 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.rspec +3 -0
- data/.rubocop.yml +174 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +63 -0
- data/Rakefile +14 -0
- data/lib/nanoc-rust.rb +160 -0
- data/nanoc-rust.gemspec +23 -0
- data/rust/Cargo.lock +4 -0
- data/rust/Cargo.toml +6 -0
- data/rust/Makefile +15 -0
- data/rust/extconf.rb +3 -0
- data/rust/src/digraph.rs +336 -0
- data/rust/src/nanoc_rust.rs +191 -0
- data/spec/directed_graph_spec.rb +228 -0
- data/spec/spec_helper.rb +12 -0
- data/test.sh +9 -0
- metadata +89 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: 8db56be993dae34ec1512f2cd9af7467c7600529
         | 
| 4 | 
            +
              data.tar.gz: a0514e0c847376638155b4f8e6e46038ad3902fa
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: cd68879cd68f971b3d6295288f782d0a3392c716797df889159ccc0187ef24efa41effa961cbff1a172969e082dd00d439213dbc24a9a7ac09174c16b6d5e727
         | 
| 7 | 
            +
              data.tar.gz: 756b079f316ef77d1b8c4c0abf85ff510f0a649cf7a15feb4947c93b8ebffb08b13fa789456d92096a7867017099bb44517c01696980bf83846ca3c7f35a6d6a
         | 
    
        data/.gitignore
    ADDED
    
    
    
        data/.rspec
    ADDED
    
    
    
        data/.rubocop.yml
    ADDED
    
    | @@ -0,0 +1,174 @@ | |
| 1 | 
            +
            # ----- CONFIGURED -----
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            AllCops:
         | 
| 4 | 
            +
              TargetRubyVersion: 2.3
         | 
| 5 | 
            +
              DisplayCopNames: true
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            # We use filenames such as “create-site.rb” that translate to method names.
         | 
| 8 | 
            +
            FileName:
         | 
| 9 | 
            +
              Exclude:
         | 
| 10 | 
            +
                - 'lib/nanoc/cli/commands/*.rb'
         | 
| 11 | 
            +
                - 'Appraisals'
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            # A common pattern in tests is to define anonymous classes in which methods are defined, which trips
         | 
| 14 | 
            +
            # up Rubocop’s nested method definition cop.
         | 
| 15 | 
            +
            Lint/NestedMethodDefinition:
         | 
| 16 | 
            +
              Exclude:
         | 
| 17 | 
            +
                - 'test/**/*.rb'
         | 
| 18 | 
            +
                - 'spec/**/*.rb'
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            # This is used in tests, to verify the effect of state-changing functions.
         | 
| 21 | 
            +
            Style/GlobalVars:
         | 
| 22 | 
            +
              Exclude:
         | 
| 23 | 
            +
                - 'test/**/*.rb'
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            Style/TrailingCommaInArguments:
         | 
| 26 | 
            +
              EnforcedStyleForMultiline: comma
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            Style/TrailingCommaInLiteral:
         | 
| 29 | 
            +
              EnforcedStyleForMultiline: comma
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            # `rescue nil` is useful in specs where the exception is not important, but
         | 
| 32 | 
            +
            # the size effects are.
         | 
| 33 | 
            +
            Style/RescueModifier:
         | 
| 34 | 
            +
              Exclude:
         | 
| 35 | 
            +
                - 'spec/**/*.rb'
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            Layout/IndentArray:
         | 
| 38 | 
            +
              EnforcedStyle: consistent
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            Lint/DuplicateMethods:
         | 
| 41 | 
            +
              Exclude:
         | 
| 42 | 
            +
                - 'test/data_sources/test_filesystem.rb'
         | 
| 43 | 
            +
                - 'spec/spec_helper.rb'
         | 
| 44 | 
            +
             | 
| 45 | 
            +
            # This needs to be fixed in Ruby 2.4.
         | 
| 46 | 
            +
            Lint/UnifiedInteger:
         | 
| 47 | 
            +
              Enabled: false
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            Layout/IndentHeredoc:
         | 
| 50 | 
            +
              EnforcedStyle: squiggly
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            # This breaks RSpec on occasion, e.g. `expect { subject }.not_to change { foo }`,
         | 
| 53 | 
            +
            # and generally does not provide useful warnings
         | 
| 54 | 
            +
            Lint/AmbiguousBlockAssociation:
         | 
| 55 | 
            +
              Enabled: false
         | 
| 56 | 
            +
             | 
| 57 | 
            +
            # ----- TO ENABLE LATER -----
         | 
| 58 | 
            +
             | 
| 59 | 
            +
            # Valid cops, but fixing the offenses they report is non-trivial.
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            RegexpLiteral:
         | 
| 62 | 
            +
              Enabled: false
         | 
| 63 | 
            +
             | 
| 64 | 
            +
            ClassAndModuleChildren:
         | 
| 65 | 
            +
              Enabled: false
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            Style/EmptyElse:
         | 
| 68 | 
            +
              Enabled: false
         | 
| 69 | 
            +
             | 
| 70 | 
            +
            Style/Next:
         | 
| 71 | 
            +
              Enabled: false
         | 
| 72 | 
            +
             | 
| 73 | 
            +
             | 
| 74 | 
            +
             | 
| 75 | 
            +
            # ----- DISABLED (hard) -----
         | 
| 76 | 
            +
             | 
| 77 | 
            +
            # Rubocop trips up on this.
         | 
| 78 | 
            +
            Layout/LeadingCommentSpace:
         | 
| 79 | 
            +
              Enabled: false
         | 
| 80 | 
            +
             | 
| 81 | 
            +
             | 
| 82 | 
            +
             | 
| 83 | 
            +
            # ----- DISABLED (security) -----
         | 
| 84 | 
            +
             | 
| 85 | 
            +
            # Nanoc runs offline in a trusted environment, and these security checks are false positives.
         | 
| 86 | 
            +
             | 
| 87 | 
            +
            Security/YAMLLoad:
         | 
| 88 | 
            +
              Enabled: false
         | 
| 89 | 
            +
             | 
| 90 | 
            +
            Security/MarshalLoad:
         | 
| 91 | 
            +
              Enabled: false
         | 
| 92 | 
            +
             | 
| 93 | 
            +
            Security/Eval:
         | 
| 94 | 
            +
              Exclude:
         | 
| 95 | 
            +
                - 'test/**/*.rb'
         | 
| 96 | 
            +
                - 'spec/**/*.rb'
         | 
| 97 | 
            +
                - 'lib/nanoc/base/entities/code_snippet.rb'
         | 
| 98 | 
            +
                - 'lib/nanoc/filters/erubi.rb'
         | 
| 99 | 
            +
             | 
| 100 | 
            +
             | 
| 101 | 
            +
            # ----- DISABLED (metrics) -----
         | 
| 102 | 
            +
             | 
| 103 | 
            +
            # Cops for metrics are disabled because they should not cause tests to fail.
         | 
| 104 | 
            +
             | 
| 105 | 
            +
            Metrics/AbcSize:
         | 
| 106 | 
            +
              Enabled: false
         | 
| 107 | 
            +
             | 
| 108 | 
            +
            Metrics/BlockLength:
         | 
| 109 | 
            +
              Enabled: false
         | 
| 110 | 
            +
             | 
| 111 | 
            +
            Metrics/BlockNesting:
         | 
| 112 | 
            +
              Enabled: false
         | 
| 113 | 
            +
             | 
| 114 | 
            +
            Metrics/ClassLength:
         | 
| 115 | 
            +
              Enabled: false
         | 
| 116 | 
            +
             | 
| 117 | 
            +
            Metrics/CyclomaticComplexity:
         | 
| 118 | 
            +
              Enabled: false
         | 
| 119 | 
            +
             | 
| 120 | 
            +
            Metrics/LineLength:
         | 
| 121 | 
            +
              Enabled: false
         | 
| 122 | 
            +
             | 
| 123 | 
            +
            Metrics/MethodLength:
         | 
| 124 | 
            +
              Enabled: false
         | 
| 125 | 
            +
             | 
| 126 | 
            +
            Metrics/ModuleLength:
         | 
| 127 | 
            +
              Enabled: false
         | 
| 128 | 
            +
             | 
| 129 | 
            +
            Metrics/ParameterLists:
         | 
| 130 | 
            +
              Enabled: false
         | 
| 131 | 
            +
             | 
| 132 | 
            +
            Metrics/PerceivedComplexity:
         | 
| 133 | 
            +
              Enabled: false
         | 
| 134 | 
            +
             | 
| 135 | 
            +
             | 
| 136 | 
            +
             | 
| 137 | 
            +
            # ----- DISABLED (opinionated) -----
         | 
| 138 | 
            +
             | 
| 139 | 
            +
            # We should embrace UTF-8, not avoid it. Since the Encoding cop is enabled,
         | 
| 140 | 
            +
            # there’s no point in enforcing ASCII comments.
         | 
| 141 | 
            +
            AsciiComments:
         | 
| 142 | 
            +
              Enabled: false
         | 
| 143 | 
            +
             | 
| 144 | 
            +
            # It does not make sense to enforce everything to have documentation.
         | 
| 145 | 
            +
            Documentation:
         | 
| 146 | 
            +
              Enabled: false
         | 
| 147 | 
            +
             | 
| 148 | 
            +
            # Nanoc suppresses exceptions for valid reasons in a few cases.
         | 
| 149 | 
            +
            HandleExceptions:
         | 
| 150 | 
            +
              Enabled: false
         | 
| 151 | 
            +
             | 
| 152 | 
            +
            # if/unless at the end of the line makes it too easy to oversee.
         | 
| 153 | 
            +
            IfUnlessModifier:
         | 
| 154 | 
            +
              Enabled: false
         | 
| 155 | 
            +
             | 
| 156 | 
            +
            # Personal preference is to have decent constructors for exceptions rather than
         | 
| 157 | 
            +
            # just a class and a message.
         | 
| 158 | 
            +
            RaiseArgs:
         | 
| 159 | 
            +
              Enabled: false
         | 
| 160 | 
            +
             | 
| 161 | 
            +
            # Personal preference is to use `raise` to signal exceptions (normal control
         | 
| 162 | 
            +
            # flow should not use exceptions anyway).
         | 
| 163 | 
            +
            SignalException:
         | 
| 164 | 
            +
              Enabled: false
         | 
| 165 | 
            +
             | 
| 166 | 
            +
            # Some methods that appear to be accessors (return a single value or set a
         | 
| 167 | 
            +
            # single value) should still not be considered to be accessors. This is a purely
         | 
| 168 | 
            +
            # semantic difference.
         | 
| 169 | 
            +
            TrivialAccessors:
         | 
| 170 | 
            +
              Enabled: false
         | 
| 171 | 
            +
             | 
| 172 | 
            +
            # This does not always semantically make sense.
         | 
| 173 | 
            +
            GuardClause:
         | 
| 174 | 
            +
              Enabled: false
         | 
    
        data/Gemfile
    ADDED
    
    
    
        data/Gemfile.lock
    ADDED
    
    | @@ -0,0 +1,63 @@ | |
| 1 | 
            +
            PATH
         | 
| 2 | 
            +
              remote: .
         | 
| 3 | 
            +
              specs:
         | 
| 4 | 
            +
                nanoc-rust (0.1)
         | 
| 5 | 
            +
                  ffi (~> 1.9)
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            GEM
         | 
| 8 | 
            +
              remote: https://rubygems.org/
         | 
| 9 | 
            +
              specs:
         | 
| 10 | 
            +
                ast (2.3.0)
         | 
| 11 | 
            +
                diff-lcs (1.3)
         | 
| 12 | 
            +
                ffi (1.9.18)
         | 
| 13 | 
            +
                fuubar (2.2.0)
         | 
| 14 | 
            +
                  rspec-core (~> 3.0)
         | 
| 15 | 
            +
                  ruby-progressbar (~> 1.4)
         | 
| 16 | 
            +
                parallel (1.11.2)
         | 
| 17 | 
            +
                parser (2.4.0.0)
         | 
| 18 | 
            +
                  ast (~> 2.2)
         | 
| 19 | 
            +
                powerpack (0.1.1)
         | 
| 20 | 
            +
                rainbow (2.2.2)
         | 
| 21 | 
            +
                  rake
         | 
| 22 | 
            +
                rake (12.0.0)
         | 
| 23 | 
            +
                rspec (3.6.0)
         | 
| 24 | 
            +
                  rspec-core (~> 3.6.0)
         | 
| 25 | 
            +
                  rspec-expectations (~> 3.6.0)
         | 
| 26 | 
            +
                  rspec-mocks (~> 3.6.0)
         | 
| 27 | 
            +
                rspec-core (3.6.0)
         | 
| 28 | 
            +
                  rspec-support (~> 3.6.0)
         | 
| 29 | 
            +
                rspec-expectations (3.6.0)
         | 
| 30 | 
            +
                  diff-lcs (>= 1.2.0, < 2.0)
         | 
| 31 | 
            +
                  rspec-support (~> 3.6.0)
         | 
| 32 | 
            +
                rspec-its (1.2.0)
         | 
| 33 | 
            +
                  rspec-core (>= 3.0.0)
         | 
| 34 | 
            +
                  rspec-expectations (>= 3.0.0)
         | 
| 35 | 
            +
                rspec-mocks (3.6.0)
         | 
| 36 | 
            +
                  diff-lcs (>= 1.2.0, < 2.0)
         | 
| 37 | 
            +
                  rspec-support (~> 3.6.0)
         | 
| 38 | 
            +
                rspec-support (3.6.0)
         | 
| 39 | 
            +
                rubocop (0.49.1)
         | 
| 40 | 
            +
                  parallel (~> 1.10)
         | 
| 41 | 
            +
                  parser (>= 2.3.3.1, < 3.0)
         | 
| 42 | 
            +
                  powerpack (~> 0.1)
         | 
| 43 | 
            +
                  rainbow (>= 1.99.1, < 3.0)
         | 
| 44 | 
            +
                  ruby-progressbar (~> 1.7)
         | 
| 45 | 
            +
                  unicode-display_width (~> 1.0, >= 1.0.1)
         | 
| 46 | 
            +
                ruby-progressbar (1.8.1)
         | 
| 47 | 
            +
                unicode-display_width (1.3.0)
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            PLATFORMS
         | 
| 50 | 
            +
              ruby
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            DEPENDENCIES
         | 
| 53 | 
            +
              bundler (~> 1.15)
         | 
| 54 | 
            +
              fuubar
         | 
| 55 | 
            +
              nanoc-rust!
         | 
| 56 | 
            +
              rake
         | 
| 57 | 
            +
              rspec
         | 
| 58 | 
            +
              rspec-its (~> 1.2)
         | 
| 59 | 
            +
              rspec-mocks
         | 
| 60 | 
            +
              rubocop (~> 0.49)
         | 
| 61 | 
            +
             | 
| 62 | 
            +
            BUNDLED WITH
         | 
| 63 | 
            +
               1.15.1
         | 
    
        data/Rakefile
    ADDED
    
    | @@ -0,0 +1,14 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'rubocop/rake_task'
         | 
| 4 | 
            +
            require 'rspec/core/rake_task'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            RuboCop::RakeTask.new(:rubocop)
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            RSpec::Core::RakeTask.new(:spec) do |t|
         | 
| 9 | 
            +
              t.verbose = false
         | 
| 10 | 
            +
            end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            task test: %i[spec rubocop]
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            task default: :test
         | 
    
        data/lib/nanoc-rust.rb
    ADDED
    
    | @@ -0,0 +1,160 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'ffi'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module NanocRust
         | 
| 6 | 
            +
              module FFI
         | 
| 7 | 
            +
                class Edge < ::FFI::Struct
         | 
| 8 | 
            +
                  layout  :from, :uint32,
         | 
| 9 | 
            +
                          :to,   :uint32,
         | 
| 10 | 
            +
                          :idx,  :uint32
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def to_a(props)
         | 
| 13 | 
            +
                    [self[:from], self[:to], props[self[:idx]]]
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                extend ::FFI::Library
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                root = File.expand_path(File.dirname(__FILE__) + '/..')
         | 
| 20 | 
            +
                ffi_lib root + '/rust/dist/libnanoc_rust.' + ::FFI::Platform::LIBSUFFIX
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                attach_function :drop_string, %i[pointer],        :void
         | 
| 23 | 
            +
                attach_function :drop_array,  %i[pointer uint32], :void
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                attach_function :digraph_create, [], :pointer
         | 
| 26 | 
            +
                attach_function :digraph_destroy, [:pointer], :void
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                attach_function :digraph_inspect, [:pointer], :pointer
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                attach_function :digraph_add_vertex,        %i[pointer string],               :void
         | 
| 31 | 
            +
                attach_function :digraph_add_edge,          %i[pointer string string uint32], :void
         | 
| 32 | 
            +
                attach_function :digraph_delete_edges_to,   %i[pointer string],               :void
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                attach_function :digraph_direct_predecessors_of, %i[pointer string pointer], :pointer
         | 
| 35 | 
            +
                attach_function :digraph_predecessors_of,        %i[pointer string pointer], :pointer
         | 
| 36 | 
            +
                attach_function :digraph_direct_successors_of,   %i[pointer string pointer], :pointer
         | 
| 37 | 
            +
                attach_function :digraph_successors_of,          %i[pointer string pointer], :pointer
         | 
| 38 | 
            +
                attach_function :digraph_edges,                  %i[pointer pointer],        :pointer
         | 
| 39 | 
            +
                attach_function :digraph_vertices,               %i[pointer pointer],        :pointer
         | 
| 40 | 
            +
                attach_function :digraph_props_for,              %i[pointer pointer pointer], :uint32
         | 
| 41 | 
            +
              end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
              class DirectedGraph
         | 
| 44 | 
            +
                def initialize(vertices)
         | 
| 45 | 
            +
                  raw = NanocRust::FFI.digraph_create
         | 
| 46 | 
            +
                  @wrapped = ::FFI::AutoPointer.new(raw, NanocRust::FFI.method(:digraph_destroy))
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  @_props = {}
         | 
| 49 | 
            +
                  @_next_prop_idx = 1
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  vertices.each { |v| add_vertex(v) }
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                def inspect
         | 
| 55 | 
            +
                  ptr = NanocRust::FFI.digraph_inspect(@wrapped)
         | 
| 56 | 
            +
                  s = ptr.read_string
         | 
| 57 | 
            +
                  ::NanocRust::FFI.drop_string(ptr)
         | 
| 58 | 
            +
                  s
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                # mutating
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                def add_vertex(v)
         | 
| 64 | 
            +
                  NanocRust::FFI.digraph_add_vertex(@wrapped, v)
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                def add_edge(v1, v2, props: nil)
         | 
| 68 | 
            +
                  @_props[@_next_prop_idx] = props
         | 
| 69 | 
            +
                  NanocRust::FFI.digraph_add_edge(@wrapped, v1, v2, @_next_prop_idx)
         | 
| 70 | 
            +
                  @_next_prop_idx += 1
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                def delete_edges_to(v)
         | 
| 74 | 
            +
                  NanocRust::FFI.digraph_delete_edges_to(@wrapped, v)
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                # querying
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                def direct_predecessors_of(v)
         | 
| 80 | 
            +
                  count_ptr = new_count_ptr
         | 
| 81 | 
            +
                  ptr = NanocRust::FFI.digraph_direct_predecessors_of(@wrapped, v, count_ptr)
         | 
| 82 | 
            +
                  ptr_to_array_of_strings(ptr, count_ptr)
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                def predecessors_of(v)
         | 
| 86 | 
            +
                  count_ptr = new_count_ptr
         | 
| 87 | 
            +
                  ptr = NanocRust::FFI.digraph_predecessors_of(@wrapped, v, count_ptr)
         | 
| 88 | 
            +
                  ptr_to_array_of_strings(ptr, count_ptr)
         | 
| 89 | 
            +
                end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                def direct_successors_of(v)
         | 
| 92 | 
            +
                  count_ptr = new_count_ptr
         | 
| 93 | 
            +
                  ptr = NanocRust::FFI.digraph_direct_successors_of(@wrapped, v, count_ptr)
         | 
| 94 | 
            +
                  ptr_to_array_of_strings(ptr, count_ptr)
         | 
| 95 | 
            +
                end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                def successors_of(v)
         | 
| 98 | 
            +
                  count_ptr = new_count_ptr
         | 
| 99 | 
            +
                  ptr = NanocRust::FFI.digraph_successors_of(@wrapped, v, count_ptr)
         | 
| 100 | 
            +
                  ptr_to_array_of_strings(ptr, count_ptr)
         | 
| 101 | 
            +
                end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                def vertices
         | 
| 104 | 
            +
                  count_ptr = new_count_ptr
         | 
| 105 | 
            +
                  ptr = NanocRust::FFI.digraph_vertices(@wrapped, count_ptr)
         | 
| 106 | 
            +
                  ptr_to_array_of_u32_tuples(ptr, count_ptr)
         | 
| 107 | 
            +
                end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                def edges
         | 
| 110 | 
            +
                  count_ptr = new_count_ptr
         | 
| 111 | 
            +
                  ptr = NanocRust::FFI.digraph_edges(@wrapped, count_ptr)
         | 
| 112 | 
            +
                  ptr_to_array_of_u32_tuples(ptr, count_ptr)
         | 
| 113 | 
            +
                end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                def props_for(v1, v2)
         | 
| 116 | 
            +
                  idx = NanocRust::FFI.digraph_props_for(@wrapped, v1, v2)
         | 
| 117 | 
            +
                  @_props[idx]
         | 
| 118 | 
            +
                end
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                private
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                def new_count_ptr
         | 
| 123 | 
            +
                  ::FFI::MemoryPointer.new(:size_t, 1)
         | 
| 124 | 
            +
                end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                def ptr_to_array_of_strings(ptr, count_ptr)
         | 
| 127 | 
            +
                  # Read count
         | 
| 128 | 
            +
                  count = count_ptr.read_uint32
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                  # Read strings
         | 
| 131 | 
            +
                  string_ptrs = ptr.read_array_of_pointer(count)
         | 
| 132 | 
            +
                  strings = string_ptrs.map(&:read_string)
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                  # Deallocate
         | 
| 135 | 
            +
                  string_ptrs.each { |s| NanocRust::FFI.drop_string(s) }
         | 
| 136 | 
            +
                  NanocRust::FFI.drop_array(ptr, count)
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                  strings
         | 
| 139 | 
            +
                end
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                def ptr_to_array_of_u32_tuples(ptr, count_ptr)
         | 
| 142 | 
            +
                  # Read count
         | 
| 143 | 
            +
                  count = count_ptr.read_uint32
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                  # Read edges
         | 
| 146 | 
            +
                  edges_ptr = ::FFI::Pointer.new(NanocRust::FFI::Edge, ptr)
         | 
| 147 | 
            +
                  edges = (0...count).map { |i| NanocRust::FFI::Edge.new(edges_ptr[i]).to_a(@_props) }
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                  # Deallocate
         | 
| 150 | 
            +
                  NanocRust::FFI.drop_array(ptr, count)
         | 
| 151 | 
            +
             | 
| 152 | 
            +
                  edges
         | 
| 153 | 
            +
                end
         | 
| 154 | 
            +
              end
         | 
| 155 | 
            +
             | 
| 156 | 
            +
              def self.activate!
         | 
| 157 | 
            +
                Nanoc::Int.send(:remove_const, 'DirectedGraph') if Nanoc::Int.const_defined?('DirectedGraph')
         | 
| 158 | 
            +
                Nanoc::Int.const_set('DirectedGraph', NanocRust::DirectedGraph)
         | 
| 159 | 
            +
              end
         | 
| 160 | 
            +
            end
         | 
    
        data/nanoc-rust.gemspec
    ADDED
    
    | @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Gem::Specification.new do |s|
         | 
| 4 | 
            +
              s.name        = 'nanoc-rust'
         | 
| 5 | 
            +
              s.version     = '0.1'
         | 
| 6 | 
            +
              s.homepage    = 'http://nanoc.ws/'
         | 
| 7 | 
            +
              s.summary     = 'Faster bits for Nanoc'
         | 
| 8 | 
            +
              s.description = 'Contains parts of Nanoc rewritten in Rust for speed'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              s.author  = 'Denis Defreyne'
         | 
| 11 | 
            +
              s.email   = 'denis@stoneship.org'
         | 
| 12 | 
            +
              s.license = 'MIT'
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              s.files              = `git ls-files -z`.split("\x0")
         | 
| 15 | 
            +
              s.require_paths      = ['lib']
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              s.required_ruby_version = '>= 2.3.0'
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              s.extensions = Dir['rust/extconf.rb']
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              s.add_runtime_dependency('ffi', '~> 1.9')
         | 
| 22 | 
            +
              s.add_development_dependency('bundler', '~> 1.15')
         | 
| 23 | 
            +
            end
         | 
    
        data/rust/Cargo.lock
    ADDED
    
    
    
        data/rust/Cargo.toml
    ADDED
    
    
    
        data/rust/Makefile
    ADDED
    
    | @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            default: build
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            test:
         | 
| 4 | 
            +
            	cargo test
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            build:
         | 
| 7 | 
            +
            	cargo build --release
         | 
| 8 | 
            +
            	mkdir -p dist
         | 
| 9 | 
            +
            	[ -f target/release/libnanoc_rust.dylib ] && cp target/release/libnanoc_rust.dylib dist || true
         | 
| 10 | 
            +
            	[ -f target/release/libnanoc_rust.so ] && cp target/release/libnanoc_rust.so dist || true
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            clean:
         | 
| 13 | 
            +
            	rm -rf target dist
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            install:
         | 
    
        data/rust/extconf.rb
    ADDED
    
    
    
        data/rust/src/digraph.rs
    ADDED
    
    | @@ -0,0 +1,336 @@ | |
| 1 | 
            +
            use std::collections::HashMap;
         | 
| 2 | 
            +
            use std::collections::hash_set;
         | 
| 3 | 
            +
            use std::collections::HashSet;
         | 
| 4 | 
            +
            use std::hash::Hash;
         | 
| 5 | 
            +
            use std::fmt::Debug;
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            pub struct DirectedGraph<T: Eq + Hash> {
         | 
| 8 | 
            +
                indices: HashMap<T, u32>,
         | 
| 9 | 
            +
                next_index: u32,
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                vertices: HashSet<T>,
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                froms: HashMap<T, HashSet<T>>,
         | 
| 14 | 
            +
                tos: HashMap<T, HashSet<T>>,
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                edge_props: HashMap<(T, T), u32>,
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                empty: HashSet<T>,
         | 
| 19 | 
            +
            }
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            type VertexIter<'a, T> = hash_set::Iter<'a, T>;
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            // TODO: Move to nanoc_rust.rs
         | 
| 24 | 
            +
            #[repr(C)]
         | 
| 25 | 
            +
            pub struct Edge {
         | 
| 26 | 
            +
                from: u32,
         | 
| 27 | 
            +
                to: u32,
         | 
| 28 | 
            +
                props: u32,
         | 
| 29 | 
            +
            }
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            impl<T: Eq + Hash + Clone + Debug> DirectedGraph<T> {
         | 
| 32 | 
            +
                pub fn new() -> Self {
         | 
| 33 | 
            +
                    DirectedGraph {
         | 
| 34 | 
            +
                        indices: HashMap::new(),
         | 
| 35 | 
            +
                        next_index: 0,
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                        froms: HashMap::new(),
         | 
| 38 | 
            +
                        tos: HashMap::new(),
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                        edge_props: HashMap::new(),
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                        vertices: HashSet::new(),
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                        empty: HashSet::new(),
         | 
| 45 | 
            +
                    }
         | 
| 46 | 
            +
                }
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                // Modifying
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                pub fn add_vertex(&mut self, v: T) {
         | 
| 51 | 
            +
                    self.vertices.insert(v.clone());
         | 
| 52 | 
            +
                    self.indices.entry(v).or_insert(self.next_index);
         | 
| 53 | 
            +
                    self.next_index += 1;
         | 
| 54 | 
            +
                }
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                pub fn add_edge(&mut self, v1: T, v2: T, props: u32) {
         | 
| 57 | 
            +
                    self.add_vertex(v1.clone());
         | 
| 58 | 
            +
                    self.add_vertex(v2.clone());
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    self.edge_props.insert((v1.clone(), v2.clone()), props);
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                    self.tos.entry(v2.clone()).or_insert(HashSet::new()).insert(v1.clone());
         | 
| 63 | 
            +
                    self.froms.entry(v1).or_insert(HashSet::new()).insert(v2);
         | 
| 64 | 
            +
                }
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                pub fn delete_edges_to(&mut self, v: T) {
         | 
| 67 | 
            +
                    if let Some(mut tos) = self.tos.get_mut(&v) {
         | 
| 68 | 
            +
                        for to in tos.iter() {
         | 
| 69 | 
            +
                            self.edge_props.remove(&(to.clone(), v.clone()));
         | 
| 70 | 
            +
                        }
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                        tos.clear();
         | 
| 73 | 
            +
                    }
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                    for (_, mut froms) in &mut self.froms {
         | 
| 76 | 
            +
                        froms.remove(&v);
         | 
| 77 | 
            +
                    }
         | 
| 78 | 
            +
                }
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                // Querying
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                pub fn props_for(&self, v1: T, v2: T) -> u32 {
         | 
| 83 | 
            +
                    let p: Option<&u32> = self.edge_props.get(&(v1, v2));
         | 
| 84 | 
            +
                    if let Some(v) = p.cloned() {
         | 
| 85 | 
            +
                        v
         | 
| 86 | 
            +
                    } else {
         | 
| 87 | 
            +
                        0
         | 
| 88 | 
            +
                    }
         | 
| 89 | 
            +
                }
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                pub fn vertices_iter(&self) -> VertexIter<T> {
         | 
| 92 | 
            +
                    self.vertices.iter()
         | 
| 93 | 
            +
                }
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                pub fn edges(&self) -> Vec<Edge> {
         | 
| 96 | 
            +
                    let mut res: Vec<Edge> = Vec::new();
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                    let vertices = self.vertices_iter().collect::<Vec<_>>();
         | 
| 99 | 
            +
                    for from in vertices {
         | 
| 100 | 
            +
                        for to in self.direct_successors_of(from.clone()) {
         | 
| 101 | 
            +
                            res.push(Edge {
         | 
| 102 | 
            +
                                from: *self.indices.get(&from).unwrap(),
         | 
| 103 | 
            +
                                to: *self.indices.get(&to).unwrap(),
         | 
| 104 | 
            +
                                props: self.props_for(from.clone(), to.clone()),
         | 
| 105 | 
            +
                            });
         | 
| 106 | 
            +
                        }
         | 
| 107 | 
            +
                    }
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                    res
         | 
| 110 | 
            +
                }
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                pub fn direct_successors_of(&self, v: T) -> VertexIter<T> {
         | 
| 113 | 
            +
                    let froms = self.froms.get(&v);
         | 
| 114 | 
            +
                    if let Some(froms) = froms {
         | 
| 115 | 
            +
                        froms.iter()
         | 
| 116 | 
            +
                    } else {
         | 
| 117 | 
            +
                        self.empty.iter()
         | 
| 118 | 
            +
                    }
         | 
| 119 | 
            +
                }
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                pub fn direct_predecessors_of(&self, v: T) -> VertexIter<T> {
         | 
| 122 | 
            +
                    let tos = self.tos.get(&v);
         | 
| 123 | 
            +
                    if let Some(tos) = tos {
         | 
| 124 | 
            +
                        tos.iter()
         | 
| 125 | 
            +
                    } else {
         | 
| 126 | 
            +
                        self.empty.iter()
         | 
| 127 | 
            +
                    }
         | 
| 128 | 
            +
                }
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                pub fn successors_of(&self, v: T) -> Vec<T> {
         | 
| 131 | 
            +
                    let mut all_vertices: HashSet<T> = HashSet::new();
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                    let mut processed_vertices: HashSet<T> = HashSet::new();
         | 
| 134 | 
            +
                    let mut unprocessed_vertices: Vec<T> = vec![v];
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                    while let Some(vertex) = unprocessed_vertices.pop() {
         | 
| 137 | 
            +
                        // Get next unprocessed vertex
         | 
| 138 | 
            +
                        if processed_vertices.contains(&vertex) {
         | 
| 139 | 
            +
                            continue;
         | 
| 140 | 
            +
                        }
         | 
| 141 | 
            +
                        processed_vertices.insert(vertex.clone());
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                        // Add successors of this vertex
         | 
| 144 | 
            +
                        for v in self.direct_successors_of(vertex) {
         | 
| 145 | 
            +
                            all_vertices.insert(v.clone());
         | 
| 146 | 
            +
                            unprocessed_vertices.push(v.clone());
         | 
| 147 | 
            +
                        }
         | 
| 148 | 
            +
                    }
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                    return all_vertices.iter().cloned().collect::<Vec<_>>();
         | 
| 151 | 
            +
                }
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                pub fn predecessors_of(&self, v: T) -> Vec<T> {
         | 
| 154 | 
            +
                    let mut all_vertices: HashSet<T> = HashSet::new();
         | 
| 155 | 
            +
             | 
| 156 | 
            +
                    let mut processed_vertices: HashSet<T> = HashSet::new();
         | 
| 157 | 
            +
                    let mut unprocessed_vertices: Vec<T> = vec![v];
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                    while let Some(vertex) = unprocessed_vertices.pop() {
         | 
| 160 | 
            +
                        // Get next unprocessed vertex
         | 
| 161 | 
            +
                        if processed_vertices.contains(&vertex) {
         | 
| 162 | 
            +
                            continue;
         | 
| 163 | 
            +
                        }
         | 
| 164 | 
            +
                        processed_vertices.insert(vertex.clone());
         | 
| 165 | 
            +
             | 
| 166 | 
            +
                        // Add predecessors of this vertex
         | 
| 167 | 
            +
                        for v in self.direct_predecessors_of(vertex) {
         | 
| 168 | 
            +
                            all_vertices.insert(v.clone());
         | 
| 169 | 
            +
                            unprocessed_vertices.push(v.clone());
         | 
| 170 | 
            +
                        }
         | 
| 171 | 
            +
                    }
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                    return all_vertices.iter().cloned().collect::<Vec<_>>();
         | 
| 174 | 
            +
                }
         | 
| 175 | 
            +
            }
         | 
| 176 | 
            +
             | 
| 177 | 
            +
            #[cfg(test)]
         | 
| 178 | 
            +
            mod tests {
         | 
| 179 | 
            +
                use super::*;
         | 
| 180 | 
            +
                use std::fmt::Debug;
         | 
| 181 | 
            +
                use std::iter::IntoIterator;
         | 
| 182 | 
            +
             | 
| 183 | 
            +
                macro_rules! assert_iters_eq {
         | 
| 184 | 
            +
                    ( $a:expr, $b:expr ) => {{
         | 
| 185 | 
            +
                        match ($a, $b) {
         | 
| 186 | 
            +
                            (a, b) => {
         | 
| 187 | 
            +
                                let mut v1 = a.collect::<Vec<_>>();
         | 
| 188 | 
            +
                                let mut v2 = b.collect::<Vec<_>>();
         | 
| 189 | 
            +
             | 
| 190 | 
            +
                                v1.sort();
         | 
| 191 | 
            +
                                v2.sort();
         | 
| 192 | 
            +
             | 
| 193 | 
            +
                                assert_eq!(v1, v2);
         | 
| 194 | 
            +
                            }
         | 
| 195 | 
            +
                        }
         | 
| 196 | 
            +
                    }}
         | 
| 197 | 
            +
                }
         | 
| 198 | 
            +
             | 
| 199 | 
            +
                fn assert_empty<I, T: Debug + Ord>(c: I)
         | 
| 200 | 
            +
                where
         | 
| 201 | 
            +
                    I: IntoIterator<Item = T>,
         | 
| 202 | 
            +
                {
         | 
| 203 | 
            +
                    assert_eq!(0, c.into_iter().collect::<Vec<_>>().len());
         | 
| 204 | 
            +
                }
         | 
| 205 | 
            +
             | 
| 206 | 
            +
                #[test]
         | 
| 207 | 
            +
                fn new_digraph_is_empty() {
         | 
| 208 | 
            +
                    let dg: DirectedGraph<i32> = DirectedGraph::new();
         | 
| 209 | 
            +
             | 
| 210 | 
            +
                    assert_empty(dg.vertices_iter());
         | 
| 211 | 
            +
             | 
| 212 | 
            +
                    assert_empty(dg.direct_successors_of(0));
         | 
| 213 | 
            +
                    assert_empty(dg.direct_successors_of(1));
         | 
| 214 | 
            +
                    assert_empty(dg.direct_successors_of(2));
         | 
| 215 | 
            +
                    assert_empty(dg.direct_successors_of(3));
         | 
| 216 | 
            +
             | 
| 217 | 
            +
                    assert_empty(dg.direct_predecessors_of(0));
         | 
| 218 | 
            +
                    assert_empty(dg.direct_predecessors_of(1));
         | 
| 219 | 
            +
                    assert_empty(dg.direct_predecessors_of(2));
         | 
| 220 | 
            +
                    assert_empty(dg.direct_predecessors_of(3));
         | 
| 221 | 
            +
                }
         | 
| 222 | 
            +
             | 
| 223 | 
            +
                #[test]
         | 
| 224 | 
            +
                fn add_new_vertex() {
         | 
| 225 | 
            +
                    let mut dg: DirectedGraph<i32> = DirectedGraph::new();
         | 
| 226 | 
            +
                    dg.add_vertex(1);
         | 
| 227 | 
            +
             | 
| 228 | 
            +
                    assert_eq!(1, dg.vertices_iter().len());
         | 
| 229 | 
            +
                    assert_iters_eq!(vec![1].iter(), dg.vertices_iter());
         | 
| 230 | 
            +
             | 
| 231 | 
            +
                    assert_empty(dg.direct_successors_of(0));
         | 
| 232 | 
            +
                    assert_empty(dg.direct_successors_of(1));
         | 
| 233 | 
            +
                    assert_empty(dg.direct_successors_of(2));
         | 
| 234 | 
            +
                    assert_empty(dg.direct_successors_of(3));
         | 
| 235 | 
            +
             | 
| 236 | 
            +
                    assert_empty(dg.direct_predecessors_of(0));
         | 
| 237 | 
            +
                    assert_empty(dg.direct_predecessors_of(1));
         | 
| 238 | 
            +
                    assert_empty(dg.direct_predecessors_of(2));
         | 
| 239 | 
            +
                    assert_empty(dg.direct_predecessors_of(3));
         | 
| 240 | 
            +
                }
         | 
| 241 | 
            +
             | 
| 242 | 
            +
                #[test]
         | 
| 243 | 
            +
                fn add_existing_vertex() {
         | 
| 244 | 
            +
                    let mut dg: DirectedGraph<i32> = DirectedGraph::new();
         | 
| 245 | 
            +
                    dg.add_vertex(1);
         | 
| 246 | 
            +
                    dg.add_vertex(1);
         | 
| 247 | 
            +
             | 
| 248 | 
            +
                    assert_eq!(1, dg.vertices_iter().len());
         | 
| 249 | 
            +
                    assert_iters_eq!(vec![1].iter(), dg.vertices_iter());
         | 
| 250 | 
            +
             | 
| 251 | 
            +
                    assert_empty(dg.direct_successors_of(0));
         | 
| 252 | 
            +
                    assert_empty(dg.direct_successors_of(1));
         | 
| 253 | 
            +
                    assert_empty(dg.direct_successors_of(2));
         | 
| 254 | 
            +
                    assert_empty(dg.direct_successors_of(3));
         | 
| 255 | 
            +
             | 
| 256 | 
            +
                    assert_empty(dg.direct_predecessors_of(0));
         | 
| 257 | 
            +
                    assert_empty(dg.direct_predecessors_of(1));
         | 
| 258 | 
            +
                    assert_empty(dg.direct_predecessors_of(2));
         | 
| 259 | 
            +
                    assert_empty(dg.direct_predecessors_of(3));
         | 
| 260 | 
            +
                }
         | 
| 261 | 
            +
             | 
| 262 | 
            +
                #[test]
         | 
| 263 | 
            +
                fn add_multiple_vertices() {
         | 
| 264 | 
            +
                    let mut dg: DirectedGraph<i32> = DirectedGraph::new();
         | 
| 265 | 
            +
                    dg.add_vertex(1);
         | 
| 266 | 
            +
                    dg.add_vertex(3);
         | 
| 267 | 
            +
             | 
| 268 | 
            +
                    assert_eq!(2, dg.vertices_iter().len());
         | 
| 269 | 
            +
                    assert_iters_eq!(vec![1, 3].iter(), dg.vertices_iter());
         | 
| 270 | 
            +
             | 
| 271 | 
            +
                    assert_empty(dg.direct_successors_of(0));
         | 
| 272 | 
            +
                    assert_empty(dg.direct_successors_of(1));
         | 
| 273 | 
            +
                    assert_empty(dg.direct_successors_of(2));
         | 
| 274 | 
            +
                    assert_empty(dg.direct_successors_of(3));
         | 
| 275 | 
            +
             | 
| 276 | 
            +
                    assert_empty(dg.direct_predecessors_of(0));
         | 
| 277 | 
            +
                    assert_empty(dg.direct_predecessors_of(1));
         | 
| 278 | 
            +
                    assert_empty(dg.direct_predecessors_of(2));
         | 
| 279 | 
            +
                    assert_empty(dg.direct_predecessors_of(3));
         | 
| 280 | 
            +
                }
         | 
| 281 | 
            +
             | 
| 282 | 
            +
                #[test]
         | 
| 283 | 
            +
                fn add_edges_has_vertices() {
         | 
| 284 | 
            +
                    let mut dg: DirectedGraph<i32> = DirectedGraph::new();
         | 
| 285 | 
            +
                    dg.add_edge(1, 2);
         | 
| 286 | 
            +
                    dg.add_edge(1, 3);
         | 
| 287 | 
            +
             | 
| 288 | 
            +
                    assert_eq!(3, dg.vertices_iter().len());
         | 
| 289 | 
            +
                    assert_iters_eq!(vec![1, 2, 3].iter(), dg.vertices_iter());
         | 
| 290 | 
            +
                }
         | 
| 291 | 
            +
             | 
| 292 | 
            +
                #[test]
         | 
| 293 | 
            +
                fn add_edges_has_successors_and_predecessors() {
         | 
| 294 | 
            +
                    let mut dg: DirectedGraph<i32> = DirectedGraph::new();
         | 
| 295 | 
            +
                    dg.add_edge(1, 2);
         | 
| 296 | 
            +
                    dg.add_edge(1, 3);
         | 
| 297 | 
            +
             | 
| 298 | 
            +
                    // 0
         | 
| 299 | 
            +
                    assert_empty(dg.direct_successors_of(0));
         | 
| 300 | 
            +
                    assert_empty(dg.direct_predecessors_of(0));
         | 
| 301 | 
            +
             | 
| 302 | 
            +
                    // 1
         | 
| 303 | 
            +
                    assert_iters_eq!(vec![2, 3].iter(), dg.direct_successors_of(1));
         | 
| 304 | 
            +
                    assert_empty(dg.direct_predecessors_of(1));
         | 
| 305 | 
            +
             | 
| 306 | 
            +
                    // 2
         | 
| 307 | 
            +
                    assert_empty(dg.direct_successors_of(2));
         | 
| 308 | 
            +
                    assert_iters_eq!(vec![1].iter(), dg.direct_predecessors_of(2));
         | 
| 309 | 
            +
             | 
| 310 | 
            +
                    // 3
         | 
| 311 | 
            +
                    assert_empty(dg.direct_successors_of(3));
         | 
| 312 | 
            +
                    assert_iters_eq!(vec![1].iter(), dg.direct_predecessors_of(3));
         | 
| 313 | 
            +
                }
         | 
| 314 | 
            +
             | 
| 315 | 
            +
                #[test]
         | 
| 316 | 
            +
                fn delete_edges_to() {
         | 
| 317 | 
            +
                    let mut dg: DirectedGraph<i32> = DirectedGraph::new();
         | 
| 318 | 
            +
                    dg.add_edge(0, 1);
         | 
| 319 | 
            +
                    dg.add_edge(0, 2);
         | 
| 320 | 
            +
                    dg.add_edge(0, 3);
         | 
| 321 | 
            +
                    dg.add_edge(1, 2);
         | 
| 322 | 
            +
                    dg.add_edge(1, 3);
         | 
| 323 | 
            +
                    dg.add_edge(2, 3);
         | 
| 324 | 
            +
                    dg.delete_edges_to(1);
         | 
| 325 | 
            +
             | 
| 326 | 
            +
                    assert_iters_eq!(vec![2, 3].iter(), dg.direct_successors_of(0));
         | 
| 327 | 
            +
                    assert_iters_eq!(vec![2, 3].iter(), dg.direct_successors_of(1));
         | 
| 328 | 
            +
                    assert_iters_eq!(vec![3].iter(), dg.direct_successors_of(2));
         | 
| 329 | 
            +
                    assert_empty(dg.direct_successors_of(3));
         | 
| 330 | 
            +
             | 
| 331 | 
            +
                    assert_empty(dg.direct_predecessors_of(0));
         | 
| 332 | 
            +
                    assert_empty(dg.direct_predecessors_of(1));
         | 
| 333 | 
            +
                    assert_iters_eq!(vec![0, 1].iter(), dg.direct_predecessors_of(2));
         | 
| 334 | 
            +
                    assert_iters_eq!(vec![0, 1, 2].iter(), dg.direct_predecessors_of(3));
         | 
| 335 | 
            +
                }
         | 
| 336 | 
            +
            }
         | 
| @@ -0,0 +1,191 @@ | |
| 1 | 
            +
            #![feature(libc)]
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            extern crate libc;
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            mod digraph;
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            pub mod ffi {
         | 
| 8 | 
            +
                use digraph::{DirectedGraph, Edge};
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                use libc::c_char;
         | 
| 11 | 
            +
                use std::ffi::{CStr, CString};
         | 
| 12 | 
            +
                use std::fmt::{Debug, Error, Formatter};
         | 
| 13 | 
            +
                use std::mem;
         | 
| 14 | 
            +
                use std::ptr;
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                type R = *const c_char;
         | 
| 17 | 
            +
                type T = Vec<u8>;
         | 
| 18 | 
            +
                type D = DirectedGraph<T>;
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                impl Debug for D {
         | 
| 21 | 
            +
                    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
         | 
| 22 | 
            +
                        write!(f, "Nanoc::Int::DirectedGraph(")?;
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                        let mut idx = 0;
         | 
| 25 | 
            +
                        let mut vertices = self.vertices_iter().collect::<Vec<_>>();
         | 
| 26 | 
            +
                        vertices.sort();
         | 
| 27 | 
            +
                        for from in vertices {
         | 
| 28 | 
            +
                            for to in self.direct_successors_of(from.clone()) {
         | 
| 29 | 
            +
                                if idx > 0 {
         | 
| 30 | 
            +
                                    write!(f, ", ")?;
         | 
| 31 | 
            +
                                }
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                                write!(
         | 
| 34 | 
            +
                                    f,
         | 
| 35 | 
            +
                                    "{:?} -> {:?} props=nil",
         | 
| 36 | 
            +
                                    String::from_utf8_lossy(&*from.clone()),
         | 
| 37 | 
            +
                                    String::from_utf8_lossy(&*to.clone()),
         | 
| 38 | 
            +
                                )?;
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                                idx += 1;
         | 
| 41 | 
            +
                            }
         | 
| 42 | 
            +
                        }
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                        write!(f, ")")?;
         | 
| 45 | 
            +
                        Ok(())
         | 
| 46 | 
            +
                    }
         | 
| 47 | 
            +
                }
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                // Converting raw <-> actual
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                fn r2t(r: R) -> T {
         | 
| 52 | 
            +
                    unsafe {
         | 
| 53 | 
            +
                        if r.is_null() {
         | 
| 54 | 
            +
                            vec![]
         | 
| 55 | 
            +
                        } else {
         | 
| 56 | 
            +
                            CStr::from_ptr(r).to_bytes().to_vec()
         | 
| 57 | 
            +
                        }
         | 
| 58 | 
            +
                    }
         | 
| 59 | 
            +
                }
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                fn t2r(t: T) -> R {
         | 
| 62 | 
            +
                    unsafe {
         | 
| 63 | 
            +
                        if t.is_empty() {
         | 
| 64 | 
            +
                            ptr::null()
         | 
| 65 | 
            +
                        } else {
         | 
| 66 | 
            +
                            let cs = CString::from_vec_unchecked(t);
         | 
| 67 | 
            +
                            let ptr = cs.as_ptr();
         | 
| 68 | 
            +
                            mem::forget(cs);
         | 
| 69 | 
            +
                            ptr
         | 
| 70 | 
            +
                        }
         | 
| 71 | 
            +
                    }
         | 
| 72 | 
            +
                }
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                // drop
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                #[no_mangle]
         | 
| 77 | 
            +
                pub extern "C" fn drop_string(ptr: *mut c_char) {
         | 
| 78 | 
            +
                    unsafe { CString::from_raw(ptr); }
         | 
| 79 | 
            +
                }
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                #[no_mangle]
         | 
| 82 | 
            +
                pub extern "C" fn drop_array(ptr: *mut *mut c_char, length: usize) {
         | 
| 83 | 
            +
                    unsafe { Vec::<*mut c_char>::from_raw_parts(ptr, length, length); };
         | 
| 84 | 
            +
                }
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                // create+destroy
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                #[no_mangle]
         | 
| 89 | 
            +
                pub extern "C" fn digraph_create() -> Box<D> {
         | 
| 90 | 
            +
                    Box::new(DirectedGraph::new())
         | 
| 91 | 
            +
                }
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                #[no_mangle]
         | 
| 94 | 
            +
                pub extern "C" fn digraph_destroy(dg: &mut D) {
         | 
| 95 | 
            +
                    mem::drop(dg);
         | 
| 96 | 
            +
                }
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                // query
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                #[no_mangle]
         | 
| 101 | 
            +
                pub extern "C" fn digraph_inspect(dg: &D) -> R {
         | 
| 102 | 
            +
                    let s = format!("{:?}", dg);
         | 
| 103 | 
            +
                    CString::new(s).unwrap().into_raw()
         | 
| 104 | 
            +
                }
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                #[no_mangle]
         | 
| 107 | 
            +
                pub extern "C" fn digraph_props_for(dg: &D, v1: R, v2: R) -> u32 {
         | 
| 108 | 
            +
                    dg.props_for(r2t(v1), r2t(v2))
         | 
| 109 | 
            +
                }
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                #[no_mangle]
         | 
| 112 | 
            +
                pub extern "C" fn digraph_edges(dg: &D, out_count: *mut usize) -> *const Edge {
         | 
| 113 | 
            +
                    let mut vec = dg.edges();
         | 
| 114 | 
            +
                    vec.shrink_to_fit();
         | 
| 115 | 
            +
                    let ptr = vec.as_ptr();
         | 
| 116 | 
            +
                    unsafe { ptr::write(out_count, vec.len()); };
         | 
| 117 | 
            +
                    mem::forget(vec);
         | 
| 118 | 
            +
                    ptr
         | 
| 119 | 
            +
                }
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                #[no_mangle]
         | 
| 122 | 
            +
                pub extern "C" fn digraph_vertices(dg: &D, out_count: *mut usize) -> *const R {
         | 
| 123 | 
            +
                    let mut vec: Vec<R> = dg.vertices_iter().cloned().map(|a| t2r(a)).collect();
         | 
| 124 | 
            +
                    vec.shrink_to_fit();
         | 
| 125 | 
            +
                    let ptr = vec.as_ptr();
         | 
| 126 | 
            +
                    unsafe { ptr::write(out_count, vec.len()); };
         | 
| 127 | 
            +
                    mem::forget(vec);
         | 
| 128 | 
            +
                    ptr
         | 
| 129 | 
            +
                }
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                #[no_mangle]
         | 
| 132 | 
            +
                pub extern "C" fn digraph_direct_predecessors_of(dg: &D, v: R, out_count: *mut usize) -> *const R {
         | 
| 133 | 
            +
                    let mut vec: Vec<R> = dg.direct_predecessors_of(r2t(v)).cloned().map(|a| t2r(a)).collect();
         | 
| 134 | 
            +
                    vec.shrink_to_fit();
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                    let ptr = vec.as_ptr();
         | 
| 137 | 
            +
                    unsafe { ptr::write(out_count, vec.len()); };
         | 
| 138 | 
            +
                    mem::forget(vec);
         | 
| 139 | 
            +
                    ptr
         | 
| 140 | 
            +
                }
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                #[no_mangle]
         | 
| 143 | 
            +
                pub extern "C" fn digraph_predecessors_of(dg: &D, v: R, out_count: *mut usize) -> *const R {
         | 
| 144 | 
            +
                    let mut vec: Vec<R> = dg.predecessors_of(r2t(v)).iter().cloned().map(|a| t2r(a)).collect();
         | 
| 145 | 
            +
                    vec.shrink_to_fit();
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                    let ptr = vec.as_ptr();
         | 
| 148 | 
            +
                    unsafe { ptr::write(out_count, vec.len()); };
         | 
| 149 | 
            +
                    mem::forget(vec);
         | 
| 150 | 
            +
                    ptr
         | 
| 151 | 
            +
                }
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                #[no_mangle]
         | 
| 154 | 
            +
                pub extern "C" fn digraph_direct_successors_of(dg: &D, v: R, out_count: *mut usize) -> *const R {
         | 
| 155 | 
            +
                    let mut vec: Vec<R> = dg.direct_successors_of(r2t(v)).cloned().map(|a| t2r(a)).collect();
         | 
| 156 | 
            +
                    vec.shrink_to_fit();
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                    let ptr = vec.as_ptr();
         | 
| 159 | 
            +
                    unsafe { ptr::write(out_count, vec.len()); };
         | 
| 160 | 
            +
                    mem::forget(vec);
         | 
| 161 | 
            +
                    ptr
         | 
| 162 | 
            +
                }
         | 
| 163 | 
            +
             | 
| 164 | 
            +
                #[no_mangle]
         | 
| 165 | 
            +
                pub extern "C" fn digraph_successors_of(dg: &D, v: R, out_count: *mut usize) -> *const R {
         | 
| 166 | 
            +
                    let mut vec: Vec<R> = dg.successors_of(r2t(v)).iter().cloned().map(|a| t2r(a)).collect();
         | 
| 167 | 
            +
                    vec.shrink_to_fit();
         | 
| 168 | 
            +
             | 
| 169 | 
            +
                    let ptr = vec.as_ptr();
         | 
| 170 | 
            +
                    unsafe { ptr::write(out_count, vec.len()); };
         | 
| 171 | 
            +
                    mem::forget(vec);
         | 
| 172 | 
            +
                    ptr
         | 
| 173 | 
            +
                }
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                // mutate
         | 
| 176 | 
            +
             | 
| 177 | 
            +
                #[no_mangle]
         | 
| 178 | 
            +
                pub extern "C" fn digraph_add_vertex(dg: &mut D, v: R) {
         | 
| 179 | 
            +
                    dg.add_vertex(r2t(v))
         | 
| 180 | 
            +
                }
         | 
| 181 | 
            +
             | 
| 182 | 
            +
                #[no_mangle]
         | 
| 183 | 
            +
                pub extern "C" fn digraph_add_edge(dg: &mut D, v1: R, v2: R, p: u32) {
         | 
| 184 | 
            +
                    dg.add_edge(r2t(v1), r2t(v2), p)
         | 
| 185 | 
            +
                }
         | 
| 186 | 
            +
             | 
| 187 | 
            +
                #[no_mangle]
         | 
| 188 | 
            +
                pub extern "C" fn digraph_delete_edges_to(dg: &mut D, v: R) {
         | 
| 189 | 
            +
                    dg.delete_edges_to(r2t(v));
         | 
| 190 | 
            +
                }
         | 
| 191 | 
            +
            }
         | 
| @@ -0,0 +1,228 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe NanocRust::DirectedGraph do
         | 
| 4 | 
            +
              subject(:graph) { described_class.new(%w[1 2 3]) }
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              describe '#edges' do
         | 
| 7 | 
            +
                subject { graph.edges }
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                context 'empty graph' do
         | 
| 10 | 
            +
                  it { is_expected.to be_empty }
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                context 'graph with vertices, but no edges' do
         | 
| 14 | 
            +
                  before do
         | 
| 15 | 
            +
                    graph.add_vertex('1')
         | 
| 16 | 
            +
                    graph.add_vertex('2')
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  it { is_expected.to be_empty }
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                context 'graph with edges from previously added vertices' do
         | 
| 23 | 
            +
                  before do
         | 
| 24 | 
            +
                    graph.add_vertex('1')
         | 
| 25 | 
            +
                    graph.add_vertex('2')
         | 
| 26 | 
            +
                    graph.add_vertex('3')
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    graph.add_edge('1', '2')
         | 
| 29 | 
            +
                    graph.add_edge('1', '3')
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  it { is_expected.to match_array([[0, 1, nil], [0, 2, nil]]) }
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                context 'graph with edges from new vertices' do
         | 
| 36 | 
            +
                  before do
         | 
| 37 | 
            +
                    graph.add_edge('1', '2')
         | 
| 38 | 
            +
                    graph.add_edge('1', '3')
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  it { is_expected.to match_array([[0, 1, nil], [0, 2, nil]]) }
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                context 'graph with edge props' do
         | 
| 45 | 
            +
                  before do
         | 
| 46 | 
            +
                    graph.add_edge('1', '2', props: { name: 'Mr. C' })
         | 
| 47 | 
            +
                    graph.add_edge('1', '3', props: { name: 'Cooper' })
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  it { is_expected.to match_array([[0, 1, { name: 'Mr. C' }], [0, 2, { name: 'Cooper' }]]) }
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
              describe '#props_for' do
         | 
| 55 | 
            +
                subject { graph.props_for('1', '2') }
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                context 'no edge' do
         | 
| 58 | 
            +
                  it { is_expected.to be_nil }
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                context 'edge, but no props' do
         | 
| 62 | 
            +
                  before { graph.add_edge('1', '2') }
         | 
| 63 | 
            +
                  it { is_expected.to be_nil }
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                context 'edge with props' do
         | 
| 67 | 
            +
                  before { graph.add_edge('1', '2', props: { name: 'Mr. C' }) }
         | 
| 68 | 
            +
                  it { is_expected.to eq(name: 'Mr. C') }
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                  context 'deleted edge (#delete_edges_to)' do
         | 
| 71 | 
            +
                    before { graph.delete_edges_to('2') }
         | 
| 72 | 
            +
                    it { is_expected.to be_nil }
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
              end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
              describe '#direct_predecessors_of' do
         | 
| 78 | 
            +
                subject { graph.direct_predecessors_of('2') }
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                context 'no edges' do
         | 
| 81 | 
            +
                  it { is_expected.to be_empty }
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                context 'one edge to' do
         | 
| 85 | 
            +
                  before { graph.add_edge('1', '2') }
         | 
| 86 | 
            +
                  it { is_expected.to eq(['1']) }
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                context 'two edges to' do
         | 
| 90 | 
            +
                  before do
         | 
| 91 | 
            +
                    graph.add_edge('1', '2')
         | 
| 92 | 
            +
                    graph.add_edge('3', '2')
         | 
| 93 | 
            +
                  end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                  it { is_expected.to match_array(%w[1 3]) }
         | 
| 96 | 
            +
                end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                context 'edge from' do
         | 
| 99 | 
            +
                  before { graph.add_edge('2', '3') }
         | 
| 100 | 
            +
                  it { is_expected.to be_empty }
         | 
| 101 | 
            +
                end
         | 
| 102 | 
            +
              end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
              describe '#direct_successors_of' do
         | 
| 105 | 
            +
                subject { graph.direct_successors_of('2') }
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                context 'no edges' do
         | 
| 108 | 
            +
                  it { is_expected.to be_empty }
         | 
| 109 | 
            +
                end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                context 'one edge to' do
         | 
| 112 | 
            +
                  before { graph.add_edge('1', '2') }
         | 
| 113 | 
            +
                  it { is_expected.to be_empty }
         | 
| 114 | 
            +
                end
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                context 'one edge from' do
         | 
| 117 | 
            +
                  before { graph.add_edge('2', '3') }
         | 
| 118 | 
            +
                  it { is_expected.to eq(['3']) }
         | 
| 119 | 
            +
                end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                context 'two edges from' do
         | 
| 122 | 
            +
                  before do
         | 
| 123 | 
            +
                    graph.add_edge('2', '1')
         | 
| 124 | 
            +
                    graph.add_edge('2', '3')
         | 
| 125 | 
            +
                  end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                  it { is_expected.to match_array(%w[1 3]) }
         | 
| 128 | 
            +
                end
         | 
| 129 | 
            +
              end
         | 
| 130 | 
            +
             | 
| 131 | 
            +
              describe '#predecessors_of' do
         | 
| 132 | 
            +
                subject { graph.predecessors_of('2') }
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                context 'no predecessors' do
         | 
| 135 | 
            +
                  before do
         | 
| 136 | 
            +
                    graph.add_edge('2', '3')
         | 
| 137 | 
            +
                  end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                  it { is_expected.to be_empty }
         | 
| 140 | 
            +
                end
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                context 'direct predecessor' do
         | 
| 143 | 
            +
                  before do
         | 
| 144 | 
            +
                    graph.add_edge('2', '3')
         | 
| 145 | 
            +
                    graph.add_edge('1', '2')
         | 
| 146 | 
            +
                  end
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                  context 'no indirect predecessors' do
         | 
| 149 | 
            +
                    it { is_expected.to match_array(['1']) }
         | 
| 150 | 
            +
                  end
         | 
| 151 | 
            +
             | 
| 152 | 
            +
                  context 'indirect predecessors' do
         | 
| 153 | 
            +
                    before { graph.add_edge('3', '1') }
         | 
| 154 | 
            +
                    it { is_expected.to match_array(%w[1 2 3]) }
         | 
| 155 | 
            +
                  end
         | 
| 156 | 
            +
                end
         | 
| 157 | 
            +
              end
         | 
| 158 | 
            +
             | 
| 159 | 
            +
              describe '#successors_of' do
         | 
| 160 | 
            +
                subject { graph.successors_of('2') }
         | 
| 161 | 
            +
             | 
| 162 | 
            +
                context 'no successors' do
         | 
| 163 | 
            +
                  before do
         | 
| 164 | 
            +
                    graph.add_edge('1', '2')
         | 
| 165 | 
            +
                  end
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                  it { is_expected.to be_empty }
         | 
| 168 | 
            +
                end
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                context 'direct predecessor' do
         | 
| 171 | 
            +
                  before do
         | 
| 172 | 
            +
                    graph.add_edge('1', '2')
         | 
| 173 | 
            +
                    graph.add_edge('2', '3')
         | 
| 174 | 
            +
                  end
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                  context 'no indirect successors' do
         | 
| 177 | 
            +
                    it { is_expected.to match_array(['3']) }
         | 
| 178 | 
            +
                  end
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                  context 'indirect successors' do
         | 
| 181 | 
            +
                    before { graph.add_edge('3', '1') }
         | 
| 182 | 
            +
                    it { is_expected.to match_array(%w[1 2 3]) }
         | 
| 183 | 
            +
                  end
         | 
| 184 | 
            +
                end
         | 
| 185 | 
            +
              end
         | 
| 186 | 
            +
             | 
| 187 | 
            +
              describe '#inspect' do
         | 
| 188 | 
            +
                subject { graph.inspect }
         | 
| 189 | 
            +
             | 
| 190 | 
            +
                context 'empty graph' do
         | 
| 191 | 
            +
                  it { is_expected.to eq('Nanoc::Int::DirectedGraph()') }
         | 
| 192 | 
            +
                end
         | 
| 193 | 
            +
             | 
| 194 | 
            +
                context 'one edge, no props' do
         | 
| 195 | 
            +
                  before do
         | 
| 196 | 
            +
                    graph.add_edge('1', '2')
         | 
| 197 | 
            +
                  end
         | 
| 198 | 
            +
             | 
| 199 | 
            +
                  it { is_expected.to eq('Nanoc::Int::DirectedGraph("1" -> "2" props=nil)') }
         | 
| 200 | 
            +
                end
         | 
| 201 | 
            +
             | 
| 202 | 
            +
                context 'two edges, no props' do
         | 
| 203 | 
            +
                  before do
         | 
| 204 | 
            +
                    graph.add_edge('1', '2')
         | 
| 205 | 
            +
                    graph.add_edge('2', '3')
         | 
| 206 | 
            +
                  end
         | 
| 207 | 
            +
             | 
| 208 | 
            +
                  it { is_expected.to eq('Nanoc::Int::DirectedGraph("1" -> "2" props=nil, "2" -> "3" props=nil)') }
         | 
| 209 | 
            +
                end
         | 
| 210 | 
            +
             | 
| 211 | 
            +
                context 'one edge, props' do
         | 
| 212 | 
            +
                  before do
         | 
| 213 | 
            +
                    graph.add_edge('1', '2', props: 'giraffe')
         | 
| 214 | 
            +
                  end
         | 
| 215 | 
            +
             | 
| 216 | 
            +
                  it { is_expected.to eq('Nanoc::Int::DirectedGraph("1" -> "2" props="giraffe")') }
         | 
| 217 | 
            +
                end
         | 
| 218 | 
            +
             | 
| 219 | 
            +
                context 'two edges, props' do
         | 
| 220 | 
            +
                  before do
         | 
| 221 | 
            +
                    graph.add_edge('1', '2', props: 'donkey')
         | 
| 222 | 
            +
                    graph.add_edge('2', '3', props: 'zebra')
         | 
| 223 | 
            +
                  end
         | 
| 224 | 
            +
             | 
| 225 | 
            +
                  it { is_expected.to eq('Nanoc::Int::DirectedGraph("1" -> "2" props="donkey", "2" -> "3" props="zebra")') }
         | 
| 226 | 
            +
                end
         | 
| 227 | 
            +
              end
         | 
| 228 | 
            +
            end
         | 
    
        data/spec/spec_helper.rb
    ADDED
    
    
    
        data/test.sh
    ADDED
    
    
    
        metadata
    ADDED
    
    | @@ -0,0 +1,89 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: nanoc-rust
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: '0.1'
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Denis Defreyne
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2017-07-16 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: ffi
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - "~>"
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '1.9'
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - "~>"
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '1.9'
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: bundler
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - "~>"
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '1.15'
         | 
| 34 | 
            +
              type: :development
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - "~>"
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '1.15'
         | 
| 41 | 
            +
            description: Contains parts of Nanoc rewritten in Rust for speed
         | 
| 42 | 
            +
            email: denis@stoneship.org
         | 
| 43 | 
            +
            executables: []
         | 
| 44 | 
            +
            extensions:
         | 
| 45 | 
            +
            - rust/extconf.rb
         | 
| 46 | 
            +
            extra_rdoc_files: []
         | 
| 47 | 
            +
            files:
         | 
| 48 | 
            +
            - ".gitignore"
         | 
| 49 | 
            +
            - ".rspec"
         | 
| 50 | 
            +
            - ".rubocop.yml"
         | 
| 51 | 
            +
            - Gemfile
         | 
| 52 | 
            +
            - Gemfile.lock
         | 
| 53 | 
            +
            - Rakefile
         | 
| 54 | 
            +
            - lib/nanoc-rust.rb
         | 
| 55 | 
            +
            - nanoc-rust.gemspec
         | 
| 56 | 
            +
            - rust/Cargo.lock
         | 
| 57 | 
            +
            - rust/Cargo.toml
         | 
| 58 | 
            +
            - rust/Makefile
         | 
| 59 | 
            +
            - rust/extconf.rb
         | 
| 60 | 
            +
            - rust/src/digraph.rs
         | 
| 61 | 
            +
            - rust/src/nanoc_rust.rs
         | 
| 62 | 
            +
            - spec/directed_graph_spec.rb
         | 
| 63 | 
            +
            - spec/spec_helper.rb
         | 
| 64 | 
            +
            - test.sh
         | 
| 65 | 
            +
            homepage: http://nanoc.ws/
         | 
| 66 | 
            +
            licenses:
         | 
| 67 | 
            +
            - MIT
         | 
| 68 | 
            +
            metadata: {}
         | 
| 69 | 
            +
            post_install_message: 
         | 
| 70 | 
            +
            rdoc_options: []
         | 
| 71 | 
            +
            require_paths:
         | 
| 72 | 
            +
            - lib
         | 
| 73 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 74 | 
            +
              requirements:
         | 
| 75 | 
            +
              - - ">="
         | 
| 76 | 
            +
                - !ruby/object:Gem::Version
         | 
| 77 | 
            +
                  version: 2.3.0
         | 
| 78 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 79 | 
            +
              requirements:
         | 
| 80 | 
            +
              - - ">="
         | 
| 81 | 
            +
                - !ruby/object:Gem::Version
         | 
| 82 | 
            +
                  version: '0'
         | 
| 83 | 
            +
            requirements: []
         | 
| 84 | 
            +
            rubyforge_project: 
         | 
| 85 | 
            +
            rubygems_version: 2.6.12
         | 
| 86 | 
            +
            signing_key: 
         | 
| 87 | 
            +
            specification_version: 4
         | 
| 88 | 
            +
            summary: Faster bits for Nanoc
         | 
| 89 | 
            +
            test_files: []
         |