Qcert.Backend.SparkData




Section SparkData.

  Context {f:foreign_runtime}.
  Context {h:brand_relation_t}.
  Context {fttojs: ForeignToJavascript.foreign_to_javascript}.
  Context {ftype:foreign_type}.
  Context {fdtyping:foreign_data_typing}.
  Context {m:brand_model}.

  Definition data_to_blob (d: data): string :=
    dataToJS quotel_double d.

  Lemma dataToJS_correctly_escapes_quote_inside_string:
    dataToJS """" (dstring "abc""cde") = """abc\""cde"""%string.

  Fixpoint typed_data_to_json (d: data) (r: rtype₀): option json :=
    match d, r with
    | _, Top₀Some (jstring (data_to_blob d))
    | dunit, Unit₀Some jnil
    | dnat i, Nat₀Some (jnumber i)
    | dbool b, Bool₀Some (jbool b)
    | dstring s, String₀Some (jstring s)
    | dcoll xs, (Coll₀ et) ⇒
      let listo := map (flip typed_data_to_json et) xs in
      lift jarray (listo_to_olist listo)
    | drec fs, Rec₀ Closed ft
      let fix convert_fields ds ts :=
          match ds, ts with
          | nil, nilSome nil
          | nil, _None
          | _, nilNone
          | ((f, d)::ds), ((_, t)::ts) ⇒
            match typed_data_to_json d t, convert_fields ds ts with
            | Some json, Some tailSome ((f, json)::tail)
            | _, _None
            end
          end in
      lift (fun fieldsjobject (("$blob"%string, jstring "")
                                     :: ("$known"%string, jobject fields) :: nil))
           (convert_fields fs ft)
    | drec fs, Rec₀ Open ft
      
      let fix convert_known_fields ds ts us :=
          match ts, ds with
          
          | nil, dsSome (nil, us ++ ds)
          | _, nilNone
          | ((tf, t)::ts), ((df, d)::ds) ⇒
            if string_dec tf df
            then match typed_data_to_json d t, convert_known_fields ds ts us with
                 | Some json, Some (tail, us)Some (((tf, json)::tail), us)
                 | _, _None
                 end
            else
              convert_known_fields ds ts ((df, d)::us)
          end in
      
      match convert_known_fields fs ft nil with
      | Some (typed_fields, dotdot)
        Some (jobject (("$blob"%string, jstring (data_to_blob (drec dotdot)))
                         :: ("$known"%string, jobject typed_fields) :: nil))
      | NoneNone
      end
    | dleft l, Either₀ lt rt
      lift (fun ljobject (("$left"%string, l)::("$right"%string, jnil)::nil))
           (typed_data_to_json l lt)
    | dright r, Either₀ lt rt
      lift (fun rjobject (("$left"%string, jnil)::("$right"%string, r)::nil))
           (typed_data_to_json r rt)
    | dbrand bs v, Brand₀ bts
      
      Some (jobject (("$data"%string, jstring (data_to_blob v))
                       :: ("$type"%string, jarray (map jstring bs)) :: nil))
    | _, _None
    end.

  Definition typed_data_to_json_string (d: data) (r: rtype): string :=
    match typed_data_to_json d (proj1_sig r) with
    | Some jsonjsonToJS """" json
    
    | None ⇒ "typed_data_to_json_string failed. This cannot happen. Get rid of this case by proving that typed_data_to_json always succeeds for well-typed data."
    end.

  Lemma test_json_with_toplevel_brand:
    (typed_data_to_json
       (dbrand ("Person"%string::nil)
               (drec (("age"%string, dnat 35)
                        :: ("name"%string, dstring "Fred")
                        :: ("friends"%string, dcoll ((dbrand ("Person"%string::nil)
                                                             (drec (("age"%string, dnat 42)
                                                                      :: ("name"%string, dstring "Berta")
                                                                      :: ("friends"%string, dcoll nil) :: nil))) :: nil)) :: nil)))
       (Brand₀ ("Person"%string::nil)))
    = Some
        (jobject
           (("$data"%string,
             jstring
               "{""age"": 35, ""name"": ""Fred"", ""friends"": [{""type"": [""Person""], ""data"": {""age"": 42, ""name"": ""Berta"", ""friends"": []}}]}")
              :: ("$type"%string, jarray (jstring "Person" :: nil)) :: nil)).

  Lemma test_json_with_nested_brand:
    (typed_data_to_json
       (drec (("age"%string, dnat 35)
                :: ("name"%string, dstring "Fred")
                :: ("friends"%string, dcoll ((dbrand ("Person"%string::nil)
                                                     (drec (("age"%string, dnat 42)
                                                              :: ("name"%string, dstring "Berta")
                                                              :: ("friends"%string, dcoll nil) :: nil))) :: nil)) :: nil))
       (Rec₀ Closed (("age"%string, Nat₀)
                       :: ("name"%string, String₀)
                       :: ("friends"%string, (Coll₀ (Brand₀ ("Person"%string::nil)))) :: nil)))
    = Some
        (jobject
           (("$blob"%string, jstring "")
              :: ("$known"%string,
                  jobject
                    (("age"%string, jnumber 35)
                       :: ("name"%string, jstring "Fred")
                       :: ("friends"%string,
                           jarray
                             (jobject
                                (("$data"%string, jstring "{""age"": 42, ""name"": ""Berta"", ""friends"": []}")
                                   :: ("$type"%string, jarray (jstring "Person" :: nil)) :: nil) :: nil)) :: nil)) :: nil)).


  Context {ftojson:foreign_to_JSON}.

  Definition data_to_sjson (d:data) (r:rtype) : option string :=
    
    lift (jsonToJS """") (typed_data_to_json d (proj1_sig r)).

End SparkData.