# `FdsnPlugs.SourceIdentifier`
[🔗](https://gricad-gitlab.univ-grenoble-alpes.fr/OSUG/RESIF/fdsn_plugs)

This modules defines a source identifier type and all logic for validation and manipulation.

# `t`

```elixir
@type t() :: %FdsnPlugs.SourceIdentifier{
  cha: String.t(),
  end: DateTime.t(),
  loc: String.t(),
  net: String.t(),
  sta: String.t(),
  start: DateTime.t()
}
```

An FdsnPlugs.SourceIdentifier describes an NSLC and time boundaries.

# `build_list`

```elixir
@spec build_list(
  String.t(),
  String.t(),
  String.t(),
  String.t(),
  String.t(),
  String.t()
) ::
  {:ok, [t()]} | {:error, String.t()}
```

Builds a list of FDSN source identifiers.
The date is parsed. In case of failures, returns {:error, message}.
On success, returns a list of FdsnPlugs.SourceIdentifier.

## Examples

    iex> FdsnPlugs.SourceIdentifier.build_list("FR,RA","*","*","H?Z")
    {:ok,
    [
        %FdsnPlugs.SourceIdentifier{
        net: "FR",
        sta: "*",
        loc: "*",
        cha: "H?Z",
        start: ~U[1000-01-01 00:00:00Z],
        end: ~U[3000-01-01 00:00:00Z]
        },
        %FdsnPlugs.SourceIdentifier{
        net: "RA",
        sta: "*",
        loc: "*",
        cha: "H?Z",
        start: ~U[1000-01-01 00:00:00Z],
        end: ~U[3000-01-01 00:00:00Z]
        }
    ]}
    iex> FdsnPlugs.SourceIdentifier.build_list()
    {:error, "Requesting all nslc is not allowed. Provide at least one of net, sta, loc, cha"}
    iex> FdsnPlugs.SourceIdentifier.build_list("*", "*", "--", "*")
    {:ok,
    [
    %FdsnPlugs.SourceIdentifier{
    net: "*",
    sta: "*",
    loc: "",
    cha: "*",
    start: ~U[1000-01-01 00:00:00Z],
    end: ~U[3000-01-01 00:00:00Z]
    }
    ]}
    iex> FdsnPlugs.SourceIdentifier.build_list("*", "*", "  ", "*")
    {:ok,
    [
    %FdsnPlugs.SourceIdentifier{
    net: "*",
    sta: "*",
    loc: "",
    cha: "*",
    start: ~U[1000-01-01 00:00:00Z],
    end: ~U[3000-01-01 00:00:00Z]
    }
    ]}
    iex> FdsnPlugs.SourceIdentifier.build_list("*", "*", "  ", "*", "2024-01-01", "2025-01-01")
    {:ok,
    [
    %FdsnPlugs.SourceIdentifier{
    net: "*",
    sta: "*",
    loc: "",
    cha: "*",
    start: ~U[2024-01-01 00:00:00Z],
    end: ~U[2025-01-01 00:00:00Z]
    }
    ]}
    iex> FdsnPlugs.SourceIdentifier.build_list("*", "STA", "  ", "*", "2024-01-01")
    {:ok,
    [
    %FdsnPlugs.SourceIdentifier{
        net: "*",
        sta: "STA",
        loc: "",
        cha: "*",
        start: ~U[2024-01-01 00:00:00Z],
        end: ~U[3000-01-01 00:00:00Z]
    }
    ]}
    iex> FdsnPlugs.SourceIdentifier.build_list("*", "STA", "  ")
    {:ok,
    [
    %FdsnPlugs.SourceIdentifier{
        net: "*",
        sta: "STA",
        loc: "",
        cha: "*",
        start: ~U[1000-01-01 00:00:00Z],
        end: ~U[3000-01-01 00:00:00Z]
    }
    ]}
    iex> FdsnPlugs.SourceIdentifier.build_list("*", "STA")
    {:ok,
    [
    %FdsnPlugs.SourceIdentifier{
        net: "*",
        sta: "STA",
        loc: "*",
        cha: "*",
        start: ~U[1000-01-01 00:00:00Z],
        end: ~U[3000-01-01 00:00:00Z]
    }
    ]}
    iex> FdsnPlugs.SourceIdentifier.build_list("XX")
    {:ok,
    [
    %FdsnPlugs.SourceIdentifier{
        net: "XX",
        sta: "*",
        loc: "*",
        cha: "*",
        start: ~U[1000-01-01 00:00:00Z],
        end: ~U[3000-01-01 00:00:00Z]
    }
    ]}
    iex> FdsnPlugs.SourceIdentifier.build_list("*", "*", "  ", "*", "2024-01-01", "2025-01-0")

# `expand_multi_values`

```elixir
@spec expand_multi_values(t()) :: [t()]
```

If the stream has multiple values on :net, :sta, :loc or :cha, then the function splits the stream and returns a list of stream.
If there is nothing to expand, it returns a list with only the stream given in parameter.

## Parameters
- stream: a %FdsnPlugs.SourceIdentifier{} structure

## Examples

    iex> FdsnPlugs.SourceIdentifier.expand_multi_values(%{net: "FR,RA", sta: "CIEL,ABCD", loc: "00,01", cha: "HNZ,HNE"})
    [
        %{cha: "HNZ", loc: "00", net: "FR", sta: "CIEL"},
        %{cha: "HNZ", loc: "00", net: "RA", sta: "CIEL"},
        %{cha: "HNZ", loc: "00", net: "FR", sta: "ABCD"},
        %{cha: "HNZ", loc: "00", net: "RA", sta: "ABCD"},
        %{cha: "HNZ", loc: "01", net: "FR", sta: "CIEL"},
        %{cha: "HNZ", loc: "01", net: "RA", sta: "CIEL"},
        %{cha: "HNZ", loc: "01", net: "FR", sta: "ABCD"},
        %{cha: "HNZ", loc: "01", net: "RA", sta: "ABCD"},
        %{cha: "HNE", loc: "00", net: "FR", sta: "CIEL"},
        %{cha: "HNE", loc: "00", net: "RA", sta: "CIEL"},
        %{cha: "HNE", loc: "00", net: "FR", sta: "ABCD"},
        %{cha: "HNE", loc: "00", net: "RA", sta: "ABCD"},
        %{cha: "HNE", loc: "01", net: "FR", sta: "CIEL"},
        %{cha: "HNE", loc: "01", net: "RA", sta: "CIEL"},
        %{cha: "HNE", loc: "01", net: "FR", sta: "ABCD"},
        %{cha: "HNE", loc: "01", net: "RA", sta: "ABCD"}
    ]

# `parse_date`

```elixir
@spec parse_date(DateTime.t() | String.t()) ::
  {:ok, DateTime.t(), integer()} | {:error, atom()}
```

Parse a date from all accepted formats in the FDSN norm.
- just a date
- a date with time separated by T
All dates are assumed to be UTC
Return a tupple {:ok, DateTime} or {:error, "error message"}

## Examples

### FDSN supported:

    iex> FdsnPlugs.SourceIdentifier.parse_date("2023-01-01")
    {:ok, ~U[2023-01-01 00:00:00Z], 0}

    iex> FdsnPlugs.SourceIdentifier.parse_date("2023-01-01T23:59:59")
    {:ok, ~U[2023-01-01 23:59:59Z], 0}

    iex> FdsnPlugs.SourceIdentifier.parse_date("2023-01-01T23:59:59.999000")
    {:ok, ~U[2023-01-01 23:59:59.999000Z], 0}

### Extra formats supported:

    iex> FdsnPlugs.SourceIdentifier.parse_date("2023-01-01T23:59:59Z")
    {:ok, ~U[2023-01-01 23:59:59Z], 0}

    iex> FdsnPlugs.SourceIdentifier.parse_date("2023-01-01T23:59:59.999")
    {:ok, ~U[2023-01-01 23:59:59.999Z], 0}

    iex> FdsnPlugs.SourceIdentifier.parse_date(~U[2023-01-01 23:59:59.999Z])
    {:ok, ~U[2023-01-01 23:59:59.999Z], 0}

    iex> FdsnPlugs.SourceIdentifier.parse_date("2023-01-01T23:59:59.999000Z")
    {:ok, ~U[2023-01-01 23:59:59.999000Z], 0}

# Catch errors:

    iex> FdsnPlugs.SourceIdentifier.parse_date("2023-02-31T23:59:59.999")
    {:error, :invalid_date}

# `split_channel`

```elixir
@spec split_channel(t()) :: {:ok, [...]} | {:error, String.t()}
```

Splits securely the channel code in 3 parts, even if the channel code is just one or two characters.

## Examples

    iex> FdsnPlugs.SourceIdentifier.split_channel(%FdsnPlugs.SourceIdentifier{cha: "*"})
    {:ok, ["*", "*", "*"]}
    iex> FdsnPlugs.SourceIdentifier.split_channel(%FdsnPlugs.SourceIdentifier{cha: "HN*"})
    {:ok, ["H", "N", "*"]}
    iex> FdsnPlugs.SourceIdentifier.split_channel(%FdsnPlugs.SourceIdentifier{cha: "H*"})
    {:ok, ["H", "*", "*"]}
    iex> FdsnPlugs.SourceIdentifier.split_channel(%FdsnPlugs.SourceIdentifier{cha: "*H"})
    {:ok, ["*", "*", "H"]}
    iex> FdsnPlugs.SourceIdentifier.split_channel(%FdsnPlugs.SourceIdentifier{cha: "*H*"})
    {:ok, ["*", "H", "*"]}
    iex> FdsnPlugs.SourceIdentifier.split_channel(%FdsnPlugs.SourceIdentifier{cha: "H"})
    {:error, "Missing two characters on channel specification. Did you mean H* ?"}
    iex> FdsnPlugs.SourceIdentifier.split_channel(%FdsnPlugs.SourceIdentifier{cha: "HH"})
    {:error, "Missing one character on channel specification. Did you mean HH* ?"}
    iex> FdsnPlugs.SourceIdentifier.split_channel(%FdsnPlugs.SourceIdentifier{cha: "HHHH"})
    {:error, "Channel HHHH has more than 3 characters !"}

# `valid_channel?`

```elixir
@spec valid_channel?(t()) :: boolean()
```

This function tests if a channel is correctly specified. If the length is not full, then it should contain a star     
## Examples

    iex> FdsnPlugs.SourceIdentifier.valid_channel?(%FdsnPlugs.SourceIdentifier{cha: "*"})
    true
    iex> FdsnPlugs.SourceIdentifier.valid_channel?(%FdsnPlugs.SourceIdentifier{cha: "HH"})
    false
    iex> FdsnPlugs.SourceIdentifier.valid_channel?(%FdsnPlugs.SourceIdentifier{cha: "jaVasCript%3A%2F%2A"})
    false

# `valid_location?`

```elixir
@spec valid_location?(t()) :: boolean()
```

Tests if a location is correctly specified.
## Examples

    iex> FdsnPlugs.SourceIdentifier.valid_location?(%FdsnPlugs.SourceIdentifier{loc: "*"})
    true
    iex> FdsnPlugs.SourceIdentifier.valid_location?(%FdsnPlugs.SourceIdentifier{loc: "--"})
    true
    iex> FdsnPlugs.SourceIdentifier.valid_location?(%FdsnPlugs.SourceIdentifier{loc: ""})
    true
    iex> FdsnPlugs.SourceIdentifier.valid_location?(%FdsnPlugs.SourceIdentifier{loc: "0r"})
    false
    iex> FdsnPlugs.SourceIdentifier.valid_location?(%FdsnPlugs.SourceIdentifier{loc: "F?"})
    true
    iex> FdsnPlugs.SourceIdentifier.valid_location?(%FdsnPlugs.SourceIdentifier{loc: "?"})
    true
    iex> FdsnPlugs.SourceIdentifier.valid_location?(%FdsnPlugs.SourceIdentifier{loc: "SEIS2026"})
    true

# `valid_network?`

```elixir
@spec valid_network?(t()) :: boolean()
```

This function tests if a network is correctly specified.
## Examples

    iex> FdsnPlugs.SourceIdentifier.valid_network?(%FdsnPlugs.SourceIdentifier{net: "*"})
    true
    iex> FdsnPlugs.SourceIdentifier.valid_network?(%FdsnPlugs.SourceIdentifier{net: "FR"})
    true
    iex> FdsnPlugs.SourceIdentifier.valid_network?(%FdsnPlugs.SourceIdentifier{net: "xf-2004"})
    false
    iex> FdsnPlugs.SourceIdentifier.valid_network?(%FdsnPlugs.SourceIdentifier{net: "F?"})
    true
    iex> FdsnPlugs.SourceIdentifier.valid_network?(%FdsnPlugs.SourceIdentifier{net: "?"})
    true
    iex> FdsnPlugs.SourceIdentifier.valid_network?(%FdsnPlugs.SourceIdentifier{net: "SEIS2026"})
    true

# `valid_station?`

```elixir
@spec valid_station?(t()) :: boolean()
```

Tests if a station is correctly specified.
## Examples

    iex> FdsnPlugs.SourceIdentifier.valid_station?(%FdsnPlugs.SourceIdentifier{sta: "*"})
    true
    iex> FdsnPlugs.SourceIdentifier.valid_station?(%FdsnPlugs.SourceIdentifier{sta: "STA1-8"})
    true
    iex> FdsnPlugs.SourceIdentifier.valid_station?(%FdsnPlugs.SourceIdentifier{sta: "xf-2004"})
    false
    iex> FdsnPlugs.SourceIdentifier.valid_station?(%FdsnPlugs.SourceIdentifier{sta: "F?"})
    true
    iex> FdsnPlugs.SourceIdentifier.valid_station?(%FdsnPlugs.SourceIdentifier{sta: "?"})
    true
    iex> FdsnPlugs.SourceIdentifier.valid_station?(%FdsnPlugs.SourceIdentifier{sta: "SEIS2026"})
    true

---

*Consult [api-reference.md](api-reference.md) for complete listing*
