chiark / gitweb /
html
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 6 Sep 2020 10:53:50 +0000 (11:53 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 6 Sep 2020 10:53:50 +0000 (11:53 +0100)
src/api.rs
src/cmdlistener.rs
src/commands.rs
src/gamestate.rs
src/imports.rs
src/pieces.rs
src/session.rs
src/spec.rs
src/updates.rs
templates/session.tera

index 75e8f0efc1b7789d5e1678555f1ab88473f74135..5809498ac8d0e25adff2187cb60b44d27e7c8c64 100644 (file)
@@ -149,9 +149,9 @@ impl ApiPieceOp for ApiPieceGrab {
     let update = PieceUpdateOp::Modify(());
 
     let logent = LogEntry {
-      html : format!("{} grasped {}",
+      html : Html(format!("{} grasped {}",
                      &htmlescape::encode_minimal(&pl.nick),
-                     pc.describe_html(&lens.log_pri(piece, pc))),
+                     pc.describe_html(&lens.log_pri(piece, pc)).0)),
     };
 
     (update, vec![logent])
@@ -181,9 +181,9 @@ impl ApiPieceOp for ApiPieceUngrab {
     let update = PieceUpdateOp::Modify(());
 
     let logent = LogEntry {
-      html : format!("{} released {}",
+      html : Html(format!("{} released {}",
                      &htmlescape::encode_minimal(&pl.nick),
-                     pc.describe_html(&lens.log_pri(piece, pc))),
+                     pc.describe_html(&lens.log_pri(piece, pc)).0)),
     };
 
     (update, vec![logent])
index ee17ef239aee4db4dd35d44df175bf35a7c65c95..2ae738cf42eb3390caaad6e992b80361ab8fd856 100644 (file)
@@ -134,7 +134,8 @@ fn execute_game_insn(cs: &CommandStream,
       ig.gs.table_size = size;
       (U{ pcs: vec![],
           log: vec![ LogEntry {
-            html: format!("The table was resized to {}x{}", size[0], size[1]),
+            html: Html(format!("The table was resized to {}x{}",
+                               size[0], size[1])),
           }],
           raw: Some(vec![ PreparedUpdateEntry::SetTableSize(size) ]) },
        Fine)
@@ -145,8 +146,8 @@ fn execute_game_insn(cs: &CommandStream,
         Err(ME::AlreadyExists)?;
       }
       let logentry = LogEntry {
-        html: format!("The facilitator added a player: {}",
-                      htmlescape::encode_minimal(&pl.nick)),
+        html: Html(format!("The facilitator added a player: {}",
+                      htmlescape::encode_minimal(&pl.nick))),
       };
       let (player, logentry) = ig.player_new(pl, logentry)?;
       (U{ pcs: vec![],
@@ -168,8 +169,8 @@ fn execute_game_insn(cs: &CommandStream,
       let old_state = ig.player_remove(player)?;
       (U{ pcs: vec![],
           log: old_state.iter().map(|pl| LogEntry {
-            html: format!("The facilitator removed a player: {}",
-                          htmlescape::encode_minimal(&pl.nick)),
+            html: Html(format!("The facilitator removed a player: {}",
+                          htmlescape::encode_minimal(&pl.nick))),
           }).collect(),
           raw: None},
        Fine)
@@ -222,8 +223,8 @@ fn execute_game_insn(cs: &CommandStream,
       p.p.delete_hook(&p, gs);
       (U{ pcs: vec![(piece, PieceUpdateOp::Delete())],
           log: vec![ LogEntry {
-            html: format!("A piece {} was removed from the game",
-                          desc_html),
+            html: Html(format!("A piece {} was removed from the game",
+                          desc_html.0)),
           }],
           raw: None },
        Fine)
@@ -260,7 +261,8 @@ fn execute_game_insn(cs: &CommandStream,
 
       (U{ pcs: updates,
           log: vec![ LogEntry {
-            html: format!("The facilitaror added {} pieces", count),
+            html: Html(format!("The facilitaror added {} pieces",
+                               count)),
           }],
           raw: None },
        Fine)
@@ -365,7 +367,7 @@ impl UpdateHandler {
 
         if bulk.logs {
           buf.log_updates(vec![LogEntry {
-            html: "The facilitator (re)configured the game".to_owned(),
+            html: Html::lit("The facilitator (re)configured the game")
           }]);
         }
 
index 902d3aab7ae7be66a2b31bd19492d0a70782430c..143dbe403675c26c77b5db9393640c5093a44ce7 100644 (file)
@@ -61,7 +61,7 @@ pub struct MgmtGamePieceInfo {
   pub piece: PieceId,
   pub pos: Pos,
   pub face: FaceId,
-  pub desc_html: String,
+  pub desc_html: Html,
 }
 
 #[derive(Debug,Copy,Clone,Serialize,Deserialize)]
index 1b3b0cbedfff9d183bbaa1d98c1b58ccb944c017..c93fa041fc79ffa0ba78b67c7308655fe3391b01 100644 (file)
@@ -22,6 +22,10 @@ visible_slotmap_key!{ VisiblePieceId('.') }
 #[serde(try_from="f64")]
 pub struct ZCoord(pub f64);
 
+#[derive(Clone,Debug,Serialize,Deserialize)]
+#[serde(transparent)]
+pub struct Html (pub String);
+
 pub const DEFAULT_TABLE_SIZE : Pos = [ 400, 200 ];
 
 // ---------- general data types ----------
@@ -65,7 +69,7 @@ pub struct PlayerState {
 
 #[derive(Debug,Serialize,Deserialize)]
 pub struct LogEntry {
-  pub html : String,
+  pub html : Html,
 }
 
 // ---------- piece trait, and rendering ----------
@@ -77,18 +81,18 @@ type SE = SVGProcessingError;
 #[typetag::serde]
 pub trait Piece : Send + Debug {
   // #[throws] doesn't work here for some reason
-  fn svg_piece(&self, f: &mut String, pri: &PieceRenderInstructions) -> IR;
+  fn svg_piece(&self, f: &mut Html, pri: &PieceRenderInstructions) -> IR;
 
   #[throws(IE)]
-  fn surround_path(&self, pri : &PieceRenderInstructions) -> String;
+  fn surround_path(&self, pri : &PieceRenderInstructions) -> Html;
 
-  fn svg_x_defs(&self, f: &mut String, pri : &PieceRenderInstructions) -> IR;
+  fn svg_x_defs(&self, f: &mut Html, pri : &PieceRenderInstructions) -> IR;
 
   #[throws(IE)]
   fn thresh_dragraise(&self, pri : &PieceRenderInstructions)
                       -> Option<Coord>;
 
-  fn describe_html(&self, face : Option<FaceId>) -> String;
+  fn describe_html(&self, face : Option<FaceId>) -> Html;
 
   fn delete_hook(&self, _p: &PieceState, _gs: &mut GameState)
                  -> ExecuteGameChangeUpdates { 
@@ -170,26 +174,30 @@ impl ClampTable for Pos {
   }
 }
 
+impl Html {
+  pub fn lit(s: &str) -> Self { Html(s.to_owned()) }
+}
+
 // ---------- game state - rendering etc. ----------
 
 impl PieceState {
   #[throws(IE)]
-  pub fn make_defs(&self, pri : &PieceRenderInstructions) -> String {
+  pub fn make_defs(&self, pri : &PieceRenderInstructions) -> Html {
     let pr = self;
-    let mut defs = String::new();
+    let mut defs = Html(String::new());
     let dragraise = match pr.p.thresh_dragraise(pri)? {
       Some(n) if n < 0 => throw!(SE::NegativeDragraise),
       Some(n) => n,
       None => -1,
     };
-    write!(defs,
+    write!(&mut defs.0,
            r##"<g id="piece{}" data-dragraise="{}">"##,
            pri.id, dragraise)?;
     pr.p.svg_piece(&mut defs, &pri)?;
-    write!(defs, r##"</g>"##)?;
-    write!(defs,
+    write!(&mut defs.0, r##"</g>"##)?;
+    write!(&mut defs.0,
            r##"<path id="surround{}" d="{}"/>"##,
-           pri.id, pr.p.surround_path(&pri)?)?;
+           pri.id, pr.p.surround_path(&pri)?.0)?;
     pr.p.svg_x_defs(&mut defs, &pri)?;
     defs
   }
@@ -206,7 +214,7 @@ impl PieceState {
     }
   }
 
-  pub fn describe_html(&self, pri : &PieceRenderInstructions) -> String {
+  pub fn describe_html(&self, pri : &PieceRenderInstructions) -> Html {
     self.p.describe_html(Some(pri.face))
   }
 }
index 18b3d3a3f96635407c166383d365c401efcc852a..fb2aca42e65a1019b228e9343662e3a170cc7d62 100644 (file)
@@ -95,4 +95,4 @@ pub type AE = anyhow::Error;
 pub type OE = OnlineError;
 
 pub type SvgData = Vec<u8>;
-pub type Colour = String;
+pub type Colour = Html;
index 092397ff9400616d70f97d7a4feb972280df022f..8203a38b5874041bc26165ef35633f20b4bd770a 100644 (file)
@@ -7,9 +7,9 @@ type ColourMap = IndexVec<FaceId,Colour>;
 #[derive(Debug,Serialize,Deserialize)]
 // todo: this serialisation is rather large
 struct SimpleShape {
-  desc : String,
-  path : String,
-  scaled_path : String,
+  desc : Html,
+  path : Html,
+  scaled_path : Html,
   approx_dia : Coord,
   colours : ColourMap,
 }
@@ -36,7 +36,7 @@ type IE = InternalError;
 type SE = SVGProcessingError;
 
 #[throws(SE)]
-pub fn svg_rescale_path(input: &str, scale: f64) -> String {
+pub fn svg_rescale_path(input: &Html, scale: f64) -> Html {
   type BM = u64;
   type BI = u32;
   #[derive(Debug,Copy,Clone)]
@@ -61,7 +61,7 @@ pub fn svg_rescale_path(input: &str, scale: f64) -> String {
   let mut map = ALWAYS_MAP;
   let mut first = iter::once(());
 
-  for w in input.split_ascii_whitespace() {
+  for w in input.0.split_ascii_whitespace() {
     if first.next().is_none() { write!(&mut out, " ")?; }
     match w {
       "L" | "l" | "M" | "m" |
@@ -80,20 +80,20 @@ pub fn svg_rescale_path(input: &str, scale: f64) -> String {
     write!(&mut out, "{}", w)?;
   }
 
-  trace!("rescaled by {}: {} as {}",scale,&input,&out);
-  out
+  trace!("rescaled by {}: {:?} as {:?}",scale,input,&out);
+  Html(out)
 }
 
 #[typetag::serde]
 impl Piece for SimpleShape {
   #[throws(IE)]
-  fn svg_piece(&self, f: &mut String, pri: &PieceRenderInstructions) {
-    write!(f, r##"<path fill="{}" d="{}"/>"##,
-           self.colours[pri.face],
-           &self.path)?;
+  fn svg_piece(&self, f: &mut Html, pri: &PieceRenderInstructions) {
+    write!(&mut f.0, r##"<path fill="{}" d="{}"/>"##,
+           self.colours[pri.face].0,
+           &self.path.0)?;
   }
   #[throws(IE)]
-  fn surround_path(&self, _pri : &PieceRenderInstructions) -> String {
+  fn surround_path(&self, _pri : &PieceRenderInstructions) -> Html {
     self.scaled_path.clone()
   }
   #[throws(IE)]
@@ -102,19 +102,19 @@ impl Piece for SimpleShape {
     Some(self.approx_dia / 2)
   }
   #[throws(IE)]
-  fn svg_x_defs(&self, _f: &mut String, _pri : &PieceRenderInstructions) {
+  fn svg_x_defs(&self, _f: &mut Html, _pri : &PieceRenderInstructions) {
   }
-  fn describe_html(&self, face : Option<FaceId>) -> String {
-    if let Some(face) = face {
-      format!("a {} {}", self.colours[face], self.desc)
+  fn describe_html(&self, face : Option<FaceId>) -> Html {
+    Html(if let Some(face) = face {
+      format!("a {} {}", self.colours[face].0, self.desc.0)
     } else {
-      format!("a {}", self.desc)
-    }
+      format!("a {}", self.desc.0)
+    })
   }
 }
 
 impl SimpleShape {
-  fn new_from_path(desc: String, path: String, approx_dia: Coord,
+  fn new_from_path(desc: Html, path: Html, approx_dia: Coord,
                    faces: &IndexVec<FaceId,ColourSpec>)
                    -> Result<Box<dyn Piece>,SpecError> {
     let scaled_path = svg_rescale_path(&path, SELECT_SCALE)?;
@@ -141,12 +141,13 @@ fn simple_resolve_spec_face(faces: &IndexSlice<FaceId,[ColourSpec]>,
 impl PieceSpec for piece_specs::Disc {
   #[throws(SpecError)]
   fn load(&self) -> Box<dyn Piece> {
-    let unit_path =
+    let unit_path = Html::lit(
       "M 0 1  a 1 1 0 1 0 0 -2 \
-              a 1 1 0 1 0 0  2  z";
+              a 1 1 0 1 0 0  2  z"
+    );
     let scale = (self.diam as f64) * 0.5;
     let path = svg_rescale_path(&unit_path, scale)?;
-    SimpleShape::new_from_path("circle".to_owned(), path, self.diam,
+    SimpleShape::new_from_path(Html::lit("circle"), path, self.diam,
                                &self.faces)?
   }
   #[throws(SpecError)]
@@ -164,9 +165,9 @@ impl PieceSpec for piece_specs::Square {
       [x, y] => (x,y),
       _ => throw!(SpecError::ImproperSizeSpec),
     };
-    let path = format!("M {} {} h {} v {} h {} z",
-                       -(x as f64)*0.5, -(y as f64)*0.5, x, y, -x);
-    SimpleShape::new_from_path("square".to_owned(), path, (x+y+1)/2,
+    let path = Html(format!("M {} {} h {} v {} h {} z",
+                            -(x as f64)*0.5, -(y as f64)*0.5, x, y, -x));
+    SimpleShape::new_from_path(Html::lit("square"), path, (x+y+1)/2,
                                &self.faces)?
   }
   #[throws(SpecError)]
index 26a486b51f957793bcb830675199e2a74c35da77..5b66144fdd4314ab110ddac4e332a6982a46f3fb 100644 (file)
@@ -9,7 +9,7 @@ struct SessionRenderContext {
   gen : Generation,
   table_size : Pos,
   uses : Vec<SessionPieceContext>,
-  defs : Vec<(VisiblePieceId,String)>,
+  defs : Vec<(VisiblePieceId,Html)>,
   nick : String,
   load : String,
   log : Vec<(Generation,Arc<LogEntry>)>,
index d8a72cedcda546832c3886a2741fa0d2b77fa37c..4034146df59b2075149aefedbff976592deccaa0 100644 (file)
@@ -163,7 +163,7 @@ pub mod implementation {
       if !RE.is_match(s) {
         throw!(SpecError::UnsupportedColourSpec);
       }
-      spec.0.clone()
+      Html(spec.0.clone())
     }
   }
 }
index 9f736ed186bb193f8ede5a2f19b4ee759a66cddd..8fbadf27637c5ee85d3125ad2093148c106bb893 100644 (file)
@@ -51,7 +51,7 @@ pub enum PreparedUpdateEntry {
 #[derive(Debug,Clone,Serialize)]
 pub struct PreparedPieceState {
   pub pos : Pos,
-  pub svg : String,
+  pub svg : Html,
   pub held : Option<PlayerId>,
   pub z : ZCoord,
   pub zg : Generation,
@@ -126,10 +126,10 @@ impl PreparedUpdateEntry {
     match self {
       Piece { ref op, .. } => {
         50 +
-        op.new_state().map(|x| x.svg.len()).unwrap_or(0)
+        op.new_state().map(|x| x.svg.0.len()).unwrap_or(0)
       },
       Log(logent) => {
-        logent.html.as_bytes().len() * 3
+        logent.html.0.as_bytes().len() * 3
       },
       SetTableSize(_) |
       Error(_,_) => {
index ad3d5bccacb40fc7e1a95b01af6fea7a590cca13..90c136d8cdccc286e02c3d14cd79cbe289446616 100644 (file)
@@ -4,7 +4,7 @@
       data-gen="{{gen}}"
       data-load="{{ load | escape }}"
       >
-<h1>Hi {{nick}}!</h1>
+<h1>Hi {{nick | escape}}!</h1>
 <pre id="error"></pre>
 <p>
 <div id="status">nothing</div>