{"id":20199888,"url":"https://github.com/pragmatic-elixir-meetup/port_exs","last_synced_at":"2026-05-27T23:32:40.894Z","repository":{"id":143115257,"uuid":"199287398","full_name":"Pragmatic-Elixir-Meetup/port_exs","owner":"Pragmatic-Elixir-Meetup","description":null,"archived":false,"fork":false,"pushed_at":"2019-08-03T16:03:19.000Z","size":450,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-12-01T00:48:57.024Z","etag":null,"topics":["elixir","nerves-project","rfid"],"latest_commit_sha":null,"homepage":null,"language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Pragmatic-Elixir-Meetup.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-07-28T12:57:10.000Z","updated_at":"2019-08-21T01:51:31.000Z","dependencies_parsed_at":null,"dependency_job_id":"23ea2b8a-1d2b-4040-ab64-b8b0ebe207a5","html_url":"https://github.com/Pragmatic-Elixir-Meetup/port_exs","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Pragmatic-Elixir-Meetup/port_exs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pragmatic-Elixir-Meetup%2Fport_exs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pragmatic-Elixir-Meetup%2Fport_exs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pragmatic-Elixir-Meetup%2Fport_exs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pragmatic-Elixir-Meetup%2Fport_exs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Pragmatic-Elixir-Meetup","download_url":"https://codeload.github.com/Pragmatic-Elixir-Meetup/port_exs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pragmatic-Elixir-Meetup%2Fport_exs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33588345,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-27T02:00:06.184Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elixir","nerves-project","rfid"],"created_at":"2024-11-14T04:40:22.313Z","updated_at":"2026-05-27T23:32:40.888Z","avatar_url":"https://github.com/Pragmatic-Elixir-Meetup.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n###\n\nElixir 通过 Port 调用 Rust 进程\n\nSteven Gu\nsteven.gu@letote.cn\n\n###\n\n自我介绍\n\n1. Letote 的 Rails Coder\n2. 参与过 Letote 的 Elixir 项目开发\n3. 学习 Rust 的嵌入式应用\n\n###\n\nNIF 和 Port\n\n1. NIF - https://github.com/rusterlium/rustler\n2. Port - http://erlang.org/doc/man/erlang.html#open_port-2\n\n###\n\nLetote 应用\n\n1. RFID 设备标识衣服或者首饰\n2. 通过 RFID 识别码跟踪状态\n3. 使用 RFID 设备提供商的 SDK\n\n###\n\n选择 Port 的原因\n\n1. Letote 的相关服务构建在 Elixir/Phoenix 基础上\n2. RFID 设备提供商的 SDK 是 C lib，内部包含线程（pthread）\n3. RFID 设备提供商的 SDK 使用 Callback Function 通知识别码\n\n###\n\n选择 Rust 的原因\n\n1. 实验尝试\n2. C 的结构化封装有难度（glibc，ffmpeg），C++ 成本高\n3. Rust 友好安全的编译期内存检测\n4. Cargo 工具链针对多平台 Targets 的支持，省去了 C Makefile 的复杂度\n\n###\n\nRust 相关库\n\n1. rust-bindgen - https://github.com/rust-lang/rust-bindgen（C binding）\n2. libc - https://github.com/rust-lang/libc (pipe, select, epoll 等)\n3. nix - https://github.com/nix-rust/nix (u8, i8, c_char, errno 等)\n\n###\n\nSimple Example\n\n```\nmix new simple_ex\ncd simple_ex\ncargo init\n```\n\n###\n\nMix Task\n\n```\ncargo build --bin simple_ex --release --target-dir \\\n    _build/dev/lib/simple_ex/priv\n```\n\n```\ndefmodule Mix.Tasks.Compile.Cargo do\n  use Mix.Task\n\n  defp build do\n    System.cmd(\"cargo\", build_cmd_args, [\n      stderr_to_stdout: true,\n      into: IO.stream(:stdio, :line)\n    ])\n  end\nend\n```\n\n###\n\nElixir Part（1）\n\n```\ndefmodule SimpleEx do\n  use GenServer\n\n  def init(nil) do\n    executable = :code.priv_dir(:simple_ex) ++ '/release/simple_ex'\n\n    port = Port.open({:spawn_executable, executable}, [\n      {:packet, 2},\n      :binary,\n      :exit_status,\n      :use_stdio\n      ])\n  end\nend\n```\n\n###\n\nElixir Part（2）\n\n```\n  def handle_info({_port, {:data, \u003c\u003c data :: binary \u003e\u003e}}, state) do\n    code = :erlang.binary_to_term(data)\n\n    IO.puts \"elixir_SimpleEx: Receives - #{code}\"\n\n    {:noreply, state}\n  end\n```\n\n###\n\nRust Part\n\n```\nlet mut sender = io::stdout();\n\nsender.\n  write_u16::\u003cBigEndian\u003e(encoded_data.len() as u16).\n  expect(\"failed to write data size\");\n\nsender.write_all(\u0026encoded_data).expect(\"failed to write data\");\n\nsender.flush().expect(\"failed to flush stdout\");\n```\n\n###\n\nSimple Example\n\nhttps://github.com/silathdiir/port_exs\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpragmatic-elixir-meetup%2Fport_exs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpragmatic-elixir-meetup%2Fport_exs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpragmatic-elixir-meetup%2Fport_exs/lists"}