Manipulate returned erlang type in elixir

I am trying to interface with the nksip library and mostly got it but I am having a hard time translating this:

{ok, Body} = nksip_request:body(Req),
SDP1 = Body#sdp{vsn=Body#sdp.vsn+1000},

What is returned is defined in the erlang sources as:

-type sdp() :: #sdp{}.

I am not really clear on what that is so I won’t give it a name but trying out in iex I can tell you that is_tuple() returns true for it xD

I think I understand what the erlang code does but have no idea how to translate it: body can be multiple things so it ensures it is an sdp “type” and then return a modified version with vsn incremented by 1000.

The thing that is returned is a Record, a ‘tagged tuple’. Records are Erlang’s way to build custom data types. They have certain limitations that Elixir’s structs do not have, but structs are a lot newer (Maps are a fairly recent addition to Erlang/OTP). (Also, there are some situations where records are better.)

Hopefully in the documentation of nksip there will be documentation about what fields their record has.

Elixir has support for records in the Record module, but this support is somewhat limited when compared to structs.

Thanks, I thought it was a record but I was not sure, I tried Record.extract_all() but it found nothing in the file defining this type, I am not really sure how it extracts informations though.
If I know the fields inside and create an elixir struct how can I then use it to access and update fields in this record ?
Since the record once modified is passed to an erlang function I imagine I would also need to translate the struct bak to a record.

#sdp{} is defined in nksip/include/nksip.hrl. This path can be used in erlangs -include_lib(), perhaps you need to do some more lifting to make it findable for Record.extract(_all).

What I tried is this:

iex(16)> Record.extract_all(from: "deps/nksip/src/nksip_sdp.erl")
[]
iex(17)> Record.extract_all(from: "deps/nksip/src/nksip.erl")
[uri: [scheme: :undefined, user: "", pass: "", domain: "invalid.invalid",
  port: 0, path: "", opts: [], headers: [], ext_opts: [], ext_headers: [],
  disp: ""]]

Apparently it finds uri at least but not sdp :confused:

Please try this:

Record.extract_all(from: "deps/nksip/include/nksip.hrl")

In erlang records are defined in header files (*.hrl) most of the time, especially if they are meant to get re-used across modules or are a documented and not opaque type of the API. So you have to extract from the corresponding headerfile, not from an erlang source (*.erl) file.

Contents of an erlang source file is considered private unless you can find those very same content in the compiled bytecode afterwards, accessible via functions in that module.


Edit, I’m not sure though if it is a good idea to try to load from deps. Maybe there is a better way to ask where that file might live? Something more like erlangs magic path resultion in -include_lib()?

Nice !
It found a lot of records but amongst thme was sdp

iex(20)> Record.extract_all(from: "deps/nksip/include/nksip.hrl")
sdp: [sdp_vsn: "0", user: "-", id: 0, vsn: 0,
  address: {"IN", "IP4", "0.0.0.0"}, session: "nksip", info: :undefined,
  uri: :undefined, email: :undefined, phone: :undefined, connect: :undefined,
  bandwidth: [], time: [], zone: :undefined, key: :undefined, attributes: [],
  medias: []],

Now I am not really sure what is the next step, I tried the naive approach:

iex(21)> sdp = :nksip_sdp.new()
{:sdp, "0", "-", 1498040058, 1498040058, {"IN", "IP4", "auto.nksip"}, "nksip",
 :undefined, :undefined, :undefined, :undefined, {"IN", "IP4", "auto.nksip"},
 [], [{0, 0, []}], :undefined, :undefined, [],
 [{:sdp_m, "audio", 1080, 1, "RTP/AVP", ["0"], :undefined, :undefined, [],
   :undefined, [{"rtpmap", ["0", "PCMU/8000"]}, {"inactive", []}]}]}
iex(22)> sdp.vsn
** (UndefinedFunctionError) function :sdp.vsn/1 is undefined (module :sdp is not available)

I was not really expecting it to be that easy but I tried xD

Ps: Loading from deps is not a huge problem in my current usecase, when I manage to make it working I might have another look. I looked at include_lib but it looks in the folder where binaries (beam) are stored, no hrl to be found there.

If I do get the documentation of Record right, it should be:

sdp_data = :nksip_sdp.new()
sdp(sdp_data, :vsn)

That is (or should be) equivalent to the following erlang snippet:

SdpData = nksip_sdp:new()
SdpData#sdp.vsn

PS: @AstonJ can you install an erlang highlighter that we can triger with erl? Or is already one installed and I simply use the wrong name for the fence?

1 Like

Not sure why, but it wasn’t added - done now :003:

I managed to find the solution by playing around starting from your code, while most is documented putting eveything together was not so straightforward, thanks for the help.

Here is the resulting code:

defmodule Nksip do
  require Record
  Record.defrecord :sdp, Record.extract(:sdp, from: "deps/nksip/include/nksip.hrl")
end

require Nksip
sdp = :nksip_sdp.new()
sdp = Nksip.sdp(sdp, vsn: Nksip.sdp(sdp, :vsn) + 1000)

I realized extract/_all just prepare the informations for you to call defrecord, it does nothing by itself.

Edit: syntax highlighting seems broken now xD

1 Like

There was a trailing space behind the closing fence, because of that the system thought it were an inline code spanning multiple lines.