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])
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])
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)
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![],
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)
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)
(U{ pcs: updates,
log: vec![ LogEntry {
- html: format!("The facilitaror added {} pieces", count),
+ html: Html(format!("The facilitaror added {} pieces",
+ count)),
}],
raw: None },
Fine)
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")
}]);
}
pub piece: PieceId,
pub pos: Pos,
pub face: FaceId,
- pub desc_html: String,
+ pub desc_html: Html,
}
#[derive(Debug,Copy,Clone,Serialize,Deserialize)]
#[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 ----------
#[derive(Debug,Serialize,Deserialize)]
pub struct LogEntry {
- pub html : String,
+ pub html : Html,
}
// ---------- piece trait, and rendering ----------
#[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 {
}
}
+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
}
}
}
- pub fn describe_html(&self, pri : &PieceRenderInstructions) -> String {
+ pub fn describe_html(&self, pri : &PieceRenderInstructions) -> Html {
self.p.describe_html(Some(pri.face))
}
}
pub type OE = OnlineError;
pub type SvgData = Vec<u8>;
-pub type Colour = String;
+pub type Colour = Html;
#[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,
}
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)]
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" |
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)]
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)?;
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)]
[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)]
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>)>,
if !RE.is_match(s) {
throw!(SpecError::UnsupportedColourSpec);
}
- spec.0.clone()
+ Html(spec.0.clone())
}
}
}
#[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,
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(_,_) => {
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>