WIP documentation
- The documentation is cluttered enough as it is with the large number of procedures supporting vCard 3 and 4. Split common out into the publicly exposed bits and the private internals. This makes it obvious which common functionality a client can expect to have exposed on the main vcard module. - Add documentation (WIP) on the vcard3 module.
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -3,4 +3,5 @@ tests/*
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bin/
 | 
					bin/
 | 
				
			||||||
 | 
					doc/
 | 
				
			||||||
*.sw?
 | 
					*.sw?
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										11
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								Makefile
									
									
									
									
									
								
							@@ -1,13 +1,18 @@
 | 
				
			|||||||
# Make does not offer a recursive wildcard function, so here's one:
 | 
					# Make does not offer a recursive wildcard function, so here's one:
 | 
				
			||||||
 rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
 | 
					rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SOURCES=$(call rwildcard,src/,*.nim)
 | 
					SOURCES=$(call rwildcard,src/,*.nim)
 | 
				
			||||||
TEST_SOURCES=$(wildcard tests/*.nim)
 | 
					TEST_SOURCES=$(wildcard tests/*.nim)
 | 
				
			||||||
TESTS=$(patsubst %.nim,bin/%,$(TEST_SOURCES))
 | 
					TESTS=$(patsubst %.nim,bin/%,$(TEST_SOURCES))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: build
 | 
					.PHONY: build
 | 
				
			||||||
build: test
 | 
					build: test docs
 | 
				
			||||||
	nimble build
 | 
					
 | 
				
			||||||
 | 
					doc/vcard/vcard.html: $(SOURCES)
 | 
				
			||||||
 | 
						nim doc --project --outdir:doc/vcard src/vcard.nim
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PHONY: doc
 | 
				
			||||||
 | 
					docs: doc/vcard/vcard.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: test
 | 
					.PHONY: test
 | 
				
			||||||
test:
 | 
					test:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,26 +2,28 @@
 | 
				
			|||||||
# © 2022 Jonathan Bernard
 | 
					# © 2022 Jonathan Bernard
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## The `vcard` module implements a high-performance vCard parser for both
 | 
					## The `vcard` module implements a high-performance vCard parser for both
 | 
				
			||||||
## versions 3.0 (defined by RFCs [2425][rfc2425] and  [2426][rfc2426]) and 4.0
 | 
					## versions 3.0 (defined by RFCs 2425_ and  2426_) and 4.0 (defined by RFC
 | 
				
			||||||
## (defined by RFC [6350][rfc6350])
 | 
					## 6350_)
 | 
				
			||||||
##
 | 
					##
 | 
				
			||||||
## [rfc2425]: https://tools.ietf.org/html/rfc2425
 | 
					## .. _2425: https://tools.ietf.org/html/rfc2425
 | 
				
			||||||
## [rfc2426]: https://tools.ietf.org/html/rfc2426
 | 
					## .. _2426: https://tools.ietf.org/html/rfc2426
 | 
				
			||||||
## [rfc6350]: https://tools.ietf.org/html/rfc6350
 | 
					## .. _6350: https://tools.ietf.org/html/rfc6350
 | 
				
			||||||
import std/[streams, unicode]
 | 
					import std/[streams, unicode]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ./vcard/private/[common, lexer]
 | 
					import ./vcard/private/[internals, lexer]
 | 
				
			||||||
import ./vcard/[vcard3, vcard4]
 | 
					import ./vcard/[common, vcard3, vcard4]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export vcard3, vcard4
 | 
					export vcard3, vcard4
 | 
				
			||||||
export common.VC_XParam,
 | 
					export common.VC_Param,
 | 
				
			||||||
 | 
					       common.VC_XParam,
 | 
				
			||||||
 | 
					       common.VCard,
 | 
				
			||||||
       common.VCardParsingError,
 | 
					       common.VCardParsingError,
 | 
				
			||||||
       common.VCardVersion,
 | 
					       common.VCardVersion,
 | 
				
			||||||
       common.VCard,
 | 
					       common.allPropsOfType,
 | 
				
			||||||
       common.getSingleValue,
 | 
					       common.getMultipleValues,
 | 
				
			||||||
       common.getMultipleValues
 | 
					       common.getSingleValue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
proc add[T](vc: VCard, content: varargs[T]): void =
 | 
					proc add*[T](vc: VCard, content: varargs[T]): void =
 | 
				
			||||||
  if vc.parsedVersion == VCardV3: add(cast[VCard3](vc), content)
 | 
					  if vc.parsedVersion == VCardV3: add(cast[VCard3](vc), content)
 | 
				
			||||||
  else: add(cast[VCard4](vc), content)
 | 
					  else: add(cast[VCard4](vc), content)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -53,12 +55,18 @@ proc readVCard*(p: var VCardParser): VCard =
 | 
				
			|||||||
  if result.parsedVersion == VCardV3:
 | 
					  if result.parsedVersion == VCardV3:
 | 
				
			||||||
    while (p.skip(CRLF, true)): discard
 | 
					    while (p.skip(CRLF, true)): discard
 | 
				
			||||||
 | 
					
 | 
				
			||||||
proc parseVCards*(input: Stream, filename = "input"): seq[VCard] =
 | 
					proc initVCardParser*(input: Stream, filename = "input"): VCardParser =
 | 
				
			||||||
  var p: VCardParser
 | 
					  result.filename = filename
 | 
				
			||||||
  p.filename = filename
 | 
					  lexer.open(result, input)
 | 
				
			||||||
  lexer.open(p, input)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # until EOF
 | 
					proc initVCardParser*(content: string, filename = "input"): VCardParser =
 | 
				
			||||||
 | 
					  initVCardParser(newStringStream(content), filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					proc initVCardParserFromFile*(filepath: string): VCardParser =
 | 
				
			||||||
 | 
					  initVCardParser(newFileStream(filepath, fmRead), filepath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					proc parseVCards*(input: Stream, filename = "input"): seq[VCard] =
 | 
				
			||||||
 | 
					  var p = initVCardParser(input, filename)
 | 
				
			||||||
  while p.peek != '\0': result.add(p.readVCard)
 | 
					  while p.peek != '\0': result.add(p.readVCard)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
proc parseVCards*(content: string, filename = "input"): seq[VCard] =
 | 
					proc parseVCards*(content: string, filename = "input"): seq[VCard] =
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										91
									
								
								src/vcard/common.nim
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								src/vcard/common.nim
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
				
			|||||||
 | 
					# Common Functionality
 | 
				
			||||||
 | 
					# © 2022-2023 Jonathan Bernard
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## This module contains type definitions and func/proc definitions that are
 | 
				
			||||||
 | 
					## used in common between both the 3.0 and 4.0 parser implementations.
 | 
				
			||||||
 | 
					import std/[options, sequtils]
 | 
				
			||||||
 | 
					import zero_functional
 | 
				
			||||||
 | 
					import ./private/lexer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type
 | 
				
			||||||
 | 
					  VC_Param* = tuple[name: string, values: seq[string]]
 | 
				
			||||||
 | 
					    ## Representation of vCard parameter and its values.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VCardVersion* = enum
 | 
				
			||||||
 | 
					    ## enum used to differentiate VCard3 and VCard4 versions.
 | 
				
			||||||
 | 
					    VCardV3 = "3.0", VCardV4 = "4.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VCardParser* = object of VCardLexer
 | 
				
			||||||
 | 
					    ## Common vCard parser object
 | 
				
			||||||
 | 
					    filename*: string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VCardParsingError* = object of ValueError
 | 
				
			||||||
 | 
					    ## Error raised when invalid input is detected while parsing a vCard
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC_XParam* = tuple[name, value: string]
 | 
				
			||||||
 | 
					    ## Representation of vCard extended parameters (starting with "X-").
 | 
				
			||||||
 | 
					    ## Because the meaning of these parameters is implementation-specific, no
 | 
				
			||||||
 | 
					    ## parsing of the parameter value is performed, it is returned verbatim.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VCard* = ref object of RootObj
 | 
				
			||||||
 | 
					    ## Abstract base class for all vCards. `parsedVersion` can be used to
 | 
				
			||||||
 | 
					    ## interrogate any concrete instance of this class. `asVCard3` and
 | 
				
			||||||
 | 
					    ## `asVCard4` exist as convenience functions to cast an instance to one of
 | 
				
			||||||
 | 
					    ## the subclasses depending on the value of `parsedVersion`.
 | 
				
			||||||
 | 
					    parsedVersion*: VCardVersion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					proc getMultipleValues*(
 | 
				
			||||||
 | 
					    params: openarray[VC_Param],
 | 
				
			||||||
 | 
					    name: string
 | 
				
			||||||
 | 
					  ): seq[string] =
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ## Get all of the values for a given parameter in a single list. There are
 | 
				
			||||||
 | 
					  ## two patterns for multi-valued parameters defined in the vCard 3.0 RFCs:
 | 
				
			||||||
 | 
					  ##
 | 
				
			||||||
 | 
					  ##   - TYPE=work,cell,voice
 | 
				
			||||||
 | 
					  ##   - TYPE=work;TYPE=cell;TYPE=voice
 | 
				
			||||||
 | 
					  ##
 | 
				
			||||||
 | 
					  ## Parameter values can be specific using both patterns. This method joins
 | 
				
			||||||
 | 
					  ## all defined values regardless of the pattern used to define them.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let ps = params.toSeq
 | 
				
			||||||
 | 
					  ps -->
 | 
				
			||||||
 | 
					    filter(it.name == name).
 | 
				
			||||||
 | 
					    map(it.values).
 | 
				
			||||||
 | 
					    flatten()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					proc getSingleValue*(
 | 
				
			||||||
 | 
					    params: openarray[VC_Param],
 | 
				
			||||||
 | 
					    name: string
 | 
				
			||||||
 | 
					  ): Option[string] =
 | 
				
			||||||
 | 
					  ## Get the first single value defined for a parameter.
 | 
				
			||||||
 | 
					  ##
 | 
				
			||||||
 | 
					  ## Many parameters only support a single value, depending on the content type.
 | 
				
			||||||
 | 
					  ## In order to support multi-valued parameters our implementation stores all
 | 
				
			||||||
 | 
					  ## parameters as seq[string]. This function is a convenience around that.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let ps = params.toSeq
 | 
				
			||||||
 | 
					  let foundParam = ps --> find(it.name == name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if foundParam.isSome and foundParam.get.values.len > 0:
 | 
				
			||||||
 | 
					    return some(foundParam.get.values[0])
 | 
				
			||||||
 | 
					  else:
 | 
				
			||||||
 | 
					    return none[string]()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func allPropsOfType*[T, VC: VCard](vc: VC): seq[T] =
 | 
				
			||||||
 | 
					  ## Get all instances of the requested property type present on the given
 | 
				
			||||||
 | 
					  ## vCard.
 | 
				
			||||||
 | 
					  ##
 | 
				
			||||||
 | 
					  ## This can be useful when there is some logic that hides multiple instances
 | 
				
			||||||
 | 
					  ## of a property, or returns a limited subset. For example, on 3.0 versions
 | 
				
			||||||
 | 
					  ## of vCards, this library assumes that there will only be one instance of
 | 
				
			||||||
 | 
					  ## the NAME property. The 3.0 spec implies that the NAME property should only
 | 
				
			||||||
 | 
					  ## be present at most once, but does not explicitly state this. It is
 | 
				
			||||||
 | 
					  ## possible for a 3.0 vCard to contain multiple NAME properties. using
 | 
				
			||||||
 | 
					  ## `vc3.name` will only return the first. This function allows a caller to
 | 
				
			||||||
 | 
					  ## retrieve all instances for any given property type. For example:
 | 
				
			||||||
 | 
					  ##
 | 
				
			||||||
 | 
					  ## .. code-block:: nim
 | 
				
			||||||
 | 
					  ##   let vc3 = parseVCards(...)
 | 
				
			||||||
 | 
					  ##   let allNames = allPropsOfType[VC3_Name](vc3)
 | 
				
			||||||
 | 
					  vc.content.filterIt(it of typeof(T)).mapIt(cast[T](it))
 | 
				
			||||||
@@ -6,56 +6,27 @@
 | 
				
			|||||||
## implementations.
 | 
					## implementations.
 | 
				
			||||||
##
 | 
					##
 | 
				
			||||||
## This module is not intended to be exposed to library consumers. It is
 | 
					## This module is not intended to be exposed to library consumers. It is
 | 
				
			||||||
## intended to be imported by the vcard3 and vcard4 implementations. There are
 | 
					## intended to be imported by the vcard3 and vcard4 implementations.
 | 
				
			||||||
## a handful of functions (under the **Public Definitions** section) that will
 | 
					 | 
				
			||||||
## be re-exported by the root `vcard` module and available on that namespace to
 | 
					 | 
				
			||||||
## users of the library.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import std/[macros, options, strutils, times, unicode]
 | 
					import std/[macros, options, strutils, times, unicode]
 | 
				
			||||||
import zero_functional
 | 
					import zero_functional
 | 
				
			||||||
from std/sequtils import toSeq
 | 
					from std/sequtils import toSeq
 | 
				
			||||||
import ./lexer
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Internal Types (used by `vcard{3,4}`)
 | 
					import ./lexer
 | 
				
			||||||
## =====================================
 | 
					import ../common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Internal Types (used by `vcard{3,4}`)
 | 
				
			||||||
 | 
					# =====================================
 | 
				
			||||||
type
 | 
					type
 | 
				
			||||||
  VC_PropCardinality* = enum
 | 
					  VC_PropCardinality* = enum
 | 
				
			||||||
    ## enum used to define the possible cardinalities of VCard properties.
 | 
					    ## enum used to define the possible cardinalities of vCard properties.
 | 
				
			||||||
    vpcAtMostOne,
 | 
					    vpcAtMostOne,
 | 
				
			||||||
    vpcExactlyOne,
 | 
					    vpcExactlyOne,
 | 
				
			||||||
    vpcAtLeastOne
 | 
					    vpcAtLeastOne
 | 
				
			||||||
    vpcAny
 | 
					    vpcAny
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## External Types (exported on `vcard`)
 | 
					# Internal constants (used by `vcard{3,4}`)
 | 
				
			||||||
## ====================================
 | 
					# =========================================
 | 
				
			||||||
type
 | 
					 | 
				
			||||||
  VC_Param* = tuple[name: string, values: seq[string]]
 | 
					 | 
				
			||||||
    ## Representation of VCard parameter and its values.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VCardVersion* = enum VCardV3 = "3.0", VCardV4 = "4.0" ## \
 | 
					 | 
				
			||||||
    ## enum used to differentiate VCard3 and VCard4 versions.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VCardParser* = object of VCardLexer
 | 
					 | 
				
			||||||
    ## Common VCard parser object
 | 
					 | 
				
			||||||
    filename*: string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VCardParsingError* = object of ValueError
 | 
					 | 
				
			||||||
    ## Error raised when invalid input is detected while parsing a VCard
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC_XParam* = tuple[name, value: string]
 | 
					 | 
				
			||||||
    ## Representation of VCard extended parameters (starting with "X-").
 | 
					 | 
				
			||||||
    ## Because the meaning of these parameters is implementation-specific, no
 | 
					 | 
				
			||||||
    ## parsing of the parameter value is performed, it is returned verbatim.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VCard* = ref object of RootObj
 | 
					 | 
				
			||||||
    ## Abstract base class for all VCards. `parsedVersion` can be used to
 | 
					 | 
				
			||||||
    ## interrogate any concrete instance of this class. `asVCard3` and
 | 
					 | 
				
			||||||
    ## `asVCard4` exist as convenience functions to cast an instance to one of
 | 
					 | 
				
			||||||
    ## the subclasses depending on the value of `parsedVersion`.
 | 
					 | 
				
			||||||
    parsedVersion*: VCardVersion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Internal constants (used by `vcard{3,4}`)
 | 
					 | 
				
			||||||
## =========================================
 | 
					 | 
				
			||||||
const CRLF* = "\r\n"
 | 
					const CRLF* = "\r\n"
 | 
				
			||||||
const WSP* = {' ', '\t'}
 | 
					const WSP* = {' ', '\t'}
 | 
				
			||||||
const DIGIT* = { '0'..'9' }
 | 
					const DIGIT* = { '0'..'9' }
 | 
				
			||||||
@@ -79,8 +50,8 @@ const DATE_TIME_FMTS = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const ALL_DATE_AND_OR_TIME_FMTS = DATE_TIME_FMTS.toSeq & DATE_FMTS.toSeq
 | 
					const ALL_DATE_AND_OR_TIME_FMTS = DATE_TIME_FMTS.toSeq & DATE_FMTS.toSeq
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Internal Utility/Implementation Functions
 | 
					# Internal Utility/Implementation Functions
 | 
				
			||||||
## =========================================
 | 
					# =========================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
proc parseDateTimeStr(
 | 
					proc parseDateTimeStr(
 | 
				
			||||||
    dateStr: string,
 | 
					    dateStr: string,
 | 
				
			||||||
@@ -127,10 +98,8 @@ macro assignFields*(assign: untyped, fields: varargs[untyped]): untyped =
 | 
				
			|||||||
    exp.add(f, f)
 | 
					    exp.add(f, f)
 | 
				
			||||||
    result.add(exp)
 | 
					    result.add(exp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Internal Parsing Functionality
 | 
					# Internal Parsing Functionality
 | 
				
			||||||
## ==============================
 | 
					# ==============================
 | 
				
			||||||
 | 
					 | 
				
			||||||
proc getSingleValue*(params: openarray[VC_Param], name: string): Option[string];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
proc error*(p: VCardParser, msg: string) =
 | 
					proc error*(p: VCardParser, msg: string) =
 | 
				
			||||||
  raise newException(VCardParsingError, "$1($2, $3) Error: $4" %
 | 
					  raise newException(VCardParsingError, "$1($2, $3) Error: $4" %
 | 
				
			||||||
@@ -187,7 +156,7 @@ proc isNext*(p: var VCardParser, expected: string, caseSensitive = false): bool
 | 
				
			|||||||
  p.returnToBookmark
 | 
					  p.returnToBookmark
 | 
				
			||||||
 | 
					
 | 
				
			||||||
proc readGroup*(p: var VCardParser): Option[string] =
 | 
					proc readGroup*(p: var VCardParser): Option[string] =
 | 
				
			||||||
  ## All VCARD content items can be optionally prefixed with a group name. This
 | 
					  ## All vCard content items can be optionally prefixed with a group name. This
 | 
				
			||||||
  ## scans the input to see if there is a group defined at the current read
 | 
					  ## scans the input to see if there is a group defined at the current read
 | 
				
			||||||
  ## location. If there is a valid group, the group name is returned and the
 | 
					  ## location. If there is a valid group, the group name is returned and the
 | 
				
			||||||
  ## read position is advanced past the '.' to the start of the content type
 | 
					  ## read position is advanced past the '.' to the start of the content type
 | 
				
			||||||
@@ -302,8 +271,8 @@ proc validateRequiredParameters*(
 | 
				
			|||||||
    if pv.isSome and pv.get != v:
 | 
					    if pv.isSome and pv.get != v:
 | 
				
			||||||
      p.error("parameter '$1' must have the value '$2'" % [n, v])
 | 
					      p.error("parameter '$1' must have the value '$2'" % [n, v])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Internal Serialization Utilities
 | 
					# Internal Serialization Utilities
 | 
				
			||||||
## ================================
 | 
					# ================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func foldContentLine*(s: string): string =
 | 
					func foldContentLine*(s: string): string =
 | 
				
			||||||
  result = ""
 | 
					  result = ""
 | 
				
			||||||
@@ -312,61 +281,3 @@ func foldContentLine*(s: string): string =
 | 
				
			|||||||
    result &= rem[0..<75] & "\r\n "
 | 
					    result &= rem[0..<75] & "\r\n "
 | 
				
			||||||
    rem = rem[75..^1]
 | 
					    rem = rem[75..^1]
 | 
				
			||||||
  result &= rem
 | 
					  result &= rem
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Publicly Exported Procedure and Functions
 | 
					 | 
				
			||||||
## =========================================
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
proc getMultipleValues*(
 | 
					 | 
				
			||||||
    params: openarray[VC_Param],
 | 
					 | 
				
			||||||
    name: string
 | 
					 | 
				
			||||||
  ): seq[string] =
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ## Get all of the values for a given parameter in a single list. There are
 | 
					 | 
				
			||||||
  ## two patterns for multi-valued parameters defined in the VCard3 RFCs:
 | 
					 | 
				
			||||||
  ##
 | 
					 | 
				
			||||||
  ##   - TYPE=work,cell,voice
 | 
					 | 
				
			||||||
  ##   - TYPE=work;TYPE=cell;TYPE=voice
 | 
					 | 
				
			||||||
  ##
 | 
					 | 
				
			||||||
  ## Parameter values can be specific using both patterns. This method joins
 | 
					 | 
				
			||||||
  ## all defined values regardless of the pattern used to define them.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let ps = params.toSeq
 | 
					 | 
				
			||||||
  ps -->
 | 
					 | 
				
			||||||
    filter(it.name == name).
 | 
					 | 
				
			||||||
    map(it.values).
 | 
					 | 
				
			||||||
    flatten()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
proc getSingleValue*(
 | 
					 | 
				
			||||||
    params: openarray[VC_Param],
 | 
					 | 
				
			||||||
    name: string
 | 
					 | 
				
			||||||
  ): Option[string] =
 | 
					 | 
				
			||||||
  ## Get the first single value defined for a parameter.
 | 
					 | 
				
			||||||
  ##
 | 
					 | 
				
			||||||
  ## Many parameters only support a single value, depending on the content type.
 | 
					 | 
				
			||||||
  ## In order to support multi-valued parameters our implementation stores all
 | 
					 | 
				
			||||||
  ## parameters as seq[string]. This function is a convenience around that.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let ps = params.toSeq
 | 
					 | 
				
			||||||
  let foundParam = ps --> find(it.name == name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if foundParam.isSome and foundParam.get.values.len > 0:
 | 
					 | 
				
			||||||
    return some(foundParam.get.values[0])
 | 
					 | 
				
			||||||
  else:
 | 
					 | 
				
			||||||
    return none[string]()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func allPropsOfType*[T, VC: VCard](vc: VC): seq[T] = findAll[T](vc)
 | 
					 | 
				
			||||||
  ## Get all instances of the requested property type present on the given
 | 
					 | 
				
			||||||
  ## VCard.
 | 
					 | 
				
			||||||
  ##
 | 
					 | 
				
			||||||
  ## This can be useful when there is some logic that hides multiple instances
 | 
					 | 
				
			||||||
  ## of a property, or returns a limited subset. For example, on 3.0 versions
 | 
					 | 
				
			||||||
  ## of VCards, this library assumes that there will only be one instance of
 | 
					 | 
				
			||||||
  ## the NAME property. The 3.0 spec implies that the NAME property should only
 | 
					 | 
				
			||||||
  ## be present at most once, but does not explicitly state this. It is
 | 
					 | 
				
			||||||
  ## possible for a 3.0 VCard to contain multiple NAME properties. using
 | 
					 | 
				
			||||||
  ## `vc3.name` will only return the first. This function allows a caller to
 | 
					 | 
				
			||||||
  ## retrieve all instances for any given property type. For example:
 | 
					 | 
				
			||||||
  ##
 | 
					 | 
				
			||||||
  ##     let vc3 = parseVCards(...)
 | 
					 | 
				
			||||||
  ##     let allNames = allPropsOfType[VC3_Name](vc3)
 | 
					 | 
				
			||||||
@@ -1,9 +1,9 @@
 | 
				
			|||||||
# VCard-specific Lexer
 | 
					# vCard-specific Lexer
 | 
				
			||||||
# © 2022-2023 Jonathan Bernard
 | 
					# © 2022-2023 Jonathan Bernard
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## This module defines a lexer with functionality useful for parsing VCard
 | 
					## This module defines a lexer with functionality useful for parsing vCard
 | 
				
			||||||
## content. Specifically:
 | 
					## content. Specifically:
 | 
				
			||||||
## - it understands the VCard line-folding logic and transparently joins folded
 | 
					## - it understands the vCard line-folding logic and transparently joins folded
 | 
				
			||||||
##   lines as it read input off its input stream.
 | 
					##   lines as it read input off its input stream.
 | 
				
			||||||
## - it supports multiple nested bookmarks to make look-ahead decisions more
 | 
					## - it supports multiple nested bookmarks to make look-ahead decisions more
 | 
				
			||||||
##   convenient
 | 
					##   convenient
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,21 +1,22 @@
 | 
				
			|||||||
# vCard 3.0 and 4.0 Nim implementation
 | 
					# vCard 3.0 implementation
 | 
				
			||||||
# © 2022 Jonathan Bernard
 | 
					# © 2022 Jonathan Bernard
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## The `vcard` module implements a high-performance vCard parser for both
 | 
					## This module implements a high-performance vCard parser for vCard version
 | 
				
			||||||
## versions 3.0 (defined by RFCs [2425][rfc2425] and  [2426][rfc2426]) and 4.0
 | 
					## 3.0 (defined in RFCs 2425_ and  2426_).
 | 
				
			||||||
## (defined by RFC [6350][rfc6350])
 | 
					 | 
				
			||||||
##
 | 
					##
 | 
				
			||||||
## [rfc2425]: https://tools.ietf.org/html/rfc2425
 | 
					## .. _rfc2425: https://tools.ietf.org/html/rfc2425
 | 
				
			||||||
## [rfc2426]: https://tools.ietf.org/html/rfc2426
 | 
					## .. _rfc2426: https://tools.ietf.org/html/rfc2426
 | 
				
			||||||
## [rfc6350]: https://tools.ietf.org/html/rfc6350
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import std/[base64, genasts, macros, options, sequtils, streams, strutils,
 | 
					import std/[base64, genasts, macros, options, sequtils, streams, strutils,
 | 
				
			||||||
            tables, times, unicode]
 | 
					            tables, times, unicode]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import zero_functional
 | 
					import zero_functional
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ./private/[common, lexer]
 | 
					import ./common
 | 
				
			||||||
 | 
					import ./private/[internals, lexer]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Internal enumerations used to capture the value types and properties defined
 | 
				
			||||||
 | 
					# by the spec and constants used.
 | 
				
			||||||
type
 | 
					type
 | 
				
			||||||
  VC3_ValueTypes = enum
 | 
					  VC3_ValueTypes = enum
 | 
				
			||||||
    vtUri = "uri",
 | 
					    vtUri = "uri",
 | 
				
			||||||
@@ -65,177 +66,6 @@ type
 | 
				
			|||||||
    pnClass = "CLASS"
 | 
					    pnClass = "CLASS"
 | 
				
			||||||
    pnKey = "KEY"
 | 
					    pnKey = "KEY"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  VC3_Property* = ref object of RootObj
 | 
					 | 
				
			||||||
    propertyId: int
 | 
					 | 
				
			||||||
    group*: Option[string]
 | 
					 | 
				
			||||||
    name*: string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_SimpleTextProperty* = ref object of VC3_Property
 | 
					 | 
				
			||||||
    value*: string
 | 
					 | 
				
			||||||
    isPText*: bool # true if VALUE=ptext, false by default
 | 
					 | 
				
			||||||
    language*: Option[string]
 | 
					 | 
				
			||||||
    xParams: seq[VC_XParam]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_BinaryProperty* = ref object of VC3_Property
 | 
					 | 
				
			||||||
    valueType*: Option[string] # binary / uri. Stored separately from ENCODING
 | 
					 | 
				
			||||||
                               # (captured in the isInline field) because the
 | 
					 | 
				
			||||||
                               # VALUE parameter is not set by default, but is
 | 
					 | 
				
			||||||
                               # allowed to be set.
 | 
					 | 
				
			||||||
    value*: string  # either a URI or bit sequence, both stored as string
 | 
					 | 
				
			||||||
    binaryType*: Option[string]# if using ENCODING=b, there may also be a TYPE
 | 
					 | 
				
			||||||
                               # parameter specifying the MIME type of the
 | 
					 | 
				
			||||||
                               # binary-encoded object, which is stored here.
 | 
					 | 
				
			||||||
    isInline*: bool # true if ENCODING=b, false by default
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Name* = ref object of VC3_Property
 | 
					 | 
				
			||||||
    value*: string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Profile* = ref object of VC3_Property
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Source* = ref object of VC3_Property
 | 
					 | 
				
			||||||
    valueType*: Option[string]  # uri
 | 
					 | 
				
			||||||
    value*: string # URI
 | 
					 | 
				
			||||||
    context*: Option[string]
 | 
					 | 
				
			||||||
    xParams*: seq[VC_XParam]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Fn* = ref object of VC3_SimpleTextProperty
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_N* = ref object of VC3_Property
 | 
					 | 
				
			||||||
    family*: seq[string]
 | 
					 | 
				
			||||||
    given*: seq[string]
 | 
					 | 
				
			||||||
    additional*: seq[string]
 | 
					 | 
				
			||||||
    prefixes*: seq[string]
 | 
					 | 
				
			||||||
    suffixes*: seq[string]
 | 
					 | 
				
			||||||
    language*: Option[string]
 | 
					 | 
				
			||||||
    isPText*: bool # true if VALUE=ptext, false by default
 | 
					 | 
				
			||||||
    xParams*: seq[VC_XParam]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Nickname* = ref object of VC3_SimpleTextProperty
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Photo* = ref object of VC3_BinaryProperty
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Bday* = ref object of VC3_Property
 | 
					 | 
				
			||||||
    valueType*: Option[string] # date / date-time
 | 
					 | 
				
			||||||
    value*: DateTime
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_AdrTypes* = enum
 | 
					 | 
				
			||||||
    # Standard types defined in RFC2426
 | 
					 | 
				
			||||||
    atDom = "DOM"
 | 
					 | 
				
			||||||
    atIntl = "INTL"
 | 
					 | 
				
			||||||
    atPostal = "POSTAL"
 | 
					 | 
				
			||||||
    atParcel = "PARCEL"
 | 
					 | 
				
			||||||
    atHome = "HOME"
 | 
					 | 
				
			||||||
    atWork = "WORK"
 | 
					 | 
				
			||||||
    atPref = "PREF"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Adr* = ref object of VC3_Property
 | 
					 | 
				
			||||||
    adrType*: seq[string]
 | 
					 | 
				
			||||||
    poBox*: string
 | 
					 | 
				
			||||||
    extendedAdr*: string
 | 
					 | 
				
			||||||
    streetAdr*: string
 | 
					 | 
				
			||||||
    locality*: string
 | 
					 | 
				
			||||||
    region*: string
 | 
					 | 
				
			||||||
    postalCode*: string
 | 
					 | 
				
			||||||
    country*: string
 | 
					 | 
				
			||||||
    isPText*: bool # true if VALUE=ptext, false by default
 | 
					 | 
				
			||||||
    language*: Option[string]
 | 
					 | 
				
			||||||
    xParams*: seq[VC_XParam]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Label* = ref object of VC3_SimpleTextProperty
 | 
					 | 
				
			||||||
    adrType*: seq[string]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_TelTypes* = enum
 | 
					 | 
				
			||||||
    ttHome = "HOME",
 | 
					 | 
				
			||||||
    ttWork = "WORK",
 | 
					 | 
				
			||||||
    ttPref = "PREF",
 | 
					 | 
				
			||||||
    ttVoice = "VOICE",
 | 
					 | 
				
			||||||
    ttFax = "FAX",
 | 
					 | 
				
			||||||
    ttMsg = "MSG",
 | 
					 | 
				
			||||||
    ttCell = "CELL",
 | 
					 | 
				
			||||||
    ttPager = "PAGER",
 | 
					 | 
				
			||||||
    ttBbs = "BBS",
 | 
					 | 
				
			||||||
    ttModem = "MODEM",
 | 
					 | 
				
			||||||
    ttCar = "CAR",
 | 
					 | 
				
			||||||
    ttIsdn = "ISDN",
 | 
					 | 
				
			||||||
    ttVideo = "VIDEO",
 | 
					 | 
				
			||||||
    ttPcs = "PCS"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Tel* = ref object of VC3_Property
 | 
					 | 
				
			||||||
    telType*: seq[string]
 | 
					 | 
				
			||||||
    value*: string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_EmailType* = enum
 | 
					 | 
				
			||||||
    etInternet = "INTERNET",
 | 
					 | 
				
			||||||
    etX400 = "X400"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Email* = ref object of VC3_Property
 | 
					 | 
				
			||||||
    emailType*: seq[string]
 | 
					 | 
				
			||||||
    value*: string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Mailer* = ref object of VC3_SimpleTextProperty
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_TZ* = ref object of VC3_Property
 | 
					 | 
				
			||||||
    value*: string
 | 
					 | 
				
			||||||
    isText*: bool # true if VALUE=text, false by default
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Geo* = ref object of VC3_Property
 | 
					 | 
				
			||||||
    lat*, long*: float
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Title* = ref object of VC3_SimpleTextProperty
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Role* = ref object of VC3_SimpleTextProperty
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Logo* = ref object of VC3_BinaryProperty
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Agent* = ref object of VC3_Property
 | 
					 | 
				
			||||||
    value*: string  # either an escaped vCard object, or a URI
 | 
					 | 
				
			||||||
    isInline*: bool # false if VALUE=uri, true by default
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Org* = ref object of VC3_Property
 | 
					 | 
				
			||||||
    value*: seq[string]
 | 
					 | 
				
			||||||
    isPText*: bool # true if VALUE=ptext, false by default
 | 
					 | 
				
			||||||
    language*: Option[string]
 | 
					 | 
				
			||||||
    xParams*: seq[VC_XParam]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Categories* = ref object of VC3_Property
 | 
					 | 
				
			||||||
    value*: seq[string]
 | 
					 | 
				
			||||||
    isPText*: bool # true if VALUE=ptext, false by default
 | 
					 | 
				
			||||||
    language*: Option[string]
 | 
					 | 
				
			||||||
    xParams*: seq[VC_XParam]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Note* = ref object of VC3_SimpleTextProperty
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Prodid* = ref object of VC3_SimpleTextProperty
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Rev* = ref object of VC3_Property
 | 
					 | 
				
			||||||
    valueType*: Option[string] # date / date-time
 | 
					 | 
				
			||||||
    value*: DateTime
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_SortString* = ref object of VC3_SimpleTextProperty
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Sound* = ref object of VC3_BinaryProperty
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_UID* = ref object of VC3_Property
 | 
					 | 
				
			||||||
    value*: string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_URL* = ref object of VC3_Property
 | 
					 | 
				
			||||||
    value*: string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Version* = ref object of VC3_Property
 | 
					 | 
				
			||||||
    value*: string # 3.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Class* = ref object of VC3_Property
 | 
					 | 
				
			||||||
    value*: string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_Key* = ref object of VC3_BinaryProperty
 | 
					 | 
				
			||||||
    keyType*: Option[string] # x509 / pgp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VC3_XType* = ref object of VC3_SimpleTextProperty
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VCard3* = ref object of VCard
 | 
					 | 
				
			||||||
    nextPropertyId: int
 | 
					 | 
				
			||||||
    content*: seq[VC3_Property]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const propertyCardMap: Table[VC3_PropertyName, VC_PropCardinality] = [
 | 
					const propertyCardMap: Table[VC3_PropertyName, VC_PropCardinality] = [
 | 
				
			||||||
  (pnName, vpcAtMostOne),
 | 
					  (pnName, vpcAtMostOne),
 | 
				
			||||||
  (pnProfile, vpcAtMostOne),
 | 
					  (pnProfile, vpcAtMostOne),
 | 
				
			||||||
@@ -273,8 +103,227 @@ const propertyCardMap: Table[VC3_PropertyName, VC_PropCardinality] = [
 | 
				
			|||||||
const DATE_FMT = "yyyy-MM-dd"
 | 
					const DATE_FMT = "yyyy-MM-dd"
 | 
				
			||||||
const DATETIME_FMT = "yyyy-MM-dd'T'HH:mm:sszz"
 | 
					const DATETIME_FMT = "yyyy-MM-dd'T'HH:mm:sszz"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Externally Exposed Types
 | 
				
			||||||
 | 
					## ========================
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					## The following are the object types for the vCard 3.0 implementation
 | 
				
			||||||
 | 
					## interface.
 | 
				
			||||||
 | 
					type
 | 
				
			||||||
 | 
					  VC3_Property* = ref object of RootObj
 | 
				
			||||||
 | 
					    ## Abstract base class for all vCard 3.0 property objects.
 | 
				
			||||||
 | 
					    propertyId: int
 | 
				
			||||||
 | 
					    group*: Option[string]
 | 
				
			||||||
 | 
					    name*: string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_SimpleTextProperty* = ref object of VC3_Property
 | 
				
			||||||
 | 
					    ## Abstract base class for all vCard 3.0 properties that only support the
 | 
				
			||||||
 | 
					    ## text value type (`VALUE=text` or `VALUE=ptext`).
 | 
				
			||||||
 | 
					    value*: string
 | 
				
			||||||
 | 
					    isPText*: bool ## true if VALUE=ptext, false by default
 | 
				
			||||||
 | 
					    language*: Option[string]
 | 
				
			||||||
 | 
					    xParams*: seq[VC_XParam]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_BinaryProperty* = ref object of VC3_Property
 | 
				
			||||||
 | 
					    ## Abstract base class for all vCard 3.0 properties that support the binary
 | 
				
			||||||
 | 
					    ## or uri value types (`ENCODING=b` or `VALUE=uri`).
 | 
				
			||||||
 | 
					    valueType*: Option[string] ## \
 | 
				
			||||||
 | 
					      ## binary / uri. Stored separately from ENCODING (captured in the
 | 
				
			||||||
 | 
					      ## isInline field) because the VALUE parameter is not set by default, but
 | 
				
			||||||
 | 
					      ## is allowed to be set.
 | 
				
			||||||
 | 
					    value*: string  ## \
 | 
				
			||||||
 | 
					      ## either a URI or bit sequence, both stored as string
 | 
				
			||||||
 | 
					    binaryType*: Option[string] ## \
 | 
				
			||||||
 | 
					      ## if using ENCODING=b, there may also be a TYPE parameter specifying the
 | 
				
			||||||
 | 
					      ## MIME type of the binary-encoded object, which is stored here.
 | 
				
			||||||
 | 
					    isInline*: bool ## \
 | 
				
			||||||
 | 
					      ## true if ENCODING=b, false by default
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Name* = ref object of VC3_Property
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 NAME properties.
 | 
				
			||||||
 | 
					    value*: string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Profile* = ref object of VC3_Property
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 PROFILE properties.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Source* = ref object of VC3_Property
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 SOURCE properties.
 | 
				
			||||||
 | 
					    valueType*: Option[string]  ## If set, this must be set to "uri"
 | 
				
			||||||
 | 
					    value*: string              ## The URI value.
 | 
				
			||||||
 | 
					    context*: Option[string]
 | 
				
			||||||
 | 
					    xParams*: seq[VC_XParam]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Fn* = ref object of VC3_SimpleTextProperty
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 FN properties.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_N* = ref object of VC3_Property
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 N properties.
 | 
				
			||||||
 | 
					    family*: seq[string]      ## Surname / family name
 | 
				
			||||||
 | 
					    given*: seq[string]       ## First name / given name
 | 
				
			||||||
 | 
					    additional*: seq[string]  ## Additional / middle names
 | 
				
			||||||
 | 
					    prefixes*: seq[string]    ## e.g. Mr., Dr., etc.
 | 
				
			||||||
 | 
					    suffixes*: seq[string]    ## e.g. Esq., II, etc.
 | 
				
			||||||
 | 
					    language*: Option[string]
 | 
				
			||||||
 | 
					    isPText*: bool            ## true if VALUE=ptext, false by default
 | 
				
			||||||
 | 
					    xParams*: seq[VC_XParam]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Nickname* = ref object of VC3_SimpleTextProperty
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 NICKNAME properties.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Photo* = ref object of VC3_BinaryProperty
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 PHOTO properties.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Bday* = ref object of VC3_Property
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 BDAY properties.
 | 
				
			||||||
 | 
					    valueType*: Option[string] # date / date-time
 | 
				
			||||||
 | 
					    value*: DateTime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_AdrTypes* = enum
 | 
				
			||||||
 | 
					    ## Standard types for ADR values defined in RFC2426
 | 
				
			||||||
 | 
					    atDom = "DOM"
 | 
				
			||||||
 | 
					    atIntl = "INTL"
 | 
				
			||||||
 | 
					    atPostal = "POSTAL"
 | 
				
			||||||
 | 
					    atParcel = "PARCEL"
 | 
				
			||||||
 | 
					    atHome = "HOME"
 | 
				
			||||||
 | 
					    atWork = "WORK"
 | 
				
			||||||
 | 
					    atPref = "PREF"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Adr* = ref object of VC3_Property
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 ADR properties.
 | 
				
			||||||
 | 
					    adrType*: seq[string]
 | 
				
			||||||
 | 
					    poBox*: string
 | 
				
			||||||
 | 
					    extendedAdr*: string
 | 
				
			||||||
 | 
					    streetAdr*: string
 | 
				
			||||||
 | 
					    locality*: string
 | 
				
			||||||
 | 
					    region*: string
 | 
				
			||||||
 | 
					    postalCode*: string
 | 
				
			||||||
 | 
					    country*: string
 | 
				
			||||||
 | 
					    isPText*: bool             ## `true` if `VALUE=ptext`, `false` by default
 | 
				
			||||||
 | 
					    language*: Option[string]
 | 
				
			||||||
 | 
					    xParams*: seq[VC_XParam]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Label* = ref object of VC3_SimpleTextProperty
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 LABEL properties.
 | 
				
			||||||
 | 
					    adrType*: seq[string]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_TelTypes* = enum
 | 
				
			||||||
 | 
					    ## Standard types for TEL values defined in RFC2426
 | 
				
			||||||
 | 
					    ttHome = "HOME",
 | 
				
			||||||
 | 
					    ttWork = "WORK",
 | 
				
			||||||
 | 
					    ttPref = "PREF",
 | 
				
			||||||
 | 
					    ttVoice = "VOICE",
 | 
				
			||||||
 | 
					    ttFax = "FAX",
 | 
				
			||||||
 | 
					    ttMsg = "MSG",
 | 
				
			||||||
 | 
					    ttCell = "CELL",
 | 
				
			||||||
 | 
					    ttPager = "PAGER",
 | 
				
			||||||
 | 
					    ttBbs = "BBS",
 | 
				
			||||||
 | 
					    ttModem = "MODEM",
 | 
				
			||||||
 | 
					    ttCar = "CAR",
 | 
				
			||||||
 | 
					    ttIsdn = "ISDN",
 | 
				
			||||||
 | 
					    ttVideo = "VIDEO",
 | 
				
			||||||
 | 
					    ttPcs = "PCS"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Tel* = ref object of VC3_Property
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 TEL properties.
 | 
				
			||||||
 | 
					    telType*: seq[string]
 | 
				
			||||||
 | 
					    value*: string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_EmailType* = enum
 | 
				
			||||||
 | 
					    ## Standard types for EMAIL values defined in RFC2426
 | 
				
			||||||
 | 
					    etInternet = "INTERNET",
 | 
				
			||||||
 | 
					    etX400 = "X400"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Email* = ref object of VC3_Property
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 EMAIL properties.
 | 
				
			||||||
 | 
					    emailType*: seq[string]
 | 
				
			||||||
 | 
					    value*: string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Mailer* = ref object of VC3_SimpleTextProperty
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 MAILER properties.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_TZ* = ref object of VC3_Property
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 TZ properties.
 | 
				
			||||||
 | 
					    value*: string
 | 
				
			||||||
 | 
					    isText*: bool ## `true` if `VALUE=text`, `false` by default
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Geo* = ref object of VC3_Property
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 GEO properties.
 | 
				
			||||||
 | 
					    lat*, long*: float
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Title* = ref object of VC3_SimpleTextProperty
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 TITLE properties.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Role* = ref object of VC3_SimpleTextProperty
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 ROLE properties.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Logo* = ref object of VC3_BinaryProperty
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 LOGO properties.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Agent* = ref object of VC3_Property
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 AGENT properties.
 | 
				
			||||||
 | 
					    value*: string  ## either an escaped vCard object, or a URI
 | 
				
			||||||
 | 
					    isInline*: bool ## `false` if `VALUE=uri`, `true` by default
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Org* = ref object of VC3_Property
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 ORG properties.
 | 
				
			||||||
 | 
					    value*: seq[string]
 | 
				
			||||||
 | 
					    isPText*: bool ## `true` if `VALUE=ptext`, `false` by default
 | 
				
			||||||
 | 
					    language*: Option[string]
 | 
				
			||||||
 | 
					    xParams*: seq[VC_XParam]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Categories* = ref object of VC3_Property
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 CATEGORIES properties.
 | 
				
			||||||
 | 
					    value*: seq[string]
 | 
				
			||||||
 | 
					    isPText*: bool ## `true` if `VALUE=ptext`, `false` by default
 | 
				
			||||||
 | 
					    language*: Option[string]
 | 
				
			||||||
 | 
					    xParams*: seq[VC_XParam]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Note* = ref object of VC3_SimpleTextProperty
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 NOTE properties.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Prodid* = ref object of VC3_SimpleTextProperty
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 PRODID properties.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Rev* = ref object of VC3_Property
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 REV properties.
 | 
				
			||||||
 | 
					    valueType*: Option[string] # date / date-time
 | 
				
			||||||
 | 
					    value*: DateTime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_SortString* = ref object of VC3_SimpleTextProperty
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 SORT-STRING properties.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Sound* = ref object of VC3_BinaryProperty
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 SOUND properties.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_UID* = ref object of VC3_Property
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 UID properties.
 | 
				
			||||||
 | 
					    value*: string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_URL* = ref object of VC3_Property
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 URL properties.
 | 
				
			||||||
 | 
					    value*: string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Version* = ref object of VC3_Property
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 VERSION properties.
 | 
				
			||||||
 | 
					    value*: string # 3.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Class* = ref object of VC3_Property
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 CLASS properties.
 | 
				
			||||||
 | 
					    value*: string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_Key* = ref object of VC3_BinaryProperty
 | 
				
			||||||
 | 
					    ## Concrete class representing vCard 3.0 KEY properties.
 | 
				
			||||||
 | 
					    keyType*: Option[string] # x509 / pgp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VC3_XType* = ref object of VC3_SimpleTextProperty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VCard3* = ref object of VCard
 | 
				
			||||||
 | 
					    ## Concrete class implementing the vCard 3.0 type.
 | 
				
			||||||
 | 
					    nextPropertyId: int
 | 
				
			||||||
 | 
					    content*: seq[VC3_Property]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Internal Utility/Implementation
 | 
					# Internal Utility/Implementation
 | 
				
			||||||
# =============================================================================
 | 
					# ===============================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template takePropertyId(vc3: VCard3): int =
 | 
					template takePropertyId(vc3: VCard3): int =
 | 
				
			||||||
  vc3.nextPropertyId += 1
 | 
					  vc3.nextPropertyId += 1
 | 
				
			||||||
@@ -291,8 +340,8 @@ func namesForProp(prop: VC3_PropertyName):
 | 
				
			|||||||
    ident("newVC3_" & name),
 | 
					    ident("newVC3_" & name),
 | 
				
			||||||
    ident(name.toLower))
 | 
					    ident(name.toLower))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Initializers
 | 
					## Initializers
 | 
				
			||||||
# =============================================================================
 | 
					## ============
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func newVC3_Name*(value: string, group = none[string]()): VC3_Name =
 | 
					func newVC3_Name*(value: string, group = none[string]()): VC3_Name =
 | 
				
			||||||
  return VC3_Name(name: "NAME", value: value, group: group)
 | 
					  return VC3_Name(name: "NAME", value: value, group: group)
 | 
				
			||||||
@@ -585,14 +634,17 @@ func newVC3_XType*(
 | 
				
			|||||||
    value, language, isPText, xParams, group)
 | 
					    value, language, isPText, xParams, group)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Accessors
 | 
					# Accessors
 | 
				
			||||||
# =============================================================================
 | 
					# =========
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func forGroup*(vc: openarray[VC3_Property], group: string): seq[VC3_Property] =
 | 
					func forGroup*(vc3: VCard3, group: string): seq[VC3_Property] =
 | 
				
			||||||
  return vc.filterIt(it.group.isSome and it.group.get == group)
 | 
					  ## Return all properties defined on the vCard associated with the given
 | 
				
			||||||
 | 
					  ## group.
 | 
				
			||||||
 | 
					  return vc3.content.filterIt(it.group.isSome and it.group.get == group)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func groups*(vc: openarray[VC3_Property]): seq[string] =
 | 
					func groups*(vc3: VCard3): seq[string] =
 | 
				
			||||||
 | 
					  ## Return a list of all groups present on the vCard
 | 
				
			||||||
  result = @[]
 | 
					  result = @[]
 | 
				
			||||||
  for c in vc:
 | 
					  for c in vc3.content:
 | 
				
			||||||
    if c.group.isSome:
 | 
					    if c.group.isSome:
 | 
				
			||||||
      let grp = c.group.get
 | 
					      let grp = c.group.get
 | 
				
			||||||
      if not result.contains(grp): result.add(grp)
 | 
					      if not result.contains(grp): result.add(grp)
 | 
				
			||||||
@@ -610,8 +662,11 @@ macro genPropertyAccessors(
 | 
				
			|||||||
      let funcDef = genAstOpt({kDirtyTemplate}, funcName, typeName):
 | 
					      let funcDef = genAstOpt({kDirtyTemplate}, funcName, typeName):
 | 
				
			||||||
        func funcName*(vc3: VCard3): Option[typeName] =
 | 
					        func funcName*(vc3: VCard3): Option[typeName] =
 | 
				
			||||||
          result = findFirst[typeName](vc3.content)
 | 
					          result = findFirst[typeName](vc3.content)
 | 
				
			||||||
 | 
					      funcDef[6].insert(0, newCommentStmtNode(
 | 
				
			||||||
 | 
					        "Return the single " & $pn & " property (if present)."))
 | 
				
			||||||
      result.add(funcDef)
 | 
					      result.add(funcDef)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    of vpcExactlyOne:
 | 
					    of vpcExactlyOne:
 | 
				
			||||||
      let funcDef = genAstOpt({kDirtyTemplate}, funcName, pn, typeName):
 | 
					      let funcDef = genAstOpt({kDirtyTemplate}, funcName, pn, typeName):
 | 
				
			||||||
        func funcName*(vc3: VCard3): typeName =
 | 
					        func funcName*(vc3: VCard3): typeName =
 | 
				
			||||||
@@ -621,18 +676,23 @@ macro genPropertyAccessors(
 | 
				
			|||||||
              "VCard should have exactly one $# property, but $# were found" %
 | 
					              "VCard should have exactly one $# property, but $# were found" %
 | 
				
			||||||
                [$pn, $props.len])
 | 
					                [$pn, $props.len])
 | 
				
			||||||
          result = props[0]
 | 
					          result = props[0]
 | 
				
			||||||
 | 
					      funcDef[6].insert(0, newCommentStmtNode(
 | 
				
			||||||
 | 
					        "Return the " & $pn & " property."))
 | 
				
			||||||
      result.add(funcDef)
 | 
					      result.add(funcDef)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    of vpcAtLeastOne, vpcAny:
 | 
					    of vpcAtLeastOne, vpcAny:
 | 
				
			||||||
      let funcDef = genAstOpt({kDirtyTemplate}, funcName, typeName):
 | 
					      let funcDef = genAstOpt({kDirtyTemplate}, funcName, typeName):
 | 
				
			||||||
        func funcName*(vc3: VCard3): seq[typeName] =
 | 
					        func funcName*(vc3: VCard3): seq[typeName] =
 | 
				
			||||||
          result = findAll[typeName](vc3.content)
 | 
					          result = findAll[typeName](vc3.content)
 | 
				
			||||||
 | 
					      funcDef[6].insert(0, newCommentStmtNode(
 | 
				
			||||||
 | 
					        "Return all instances of the " & $pn & " property."))
 | 
				
			||||||
      result.add(funcDef)
 | 
					      result.add(funcDef)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
genPropertyAccessors(propertyCardMap.pairs.toSeq -->
 | 
					genPropertyAccessors(propertyCardMap.pairs.toSeq -->
 | 
				
			||||||
  filter(not [pnVersion].contains(it[0])))
 | 
					  filter(not [pnVersion].contains(it[0])))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func version*(vc3: VCard3): VC3_Version =
 | 
					func version*(vc3: VCard3): VC3_Version =
 | 
				
			||||||
 | 
					  ## Return the VERSION property.
 | 
				
			||||||
  let found = findFirst[VC3_Version](vc3.content)
 | 
					  let found = findFirst[VC3_Version](vc3.content)
 | 
				
			||||||
  if found.isSome: return found.get
 | 
					  if found.isSome: return found.get
 | 
				
			||||||
  else: return VC3_Version(
 | 
					  else: return VC3_Version(
 | 
				
			||||||
@@ -641,13 +701,17 @@ func version*(vc3: VCard3): VC3_Version =
 | 
				
			|||||||
    name: "VERSION",
 | 
					    name: "VERSION",
 | 
				
			||||||
    value: "3.0")
 | 
					    value: "3.0")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func xTypes*(c: openarray[VC3_Property]): seq[VC3_XType] = findAll[VC3_XType](c)
 | 
					func xTypes*(vc3: VCard3): seq[VC3_XType] = findAll[VC3_XType](vc3.content)
 | 
				
			||||||
func xTypes*(vc3: VCard3): seq[VC3_XType] = vc3.content.xTypes
 | 
					  ## Return all extended properties (starting with `x-`).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Setters
 | 
					# Setters
 | 
				
			||||||
# =============================================================================
 | 
					# =============================================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func set*[T: VC3_Property](vc3: VCard3, content: varargs[T]): void =
 | 
					func set*[T: VC3_Property](vc3: VCard3, content: varargs[T]): void =
 | 
				
			||||||
 | 
					  ## Set the given property on the vCard. This will replace the first existing
 | 
				
			||||||
 | 
					  ## instance of this property or append this as a new property if there is no
 | 
				
			||||||
 | 
					  ## existing instance of this property. This is useful for properties which
 | 
				
			||||||
 | 
					  ## should only appear once within the vCard.
 | 
				
			||||||
  for c in content:
 | 
					  for c in content:
 | 
				
			||||||
    var nc = c
 | 
					    var nc = c
 | 
				
			||||||
    let existingIdx = vc3.content.indexOfIt(it of T)
 | 
					    let existingIdx = vc3.content.indexOfIt(it of T)
 | 
				
			||||||
@@ -659,6 +723,7 @@ func set*[T: VC3_Property](vc3: VCard3, content: varargs[T]): void =
 | 
				
			|||||||
      vc3.content[existingIdx] = nc
 | 
					      vc3.content[existingIdx] = nc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func add*[T: VC3_Property](vc3: VCard3, content: varargs[T]): void =
 | 
					func add*[T: VC3_Property](vc3: VCard3, content: varargs[T]): void =
 | 
				
			||||||
 | 
					  ## Add a new property to the end of the vCard.
 | 
				
			||||||
  for c in content:
 | 
					  for c in content:
 | 
				
			||||||
    var nc = c
 | 
					    var nc = c
 | 
				
			||||||
    nc.propertyId = vc3.takePropertyId
 | 
					    nc.propertyId = vc3.takePropertyId
 | 
				
			||||||
@@ -811,6 +876,7 @@ proc serialize(c: VC3_Property): string =
 | 
				
			|||||||
    return serialize(cast[VC3_BinaryProperty](c))
 | 
					    return serialize(cast[VC3_BinaryProperty](c))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
proc `$`*(vc3: VCard3): string =
 | 
					proc `$`*(vc3: VCard3): string =
 | 
				
			||||||
 | 
					  ## Serialize a vCard into its textual representation.
 | 
				
			||||||
  result = "BEGIN:VCARD" & CRLF
 | 
					  result = "BEGIN:VCARD" & CRLF
 | 
				
			||||||
  result &= "VERSION:3.0" & CRLF
 | 
					  result &= "VERSION:3.0" & CRLF
 | 
				
			||||||
  for c in vc3.content.filterIt(not (it of VC3_Version)):
 | 
					  for c in vc3.content.filterIt(not (it of VC3_Version)):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,8 @@ import zero_functional
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#from std/sequtils import toSeq
 | 
					#from std/sequtils import toSeq
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ./private/[common, lexer]
 | 
					import ./common
 | 
				
			||||||
 | 
					import ./private/[internals, lexer]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type
 | 
					type
 | 
				
			||||||
  VC4_ValueType* = enum
 | 
					  VC4_ValueType* = enum
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user