mapping to structs

 7th February 2024 at 2:19pm

HTML entities and parsing

I started by studying the special characters in "Conan Osíris". To make sure this is not any malfunction in Scheme, I did the HTTP request in the browser to check that the response is encoded this way. Apparently, these are HTML entities.

In the meantime, and with the help of the (always so nice) Chicken Scheme IRC channel, we found out that the response JSON should not have included HTML entities, and also that the method for decoding these, using Chicken's html->sxml function from the html-parser library, doesn't account for many special symbols.

In any case, I think I'll try and alert the API makers to the (possibly unintended) presence of HTML entities in their responses; in the meantime, I can go on with my work.

creating struct instances with maps

Working inside moving buses is not great. In any case, I managed to get some work done.

With the records vector in hand, I tried a map to create struct objects. But, in fact, I did not have a very clear idea how to create struct instances.

The Chicken Scheme documentation was, as ever, a great help.

For now, my struct will only have two fields.

; creates a record-info struct
; YEAR value must be fetched from another request
(defstruct record-info artist title)

To create the instances from the vector, I converted it into a list (as map only applies to list objects). I'm sure there are other ways, but this worked. assoc is also very useful in retrieving values from (key . value) objects, which is what constitutes the records vector.

; creates a list of record-info objects from a record vector
; Vector -> List[record-info]
(define (produces-record-info-from-vector-of-records rec-vec)
  (map (lambda (rec) (make-record-info 
					   artist: (cdr (assoc 'artistName rec))
					   title: (cdr (assoc 'name rec))))
	   (vector->list rec-vec)))
		 
(test "creates a list of record-info structs from a vector of records"
	  #t 
	  (every record-info? 
		    (produces-record-info-from-vector-of-records records)))

This is some good progress! I could already move onto creating the tiddlers with just this information. And that's what I'll do, to then track back and rework this algorithm.

In particular, I noticed every record includes a groupId field, which will lead, on another API route, to a more complete information about records. Then I'll also fetch the year and the album art of each record.


Since I'm adding another API route, this is a perfect opportunity to wonder about possible refactors. Below is a function that creates a single API route URI.

; manually creates URI string for snatched API request
; Integer Integer -> String
(define (build-snatched-uri user-id limit)
  (string-append SNATCHED-URI
				 "&id=" user-id
				 "&type=snatched"
				 "&limit=" limit))
(test EXPECTED-SNATCHED-URI
	  (build-snatched-uri (number->string USER-ID) (number->string NBR-OF-SNATCHES)))

The new route doesn't need the type and limit fields. I'm thinking that this function could be refactored into a generic build-uri-from-fields, that would take a path and a list of fields to concatenate.

; API REQUESTS

; concatenates all fields into a string
; List[(key . value)] -> String
(define (create-field-string fields)
  (if (null? fields)
	""
	(let* ((key (caar fields))
		   (value (cdar fields))
		   (rest (create-field-string (cdr fields))))
	   (string-append "&" key "=" value rest))))

; manually creates URI string for an API request
; String List[(key . value)] -> String
(define (build-uri-with-fields base-uri fields)
  (string-append base-uri
				 create-field-string fields))
(test EXPECTED-SNATCHED-URI
	  (build-uri-with-fields SNATCHED-URI
							 (list (list "id" (number->string USER-ID))
								   (list "type" "snatched")
							       (list "limit" (number->string NBR-OF-SNATCHES)))))
(test EXPECTED-GROUP-URI
	  (build-uri-with-fields GROUP-URI
							 (number->string 1234)))

I'm still unsure about the let*, but this seems like the proper way to do this. The code doesn't work, but the bus ride is almost done.